tyrav2 init

This commit is contained in:
h4570
2022-07-17 10:21:35 +02:00
parent 44c1ee4fe8
commit c8b22ff331
249 changed files with 18187 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include <tamtypes.h>
#include <vector>
#include <stdio.h>
#include <malloc.h>
#include <kernel.h>
#include <cstdlib>
#include <loadfile.h>
#include <sifrpc.h>
#include <sbv_patches.h>
#include <audsrv.h>
#include <string.h>
#include "thread/threading.hpp"
#include "audio/audio.hpp"
extern void* _gp;
void audioThread(Tyra::Audio* audio) {
while (true) audio->work();
}
namespace Tyra {
const u16 Audio::chunkSize = 4 * 1024;
const u16 Audio::threadStackSize = 2 * 1024;
Audio::Audio() {
chunkReadStatus = 0;
tyraVolume = 0;
audsrvVolume = 0;
songLoaded = false;
songPlaying = false;
songInLoop = false;
songFinished = false;
chunk = nullptr;
threadStack = nullptr;
}
Audio::~Audio() {
if (chunk) delete[] chunk;
if (threadStack) delete[] threadStack;
}
void Audio::init() {
chunk = static_cast<char*>(memalign(sizeof(char), chunkSize));
threadStack = static_cast<u8*>(memalign(sizeof(u8), threadStackSize));
initSema();
initAUDSRV();
setSongFormat();
initThread();
}
void Audio::loadSong(const char* t_path) {
if (songLoaded) unloadSong();
wav = fopen(t_path, "rb");
TYRA_ASSERT(wav != NULL, "Failed to open wav file!");
rewindSongToStart();
songLoaded = true;
TYRA_LOG("Song loaded!");
}
void Audio::playSong() {
TYRA_ASSERT(songLoaded, "Cant play song because was not loaded!");
if (songFinished) rewindSongToStart();
tyraVolume = audsrvVolume;
audsrv_set_volume(tyraVolume);
songPlaying = true;
}
void Audio::stopSong() {
tyraVolume = 0;
audsrv_set_volume(tyraVolume);
songPlaying = false;
}
/**
* Initialize AUDSRV main and ADPCM module.
* Install AUDSRV callback.
*/
void Audio::initAUDSRV() {
TYRA_LOG("Initializing AUDSRV");
int ret = audsrv_init();
TYRA_ASSERT(ret >= 0,
"AUDSRV returned error string: ", audsrv_get_error_string());
ret = audsrv_adpcm_init();
TYRA_ASSERT(ret >= 0,
"AUDSRV returned error string: ", audsrv_get_error_string());
ret = audsrv_on_fillbuf(chunkSize, (audsrv_callback_t)iSignalSema,
(void*)fillbufferSema);
TYRA_ASSERT(ret >= 0,
"AUDSRV returned error string:", audsrv_get_error_string());
TYRA_LOG("AUDSRV initialized!");
}
/** Initialize semaphore which will wait until chunk of the song is not
* finished. */
void Audio::initSema() {
TYRA_LOG("Creating audio semaphore");
sema.init_count = 0;
sema.max_count = 1;
sema.option = 0;
fillbufferSema = CreateSema(&sema);
TYRA_LOG("Audio semaphore created");
}
void Audio::initThread() {
thread.gp_reg = &_gp;
thread.func = reinterpret_cast<void*>(audioThread);
thread.stack = threadStack;
thread.stack_size = threadStackSize;
thread.initial_priority = 0x5;
threadId = CreateThread(&thread);
TYRA_ASSERT(threadId >= 0, "Create audio thread failed!");
StartThread(threadId, this);
}
/**
* Close file.
* Delete song path from memory.
*/
void Audio::unloadSong() {
songLoaded = false;
fclose(wav);
}
/** Fseek on wav. */
void Audio::rewindSongToStart() {
if (wav != NULL) fseek(wav, 0x30, SEEK_SET);
chunkReadStatus = 0;
songFinished = false;
}
/** Set WAV format to 16bit, 22050Hz, stereo. */
void Audio::setSongFormat() {
format.bits = 16;
format.freq = 22050;
format.channels = 2;
audsrv_set_format(&format);
}
void Audio::setSongVolume(const u8& t_vol) {
audsrvVolume = t_vol;
if (songPlaying) tyraVolume = t_vol;
audsrv_set_volume(tyraVolume);
}
void Audio::work() {
Threading::switchThread();
if (!songPlaying || !songLoaded) return;
if (songFinished) {
TYRA_LOG("Audio: Song finished. ");
if (songInLoop) {
TYRA_LOG("Running again.");
for (u32 i = 0; i < getSongListenersCount(); i++)
songListeners[i]->listener->onAudioFinish();
rewindSongToStart();
} else {
TYRA_LOG("Stopping song.");
stopSong();
return;
}
}
if (chunkReadStatus > 0) {
WaitSema(fillbufferSema); // wait until previous chunk wasn't finished
audsrv_play_audio(chunk, chunkReadStatus);
for (u32 i = 0; i < getSongListenersCount(); i++)
songListeners[i]->listener->onAudioTick();
}
chunkReadStatus = fread(chunk, 1, chunkSize, wav);
if (chunkReadStatus < (s32)chunkSize) songFinished = true;
}
u32 Audio::addSongListener(AudioListener* t_listener) {
AudioListenerRef* ref = new AudioListenerRef;
ref->id = rand() % 1000000;
ref->listener = t_listener;
songListeners.push_back(ref);
return ref->id;
}
void Audio::removeSongListener(const u32& t_id) {
s32 index = -1;
for (u32 i = 0; i < songListeners.size(); i++)
if (songListeners[i]->id == t_id) {
index = i;
break;
}
TYRA_ASSERT(index != -1,
"Cant remove listener because given id was not found!");
delete songListeners[index];
songListeners.erase(songListeners.begin() + index);
}
audsrv_adpcm_t* Audio::loadADPCM(const char* t_path) {
FILE* file = fopen(t_path, "rb");
fseek(file, 0, SEEK_END);
u32 adpcmFileSize = ftell(file);
u8 data[adpcmFileSize];
rewind(file);
fread(data, sizeof(u8), adpcmFileSize, file);
audsrv_adpcm_t* result = new audsrv_adpcm_t();
result->size = 0;
result->buffer = 0;
result->loop = 0;
result->pitch = 0;
result->channels = 0;
if (audsrv_load_adpcm(result, data, adpcmFileSize)) {
TYRA_LOG("AUDSRV returned error string: ", audsrv_get_error_string());
TYRA_TRAP("audsrv_load_adpcm() failed!");
}
fclose(file);
return result;
}
void Audio::playADPCM(audsrv_adpcm_t* t_adpcm) {
if (audsrv_play_adpcm(t_adpcm)) {
TYRA_LOG("AUDSRV returned error string: ", audsrv_get_error_string());
TYRA_TRAP("audsrv_play_adpcm() failed!");
}
}
void Audio::playADPCM(audsrv_adpcm_t* t_adpcm, const s8& t_ch) {
if (audsrv_ch_play_adpcm(t_ch, t_adpcm)) {
TYRA_LOG("AUDSRV returned error string: ", audsrv_get_error_string());
TYRA_TRAP("audsrv_ch_play_adpcm() failed!");
}
}
} // namespace Tyra
+39
View File
@@ -0,0 +1,39 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "engine.hpp"
namespace Tyra {
Engine::Engine() {
irx.loadDefaultDrivers();
audio.init();
pad.init();
renderer.init();
srand(time(nullptr));
}
Engine::~Engine() {}
void Engine::run(Game* t_game) {
game = t_game;
game->init();
while (true) {
realLoop();
}
}
void Engine::realLoop() {
pad.update();
game->loop();
info.update();
}
} // namespace Tyra
+63
View File
@@ -0,0 +1,63 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Wellington Carvalho <wellcoj@gmail.com>
*/
#include "debug/debug.hpp"
#include <tamtypes.h>
#include <cstdio>
#include <kernel.h>
#include <limits.h>
#include <syslimits.h>
#include <unistd.h>
#include <cstring>
#include "file/file_utils.hpp"
namespace Tyra {
FileUtils::FileUtils() {
getcwd(cwd, sizeof(cwd));
setPathInfo(cwd);
}
FileUtils::~FileUtils() {}
void FileUtils::setPathInfo(const char* path) {
char* ptr;
strcpy(this->elfName, path);
strcpy(this->elfPath, path);
ptr = strrchr(this->elfPath, '/');
if (ptr == NULL) {
ptr = strrchr(this->elfPath, '\\');
if (ptr == NULL) {
ptr = strrchr(this->elfPath, ':');
if (ptr == NULL) {
TYRA_TRAP("Did not find path! PATH: ", path);
}
}
}
ptr++;
*ptr = '\0';
}
std::string FileUtils::getCwd() {
std::string result;
char _cwd[NAME_MAX];
getcwd(_cwd, sizeof(_cwd));
result = _cwd;
return result;
}
std::string FileUtils::fromCwd(const char* file) {
auto cwd = getCwd();
return cwd + file;
}
} // namespace Tyra
+39
View File
@@ -0,0 +1,39 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020-2022, tyra - https://github.com/h4570/tyrav2
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
# Sandro Wellinator <wellcoj@gmail.com>
*/
#include "info/info.hpp"
namespace Tyra {
Info::Info() {
fps = 0;
fpsDelayer = 0;
}
Info::~Info() {}
void Info::update() {
if (fpsDelayer++ >= 4) {
fps = calcFps();
fpsDelayer = 0;
}
timer.prime();
}
float Info::calcFps() {
u32 timeDelta = timer.getTimeDelta();
if (timeDelta == 0) return -1.0F;
return 15625.0F / (float)timeDelta; // PAL
}
} // Namespace Tyra
+2
View File
@@ -0,0 +1,2 @@
$PS2SDK/iop/irx/bdm.irx
bdm_irx
+2
View File
@@ -0,0 +1,2 @@
$PS2SDK/iop/irx/bdmfs_fatfs.irx
bdmfs_fatfs_irx
+156
View File
@@ -0,0 +1,156 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Wellington Carvalho <wellcoj@gmail.com>
*/
#include "irx/irx_loader.hpp"
#include "debug/debug.hpp"
#include <stdio.h>
#include <sys/stat.h>
#include <loadfile.h>
#include <kernel.h>
#include <sifrpc.h>
#include <sbv_patches.h>
// external IRX modules
extern u8 bdm_irx[];
extern int size_bdm_irx;
extern u8 bdmfs_fatfs_irx[];
extern int size_bdmfs_fatfs_irx;
extern u8 usbd_irx[];
extern int size_usbd_irx;
extern u8 usbmass_bd_irx[];
extern int size_usbmass_bd_irx;
namespace Tyra {
IrxLoader::IrxLoader() {
SifInitRpc(0);
this->applyRpcPatches();
}
IrxLoader::~IrxLoader() {}
void IrxLoader::loadDefaultDrivers() {
this->loadAudio();
this->loadPad();
}
void IrxLoader::loadUSBDriver() { this->loadUsb(); }
/**
* @brief Apply the SBV LMB patch to allow modules to be loaded from a buffer in
* EE RAM.
*
*/
int IrxLoader::applyRpcPatches() {
int ret;
TYRA_LOG("Applying SBV Patches");
ret = sbv_patch_enable_lmb();
TYRA_ASSERT(ret >= 0,
"Failed to load Applying SBV Patches sbv_patch_enable_lmb");
ret = sbv_patch_disable_prefix_check();
TYRA_ASSERT(
ret >= 0,
"Failed to load Applying SBV Patches sbv_patch_disable_prefix_check");
ret = sbv_patch_fileio();
TYRA_ASSERT(ret >= 0, "Failed to load Applying SBV Patches sbv_patch_fileio");
TYRA_LOG("SBV Patches applyed ");
return ret;
}
int IrxLoader::loadUsb() {
int ret;
TYRA_LOG("Loading USB modules");
// Load Block Device Manager (BDM)
SifExecModuleBuffer(&bdm_irx, size_bdm_irx, 0, NULL, &ret);
TYRA_ASSERT(ret >= 0, "Failed to load module: usbhdfsd");
// Load FATFS (mass:) driver
SifExecModuleBuffer(&bdmfs_fatfs_irx, size_bdmfs_fatfs_irx, 0, NULL, &ret);
TYRA_ASSERT(ret >= 0, "Failed to load module: usbd");
// Load USB Block Device drivers
SifExecModuleBuffer(&usbd_irx, size_usbd_irx, 0, NULL, &ret);
TYRA_ASSERT(ret >= 0, "Failed to load module: usbd");
SifExecModuleBuffer(&usbmass_bd_irx, size_usbmass_bd_irx, 0, NULL, &ret);
TYRA_ASSERT(ret >= 0, "Failed to load module: usbhdfsd");
this->waitUntilUsbDeviceIsReady();
TYRA_LOG("USB/MASS modules loaded!");
return ret;
}
int IrxLoader::loadAudio() {
int ret;
TYRA_LOG("Modules loading started (LIBSD, AUDSRV)");
ret = SifLoadModule("rom0:LIBSD", 0, NULL);
TYRA_ASSERT(ret != -203, "LIBSD loading failed!");
ret = SifLoadModule("host:audsrv.irx", 0, NULL);
TYRA_ASSERT(ret != -203, "audsrv.irx loading failed!");
TYRA_LOG("Audio modules loaded");
return ret;
}
int IrxLoader::loadPad() {
int ret;
TYRA_LOG("PAD Modules loading started (SIO2MAN, PADMAN)");
ret = SifLoadModule("rom0:SIO2MAN", 0, NULL);
TYRA_ASSERT(ret >= 0,
"SifLoadModule (SIO2MAN) failed! Returned value: ", ret);
ret = SifLoadModule("rom0:PADMAN", 0, NULL);
TYRA_ASSERT(ret >= 0, "SifLoadModule (PADMAN) failed! Returned value: ", ret);
TYRA_LOG("Pad modules loaded!");
return ret;
}
void IrxLoader::delay(int count) {
int i;
int ret;
for (i = 0; i < count; i++) {
ret = 0x01000000;
while (ret--) asm("nop\nnop\nnop\nnop");
}
}
void IrxLoader::waitUntilUsbDeviceIsReady() {
struct stat buffer;
int ret = -1;
int retries = 50;
delay(5); // some delay is required by usb mass storage driver
while (ret != 0 && retries > 0) {
ret = stat("mass:/", &buffer);
/* Wait until the device is ready */
nopdelay();
retries--;
}
}
} // namespace Tyra
+2
View File
@@ -0,0 +1,2 @@
$PS2SDK/iop/irx/usbd.irx
usbd_irx
+2
View File
@@ -0,0 +1,2 @@
$PS2SDK/iop/irx/usbmass_bd.irx
usbmass_bd_irx
@@ -0,0 +1,64 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "loaders/3d/builder/mesh_builder_data.hpp"
namespace Tyra {
MeshBuilderData::MeshBuilderData() {
normalsEnabled = false;
textureCoordsEnabled = false;
manyColorsEnabled = false;
}
MeshBuilderData::~MeshBuilderData() {
if (frames) {
for (u32 i = 0; i < framesCount; i++) {
delete frames[i];
}
delete[] frames;
}
if (materials) {
for (u32 i = 0; i < materialsCount; i++) {
delete materials[i];
}
delete[] materials;
}
}
void MeshBuilderData::allocate(const u32& framesCount,
const u32& materialsCount) {
allocateFrames(framesCount);
allocateMaterials(materialsCount);
}
void MeshBuilderData::allocateFrames(const u32& count) {
this->framesCount = count;
frames = new MeshBuilderFrameData*[count];
for (u32 i = 0; i < count; i++) {
frames[i] = new MeshBuilderFrameData();
}
}
void MeshBuilderData::allocateMaterials(const u32& count) {
this->materialsCount = count;
materials = new MeshBuilderMaterialData*[count];
for (u32 i = 0; i < count; i++) {
materials[i] = new MeshBuilderMaterialData();
}
}
} // namespace Tyra
@@ -0,0 +1,49 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "loaders/3d/builder/mesh_builder_frame_data.hpp"
namespace Tyra {
MeshBuilderFrameData::MeshBuilderFrameData() {
vertices = nullptr;
normals = nullptr;
textureCoords = nullptr;
colors = nullptr;
verticesCount = 0;
textureCoordsCount = 0;
normalsCount = 0;
colorsCount = 0;
}
MeshBuilderFrameData::~MeshBuilderFrameData() {}
void MeshBuilderFrameData::allocateTextureCoords(const u32& count) {
textureCoordsCount = count;
textureCoords = new Vec4[count];
}
void MeshBuilderFrameData::allocateVertices(const u32& count) {
verticesCount = count;
vertices = new Vec4[count];
}
void MeshBuilderFrameData::allocateNormals(const u32& count) {
normalsCount = count;
normals = new Vec4[count];
}
void MeshBuilderFrameData::allocateColors(const u32& count) {
colorsCount = count;
colors = new Color[count];
}
} // namespace Tyra
@@ -0,0 +1,35 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "loaders/3d/builder/mesh_builder_material_data.hpp"
namespace Tyra {
MeshBuilderMaterialData::MeshBuilderMaterialData() {
vertexFaces = nullptr;
textureCoordFaces = nullptr;
normalFaces = nullptr;
colorFaces = nullptr;
name = "";
count = 0;
}
MeshBuilderMaterialData::~MeshBuilderMaterialData() {}
void MeshBuilderMaterialData::allocateFaces(const u32& t_count) {
vertexFaces = new u32[t_count];
textureCoordFaces = new u32[t_count];
normalFaces = new u32[t_count];
colorFaces = new u32[t_count];
count = t_count;
}
} // namespace Tyra
+194
View File
@@ -0,0 +1,194 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <stdio.h>
#include <string>
#include "debug/debug.hpp"
#include "loaders/3d/builder/mesh_builder_data.hpp"
#include "loaders/3d/md2/anorms.hpp"
#include "loaders/loader.hpp"
#include "loaders/3d/md2/md2_loader.hpp"
namespace Tyra {
// magic number "IDP2" or 844121161
#define MD2_IDENT (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')
// model version
#define MD2_VERSION 8
typedef struct {
int ident; // magic number. must be equal to "IDP2"
int version; // md2 version. must be equal to 8
int skinwidth; // width of the texture
int skinheight; // height of the texture
int framesize; // size of one frame in bytes
int num_skins; // number of textures
int num_xyz; // number of vertices
int num_st; // number of texture coordinates
int num_tris; // number of triangles
int num_glcmds; // number of opengl commands
int num_frames; // total number of frames
int ofs_skins; // offset to skin names (64 bytes each)
int ofs_st; // offset to s-t texture coordinates
int ofs_tris; // offset to triangles
int ofs_frames; // offset to frame data
int ofs_glcmds; // offset to opengl commands
int ofs_end; // offset to end of file
} md2_t;
typedef struct {
unsigned char v[3]; // compressed vertex (x, y, z) coordinates
unsigned char lightnormalindex; // index to a normal vector for the lighting
} mvertex_t;
typedef struct {
float scale[3]; // scale values
float translate[3]; // translation vector
char name[16]; // frame name
mvertex_t verts[1]; // first vertex of this frame
} frame_t;
typedef float vec3_t[3];
typedef struct {
s16 index_xyz[3]; // indexes to triangle's vertices
s16 index_st[3]; // indexes to vertices' texture coorinates
} triangle_t;
typedef struct {
s16 s;
s16 t;
} texCoord_t;
MD2Loader::MD2Loader() {}
MD2Loader::~MD2Loader() {}
MeshBuilderData* MD2Loader::load(const char* fullpath, const float& t_scale,
const u8& t_invertT) {
std::string path = fullpath;
TYRA_ASSERT(!path.empty(), "Provided path is empty!");
auto filename = getFilenameFromPath(path);
FILE* file = fopen(fullpath, "rb");
TYRA_ASSERT(file != NULL, "Failed to load: ", filename);
md2_t header;
fread(reinterpret_cast<char*>(&header), sizeof(md2_t), 1, file);
TYRA_ASSERT((header.ident == MD2_IDENT) && (header.version == MD2_VERSION),
"This MD2 file is not in correct format!");
u32 framesCount = header.num_frames;
u32 vertexCount = header.num_xyz;
u32 stsCount = header.num_st;
u32 trianglesCount = header.num_tris;
auto framesBuffer = new char[framesCount * header.framesize];
fseek(file, header.ofs_frames, SEEK_SET);
fread(framesBuffer, framesCount * header.framesize, 1, file);
auto stsBuffer = new char[stsCount * sizeof(texCoord_t)];
fseek(file, header.ofs_st, SEEK_SET);
fread(stsBuffer, stsCount * sizeof(texCoord_t), 1, file);
auto trianglesBuffer = new char[trianglesCount * sizeof(triangle_t)];
fseek(file, header.ofs_tris, SEEK_SET);
fread(trianglesBuffer, trianglesCount * sizeof(triangle_t), 1, file);
fclose(file);
auto result = new MeshBuilderData();
result->allocate(framesCount, 1);
result->normalsEnabled = true;
result->textureCoordsEnabled = true;
result->manyColorsEnabled = false;
frame_t* frame;
Vec4 temp(0.0F, 0.0F, 0.0F, 1.0F);
for (u32 j = 0; j < framesCount; j++) {
result->frames[j]->allocateVertices(vertexCount);
result->frames[j]->allocateNormals(vertexCount);
result->frames[j]->allocateTextureCoords(stsCount);
result->materials[0]->allocateFaces(trianglesCount * 3);
result->materials[0]->name = getFilenameWithoutExtension(filename);
frame = reinterpret_cast<frame_t*>(&framesBuffer[header.framesize * j]);
for (u32 i = 0; i < vertexCount; i++) {
temp.set(
((frame->verts[i].v[0] * frame->scale[0]) + frame->translate[0]) *
t_scale,
((frame->verts[i].v[1] * frame->scale[1]) + frame->translate[1]) *
t_scale,
((frame->verts[i].v[2] * frame->scale[2]) + frame->translate[2]) *
t_scale);
result->frames[j]->vertices[i].set(temp);
temp.set(ANORMS[frame->verts[i].lightnormalindex][0],
ANORMS[frame->verts[i].lightnormalindex][1],
ANORMS[frame->verts[i].lightnormalindex][2]);
result->frames[j]->normals[i].set(temp);
}
}
texCoord_t* texCoord;
TYRA_LOG("Skin width: ", header.skinwidth,
" Skin height: ", header.skinheight);
for (u32 i = 0; i < stsCount; i++) {
texCoord =
reinterpret_cast<texCoord_t*>(&stsBuffer[sizeof(texCoord_t) * i]);
temp.set(static_cast<float>(texCoord->s) / header.skinwidth,
static_cast<float>(texCoord->t) / header.skinheight, 1.0F, 0.0F);
if (t_invertT) temp.y = 1.0F - temp.y;
for (u32 j = 0; j < framesCount; j++)
result->frames[j]->textureCoords[i].set(temp);
}
triangle_t* triangle;
for (u32 i = 0; i < trianglesCount; i++) {
triangle =
reinterpret_cast<triangle_t*>(&trianglesBuffer[sizeof(triangle_t) * i]);
for (u8 j = 0; j < 3; j++) {
for (u32 x = 0; x < framesCount; x++) {
result->materials[0]->vertexFaces[(i * 3) + j] = triangle->index_xyz[j];
result->materials[0]->textureCoordFaces[(i * 3) + j] =
triangle->index_st[j];
result->materials[0]->normalFaces[(i * 3) + j] = triangle->index_xyz[j];
}
}
}
TYRA_LOG("MD2 file \"", filename, "\" loaded!");
delete[] framesBuffer;
delete[] stsBuffer;
delete[] trianglesBuffer;
return result;
}
} // namespace Tyra
+29
View File
@@ -0,0 +1,29 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "loaders/loader.hpp"
namespace Tyra {
std::string Loader::getFilenameFromPath(const std::string& path) {
std::string filename = path.substr(path.find_last_of("/\\") + 1);
if (filename.size() == path.size()) {
filename = path.substr(path.find_last_of(":\\") + 1);
}
return filename;
}
std::string Loader::getFilenameWithoutExtension(const std::string& filename) {
auto lastindex = filename.find_last_of(".");
return filename.substr(0, lastindex);
}
} // namespace Tyra
@@ -0,0 +1,32 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "loaders/texture/builder/texture_builder_data.hpp"
#include <draw_buffers.h>
namespace Tyra {
TextureBuilderData::TextureBuilderData() {
width = 0;
height = 0;
data = nullptr;
bpp = bpp32;
gsComponents = TEXTURE_COMPONENTS_RGBA;
clut = nullptr;
clutWidth = 0;
clutHeight = 0;
clutBpp = bpp32;
clutGsComponents = TEXTURE_COMPONENTS_RGBA;
}
TextureBuilderData::~TextureBuilderData() {}
} // namespace Tyra
@@ -0,0 +1,35 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <string>
#include "debug/debug.hpp"
#include "loaders/texture/base/texture_loader.hpp"
namespace Tyra {
u32 TextureLoader::getTextureSize(const u32& width, const u32& height,
const TextureBpp& bpp) {
switch (bpp) {
case bpp32:
return (width * height * 4);
case bpp24:
return (width * height * 3);
case bpp8:
return (width * height);
case bpp4:
return (width * height / 2);
default:
TYRA_TRAP("Unknown texture bpp");
}
return -1;
}
} // namespace Tyra
@@ -0,0 +1,47 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <algorithm>
#include <string>
#include "debug/debug.hpp"
#include "renderer/core/texture/models/texture.hpp"
#include "loaders/texture/base/texture_loader_selector.hpp"
namespace Tyra {
TextureLoaderSelector::TextureLoaderSelector() {}
TextureLoaderSelector::~TextureLoaderSelector() {}
// ----
// Methods
// ----
TextureLoader& TextureLoaderSelector::getLoaderByFileName(
const char* fullpath) {
std::string path = fullpath;
std::string extension = path.substr(path.find_last_of(".") + 1);
return getLoaderByExtension(extension);
}
TextureLoader& TextureLoaderSelector::getLoaderByExtension(
const std::string& extension) {
std::string extensionLower = extension;
std::transform(extensionLower.begin(), extensionLower.end(),
extensionLower.begin(), ::tolower);
if (extensionLower == "png") {
return pngLoader;
} else {
TYRA_TRAP("There is no texture loader for extension: ", extensionLower);
return pngLoader;
}
}
} // namespace Tyra
+313
View File
@@ -0,0 +1,313 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <png.h>
#include <string>
#include <draw_buffers.h>
#include "loaders/texture/png_loader.hpp"
namespace Tyra {
PngLoader::PngLoader() {}
PngLoader::~PngLoader() {}
struct PngClut {
u8 r, g, b, a;
};
/** Based on GsKit texture loading - thank you guys! */
TextureBuilderData* PngLoader::load(const char* fullPath) {
std::string path = fullPath;
TYRA_ASSERT(!path.empty(), "Provided path is empty!");
auto filename = getFilenameFromPath(path);
FILE* file = fopen(fullPath, "rb");
TYRA_ASSERT(file != nullptr, "Failed to load ", fullPath);
png_structp pngPtr;
png_infop infoPtr;
png_uint_32 width, height;
png_bytep* rowPointers = nullptr;
u32 sigRead = 0;
int bitDepth, colorType, interlaceType;
pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) nullptr,
nullptr, nullptr);
TYRA_ASSERT(pngPtr, "PNG read struct init failed for: ", filename);
infoPtr = png_create_info_struct(pngPtr);
TYRA_ASSERT(infoPtr, "PNG read struct init failed for: ", filename);
TYRA_ASSERT(!setjmp(png_jmpbuf(pngPtr)), "PNG read error for: ", filename);
png_init_io(pngPtr, file);
png_set_sig_bytes(pngPtr, sigRead);
png_read_info(pngPtr, infoPtr);
png_get_IHDR(pngPtr, infoPtr, &width, &height, &bitDepth, &colorType,
&interlaceType, nullptr, nullptr);
if (bitDepth == 16) png_set_strip_16(pngPtr);
if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 4) png_set_expand(pngPtr);
png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER);
png_read_update_info(pngPtr, infoPtr);
auto* result = new TextureBuilderData();
result->width = width;
result->height = height;
result->name = filename;
auto updatedColorType = png_get_color_type(pngPtr, infoPtr);
if (updatedColorType == PNG_COLOR_TYPE_PALETTE) {
handlePalletized(result, pngPtr, infoPtr, rowPointers, bitDepth);
} else if (updatedColorType == PNG_COLOR_TYPE_RGB_ALPHA) {
handle32bpp(result, pngPtr, infoPtr, rowPointers);
} else if (updatedColorType == PNG_COLOR_TYPE_RGB) {
handle24bpp(result, pngPtr, infoPtr, rowPointers);
} else
TYRA_TRAP("This texture depth is not supported!");
png_read_end(pngPtr, nullptr);
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
fclose(file);
return result;
}
void PngLoader::handle32bpp(TextureBuilderData* result, png_structp pngPtr,
png_infop infoPtr, png_bytep* rowPointers) {
int rowBytes = png_get_rowbytes(pngPtr, infoPtr);
result->gsComponents = TEXTURE_COMPONENTS_RGBA;
result->bpp = bpp32;
result->data = static_cast<unsigned char*>(memalign(
128, getTextureSize(result->width, result->height, result->bpp)));
rowPointers =
static_cast<png_bytep*>(calloc(result->height, sizeof(png_bytep)));
for (int row = 0; row < result->height; row++)
rowPointers[row] = static_cast<png_bytep>(malloc(rowBytes));
png_read_image(pngPtr, rowPointers);
struct Pixel {
u8 r, g, b, a;
};
struct Pixel* pixels = (struct Pixel*)result->data;
int k = 0;
for (int i = 0; i < result->height; i++) {
for (int j = 0; j < result->width; j++) {
pixels[k].r = rowPointers[i][4 * j];
pixels[k].g = rowPointers[i][4 * j + 1];
pixels[k].b = rowPointers[i][4 * j + 2];
pixels[k++].a = ((int)rowPointers[i][4 * j + 3] * 128 / 255);
}
}
for (int row = 0; row < result->height; row++) free(rowPointers[row]);
free(rowPointers);
}
void PngLoader::handle24bpp(TextureBuilderData* result, png_structp pngPtr,
png_infop infoPtr, png_bytep* rowPointers) {
int rowBytes = png_get_rowbytes(pngPtr, infoPtr);
result->gsComponents = TEXTURE_COMPONENTS_RGB;
result->bpp = bpp24;
result->data = static_cast<unsigned char*>(memalign(
128, getTextureSize(result->width, result->height, result->bpp)));
rowPointers =
static_cast<png_bytep*>(calloc(result->height, sizeof(png_bytep)));
for (int row = 0; row < result->height; row++)
rowPointers[row] = static_cast<png_bytep>(malloc(rowBytes));
png_read_image(pngPtr, rowPointers);
struct Pixel3 {
u8 r, g, b;
};
struct Pixel3* pixels = (struct Pixel3*)result->data;
int k = 0;
for (int i = 0; i < result->height; i++) {
for (int j = 0; j < result->width; j++) {
pixels[k].r = rowPointers[i][4 * j];
pixels[k].g = rowPointers[i][4 * j + 1];
pixels[k++].b = rowPointers[i][4 * j + 2];
}
}
for (int row = 0; row < result->height; row++) free(rowPointers[row]);
free(rowPointers);
}
void PngLoader::handlePalletized(TextureBuilderData* result, png_structp pngPtr,
png_infop infoPtr, png_bytep* rowPointers,
const int& bitDepth) {
png_colorp palette = nullptr;
png_bytep trans = nullptr;
int numPallete = 0;
int numTrans = 0;
png_get_PLTE(pngPtr, infoPtr, &palette, &numPallete);
png_get_tRNS(pngPtr, infoPtr, &trans, &numTrans, nullptr);
result->clutBpp = bpp32;
result->clutGsComponents = TEXTURE_COMPONENTS_RGBA;
if (bitDepth == 4) {
handle4bppPalletized(result, pngPtr, infoPtr, rowPointers, palette, trans,
numPallete, numTrans);
} else if (bitDepth == 8) {
handle8bppPalletized(result, pngPtr, infoPtr, rowPointers, palette, trans,
numPallete, numTrans);
} else {
TYRA_TRAP("Only 4 and 8 bits palettes are supported");
}
}
void PngLoader::handle8bppPalletized(TextureBuilderData* result,
png_structp pngPtr, png_infop infoPtr,
png_bytep* rowPointers, png_colorp palette,
png_bytep trans, const int& numPallete,
const int& numTrans) {
int rowBytes = png_get_rowbytes(pngPtr, infoPtr);
result->bpp = bpp8;
result->clutWidth = 16;
result->clutHeight = 16;
result->gsComponents = TEXTURE_COMPONENTS_RGBA;
result->data = static_cast<unsigned char*>(memalign(
128, getTextureSize(result->width, result->height, result->bpp)));
rowPointers =
static_cast<png_bytep*>(calloc(result->height, sizeof(png_bytep)));
for (int row = 0; row < result->height; row++)
rowPointers[row] = static_cast<png_bytep>(malloc(rowBytes));
png_read_image(pngPtr, rowPointers);
result->clut =
static_cast<unsigned char*>(memalign(128, getTextureSize(16, 16, bpp32)));
memset(result->clut, 0, getTextureSize(16, 16, bpp32));
auto* pixel = static_cast<unsigned char*>(result->data);
struct PngClut* clut = (struct PngClut*)result->clut;
for (int i = numPallete; i < 256; i++) {
memset(&clut[i], 0, sizeof(clut[i]));
}
for (int i = 0; i < numPallete; i++) {
clut[i].r = palette[i].red;
clut[i].g = palette[i].green;
clut[i].b = palette[i].blue;
clut[i].a = 0x80;
}
for (int i = 0; i < numTrans; i++) clut[i].a = trans[i] >> 1;
// rotate clut
for (int i = 0; i < numPallete; i++) {
if ((i & 0x18) == 8) {
struct PngClut tmp = clut[i];
clut[i] = clut[i + 8];
clut[i + 8] = tmp;
}
}
int k = 0;
for (int i = 0; i < result->height; i++) {
for (int j = 0; j < result->width; j++) {
memcpy(&pixel[k++], &rowPointers[i][1 * j], 1);
}
}
for (int row = 0; row < result->height; row++) free(rowPointers[row]);
free(rowPointers);
}
void PngLoader::handle4bppPalletized(TextureBuilderData* result,
png_structp pngPtr, png_infop infoPtr,
png_bytep* rowPointers, png_colorp palette,
png_bytep trans, const int& numPallete,
const int& numTrans) {
int rowBytes = png_get_rowbytes(pngPtr, infoPtr);
result->bpp = bpp4;
result->clutWidth = 8;
result->clutHeight = 2;
result->gsComponents = TEXTURE_COMPONENTS_RGBA;
result->data = static_cast<unsigned char*>(memalign(
128, getTextureSize(result->width, result->height, result->bpp)));
rowPointers =
static_cast<png_bytep*>(calloc(result->height, sizeof(png_bytep)));
for (int row = 0; row < result->height; row++)
rowPointers[row] = static_cast<png_bytep>(malloc(rowBytes));
png_read_image(pngPtr, rowPointers);
result->clut =
static_cast<unsigned char*>(memalign(128, getTextureSize(8, 2, bpp32)));
memset(result->clut, 0, getTextureSize(8, 2, bpp32));
auto* pixel = static_cast<unsigned char*>(result->data);
struct PngClut* clut = (struct PngClut*)result->clut;
for (int i = numPallete; i < 16; i++) {
memset(&clut[i], 0, sizeof(clut[i]));
}
for (int i = 0; i < numPallete; i++) {
clut[i].r = palette[i].red;
clut[i].g = palette[i].green;
clut[i].b = palette[i].blue;
clut[i].a = 0x80;
}
for (int i = 0; i < numTrans; i++) clut[i].a = trans[i] >> 1;
int k = 0;
for (int i = 0; i < result->height; i++) {
for (int j = 0; j < result->width / 2; j++)
memcpy(&pixel[k++], &rowPointers[i][1 * j], 1);
}
unsigned char* tmpdst = (unsigned char*)result->data;
unsigned char* tmpsrc = (unsigned char*)pixel;
for (u32 byte = 0;
byte < getTextureSize(result->width, result->height, result->bpp);
byte++)
tmpdst[byte] = (tmpsrc[byte] << 4) | (tmpsrc[byte] >> 4);
for (int row = 0; row < result->height; row++) free(rowPointers[row]);
free(rowPointers);
}
} // namespace Tyra
+390
View File
@@ -0,0 +1,390 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <stdio.h>
#include <string>
#include <sstream>
#include "math/m4x4.hpp"
namespace Tyra {
VECTOR M4x4::upVec = {0.0F, 1.0F, 0.0F, 1.0F};
VECTOR M4x4::viewVec = {0.0F, 0.0F, 0.0F, 1.0F};
M4x4::M4x4(const bool& t_identity) {
if (t_identity) identity();
}
void M4x4::copy(M4x4* out, const float* in) {
asm volatile(
"lqc2 $vf1, 0x00(%1) \n"
"lqc2 $vf2, 0x10(%1) \n"
"lqc2 $vf3, 0x20(%1) \n"
"lqc2 $vf4, 0x30(%1) \n"
"sqc2 $vf1, 0x00(%0) \n"
"sqc2 $vf2, 0x10(%0) \n"
"sqc2 $vf3, 0x20(%0) \n"
"sqc2 $vf4, 0x30(%0) \n"
:
: "r"(out->data), "r"(in));
}
void M4x4::operator=(const M4x4& v) { copy(this, v); }
Vec4 M4x4::operator*(const Vec4& v) const {
Vec4 result;
asm volatile(
"lqc2 $vf1, 0x00(%2) \n"
"lqc2 $vf2, 0x10(%2) \n"
"lqc2 $vf3, 0x20(%2) \n"
"lqc2 $vf4, 0x30(%2) \n"
"lqc2 $vf5, 0x00(%1) \n"
"vmulaw $ACC, $vf4, $vf0\n"
"vmaddax $ACC, $vf1, $vf5\n"
"vmadday $ACC, $vf2, $vf5\n"
"vmaddz $vf6, $vf3, $vf5\n"
"sqc2 $vf6, 0x00(%0) \n"
:
: "r"(result.xyzw), "r"(v.xyzw), "r"(this->data));
return result;
}
void M4x4::set(const float& m11, const float& m12, const float& m13,
const float& m14, const float& m21, const float& m22,
const float& m23, const float& m24, const float& m31,
const float& m32, const float& m33, const float& m34,
const float& m41, const float& m42, const float& m43,
const float& m44) {
data[0] = m11;
data[1] = m12;
data[2] = m13;
data[3] = m14;
data[4] = m21;
data[5] = m22;
data[6] = m23;
data[7] = m24;
data[8] = m31;
data[9] = m32;
data[10] = m33;
data[11] = m34;
data[12] = m41;
data[13] = m42;
data[14] = m43;
data[15] = m44;
}
void M4x4::identity() {
asm volatile(
"vsub.xyzw $vf4, $vf0, $vf0 \n\t"
"vadd.w $vf4, $vf4, $vf0 \n\t"
"vmr32.xyzw $vf5, $vf4 \n\t"
"vmr32.xyzw $vf6, $vf5 \n\t"
"vmr32.xyzw $vf7, $vf6 \n\t"
"sqc2 $vf4, 0x30(%0) \n\t"
"sqc2 $vf5, 0x20(%0) \n\t"
"sqc2 $vf6, 0x10(%0) \n\t"
"sqc2 $vf7, 0x0(%0) \n\t"
:
: "r"(this->data));
}
M4x4 M4x4::perspective(const float& fov, const float& width,
const float& height, const float& projectionScale,
const float& aspectRatio, const float& near,
const float& far) {
M4x4 res;
float fovYdiv2 = Math::HALF_ANG2RAD * fov;
float cotFOV = 1.0F / (Math::sin(fovYdiv2) / Math::cos(fovYdiv2));
float w = cotFOV * (width / projectionScale) / aspectRatio;
float h = cotFOV * (height / projectionScale);
res.data[0] = w;
res.data[1] = 0.0F;
res.data[2] = 0.0F;
res.data[3] = 0.0F;
res.data[4] = 0.0F;
res.data[5] = -h;
res.data[6] = 0.0F;
res.data[7] = 0.0F;
res.data[8] = 0.0F;
res.data[9] = 0.0F;
res.data[10] = (far + near) / (far - near);
res.data[11] = -1.0F;
res.data[12] = 0.0F;
res.data[13] = 0.0F;
res.data[14] = (2.0F * far * near) / (far - near);
res.data[15] = 0.0F;
return res;
}
M4x4 M4x4::lookAt(const Vec4& position, const Vec4& target) {
M4x4 res(true);
lookAt(&res, position, target);
return res;
}
void M4x4::lookAt(M4x4* res, const Vec4& position, const Vec4& target) {
float eye[4] alignas(sizeof(float) * 4) = {position.x, position.y, position.z,
1.0F};
float obj[4] alignas(sizeof(float) * 4) = {target.x, target.y, target.z,
1.0F};
asm volatile(
// eye
"lqc2 $vf4, 0x00(%2) \n\t"
// obj
"lqc2 $vf5, 0x00(%3) \n\t"
// view_vec = $vf7
"vsub.xyz $vf7, $vf4, $vf5 \n\t"
"vmove.xyzw $vf6, $vf0 \n\t"
// $vf6 = { 0.0f, 1.0f, 0.0f, 1.0f }
"vaddw.y $vf6, $vf0, $vf0 \n\t"
"vopmula.xyz $ACC, $vf6, $vf7 \n\t"
// vec = $vf9
"vopmsub.xyz $vf9, $vf7, $vf6 \n\t"
"vopmula.xyz $ACC, $vf7, $vf9 \n\t"
// up_vec = $vf8
"vopmsub.xyz $vf8, $vf9, $vf7 \n\t"
// view_vec
"sqc2 $vf7, 0x00(%0) \n\t"
// up_vec
"sqc2 $vf6, 0x00(%1) \n\t"
:
: "r"(viewVec), "r"(upVec), "r"(eye), "r"(obj));
M4x4 temp = setCamera(eye, viewVec, upVec);
res->identity();
cross(res->data, res->data, temp.data);
}
void M4x4::cross(float res[16], const float a[16], const float b[16]) {
asm volatile(
"lqc2 $vf1, 0x00(%1) \n\t"
"lqc2 $vf2, 0x10(%1) \n\t"
"lqc2 $vf3, 0x20(%1) \n\t"
"lqc2 $vf4, 0x30(%1) \n\t"
"lqc2 $vf5, 0x00(%2) \n\t"
"lqc2 $vf6, 0x10(%2) \n\t"
"lqc2 $vf7, 0x20(%2) \n\t"
"lqc2 $vf8, 0x30(%2) \n\t"
"vmulax.xyzw $ACC, $vf5, $vf1 \n\t"
"vmadday.xyzw $ACC, $vf6, $vf1 \n\t"
"vmaddaz.xyzw $ACC, $vf7, $vf1 \n\t"
"vmaddw.xyzw $vf1, $vf8, $vf1 \n\t"
"vmulax.xyzw $ACC, $vf5, $vf2 \n\t"
"vmadday.xyzw $ACC, $vf6, $vf2 \n\t"
"vmaddaz.xyzw $ACC, $vf7, $vf2 \n\t"
"vmaddw.xyzw $vf2, $vf8, $vf2 \n\t"
"vmulax.xyzw $ACC, $vf5, $vf3 \n\t"
"vmadday.xyzw $ACC, $vf6, $vf3 \n\t"
"vmaddaz.xyzw $ACC, $vf7, $vf3 \n\t"
"vmaddw.xyzw $vf3, $vf8, $vf3 \n\t"
"vmulax.xyzw $ACC, $vf5, $vf4 \n\t"
"vmadday.xyzw $ACC, $vf6, $vf4 \n\t"
"vmaddaz.xyzw $ACC, $vf7, $vf4 \n\t"
"vmaddw.xyzw $vf4, $vf8, $vf4 \n\t"
"sqc2 $vf1, 0x00(%0) \n\t"
"sqc2 $vf2, 0x10(%0) \n\t"
"sqc2 $vf3, 0x20(%0) \n\t"
"sqc2 $vf4, 0x30(%0) \n\t"
:
: "r"(res), "r"(b), "r"(a)
: "memory");
}
void M4x4::rotationX(const float& v) {
float c = Math::cos(v);
float s = Math::sin(v);
this->data[5] = c; // 1,1
this->data[6] = s; // 1,2
this->data[9] = -s; // 2,1
this->data[10] = c; // 2,2
}
void M4x4::rotationY(const float& v) {
float c = Math::cos(v);
float s = Math::sin(v);
this->data[0] = c; // 0,0
this->data[2] = -s; // 0,3
this->data[8] = s; // 2,0
this->data[10] = c; // 2,2
}
void M4x4::rotationZ(const float& v) {
float c = Math::cos(v);
float s = Math::sin(v);
this->data[0] = c; // 0,0
this->data[1] = s; // 0,1
this->data[4] = -s; // 1,0
this->data[5] = c; // 1,1
}
void M4x4::rotationByAngle(const float& angle, const Vec4& axis) {
Vec4 localAxis = Vec4(axis);
localAxis.normalize();
float x = localAxis.x;
float y = localAxis.y;
float z = localAxis.z;
float c = Math::cos(angle);
float s = Math::sin(angle);
this->data[0] = x * x * (1 - c) + c;
this->data[1] = y * x * (1 - c) + z * s;
this->data[2] = x * z * (1 - c) - y * s;
this->data[3] = 0.0F;
this->data[4] = x * y * (1 - c) - z * s;
this->data[5] = y * y * (1 - c) + c;
this->data[6] = y * z * (1 - c) + x * s;
this->data[7] = 0.0F;
this->data[8] = x * z * (1 - c) + y * s;
this->data[9] = y * z * (1 - c) - x * s;
this->data[10] = z * z * (1 - c) + c;
this->data[11] = 0.0F;
this->data[12] = 0.0F;
this->data[13] = 0.0F;
this->data[14] = 0.0F;
this->data[15] = 1.0F;
}
void M4x4::translationX(const float& val) {
this->data[12] = val; // 3,0
}
void M4x4::translationY(const float& val) {
this->data[13] = val; // 3,1
}
void M4x4::translationZ(const float& val) {
this->data[14] = val; // 3,2
}
void M4x4::setScale(const Vec4& val) {
this->data[0] = val.x;
this->data[5] = val.y;
this->data[10] = val.z;
this->data[15] = 1.0F;
}
M4x4 M4x4::setCamera(const float pos[4], const float vz[4], const float vy[4]) {
M4x4 res;
// M4x4 $vf4, $vf5, $vf6, $vf7
// pos $vf8
// vz $vf9
// vy $vf10
// vtmp $vf11
asm volatile(
"lqc2 $vf9, 0x00(%2) \n\t"
// mtmp.unit()
"lqc2 $vf10, 0x00(%3) \n\t"
// mtmp[1][PW] = 0.0F
"vsub.w $vf5, $vf0, $vf0 \n\t"
// vtmp.outerProduct(vy, vz);
"vopmula.xyz $ACC, $vf10, $vf9 \n\t"
"vopmsub.xyz $vf11, $vf9, $vf10 \n\t"
// mtmp[0] = vtmp.normalize();
"vmul.xyz $vf12, $vf11, $vf11 \n\t"
"vaddy.x $vf12, $vf12, $vf12 \n\t"
"vaddz.x $vf12, $vf12, $vf12 \n\t"
"vrsqrt $Q, $vf0w, $vf12x \n\t"
"vsub.xyzw $vf4, $vf0, $vf0 \n\t"
"vwaitq \n\t"
"vmulq.xyz $vf4, $vf11, $Q \n\t"
// mtmp[2] = vz.normalize();
"vmul.xyz $vf12, $vf9, $vf9 \n\t"
"vaddy.x $vf12, $vf12, $vf12 \n\t"
"vaddz.x $vf12, $vf12, $vf12 \n\t"
"vrsqrt $Q, $vf0w, $vf12x \n\t"
"vsub.xyzw $vf6, $vf0, $vf0 \n\t"
"vwaitq \n\t"
"vmulq.xyz $vf6, $vf9, $Q \n\t"
// mtmp[1].outerProduct(mtmp[2], mtmp[0]);
"vopmula.xyz $ACC, $vf6, $vf4 \n\t"
"vopmsub.xyz $vf5, $vf4, $vf6 \n\t"
// mtmp.transpose(pos);
"lqc2 $vf7, 0x00(%1) \n\t"
// m = mtmp.inverse();
"qmfc2.ni $11, $vf0 \n\t"
"qmfc2.ni $8, $vf4 \n\t"
"qmfc2.ni $9, $vf5 \n\t"
"qmfc2.ni $10, $vf6 \n\t"
"pextlw $12, $9, $8 \n\t"
"pextuw $13, $9, $8 \n\t"
"pextlw $14, $11, $10 \n\t"
"pextuw $15, $11, $10 \n\t"
"pcpyld $8, $14, $12 \n\t"
"pcpyud $9, $12, $14 \n\t"
"pcpyld $10, $15, $13 \n\t"
"qmtc2.ni $8, $vf16 \n\t"
"qmtc2.ni $9, $vf17 \n\t"
"qmtc2.ni $10, $vf18 \n\t"
"vmulax.xyz $ACC, $vf16, $vf7 \n\t"
"vmadday.xyz $ACC, $vf17, $vf7 \n\t"
"vmaddz.xyz $vf5, $vf18, $vf7 \n\t"
"vsub.xyzw $vf5, $vf0, $vf5 \n\t"
"sq $8, 0x00(%0) \n\t"
"sq $9, 0x10(%0) \n\t"
"sq $10, 0x20(%0) \n\t"
"sqc2 $vf5, 0x30(%0) \n\t"
:
: "r"(res.data), "r"(pos), "r"(vz), "r"(vy));
return res;
}
void M4x4::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void M4x4::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string M4x4::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "M4x4(";
}
res << std::fixed << std::setprecision(2);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
auto index = (i * 4) + j;
res << data[index];
if (index != 15) {
res << ", ";
}
}
if (i != 3) {
res << std::endl;
}
}
res << ")";
return res.str();
}
} // namespace Tyra
+161
View File
@@ -0,0 +1,161 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#ifdef __INTELLISENSE__
#pragma diag_suppress 1118
#endif
#include "math/math.hpp"
namespace Tyra {
float Math::cos(float x) {
float r;
asm volatile(
"lui $9, 0x3f00 \n\t"
".set noreorder \n\t"
".align 3 \n\t"
"abs.s %0, %1 \n\t"
"lui $8, 0xbe22 \n\t"
"mtc1 $9, $f1 \n\t"
"ori $8, $8, 0xf983 \n\t"
"mtc1 $8, $f8 \n\t"
"lui $9, 0x4b00 \n\t"
"mtc1 $9, $f3 \n\t"
"lui $8, 0x3f80 \n\t"
"mtc1 $8, $f2 \n\t"
"mula.s %0, $f8 \n\t"
"msuba.s $f3, $f2 \n\t"
"madda.s $f3, $f2 \n\t"
"lui $8, 0x40c9 \n\t"
"msuba.s %0, $f8 \n\t"
"ori $8, 0x0fdb \n\t"
"msub.s %0, $f1, $f2 \n\t"
"lui $9, 0xc225 \n\t"
"abs.s %0, %0 \n\t"
"lui $10, 0x3e80 \n\t"
"mtc1 $10, $f7 \n\t"
"ori $9, 0x5de1 \n\t"
"sub.s %0, %0, $f7 \n\t"
"lui $10, 0x42a3 \n\t"
"mtc1 $8, $f3 \n\t"
"ori $10, 0x3458 \n\t"
"mtc1 $9, $f4 \n\t"
"lui $8, 0xc299 \n\t"
"mtc1 $10, $f5 \n\t"
"ori $8, 0x2663 \n\t"
"mul.s $f8, %0, %0 \n\t"
"lui $9, 0x421e \n\t"
"mtc1 $8, $f6 \n\t"
"ori $9, 0xd7bb \n\t"
"mtc1 $9, $f7 \n\t"
"nop \n\t"
"mul.s $f1, %0, $f8 \n\t"
"mul.s $f9, $f8, $f8 \n\t"
"mula.s $f3, %0 \n\t"
"mul.s $f2, $f1, $f8 \n\t"
"madda.s $f4, $f1 \n\t"
"mul.s $f1, $f1, $f9 \n\t"
"mul.s %0, $f2, $f9 \n\t"
"madda.s $f5, $f2 \n\t"
"madda.s $f6, $f1 \n\t"
"madd.s %0, $f7, %0 \n\t"
".set reorder \n\t"
: "=&f"(r)
: "f"(x)
: "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$8",
"$9", "$10");
return r;
}
float Math::invSqrt(float x) { return 1.0F / sqrt(x); }
float Math::asin(float x) {
float r;
asm volatile(
"lui $9, 0x3f00 \n\t"
".set noreorder \n\t"
".align 3 \n\t"
"abs.s %0, %1 \n\t"
"lui $8, 0xbe22 \n\t"
"mtc1 $9, $f1 \n\t"
"ori $8, $8, 0xf983 \n\t"
"mtc1 $8, $f8 \n\t"
"lui $9, 0x4b00 \n\t"
"mtc1 $9, $f3 \n\t"
"lui $8, 0x3f80 \n\t"
"mtc1 $8, $f2 \n\t"
"mula.s %0, $f8 \n\t"
"msuba.s $f3, $f2 \n\t"
"madda.s $f3, $f2 \n\t"
"lui $8, 0x40c9 \n\t"
"msuba.s %0, $f8 \n\t"
"ori $8, 0x0fdb \n\t"
"msub.s %0, $f1, $f2 \n\t"
"lui $9, 0xc225 \n\t"
"abs.s %0, %0 \n\t"
"lui $10, 0x3e80 \n\t"
"mtc1 $10, $f7 \n\t"
"ori $9, 0x5de1 \n\t"
"sub.s %0, %0, $f7 \n\t"
"lui $10, 0x42a3 \n\t"
"mtc1 $8, $f3 \n\t"
"ori $10, 0x3458 \n\t"
"mtc1 $9, $f4 \n\t"
"lui $8, 0xc299 \n\t"
"mtc1 $10, $f5 \n\t"
"ori $8, 0x2663 \n\t"
"mul.s $f8, %0, %0 \n\t"
"lui $9, 0x421e \n\t"
"mtc1 $8, $f6 \n\t"
"ori $9, 0xd7bb \n\t"
"mtc1 $9, $f7 \n\t"
"nop \n\t"
"mul.s $f1, %0, $f8 \n\t"
"mul.s $f9, $f8, $f8 \n\t"
"mula.s $f3, %0 \n\t"
"mul.s $f2, $f1, $f8 \n\t"
"madda.s $f4, $f1 \n\t"
"mul.s $f1, $f1, $f9 \n\t"
"mul.s %0, $f2, $f9 \n\t"
"madda.s $f5, $f2 \n\t"
"madda.s $f6, $f1 \n\t"
"madd.s %0, $f7, %0 \n\t"
".set reorder \n\t"
: "=&f"(r)
: "f"(x)
: "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$8",
"$9", "$10");
return r;
}
float Math::mod(float x, float y) {
/*
* Portable fmod(x,y) implementation for systems
* that don't have it. Adapted from code found here:
* http://www.opensource.apple.com/source/python/python-3/python/Python/fmod.c
*/
float i, f;
if (fabs(y) < 0.00001F) {
return 0.0F;
}
i = floorf(x / y);
f = x - i * y;
if ((x < 0.0f) != (y < 0.0f)) {
f = f - y;
}
return f;
}
} // namespace Tyra
+67
View File
@@ -0,0 +1,67 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <string>
#include "math/vec4.hpp"
#include "math/plane.hpp"
namespace Tyra {
Plane::Plane() { this->distance = 0; }
/** Create by specyfying 3 points.
* This function assumes that the points
* are given in counter clockwise order
*/
Plane::Plane(const Vec4& a, const Vec4& b, const Vec4& c) {
this->update(a, b, c);
}
Plane::~Plane() {}
// ----
// Methods
// ----
/** Set plane by specyfying 3 points.
* This function assumes that the points
* are given in counter clockwise order
*/
void Plane::update(const Vec4& a, const Vec4& b, const Vec4& c) {
Vec4 aux1 = a - b;
Vec4 aux2 = c - b;
this->normal = aux2.cross(aux1);
this->normal.normalize();
this->distance = -this->normal.innerProduct(b);
}
void Plane::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void Plane::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string Plane::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "Plane(";
}
res << std::fixed << std::setprecision(4);
res << "distance: " << distance << ", " << normal.getPrint("normal") << ")";
return res.str();
}
} // namespace Tyra
+78
View File
@@ -0,0 +1,78 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "math/vec2.hpp"
namespace Tyra {
Vec2::Vec2(const float& t_x, const float& t_y) {
x = t_x;
y = t_y;
}
Vec2::Vec2(const Vec2& v) {
x = v.x;
y = v.y;
}
Vec2::Vec2() {
x = 0;
y = 0;
}
Vec2::~Vec2() {}
void Vec2::set(const float& t_x, const float& t_y) {
x = t_x;
y = t_y;
}
void Vec2::set(const Vec2& v) {
x = v.x;
y = v.y;
}
void Vec2::rotate(const float& t_angle, const float& t_x, const float& t_y) {
float s = Math::sin(t_angle);
float c = Math::cos(t_angle);
x -= t_x;
y -= t_y;
float xnew = x * c - y * s;
float ynew = x * s + y * c;
x = xnew + t_x;
y = ynew + t_y;
}
void Vec2::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void Vec2::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string Vec2::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "Vec2(";
}
res << std::fixed << std::setprecision(4);
res << x << ", " << y << ")";
return res.str();
}
} // namespace Tyra
+299
View File
@@ -0,0 +1,299 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "math/vec4.hpp"
namespace Tyra {
void Vec4::set(const float& t_x, const float& t_y, const float& t_z,
const float& t_w) {
x = t_x;
y = t_y;
z = t_z;
w = t_w;
}
Vec4 Vec4::operator+(const Vec4& v) const {
Vec4 res;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"lqc2 $vf5, 0x0(%2) \n\t"
"vadd.xyz $vf6, $vf4, $vf5 \n\t"
"sqc2 $vf6, 0x0(%0) \n\t"
:
: "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw));
return res;
}
Vec4 Vec4::operator-(const Vec4& v) const {
Vec4 res;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"lqc2 $vf5, 0x0(%2) \n\t"
"vsub.xyz $vf6, $vf4, $vf5 \n\t"
"sqc2 $vf6, 0x0(%0) \n\t"
:
: "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw));
return res;
}
Vec4 Vec4::operator*(const Vec4& v) const {
Vec4 res;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"lqc2 $vf5, 0x0(%2) \n\t"
"vmul.xyzw $vf6, $vf4, $vf5 \n\t"
"sqc2 $vf6, 0x0(%0) \n\t"
:
: "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw));
return res;
}
Vec4 Vec4::operator*(const float& v) const {
Vec4 res;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"mfc1 $8, %2 \n\t"
"qmtc2 $8, $vf5 \n\t"
"vmulx.xyz $vf6, $vf4, $vf5 \n\t"
"sqc2 $vf6, 0x0(%0) \n\t"
:
: "r"(res.xyzw), "r"(this->xyzw), "f"(v));
res.w = w; // Comment below. TODO: fix it in asm
return res;
}
Vec4 Vec4::operator/(const float& v) const {
return Vec4(x / v, y / v, z / v, w);
}
void Vec4::operator+=(const Vec4& v) {
asm volatile(
"lqc2 $vf4, 0x0(%0) \n\t"
"lqc2 $vf5, 0x0(%1) \n\t"
"vadd.xyz $vf4, $vf4, $vf5 \n\t"
"sqc2 $vf4, 0x0(%0) \n\t"
:
: "r"(this->xyzw), "r"(v.xyzw));
}
void Vec4::operator*=(const Vec4& v) {
asm volatile(
"lqc2 $vf4, 0x0(%0) \n\t"
"lqc2 $vf5, 0x0(%1) \n\t"
"vmul.xyzw $vf6, $vf4, $vf5 \n\t"
"sqc2 $vf6, 0x0(%0) \n\t"
:
: "r"(this->xyzw), "r"(v.xyzw));
}
void Vec4::operator*=(const float& v) {
// Hmm... we don't want to modify W in most of the cases
// It should be touched only during rendering process
auto tempW = w;
asm volatile(
"lqc2 $vf4, 0x0(%0) \n\t"
"mfc1 $8, %1 \n\t"
"qmtc2 $8, $vf5 \n\t"
"vmulx.xyz $vf4, $vf4, $vf5 \n\t"
"sqc2 $vf4, 0x0(%0) \n\t"
:
: "r"(this->xyzw), "f"(v));
w = tempW;
}
void Vec4::operator/=(const float& v) {
x /= v;
y /= v;
z /= v;
}
void Vec4::operator=(const Vec4& v) { copy(this, v); }
Vec4 Vec4::operator-(void) const { return Vec4(-x, -y, -z); }
void Vec4::copy(Vec4* out, const float* in) {
asm volatile(
"lqc2 $vf1, 0x00(%1) \n"
"sqc2 $vf1, 0x00(%0) \n"
:
: "r"(out->xyzw), "r"(in));
}
Vec4 Vec4::cross(const Vec4& v) const {
Vec4 res;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"lqc2 $vf5, 0x0(%2) \n\t"
"vopmula.xyz $ACC, $vf4, $vf5 \n\t"
"vopmsub.xyz $vf8, $vf5, $vf4 \n\t"
"vsub.w $vf8, $vf0, $vf0 \n\t"
"sqc2 $vf8, 0x0(%0) \n\t"
:
: "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw));
return res;
}
void Vec4::rotateZ(const int& angle) {
auto s = Math::sin(angle);
auto c = Math::cos(angle);
x = x * c - y * s;
y = x * s + y * c;
}
int Vec4::getRelativeCosBetween(const Vec4& v) const {
return dot3(v) / (length() * v.length());
}
int Vec4::getRelativeAngleBetween(const Vec4& v) const {
return acos(getRelativeCosBetween(v));
}
float Vec4::innerProduct(const Vec4& v) const {
float result;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"lqc2 $vf5, 0x0(%2) \n\t"
"vmul.xyz $vf6, $vf4, $vf5 \n\t"
"vaddy.x $vf6, $vf6, $vf6 \n\t"
"vaddz.x $vf6, $vf6, $vf6 \n\t"
"qmfc2 $2, $vf6 \n\t"
"mtc1 $2, %0 \n\t"
: "=f"(result)
: "r"(this->xyzw), "r"(v.xyzw));
return result;
}
float Vec4::length() const {
float result;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"vmul.xyz $vf5, $vf4, $vf4 \n\t"
"vaddy.x $vf5, $vf5, $vf5 \n\t"
"vaddz.x $vf5, $vf5, $vf5 \n\t"
"vsqrt $Q , $vf5x \n\t"
"vwaitq \n\t"
"vaddq.x $vf8, $vf0, $Q \n\t"
"qmfc2 $2, $vf8 \n\t"
"mtc1 $2, %0 \n\t"
: "=f"(result)
: "r"(this->xyzw));
return result;
}
void Vec4::normalize() {
asm volatile(
"lqc2 $vf4, 0x0(%0) \n\t"
"vmul.xyz $vf5, $vf4, $vf4 \n\t"
"vaddy.x $vf5, $vf5, $vf5 \n\t"
"vaddz.x $vf5, $vf5, $vf5 \n\t"
"vrsqrt $Q, $vf0w, $vf5x \n\t"
"vwaitq \n\t"
"vsub.xyz $vf6, $vf0, $vf0 \n\t"
"vaddw.xyz $vf6, $vf6, $vf4 \n\t"
"vwaitq \n\t"
"vmulq.xyz $vf6, $vf4, $Q \n\t"
"sqc2 $vf6, 0x0(%0) \n\t"
:
: "r"(this->xyzw));
}
float Vec4::distanceTo(const Vec4& v) const {
float result;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t"
"lqc2 $vf5, 0x0(%2) \n\t"
"vsub.xyz $vf6, $vf4, $vf5 \n\t"
"vmul.xyz $vf7, $vf6, $vf6 \n\t"
"vaddy.x $vf7, $vf7, $vf7 \n\t"
"vaddz.x $vf7, $vf7, $vf7 \n\t"
"vsqrt $Q , $vf7x \n\t"
"vwaitq \n\t"
"vaddq.x $vf8, $vf0, $Q \n\t"
"qmfc2 $2, $vf8 \n\t"
"mtc1 $2, %0 \n\t"
: "=f"(result)
: "r"(this->xyzw), "r"(v.xyzw));
return result;
}
u8 Vec4::shouldBeBackfaceCulled(const Vec4* cameraPos, const Vec4* v0,
const Vec4* v1, const Vec4* v2) {
float dot;
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t" // $vf4 = cameraPos
"lqc2 $vf5, 0x0(%2) \n\t" // $vf5 = v0
"lqc2 $vf6, 0x0(%3) \n\t" // $vf6 = v1
"lqc2 $vf7, 0x0(%4) \n\t" // $vf7 = v2
"vsub.xyz $vf8, $vf7, $vf5 \n\t" // $vf8 = $vf7(v2) - $vf5(v0)
"vsub.xyz $vf9, $vf6, $vf5 \n\t" // $vf9 = $vf6(v1) - $vf5(v0)
"vopmula.xyz $ACC, $vf8, $vf9 \n\t" // $vf6 = cross($vf8, $vf9)
"vopmsub.xyz $vf6, $vf9, $vf8 \n\t"
"vsub.w $vf6, $vf6, $vf6 \n\t"
"vsub.xyz $vf7, $vf5, $vf4 \n\t" // $vf7 = $vf5(v0) - $vf4(cameraPos)
"vmul.xyz $vf5, $vf7, $vf6 \n\t" // $vf5 = dot($vf7, $vf6)
"vaddy.x $vf5, $vf5, $vf5 \n\t"
"vaddz.x $vf5, $vf5, $vf5 \n\t"
"qmfc2 $2, $vf5 \n\t" // store result on `dot` variable
"mtc1 $2, %0 \n\t"
: "=f"(dot)
: "r"(cameraPos->xyzw), "r"(v0->xyzw), "r"(v1->xyzw), "r"(v2->xyzw));
return dot <= 0.0F;
}
void Vec4::lerp(const Vec4& v1, const Vec4& v2, const float& interp) {
setLerp(this, v1, v2, interp);
}
Vec4 Vec4::getByLerp(const Vec4& v1, const Vec4& v2, const float& interp) {
Vec4 result;
setLerp(&result, v1, v2, interp);
return result;
}
void Vec4::setLerp(Vec4* output, const Vec4& v1, const Vec4& v2,
const float& interp) {
asm volatile(
"lqc2 $vf4, 0x0(%1) \n\t" // $vf4 = v1
"lqc2 $vf5, 0x0(%2) \n\t" // $vf5 = v2
"mfc1 $8, %3 \n\t" // $vf6 = t
"qmtc2 $8, $vf6 \n\t" // lerp:
"vsub.xyzw $vf7, $vf5, $vf4 \n\t" // $vf7 = v2 - v1
"vmulx.xyzw $vf8, $vf7, $vf6 \n\t" // $vf8 = $vf7 * t
"vadd.xyzw $vf9, $vf8, $vf4 \n\t" // $vf9 = $vf8 + $vf4
"sqc2 $vf9, 0x0(%0) \n\t" // v0 = $vf9
:
: "r"(&output->xyzw), "r"(&v1.xyzw), "r"(&v2.xyzw), "f"(interp));
}
void Vec4::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void Vec4::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string Vec4::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "Vec4(";
}
res << std::fixed << std::setprecision(4);
res << x << ", " << y << ", " << z << ", " << w << ")";
return res.str();
}
} // namespace Tyra
+229
View File
@@ -0,0 +1,229 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020-2022, tyra - https://github.com/h4570/tyrav2
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
# Sandro Wellinator <wellcoj@gmail.com>
*/
#include <tamtypes.h>
#include <loadfile.h>
#include <sifrpc.h>
#include <stdio.h>
#include <string.h>
#include "debug/debug.hpp"
#include "pad/pad.hpp"
namespace Tyra {
/** Init vars, load modules, opens pad port and initializes pad */
Pad::Pad() {}
Pad::~Pad() {}
// ----
// Methods
// ----
void Pad::init() {
this->oldPad = 0;
padInit(0);
this->port = 0; // 0 -> Connector 1, 1 -> Connector 2
this->slot = 0; // Always zero if not using multitap
this->ret = padPortOpen(this->port, this->slot, padBuf);
TYRA_ASSERT(this->ret != 0,
"padPortOpen failed! padPortOpen returned: ", this->ret);
TYRA_ASSERT(this->initPad(), "initPad failed!");
}
/** Wait when pad will be ready (stable and ready) */
int Pad::waitPadReady() {
int state;
int lastState;
char* stateString = new char[16];
state = padGetState(this->port, this->slot);
lastState = -1;
while ((state != PAD_STATE_STABLE) && (state != PAD_STATE_FINDCTP1)) {
if (state != lastState) {
padStateInt2String(state, stateString);
TYRA_LOG("Pad state changed");
printf("Curent pad(%d,%d) status: %s\n", this->port, this->slot,
stateString);
}
lastState = state;
state = padGetState(this->port, this->slot);
}
// Were the pad ever 'out of sync'?
if (lastState != -1) TYRA_LOG("Pad is ready!");
delete[] stateString;
return 0;
}
/** Initializes and checks type of pad */
int Pad::initPad() {
TYRA_LOG("Initializing pad");
this->waitPadReady();
int modes = padInfoMode(this->port, this->slot, PAD_MODETABLE, -1);
TYRA_ASSERT(modes, "Connected device is not a dual shock controller!");
// Verify that the controller has a DUAL SHOCK mode
int i = 0;
do {
if (padInfoMode(this->port, this->slot, PAD_MODETABLE, i) ==
PAD_TYPE_DUALSHOCK)
break;
i++;
} while (i < modes);
TYRA_ASSERT(i < modes, "Connected device is not a dual shock controller!");
// If ExId != 0x0 => This controller has actuator engines
// This check should always pass if the Dual Shock test above passed
this->ret = padInfoMode(this->port, this->slot, PAD_MODECUREXID, 0);
TYRA_ASSERT(this->ret, "Connected device is not a dual shock controller!");
TYRA_LOG("Enabling dual shock functions.");
// When using MMODE_LOCK, user cant change mode with Select button
padSetMainMode(this->port, this->slot, PAD_MMODE_DUALSHOCK, PAD_MMODE_LOCK);
this->waitPadReady();
TYRA_LOG("Pad has pressure sensitive buttons? ",
padInfoPressMode(this->port, this->slot));
this->waitPadReady();
padEnterPressMode(this->port, this->slot); // Set pressure sensitive mode
this->waitPadReady();
this->actuators = padInfoAct(this->port, this->slot, -1, 0);
TYRA_LOG("# of actuators: ", this->actuators);
if (actuators != 0) {
this->actAlign[0] = 0; // Enable small engine
this->actAlign[1] = 1; // Enable big engine
this->actAlign[2] = 0xff;
this->actAlign[3] = 0xff;
this->actAlign[4] = 0xff;
this->actAlign[5] = 0xff;
this->waitPadReady();
TYRA_LOG("padSetActAlign: ",
padSetActAlign(this->port, this->slot, actAlign));
} else
TYRA_LOG("Did not find any actuators.");
this->waitPadReady();
TYRA_LOG("Pad initialized!");
return 1;
}
/** Updates state of joys/buttons. Called by engine */
void Pad::update() {
int x = 0;
this->ret = padGetState(this->port, this->slot);
while ((this->ret != PAD_STATE_STABLE) && (this->ret != PAD_STATE_FINDCTP1)) {
if (this->ret == PAD_STATE_DISCONN)
printf("Pad(%d, %d) is disconnected\n", this->port, this->slot);
this->ret = padGetState(this->port, this->slot);
}
if (x == 1) TYRA_LOG("Pad: OK!\n");
this->ret = padRead(this->port, this->slot, &this->buttons);
if (this->ret != 0) {
this->padData = 0xffff ^ this->buttons.btns;
this->newPad = this->padData & ~this->oldPad;
this->oldPad = this->padData;
this->reset();
// Digital buttons
this->rightJoyPad.h = this->buttons.rjoy_h;
this->rightJoyPad.v = this->buttons.rjoy_v;
this->leftJoyPad.h = this->buttons.ljoy_h;
this->leftJoyPad.v = this->buttons.ljoy_v;
this->rightJoyPad.isCentered =
this->buttons.rjoy_h == 127 && this->buttons.rjoy_v == 127;
this->rightJoyPad.isMoved = !this->rightJoyPad.isCentered;
this->leftJoyPad.isCentered =
this->buttons.ljoy_h == 127 && this->buttons.ljoy_v == 127;
this->leftJoyPad.isMoved = !this->leftJoyPad.isCentered;
this->handleClickedButtons();
this->handlePressedButtons();
}
}
/** Update clicked buttons state */
void Pad::handleClickedButtons() {
if (this->newPad & PAD_CROSS) this->clicked.Cross = 1;
if (this->newPad & PAD_SQUARE) this->clicked.Square = 1;
if (this->newPad & PAD_TRIANGLE) this->clicked.Triangle = 1;
if (this->newPad & PAD_CIRCLE) this->clicked.Circle = 1;
if (this->newPad & PAD_UP) this->clicked.DpadUp = 1;
if (this->newPad & PAD_DOWN) this->clicked.DpadDown = 1;
if (this->newPad & PAD_LEFT) this->clicked.DpadLeft = 1;
if (this->newPad & PAD_RIGHT) this->clicked.DpadRight = 1;
if (this->newPad & PAD_L1) this->clicked.L1 = 1;
if (this->newPad & PAD_L2) this->clicked.L2 = 1;
if (this->newPad & PAD_L3) this->clicked.L3 = 1;
if (this->newPad & PAD_R1) this->clicked.R1 = 1;
if (this->newPad & PAD_R2) this->clicked.R2 = 1;
if (this->newPad & PAD_R3) this->clicked.R3 = 1;
if (this->newPad & PAD_START) this->clicked.Start = 1;
if (this->newPad & PAD_SELECT) this->clicked.Select = 1;
}
/** Update pressed buttons state */
void Pad::handlePressedButtons() {
if (this->buttons.cross_p) this->pressed.Cross = 1;
if (this->buttons.square_p) this->pressed.Square = 1;
if (this->buttons.triangle_p) this->pressed.Triangle = 1;
if (this->buttons.circle_p) this->pressed.Circle = 1;
if (this->buttons.up_p) this->pressed.DpadUp = 1;
if (this->buttons.down_p) this->pressed.DpadDown = 1;
if (this->buttons.left_p) this->pressed.DpadLeft = 1;
if (this->buttons.right_p) this->pressed.DpadRight = 1;
if (this->buttons.l1_p) this->pressed.L1 = 1;
if (this->buttons.l2_p) this->pressed.L2 = 1;
if (this->buttons.r1_p) this->pressed.R1 = 1;
if (this->buttons.r2_p) this->pressed.R2 = 1;
}
/** Resets state of joys/buttons */
void Pad::reset() {
this->clicked.Cross = 0;
this->clicked.Square = 0;
this->clicked.Triangle = 0;
this->clicked.Circle = 0;
this->clicked.DpadUp = 0;
this->clicked.DpadDown = 0;
this->clicked.DpadLeft = 0;
this->clicked.DpadRight = 0;
this->clicked.L1 = 0;
this->clicked.L2 = 0;
this->clicked.L3 = 0;
this->clicked.R1 = 0;
this->clicked.R2 = 0;
this->clicked.R3 = 0;
this->clicked.Start = 0;
this->clicked.Select = 0;
this->pressed.Cross = 0;
this->pressed.Square = 0;
this->pressed.Triangle = 0;
this->pressed.Circle = 0;
this->pressed.DpadUp = 0;
this->pressed.DpadDown = 0;
this->pressed.DpadLeft = 0;
this->pressed.DpadRight = 0;
this->pressed.L1 = 0;
this->pressed.L2 = 0;
this->pressed.R1 = 0;
this->pressed.R2 = 0;
}
} // Namespace Tyra
+27
View File
@@ -0,0 +1,27 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/2d/renderer_2d.hpp"
namespace Tyra {
Renderer2D::Renderer2D() {}
Renderer2D::~Renderer2D() {}
void Renderer2D::init(RendererCore* t_rendererCore) { core = t_rendererCore; }
void Renderer2D::render(Sprite* sprite) {
auto* texture = core->texture.repository.getBySpriteOrMesh(sprite->getId());
auto texBuffers = core->texture.useTexture(texture);
core->texture.updateClutBuffer(texBuffers.clut);
core->renderer2D.render(sprite, texBuffers, texture);
}
} // namespace Tyra
+53
View File
@@ -0,0 +1,53 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/bbox/bbox.hpp"
namespace Tyra {
BBox::BBox(CoreBBox** t_bboxes, const u32& count) : CoreBBox(t_bboxes, count) {
setData();
}
BBox::BBox(Vec4* t_vertices, u32 count) : CoreBBox(t_vertices, count) {
setData();
}
BBox::BBox(Vec4* t_vertices, u32* faces, u32 count)
: CoreBBox(t_vertices, faces, count) {
setData();
}
BBox::BBox(Vec4* t_vertices) : CoreBBox(t_vertices) { setData(); }
void BBox::setData() {
// This might be shortened with Vec4 operator overloading, but current
// implementation is more human readable.
_height = _vertices[0].y - _vertices[2].y;
_width = _vertices[0].x - _vertices[4].x;
_depth = _vertices[0].z - _vertices[1].z;
_centerVector = _vertices[0];
_centerVector.x += (_width / 2);
_centerVector.y += (_height / 2);
_centerVector.z += (_depth / 2);
// Z-Axis faces
_frontFace = BBoxFace(_vertices[1], _vertices[7], _vertices[1].z);
_backFace = BBoxFace(_vertices[0], _vertices[6], _vertices[0].z);
// X-Axis faces
_leftFace = BBoxFace(_vertices[0], _vertices[3], _vertices[0].x);
_rightFace = BBoxFace(_vertices[4], _vertices[7], _vertices[4].x);
// Y-Axis faces
_topFace = BBoxFace(_vertices[2], _vertices[7], _vertices[2].y);
_bottomFace = BBoxFace(_vertices[0], _vertices[5], _vertices[0].y);
}
} // namespace Tyra
+144
View File
@@ -0,0 +1,144 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tamtypes.h>
#include "math/m4x4.hpp"
#include "renderer/3d/mesh/mesh.hpp"
namespace Tyra {
Mesh::Mesh(const MeshBuilderData& data) {
id = rand() % 1000000;
framesCount = data.framesCount;
TYRA_ASSERT(framesCount > 0, "Frames count must be greater than 0");
materialsCount = data.materialsCount;
TYRA_ASSERT(materialsCount > 0, "Materials count must be greater than 0");
frames = new MeshFrame*[framesCount];
for (u32 i = 0; i < framesCount; i++) {
frames[i] = new MeshFrame(data, i);
}
materials = new MeshMaterial*[materialsCount];
for (u32 i = 0; i < materialsCount; i++) {
materials[i] = new MeshMaterial(data, i);
}
translation.translate(Vec4(0.0F, 0.0F, 0.0F, 1.0F));
initMesh();
_isMother = true;
}
Mesh::Mesh(const Mesh& mesh) {
id = rand() % 1000000;
framesCount = mesh.framesCount;
materialsCount = mesh.materialsCount;
frames = new MeshFrame*[framesCount];
for (u32 i = 0; i < framesCount; i++) {
frames[i] = new MeshFrame(*mesh.frames[i]);
}
materials = new MeshMaterial*[materialsCount];
for (u32 i = 0; i < materialsCount; i++) {
materials[i] = new MeshMaterial(*mesh.materials[i]);
}
translation.translate(Vec4(0.0F, 0.0F, 0.0F, 1.0F));
initMesh();
_isMother = false;
}
Mesh::~Mesh() {
for (u32 i = 0; i < framesCount; i++) {
delete frames[i];
}
delete[] frames;
for (u32 i = 0; i < materialsCount; i++) {
delete materials[i];
delete[] materials;
}
}
M4x4 Mesh::getModelMatrix() const { return translation * rotation * scale; }
void Mesh::initMesh() {
animState.startFrame = 0;
animState.endFrame = 0;
animState.interpolation = 0.0F;
animState.animType = 0;
animState.currentFrame = 0;
animState.stayFrame = 0;
animState.isStayFrameSet = false;
animState.nextFrame = 0;
animState.speed = 0.1F;
}
void Mesh::playAnimation(const u32& t_startFrame, const u32& t_endFrame) {
TYRA_ASSERT(framesCount > 0,
"Cant play animation, because no mesh data was loaded!");
TYRA_ASSERT(framesCount != 1,
"Cant play animation, because this mesh have only one frame.");
TYRA_ASSERT(
t_endFrame < framesCount,
"End frame value is too high. Valid range: (0, getFramesCount()-1)");
animState.startFrame = t_startFrame;
animState.endFrame = t_endFrame;
if (animState.currentFrame == t_startFrame)
animState.nextFrame = t_endFrame;
else
animState.nextFrame = t_startFrame;
}
void Mesh::playAnimation(const u32& t_startFrame, const u32& t_endFrame,
const u32& t_stayFrame) {
TYRA_ASSERT(framesCount > 0,
"Cant play animation, because no mesh data was loaded!");
TYRA_ASSERT(framesCount != 1,
"Cant play animation, because this mesh have only one frame.");
TYRA_ASSERT(
t_endFrame < framesCount,
"End frame value is too high. Valid range: (0, getFramesCount()-1)");
animState.startFrame = t_startFrame;
animState.endFrame = t_endFrame;
animState.isStayFrameSet = true;
animState.stayFrame = t_stayFrame;
animState.nextFrame = t_startFrame;
}
void Mesh::animate() {
animState.interpolation += animState.speed;
if (animState.interpolation >= 1.0F) {
animState.interpolation = 0.0F;
animState.currentFrame = animState.nextFrame;
if (++animState.nextFrame > animState.endFrame) {
if (animState.isStayFrameSet) {
animState.isStayFrameSet = false;
animState.nextFrame = animState.stayFrame;
animState.startFrame = animState.stayFrame;
animState.endFrame = animState.stayFrame;
} else {
animState.nextFrame = animState.startFrame;
}
}
}
}
} // namespace Tyra
+150
View File
@@ -0,0 +1,150 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include <tamtypes.h>
#include <string>
#include "math/vec4.hpp"
#include "renderer/models/color.hpp"
#include "renderer/3d/mesh/mesh_frame.hpp"
namespace Tyra {
MeshFrame::MeshFrame(const MeshBuilderData& data, const u32& index) {
TYRA_ASSERT(index < data.framesCount && index >= 0, "Provided index \"",
index, "\" is out of range");
id = rand() % 1000000;
vertices = data.frames[index]->vertices;
TYRA_ASSERT(vertices != nullptr, "Vertices are required");
vertexCount = data.frames[index]->verticesCount;
TYRA_ASSERT(vertexCount > 0, "Vertices count must be greater than 0");
if (data.normalsEnabled) {
normals = data.frames[index]->normals;
normalsCount = data.frames[index]->normalsCount;
TYRA_ASSERT(normals != nullptr, "Normals are required");
} else {
normalsCount = 0;
normals = nullptr;
}
if (data.textureCoordsEnabled) {
textureCoords = data.frames[index]->textureCoords;
textureCoordsCount = data.frames[index]->textureCoordsCount;
TYRA_ASSERT(textureCoords != nullptr, "Texture coordinates are required");
} else {
textureCoordsCount = 0;
textureCoords = nullptr;
}
if (data.manyColorsEnabled) {
colors = data.frames[index]->colors;
colorsCount = data.frames[index]->colorsCount;
TYRA_ASSERT(colors != nullptr, "Colors are required");
} else {
colorsCount = 0;
colors = nullptr;
}
bbox =
new BBox(data.frames[index]->vertices, data.frames[index]->verticesCount);
_isMother = true;
}
MeshFrame::MeshFrame(const MeshFrame& frame) {
id = rand() % 1000000;
vertices = frame.vertices;
normals = frame.normals;
textureCoords = frame.textureCoords;
colors = frame.colors;
vertexCount = frame.vertexCount;
normalsCount = frame.normalsCount;
textureCoordsCount = frame.textureCoordsCount;
colorsCount = frame.colorsCount;
bbox = frame.bbox;
_isMother = false;
}
MeshFrame::~MeshFrame() {
if (_isMother) {
delete[] vertices;
if (normals) delete[] normals;
if (textureCoords) delete[] textureCoords;
if (colors) delete[] colors;
delete bbox;
}
}
void MeshFrame::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void MeshFrame::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string MeshFrame::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "MeshFrame(";
}
res << std::fixed << std::setprecision(2);
res << "Id: " << id << ", " << std::endl;
res << "VertexCount: " << vertexCount << ", " << std::endl;
res << "NormalsCount: " << normalsCount << ", " << std::endl;
res << "TextureCoordsCount: " << textureCoordsCount << ", " << std::endl;
res << "ColorsCount: " << colorsCount << ", " << std::endl;
res << "BBox: " << bbox->getPrint() << ", " << std::endl;
res << "Vertices: ";
for (u32 i = 0; i < vertexCount; i++) {
res << vertices[i].getPrint() << ", " << std::endl;
}
if (normals) {
res << "Normals: ";
for (u32 i = 0; i < normalsCount; i++) {
res << normals[i].getPrint() << ", " << std::endl;
}
}
if (textureCoords) {
res << "TextureCoords: ";
for (u32 i = 0; i < textureCoordsCount; i++) {
res << textureCoords[i].getPrint() << ", " << std::endl;
}
}
if (colors) {
res << "Colors: ";
for (u32 i = 0; i < colorsCount; i++) {
res << colors[i].getPrint() << ", " << std::endl;
}
}
res << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,179 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tamtypes.h>
#include <string>
#include <cstdlib>
#include "renderer/3d/mesh/mesh_material.hpp"
namespace Tyra {
MeshMaterial::MeshMaterial(const MeshBuilderData& data,
const u32& materialIndex)
: singleColor(false) {
TYRA_ASSERT(materialIndex < data.materialsCount && materialIndex >= 0,
"Provided index \"", materialIndex, "\" is out of range");
id = rand() % 1000000;
vertexFaces = data.materials[materialIndex]->vertexFaces;
TYRA_ASSERT(vertexFaces != nullptr, "Vertex faces are required");
if (data.textureCoordsEnabled) {
textureCoordFaces = data.materials[materialIndex]->textureCoordFaces;
TYRA_ASSERT(textureCoordFaces != nullptr,
"Texture coord faces are required");
} else {
textureCoordFaces = nullptr;
}
if (data.normalsEnabled) {
normalFaces = data.materials[materialIndex]->normalFaces;
TYRA_ASSERT(normalFaces != nullptr, "Normal faces are required");
} else {
normalFaces = nullptr;
}
if (data.manyColorsEnabled) {
colorFaces = data.materials[materialIndex]->colorFaces;
singleColorFlag = false;
TYRA_ASSERT(colorFaces != nullptr, "Colors faces are required");
} else {
colorFaces = nullptr;
singleColorFlag = true;
}
singleColor.set(128.0F, 128.0F, 128.0F, 128.0F);
facesCount = data.materials[materialIndex]->count;
TYRA_ASSERT(facesCount > 0, "Faces count must be greater than 0");
_name = data.materials[materialIndex]->name;
TYRA_ASSERT(_name.length() > 0, "MeshMaterial name cannot be empty");
framesCount = data.framesCount;
frames = new MeshMaterialFrame*[framesCount];
for (u32 i = 0; i < framesCount; i++) {
frames[i] = new MeshMaterialFrame(data, i, materialIndex);
}
_isMother = true;
}
MeshMaterial::MeshMaterial(const MeshMaterial& mesh) {
id = rand() % 1000000;
vertexFaces = mesh.vertexFaces;
textureCoordFaces = mesh.textureCoordFaces;
normalFaces = mesh.normalFaces;
colorFaces = mesh.colorFaces;
facesCount = mesh.facesCount;
framesCount = mesh.framesCount;
_name = mesh._name;
singleColor.set(128.0F, 128.0F, 128.0F, 128.0F);
frames = new MeshMaterialFrame*[framesCount];
for (u32 i = 0; i < framesCount; i++) {
frames[i] = new MeshMaterialFrame(*mesh.frames[i]);
}
_isMother = false;
}
MeshMaterial::~MeshMaterial() {
if (_isMother) {
delete[] vertexFaces;
if (textureCoordFaces) delete[] textureCoordFaces;
if (normalFaces) delete[] normalFaces;
if (colorFaces) delete[] colorFaces;
}
for (u32 i = 0; i < framesCount; i++) {
delete frames[i];
}
delete[] frames;
}
const BBox& MeshMaterial::getBBox(const u32& frame) const {
return frames[frame]->getBBox();
}
void MeshMaterial::setSingleColorFlag(const u8& flag) {
TYRA_ASSERT(
colorFaces != nullptr,
"Colors and color faces are required to use color-per-vertex mode");
singleColorFlag = flag;
}
void MeshMaterial::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void MeshMaterial::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string MeshMaterial::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "MeshMaterial(";
}
res << std::endl;
res << std::fixed << std::setprecision(2);
res << "Id: " << id << ", " << std::endl;
res << "Name: " << _name << ", " << std::endl;
res << "FacesCount: " << facesCount << ", " << std::endl;
res << "FramesCount: " << framesCount << ", " << std::endl;
res << "vertexFaces: " << std::endl;
for (u32 i = 0; i < facesCount; i++) {
res << vertexFaces[i] << ", ";
if (i % 3 == 2) res << std::endl;
}
if (textureCoordFaces) {
res << "TextureCoordFaces: " << std::endl;
for (u32 i = 0; i < facesCount; i++) {
res << textureCoordFaces[i] << ", ";
if (i % 3 == 2) res << std::endl;
}
}
if (normalFaces) {
res << "NormalFaces: " << std::endl;
for (u32 i = 0; i < facesCount; i++) {
res << normalFaces[i] << ", ";
if (i % 3 == 2) res << std::endl;
}
}
if (colorFaces) {
res << "ColorFaces: " << std::endl;
for (u32 i = 0; i < facesCount; i++) {
res << colorFaces[i] << ", ";
if (i % 3 == 2) res << std::endl;
}
}
res << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,51 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include <tamtypes.h>
#include "renderer/models/color.hpp"
#include "loaders/3d/builder/mesh_builder_data.hpp"
#include "renderer/3d/mesh/mesh_material_frame.hpp"
namespace Tyra {
MeshMaterialFrame::MeshMaterialFrame(const MeshBuilderData& data,
const u32& frameIndex,
const u32& materialIndex) {
TYRA_ASSERT(frameIndex < data.framesCount && frameIndex >= 0,
"Provided index \"", frameIndex, "\" is out of range");
TYRA_ASSERT(materialIndex < data.materialsCount && materialIndex >= 0,
"Provided index \"", materialIndex, "\" is out of range");
id = rand() % 1000000;
bbox = new BBox(data.frames[frameIndex]->vertices,
data.materials[materialIndex]->vertexFaces,
data.materials[materialIndex]->count);
_isMother = true;
}
MeshMaterialFrame::MeshMaterialFrame(const MeshMaterialFrame& frame) {
id = rand() % 1000000;
bbox = frame.bbox;
_isMother = false;
}
MeshMaterialFrame::~MeshMaterialFrame() {
if (_isMother) {
delete bbox;
}
}
} // namespace Tyra
@@ -0,0 +1,25 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/data/mcpip_block_data.hpp"
namespace Tyra {
McpipBlockData::McpipBlockData() {
vertices = nullptr;
textureCoords = nullptr;
comboData = nullptr;
offset = 0.0F;
count = 0;
}
McpipBlockData::~McpipBlockData() {}
} // namespace Tyra
@@ -0,0 +1,132 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0F
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.hpp"
namespace Tyra {
McpipMultiTexBlockData::McpipMultiTexBlockData() {
allocateTempData();
unroll();
dellocateTempData();
}
McpipMultiTexBlockData::~McpipMultiTexBlockData() {
if (comboData != nullptr) {
delete[] comboData;
}
}
void McpipMultiTexBlockData::allocateTempData() {
u32 tempVertsStsCount = 24;
tempVerts = new Tyra::Vec4[tempVertsStsCount];
tempVerts[0].set(-1.0F, -1.0F, -1.0F);
tempVerts[1].set(1.0F, -1.0F, -1.0F);
tempVerts[2].set(1.0F, -1.0F, 1.0F);
tempVerts[3].set(-1.0F, -1.0F, 1.0F);
tempVerts[4].set(1.0F, -1.0F, 1.0F);
tempVerts[5].set(1.0F, 1.0F, 1.0F);
tempVerts[6].set(-1.0F, 1.0F, 1.0F);
tempVerts[7].set(-1.0F, -1.0F, 1.0F);
tempVerts[8].set(-1.0F, -1.0F, 1.0F);
tempVerts[9].set(-1.0F, 1.0F, 1.0F);
tempVerts[10].set(-1.0F, 1.0F, -1.0F);
tempVerts[11].set(-1.0F, -1.0F, -1.0F);
tempVerts[12].set(1.0F, -1.0F, -1.0F);
tempVerts[13].set(1.0F, 1.0F, -1.0F);
tempVerts[14].set(1.0F, 1.0F, 1.0F);
tempVerts[15].set(1.0F, -1.0F, 1.0F);
tempVerts[16].set(-1.0F, -1.0F, -1.0F);
tempVerts[17].set(-1.0F, 1.0F, -1.0F);
tempVerts[18].set(1.0F, 1.0F, -1.0F);
tempVerts[19].set(1.0F, -1.0F, -1.0F);
tempVerts[20].set(1.0F, 1.0F, -1.0F);
tempVerts[21].set(-1.0F, 1.0F, -1.0F);
tempVerts[22].set(-1.0F, 1.0F, 1.0F);
tempVerts[23].set(1.0F, 1.0F, 1.0F);
tempTexCoords = new Tyra::Vec4[tempVertsStsCount];
tempTexCoords[0].set(0.062721F, 0.813282F, 1.0F, 0.0F);
tempTexCoords[1].set(0.062721F, 0.875327F, 1.0F, 0.0F);
tempTexCoords[2].set(0.000676F, 0.875327F, 1.0F, 0.0F);
tempTexCoords[3].set(0.000676F, 0.813282F, 1.0F, 0.0F);
tempTexCoords[4].set(0.062095F, 0.750461F, 1.0F, 0.0F);
tempTexCoords[5].set(0.062095F, 0.812244F, 1.0F, 0.0F);
tempTexCoords[6].set(0.000311F, 0.812244F, 1.0F, 0.0F);
tempTexCoords[7].set(0.000311F, 0.750461F, 1.0F, 0.0F);
tempTexCoords[8].set(0.062629F, 0.687779F, 1.0F, 0.0F);
tempTexCoords[9].set(0.000000F, 0.687779F, 1.0F, 0.0F);
tempTexCoords[10].set(0.000000F, 0.624816F, 1.0F, 0.0F);
tempTexCoords[11].set(0.062629F, 0.624816F, 1.0F, 0.0F);
tempTexCoords[12].set(0.062667F, 0.626382F, 1.0F, 0.0F);
tempTexCoords[13].set(0.000000F, 0.626382F, 1.0F, 0.0F);
tempTexCoords[14].set(0.000000F, 0.561642F, 1.0F, 0.0F);
tempTexCoords[15].set(0.062667F, 0.561642F, 1.0F, 0.0F);
tempTexCoords[16].set(0.000000F, 0.937621F, 1.0F, 0.0F);
tempTexCoords[17].set(0.000000F, 0.875134F, 1.0F, 0.0F);
tempTexCoords[18].set(0.062128F, 0.875134F, 1.0F, 0.0F);
tempTexCoords[19].set(0.062128F, 0.937621F, 1.0F, 0.0F);
tempTexCoords[20].set(0.000000F, 0.937223F, 1.0F, 0.0F);
tempTexCoords[21].set(0.062223F, 0.937223F, 1.0F, 0.0F);
tempTexCoords[22].set(0.062223F, 0.999641F, 1.0F, 0.0F);
tempTexCoords[23].set(0.000000F, 0.999641F, 1.0F, 0.0F);
// Because 16 blocks can fit in single column of 256x256 tex atlas
offset = 1.0F / 16.0F;
for (u32 i = 0; i < tempVertsStsCount; i++)
tempTexCoords[i].y = 1.0F - tempTexCoords[i].y;
tempVertFaces = new u32[36];
tempTexCoordsFaces = new u32[36];
std::string vertexFaces =
"1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17,"
"19,20,21,22,23,21,23,24";
std::stringstream ssVertexFaces(vertexFaces);
std::string texCoordFaces =
"1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17,"
"19,20,21,22,23,21,23,24";
std::stringstream ssTexCoordFaces(texCoordFaces);
int i = 0;
std::string item;
while (std::getline(ssVertexFaces, item, ','))
tempVertFaces[i++] = std::stoi(item) - 1;
i = 0;
while (std::getline(ssTexCoordFaces, item, ','))
tempTexCoordsFaces[i++] = std::stoi(item) - 1;
}
void McpipMultiTexBlockData::unroll() {
count = 36;
comboData = new Tyra::Vec4[36 * 2];
vertices = &comboData[0];
textureCoords = &comboData[36];
for (u32 i = 0; i < count; i++) {
vertices[i] = tempVerts[tempVertFaces[i]];
textureCoords[i] = tempTexCoords[tempTexCoordsFaces[i]];
}
}
void McpipMultiTexBlockData::dellocateTempData() {
delete[] tempVerts;
delete[] tempTexCoords;
delete[] tempVertFaces;
delete[] tempTexCoordsFaces;
}
} // namespace Tyra
@@ -0,0 +1,132 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0F
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.hpp"
namespace Tyra {
McpipSingleTexBlockData::McpipSingleTexBlockData() {
allocateTempData();
unroll();
dellocateTempData();
}
McpipSingleTexBlockData::~McpipSingleTexBlockData() {
if (comboData != nullptr) {
delete[] comboData;
}
}
void McpipSingleTexBlockData::allocateTempData() {
u32 tempVertsStsCount = 24;
tempVerts = new Tyra::Vec4[tempVertsStsCount];
tempVerts[0].set(-1.0F, -1.0F, -1.0F);
tempVerts[1].set(1.0F, -1.0F, -1.0F);
tempVerts[2].set(1.0F, -1.0F, 1.0F);
tempVerts[3].set(-1.0F, -1.0F, 1.0F);
tempVerts[4].set(1.0F, -1.0F, 1.0F);
tempVerts[5].set(1.0F, 1.0F, 1.0F);
tempVerts[6].set(-1.0F, 1.0F, 1.0F);
tempVerts[7].set(-1.0F, -1.0F, 1.0F);
tempVerts[8].set(-1.0F, -1.0F, 1.0F);
tempVerts[9].set(-1.0F, 1.0F, 1.0F);
tempVerts[10].set(-1.0F, 1.0F, -1.0F);
tempVerts[11].set(-1.0F, -1.0F, -1.0F);
tempVerts[12].set(1.0F, -1.0F, -1.0F);
tempVerts[13].set(1.0F, 1.0F, -1.0F);
tempVerts[14].set(1.0F, 1.0F, 1.0F);
tempVerts[15].set(1.0F, -1.0F, 1.0F);
tempVerts[16].set(-1.0F, -1.0F, -1.0F);
tempVerts[17].set(-1.0F, 1.0F, -1.0F);
tempVerts[18].set(1.0F, 1.0F, -1.0F);
tempVerts[19].set(1.0F, -1.0F, -1.0F);
tempVerts[20].set(1.0F, 1.0F, -1.0F);
tempVerts[21].set(-1.0F, 1.0F, -1.0F);
tempVerts[22].set(-1.0F, 1.0F, 1.0F);
tempVerts[23].set(1.0F, 1.0F, 1.0F);
tempTexCoords = new Tyra::Vec4[tempVertsStsCount];
tempTexCoords[0].set(0.000618F, 0.937685F, 1.0F, 0.0F);
tempTexCoords[1].set(0.062592F, 0.937685F, 1.0F, 0.0F);
tempTexCoords[2].set(0.062592F, 0.999658F, 1.0F, 0.0F);
tempTexCoords[3].set(0.000618F, 0.999658F, 1.0F, 0.0F);
tempTexCoords[4].set(0.062608F, 0.937379F, 1.0F, 0.0F);
tempTexCoords[5].set(0.062576F, 0.999352F, 1.0F, 0.0F);
tempTexCoords[6].set(0.000602F, 0.999320F, 1.0F, 0.0F);
tempTexCoords[7].set(0.000634F, 0.937346F, 1.0F, 0.0F);
tempTexCoords[8].set(0.062592F, 0.937381F, 1.0F, 0.0F);
tempTexCoords[9].set(0.062592F, 0.999354F, 1.0F, 0.0F);
tempTexCoords[10].set(0.000618F, 0.999354F, 1.0F, 0.0F);
tempTexCoords[11].set(0.000618F, 0.937381F, 1.0F, 0.0F);
tempTexCoords[12].set(0.062618F, 0.937600F, 1.0F, 0.0F);
tempTexCoords[13].set(0.062618F, 0.999542F, 1.0F, 0.0F);
tempTexCoords[14].set(0.000603F, 0.999576F, 1.0F, 0.0F);
tempTexCoords[15].set(0.000616F, 0.937585F, 1.0F, 0.0F);
tempTexCoords[16].set(0.062561F, 0.937533F, 1.0F, 0.0F);
tempTexCoords[17].set(0.062561F, 0.999446F, 1.0F, 0.0F);
tempTexCoords[18].set(0.000649F, 0.999446F, 1.0F, 0.0F);
tempTexCoords[19].set(0.000649F, 0.937533F, 1.0F, 0.0F);
tempTexCoords[20].set(0.000556F, 0.937533F, 1.0F, 0.0F);
tempTexCoords[21].set(0.062530F, 0.937533F, 1.0F, 0.0F);
tempTexCoords[22].set(0.062530F, 0.999506F, 1.0F, 0.0F);
tempTexCoords[23].set(0.000556F, 0.999506F, 1.0F, 0.0F);
// Because 16 blocks can fit in single column of 256x256 tex atlas
offset = 1.0F / 16.0F;
for (u32 i = 0; i < tempVertsStsCount; i++)
tempTexCoords[i].y = 1.0F - tempTexCoords[i].y;
tempVertFaces = new u32[36];
tempTexCoordsFaces = new u32[36];
std::string vertexFaces =
"1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17,"
"19,20,21,22,23,21,23,24";
std::stringstream ssVertexFaces(vertexFaces);
std::string texCoordFaces =
"1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17,"
"19,20,21,22,23,21,23,24";
std::stringstream ssTexCoordFaces(texCoordFaces);
int i = 0;
std::string item;
while (std::getline(ssVertexFaces, item, ','))
tempVertFaces[i++] = std::stoi(item) - 1;
i = 0;
while (std::getline(ssTexCoordFaces, item, ','))
tempTexCoordsFaces[i++] = std::stoi(item) - 1;
}
void McpipSingleTexBlockData::unroll() {
count = 36;
comboData = new Tyra::Vec4[36 * 2];
vertices = &comboData[0];
textureCoords = &comboData[36];
for (u32 i = 0; i < count; i++) {
vertices[i] = tempVerts[tempVertFaces[i]];
textureCoords[i] = tempTexCoords[tempTexCoordsFaces[i]];
}
}
void McpipSingleTexBlockData::dellocateTempData() {
delete[] tempVerts;
delete[] tempTexCoords;
delete[] tempVertFaces;
delete[] tempTexCoordsFaces;
}
} // namespace Tyra
@@ -0,0 +1,143 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tamtypes.h>
#include "renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp"
#include "thread/threading.hpp"
namespace Tyra {
MinecraftPipeline::MinecraftPipeline() { latestMode = UndefinedMcpipProgram; }
MinecraftPipeline::~MinecraftPipeline() {
if (bbox) {
delete bbox;
}
}
void MinecraftPipeline::init(RendererCore* core) {
rendererCore = core;
manager.init(core);
initBBox();
}
void MinecraftPipeline::onUse() {
manager.uploadVU1Programs();
changeMode(McPipCull, true);
}
void MinecraftPipeline::initBBox() {
const auto& block = manager.getBlockData();
bbox = new RenderBBox(block.vertices, block.count);
}
void MinecraftPipeline::render(McpipBlock* blocks, const u32& count,
Texture* t_tex, const bool& isMulti,
const bool& noClipChecks) {
auto texBuffers = rendererCore->texture.useTexture(t_tex);
rendererCore->gs.prim.mapping = 1;
manager.clearLastProgram();
std::vector<u32> cullIndexes;
if (noClipChecks) {
for (u32 i = 0; i < count; i++) cullIndexes.push_back(i);
cull(blocks, cullIndexes, &texBuffers, isMulti);
} else {
u32 culled = 0, clipped = 0;
std::vector<u32> clipIndexes;
for (u32 i = 0; i < count; i++) {
auto frustum = isInFrustum(blocks[i]);
if (frustum == CoreBBoxFrustum::IN_FRUSTUM) {
cullIndexes.push_back(i);
culled++;
} else if (frustum == CoreBBoxFrustum::PARTIALLY_IN_FRUSTUM) {
clipIndexes.push_back(i);
clipped++;
}
}
if (culled > 0) cull(blocks, cullIndexes, &texBuffers, isMulti);
if (clipped > 0) clip(blocks, clipIndexes, &texBuffers, isMulti);
}
Threading::switchThread();
}
CoreBBoxFrustum MinecraftPipeline::isInFrustum(const McpipBlock& block) const {
const auto* frustumPlanes = rendererCore->renderer3D.frustumPlanes.getAll();
return bbox->clipIsInFrustum(frustumPlanes, block.model);
}
void MinecraftPipeline::cull(McpipBlock* blocks,
const std::vector<u32>& indexes,
RendererCoreTextureBuffers* texBuffers,
const bool& isMulti) {
changeMode(McPipCull, false);
auto maxBlocksPerQBuffer = manager.culler.getMaxBlocksCountPerQBuffer();
auto partsCount = static_cast<u32>(
ceil(indexes.size() / static_cast<float>(maxBlocksPerQBuffer)));
for (u32 i = 0; i < partsCount; i++) {
u32 subArraySize = i != partsCount - 1
? maxBlocksPerQBuffer
: indexes.size() - i * maxBlocksPerQBuffer;
McpipBlock** blockPointerArray = new McpipBlock*[subArraySize];
u32 blockPointerArrayCount = 0;
for (u32 j = 0; j < subArraySize; j++) {
blockPointerArray[blockPointerArrayCount++] =
&blocks[indexes[i * maxBlocksPerQBuffer + j]];
}
manager.cull(blockPointerArray, blockPointerArrayCount, texBuffers,
isMulti);
delete[] blockPointerArray;
}
}
// -- 2nd qbuff = 36 * 3 (ST,RGBA,STQ)
// * 2 (We will have at least 2x more verts after clip)
// + Set, lod, set, clut, prim = 221
// -- 1st qbuff: 500 - 221 = 279
// Tags: 279 - for example 20 = 259
// Vert + ST from EE = 259 / 2 = 129 verts | OK!
void MinecraftPipeline::clip(McpipBlock* blocks,
const std::vector<u32>& indexes,
RendererCoreTextureBuffers* texBuffers,
const bool& isMulti) {
changeMode(McPipAsIs, false);
for (u32 i = 0; i < indexes.size(); i++) {
manager.clip(&blocks[indexes[i]], texBuffers, isMulti);
}
}
void MinecraftPipeline::changeMode(const McpipProgramName& requestedMode,
const u8& force) {
if (!force) {
if (latestMode == requestedMode) return;
}
if (requestedMode == McPipCull) {
manager.culler.configureVU1AndSendStaticData();
latestMode = McPipCull;
} else {
manager.clipper.configureVU1AndSendStaticData();
latestMode = McPipAsIs;
}
}
} // namespace Tyra
@@ -0,0 +1,34 @@
;//--------------------------------------------------------------------------------
;// MinecraftPipeline cull macros library
;//--------------------------------------------------------------------------------
;//---------------------------------------------------------
;// LoadStaticData - Load lod, set and viewproj matrix
;//---------------------------------------------------------
#macro LoadStaticData: t_lod, t_set
lq t_lod, VU1_MCPIP_AS_IS_STATIC_LOD(vi00)
lq t_set, VU1_MCPIP_AS_IS_STATIC_SET_TAG(vi00)
#endmacro
;//---------------------------------------------------------
;// LoadDynamicData - Load scale, prim and clut
;//---------------------------------------------------------
#macro LoadDynamicData: t_scale, t_prim, t_clut, t_color, t_vertexCount, t_buffer
lq t_scale, VU1_MCPIP_AS_IS_DYNAMIC_SCALE(t_buffer)
lq t_prim, VU1_MCPIP_AS_IS_DYNAMIC_PRIM(t_buffer)
lq t_clut, VU1_MCPIP_AS_IS_DYNAMIC_CLUT(t_buffer)
lq t_color, VU1_MCPIP_AS_IS_DYNAMIC_COLOR(t_buffer)
ilw.w t_vertexCount, VU1_MCPIP_AS_IS_DYNAMIC_SCALE(t_buffer)
#endmacro
;//---------------------------------------------------------
;// StoreTags - Store lod, prim, clut
;//---------------------------------------------------------
#macro StoreTags: t_lodTag, t_setTag, t_primTag, t_clut, t_destAddress
sq t_setTag, 0(t_destAddress)
sq t_lodTag, 1(t_destAddress)
sq t_setTag, 2(t_destAddress)
sq t_clut, 3(t_destAddress)
sq t_primTag, 4(t_destAddress)
iaddiu t_destAddress, t_destAddress, 5
#endmacro
@@ -0,0 +1,118 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Super fast renderer of minecraft blocks.
; Block data is statically allocated in vi00
;
; - Triangle list
; - AsIs = NO TRANSFORM
; - Colors
;---------------------------------------------------------------
.syntax new
.name VU1BlocksAsIs
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_vu1_as_is_shared_defines.h"
#include "src/renderer/3d/pipeline/minecraft/programs/as_is/macros.i"
#define STQ_STORE_OFFSET 0
#define RGBA_STORE_OFFSET 1
#define XYZ2_STORE_OFFSET 2
--enter
--endenter
#vuprog VU1BlocksAsIs
LoadStaticData{ lodTag, setTag }
begin:
xtop buffer
LoadDynamicData{ scale, primTag, clut, color, vertexCount, buffer }
iaddiu vertexData, buffer, VU1_MCPIP_AS_IS_DYNAMIC_VERTEX_DATA_ADDR
iadd stqData, vertexData, vertexCount
iadd destAddress, stqData, vertexCount
iadd kickAddress, stqData, vertexCount
StoreTags{ lodTag, setTag, primTag, clut, destAddress }
FixColor{ color }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
;--- Load vertex1
lq vertex1, (vertexData)
lq stq1, (stqData)
;--- Load vertex2
lq vertex2, 1(vertexData)
lq stq2, 1(stqData)
;--- Load vertex3
lq vertex3, 2(vertexData)
lq stq3, 2(stqData)
;--- Calculate vertex1
ScaleVertexToGSFormat{ scale, vertex1 }
div q, vf00[w], vertex1[w]
PerformTexturePerspectiveCorrection{ outputStq1, stq1 }
;--- Calculate vertex2
ScaleVertexToGSFormat{ scale, vertex2 }
div q, vf00[w], vertex2[w]
PerformTexturePerspectiveCorrection{ outputStq2, stq2 }
;--- Calculate vertex3
ScaleVertexToGSFormat{ scale, vertex3 }
div q, vf00[w], vertex3[w]
PerformTexturePerspectiveCorrection{ outputStq3, stq3 }
;--- Store vertex1
sq outputStq1, STQ_STORE_OFFSET(destAddress)
sq color, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputStq2, STQ_STORE_OFFSET+3(destAddress)
sq color, RGBA_STORE_OFFSET+3(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress)
;--- Store vertex3
sq outputStq3, STQ_STORE_OFFSET+6(destAddress)
sq color, RGBA_STORE_OFFSET+6(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu stqData, stqData, 3
iaddiu destAddress, destAddress, 9
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,28 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.hpp"
extern u32 VU1BlocksAsIs_CodeStart __attribute__((section(".vudata")));
extern u32 VU1BlocksAsIs_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
McpipAsIsVU1Program::McpipAsIsVU1Program()
: McpipProgram(McpipProgramName::McPipAsIs, &VU1BlocksAsIs_CodeStart,
&VU1BlocksAsIs_CodeEnd) {}
McpipAsIsVU1Program::~McpipAsIsVU1Program() {}
std::string McpipAsIsVU1Program::getStringName() const {
return std::string("As is");
}
} // namespace Tyra
@@ -0,0 +1,162 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.hpp"
namespace Tyra {
McpipClip::McpipClip() {
staticPacket = packet2_create(8, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
algoSettings.lerpColors = false;
algoSettings.lerpTexCoords = true;
algoSettings.lerpNormals = false;
}
McpipClip::~McpipClip() { packet2_free(staticPacket); }
void McpipClip::init(RendererCore* core, McpipBlockData* t_singleBlockData,
McpipBlockData* t_multiBlockData) {
singleBlockData = t_singleBlockData;
multiBlockData = t_multiBlockData;
rendererCore = core;
algorithm.init(rendererCore->getSettings());
setDBufferSize();
initStaticPacket();
}
u32 McpipClip::uploadVU1Program(McpipProgramsRepository* repo,
const u32& addr) {
auto* program = repo->getProgram(McpipProgramName::McPipAsIs);
return rendererCore->renderer3D.uploadVU1Program(program, addr);
}
void McpipClip::configureVU1AndSendStaticData() {
u16 start = VU1_MCPIP_AS_IS_STATIC_LAST_DATA_ADDR + 1;
rendererCore->renderer3D.setVU1DoubleBuffers(start, vu1DBufferSize);
sendVU1StaticData();
}
void McpipClip::initStaticPacket() {
packet2_utils_vu_open_unpack(staticPacket, VU1_MCPIP_AS_IS_STATIC_LOD, false);
{
packet2_utils_gs_add_lod(staticPacket, &rendererCore->gs.lod);
packet2_utils_gif_add_set(staticPacket, 1);
}
packet2_utils_vu_close_unpack(staticPacket);
packet2_utils_vu_add_end_tag(staticPacket);
}
void McpipClip::addData(McpipBlock* block, const bool& isMulti,
RendererCoreTextureBuffers* texBuffers,
packet2_t* packet, const u8& context) {
std::vector<Path1ClipVertex> clippedVertices;
auto mvp = rendererCore->renderer3D.getViewProj() * block->model;
const auto* blockData = isMulti ? multiBlockData : singleBlockData;
for (u32 i = 0; i < blockData->count / 3; i++) {
for (u8 j = 0; j < 3; j++) {
Path1ClipVertex vert = {mvp * blockData->vertices[i * 3 + j], Vec4(),
blockData->textureCoords[i * 3 + j], Vec4()};
inputTriangle.push_back(vert);
}
clippedTriangle.clear();
algorithm.clip(&clippedTriangle, inputTriangle, algoSettings);
inputTriangle.clear();
if (clippedTriangle.size() == 0) continue;
auto va = clippedTriangle.at(0);
for (u32 j = 1; j <= clippedTriangle.size() - 2; j++) {
auto vb = clippedTriangle.at(j);
auto vc = clippedTriangle.at((j + 1) % clippedTriangle.size());
clippedVertices.push_back(va);
clippedVertices.push_back(vb);
clippedVertices.push_back(vc);
}
}
addCorrections(&clippedVertices, block);
moveDataToBuffer(&clippedVertices, context);
addDataToPacket(packet, context, block, clippedVertices.size(), texBuffers);
}
void McpipClip::addCorrections(std::vector<Path1ClipVertex>* vertices,
McpipBlock* block) {
for (u32 i = 0; i < vertices->size(); i++) {
(*vertices)[i].position /= (*vertices)[i].position.w; // Perspective divide
(*vertices)[i].st += block->textureOffset; // Texture offset
}
}
void McpipClip::moveDataToBuffer(std::vector<Path1ClipVertex>* vertices,
const u8& context) {
for (u32 i = 0; i < vertices->size(); i++) {
vertexBuffers[context][i].set(vertices->at(i).position);
texCoordBuffers[context][i].set(vertices->at(i).st);
}
}
void McpipClip::addDataToPacket(packet2_t* packet, const u8& context,
McpipBlock* block, const int& count,
RendererCoreTextureBuffers* texBuffers) {
packet2_reset(packet, false);
rendererCore->texture.updateClutBuffer(texBuffers->clut);
packet2_utils_vu_open_unpack(packet, VU1_MCPIP_AS_IS_DYNAMIC_SCALE, true);
{
packet2_add_float(packet, 2048.0F); // scale
packet2_add_float(packet, 2048.0F); // scale
packet2_add_float(packet,
static_cast<float>(0xFFFFFF) / 32.0F); // scale
packet2_add_s32(packet, count); // vertex count
packet2_utils_gs_add_prim_giftag(packet, &rendererCore->gs.prim, count,
((u64)GIF_REG_ST) << 0 |
((u64)GIF_REG_RGBAQ) << 4 |
((u64)GIF_REG_XYZ2) << 8,
3, 0);
packet2_utils_gs_add_texbuff_clut(packet, texBuffers->core,
&rendererCore->texture.clut);
Packet2TyraUtils::addColor(packet, block->color);
}
packet2_utils_vu_close_unpack(packet);
u32 addr = VU1_MCPIP_AS_IS_DYNAMIC_VERTEX_DATA_ADDR;
packet2_utils_vu_add_unpack_data(packet, addr, vertexBuffers[context], count,
true);
addr += count;
packet2_utils_vu_add_unpack_data(packet, addr, texCoordBuffers[context],
count, true);
}
void McpipClip::setDBufferSize() {
vu1DBufferSize = 1000; // VU1 mem size
vu1DBufferSize -= VU1_MCPIP_AS_IS_STATIC_LAST_DATA_ADDR; // static data
vu1DBufferSize -= 1;
vu1DBufferSize /= 2; // xtop double buffer
}
void McpipClip::sendVU1StaticData() {
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(staticPacket, DMA_CHANNEL_VIF1, true);
}
} // namespace Tyra
@@ -0,0 +1,93 @@
;//--------------------------------------------------------------------------------
;// MinecraftPipeline cull macros library
;//--------------------------------------------------------------------------------
;//---------------------------------------------------------
;// StoreBlockizerStaticData - Load and store lod, clut and prim.
;// Push dest address by 5 qwords
;//---------------------------------------------------------
#macro StoreBlockizerLodClutTexPrim: t_destAddr, t_clutTex
lq setTag, VU1_MCPIP_CULL_STATIC_SET_TAG(vi00)
lq lodTag, VU1_MCPIP_CULL_STATIC_LOD(vi00)
sq setTag, 0(t_destAddr)
sq lodTag, 1(t_destAddr)
sq setTag, 2(t_destAddr)
sq t_clutTex, 3(t_destAddr)
lq primTag, VU1_MCPIP_CULL_STATIC_PRIM(vi00)
sq primTag, 4(t_destAddr)
iaddiu t_destAddr, t_destAddr, 5
#endmacro
;//---------------------------------------------------------
;// GetDestinationAddress - Load VU1 options and check
;// in which double buffer we are. If 0, return first double buff
;// if 1, return second double buffer.
;// Also toggle VU1 dbuff option, so next call of this macro will result
;// opposite buffer.
;//---------------------------------------------------------
#macro GetDestinationAddress: t_destAddr, t_kickAddr
ilw.w static1, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00)
ilw.x currDBufferOffset, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00)
ibgtz currDBufferOffset, get_dest_addr_second
get_dest_addr_first:
iaddiu t_destAddr, vi00, VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR
isw.x static1, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00)
b get_dest_addr_finish
get_dest_addr_second:
iaddiu t_destAddr, vi00, VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR
isw.x vi00, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00)
get_dest_addr_finish:
iaddiu t_kickAddr, t_destAddr, 0
#endmacro
;//---------------------------------------------------------
;// LoadScaleAndBlocksCount - Load scale vec3 and blocks count
;// from w component
;//---------------------------------------------------------
#macro LoadBufferDynamicData: t_buffer, t_scale, t_clutTex, t_blocksCount, t_viewProj
lq.xyz t_scale, VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR(t_buffer)
ilw.w t_blocksCount, VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR(t_buffer)
lq t_clutTex, VU1_MCPIP_CULL_DYNAMIC_CLUT_TEX(t_buffer)
lq t_viewProj[0], 0+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer)
lq t_viewProj[1], 1+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer)
lq t_viewProj[2], 2+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer)
lq t_viewProj[3], 3+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer)
#endmacro
;//---------------------------------------------------------
;// GetVertAndStData - Get static vertex data and tex coords
;//---------------------------------------------------------
#macro GetVertAndStData: t_vertData, t_stData
iaddiu t_vertData, vi00, VU1_MCPIP_CULL_STATIC_VERTEX_DATA
iaddiu t_stData, vi00, VU1_MCPIP_CULL_STATIC_TEX_COORD_DATA
#endmacro
;//---------------------------------------------------------
;// GetBlockData - Get mvp matrix, color, st offset for single block
;//---------------------------------------------------------
#macro GetBlockData: t_blockData, t_model, t_color, t_stOffset
lq t_model[0], 0(t_blockData)
lq t_model[1], 1(t_blockData)
lq t_model[2], 2(t_blockData)
lq t_model[3], 3(t_blockData)
lq t_color, 4(t_blockData)
lq t_stOffset, 5(t_blockData)
#endmacro
;//---------------------------------------------------------
;// GetVertexData - Get vertex and tex coord
;//---------------------------------------------------------
#macro GetVertexData: t_vertData, t_stqData, t_vertex, t_stq, t_offset
lq t_vertex, t_offset(t_vertData)
lq t_stq, t_offset(t_stqData)
#endmacro
;//---------------------------------------------------------
;// GetVertexData - Get vertex and tex coord
;//---------------------------------------------------------
#macro AddSTOffset: t_stq, t_stqOffset
add t_stq, t_stq, t_stqOffset
#endmacro
@@ -0,0 +1,139 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.hpp"
namespace Tyra {
McpipCull::McpipCull() {
staticPacket = packet2_create(8, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
}
McpipCull::~McpipCull() { packet2_free(staticPacket); }
void McpipCull::init(RendererCore* core, McpipBlockData* t_blockData) {
blockData = t_blockData;
rendererCore = core;
initStaticPacket();
setDBufferSize();
}
void McpipCull::setDBufferSize() {
vu1DBufferSize = 1000; // VU1 mem size
vu1DBufferSize -= VU1_MCPIP_CULL_STATIC_LAST_DATA_ADDR; // static data
// Our additional, mini output double buffer at the bottom of VU1 mem
u16 miniOutputDoubleBufferSize =
blockData->count * 3 +
5; // 36 * (ST, XYZ2, RGBAQ) + Set, lod, set, clut, prim
vu1DBufferSize -= miniOutputDoubleBufferSize; // First
vu1DBufferSize -= miniOutputDoubleBufferSize; // Second
TYRA_ASSERT(1000 - (miniOutputDoubleBufferSize * 2) ==
VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR,
"There is mismatch in output dbuffer1 size. Should be: ",
1000 - (miniOutputDoubleBufferSize * 2),
"but is: ", VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR);
TYRA_ASSERT(1000 - miniOutputDoubleBufferSize ==
VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR,
"There is mismatch in output dbuffer2 size. Should be: ",
1000 - miniOutputDoubleBufferSize,
"but is: ", VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR);
vu1DBufferSize /= 2; // xtop double buffer
}
u32 McpipCull::uploadVU1Program(McpipProgramsRepository* repo,
const u32& addr) {
auto* program = repo->getProgram(McpipProgramName::McPipCull);
return rendererCore->renderer3D.uploadVU1Program(program, addr);
}
void McpipCull::configureVU1AndSendStaticData() {
rendererCore->renderer3D.setVU1DoubleBuffers(
VU1_MCPIP_CULL_STATIC_LAST_DATA_ADDR, vu1DBufferSize);
sendVU1StaticData();
}
u32 McpipCull::getMaxBlocksCountPerQBuffer() const {
u32 result = vu1DBufferSize;
result -= 1; // VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT;
result -= 1; // Lod tag
result -= 4; // View proj
result /= VU1_MCPIP_CULL_QWORDS_PER_BLOCK;
return result;
}
void McpipCull::initStaticPacket() {
packet2_utils_vu_open_unpack(staticPacket, VU1_MCPIP_CULL_STATIC_LOD, false);
{
packet2_utils_gs_add_lod(staticPacket, &rendererCore->gs.lod);
packet2_utils_gs_add_prim_giftag(staticPacket, &rendererCore->gs.prim, 36,
((u64)GIF_REG_ST) << 0 |
((u64)GIF_REG_RGBAQ) << 4 |
((u64)GIF_REG_XYZ2) << 8,
3, 0);
packet2_utils_gif_add_set(staticPacket, 1);
packet2_add_u32(staticPacket, 0); // Param1 - Mini double buffer switcher
packet2_add_u32(staticPacket, 0);
packet2_add_u32(staticPacket, 0);
packet2_add_u32(staticPacket,
1); // static 1 - used in VU1's GetDestinationAddress{}
}
packet2_utils_vu_close_unpack(staticPacket);
packet2_utils_vu_add_end_tag(staticPacket);
}
void McpipCull::sendVU1StaticData() {
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(staticPacket, DMA_CHANNEL_VIF1, true);
}
void McpipCull::addData(packet2_t* packet, McpipBlock** blockPointerArray,
u32 blockPointerArrayCount,
RendererCoreTextureBuffers* texBuffers, bool isMulti) {
packet2_reset(packet, false);
rendererCore->texture.updateClutBuffer(texBuffers->clut);
packet2_utils_vu_open_unpack(
packet, VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR, true);
{
packet2_add_float(packet, 2048.0F); // scale
packet2_add_float(packet, 2048.0F); // scale
packet2_add_float(packet,
static_cast<float>(0xFFFFFF) / 32.0F); // scale
packet2_add_u32(packet, blockPointerArrayCount); // blocks count
packet2_utils_gs_add_texbuff_clut(packet, texBuffers->core,
&rendererCore->texture.clut);
Packet2TyraUtils::addM4x4(packet, rendererCore->renderer3D.getViewProj());
}
packet2_utils_vu_close_unpack(packet);
u32 addr = VU1_MCPIP_CULL_DYNAMIC_BLOCKS_DATA;
for (u32 i = 0; i < blockPointerArrayCount; i++) {
packet2_utils_vu_add_unpack_data(packet, addr, blockPointerArray[i],
VU1_MCPIP_CULL_QWORDS_PER_BLOCK, true);
addr += VU1_MCPIP_CULL_QWORDS_PER_BLOCK;
}
}
} // namespace Tyra
@@ -0,0 +1,129 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Super fast renderer of minecraft blocks.
; Block data is statically allocated in vi00
;
; - Triangle list
; - Cull = transform
; - Colors
;---------------------------------------------------------------
.syntax new
.name VU1BlocksCull
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_vu1_cull_shared_defines.h"
#include "src/renderer/3d/pipeline/minecraft/programs/cull/macros.i"
#define STQ_STORE_OFFSET 0
#define RGBA_STORE_OFFSET 1
#define XYZ2_STORE_OFFSET 2
--enter
--endenter
#vuprog VU1BlocksCull
GetVertAndStData{ staticVertexData, staticStqData }
ResetClipFlags{ }
begin:
xtop buffer
iaddiu blockData, buffer, VU1_MCPIP_CULL_DYNAMIC_BLOCKS_DATA
LoadBufferDynamicData{ buffer, scale, clutTex, blocksCount, viewProj }
;--- Loop
iadd blockCounter, buffer, blocksCount
blocksLoop:
GetBlockData{ blockData, model, color, stOffset }
MatrixMultiply{ mvp, model, viewProj }
FixColor{ color }
GetDestinationAddress{ destAddress, kickAddress }
StoreBlockizerLodClutTexPrim{ destAddress, clutTex }
iaddiu vertexData, staticVertexData, 0
iaddiu stqData, staticStqData, 0
iaddiu vertexCounter, buffer, VU1_MCPIP_CULL_VERTEX_COUNT
vertexLoop:
GetVertexData{ vertexData, stqData, vertex1, stq1, 0 }
GetVertexData{ vertexData, stqData, vertex2, stq2, 1 }
GetVertexData{ vertexData, stqData, vertex3, stq3, 2 }
;--- Calculate vertex1
MatrixMultiplyVertex{ vertex1, mvp, vertex1 }
PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET }
VertexPersCorr{ vertex1, vertex1 }
ScaleVertexToGSFormat{ scale, vertex1 }
AddSTOffset{ stq1, stOffset }
PerformTexturePerspectiveCorrection{ outputStq1, stq1 }
;--- Calculate vertex2
MatrixMultiplyVertex{ vertex2, mvp, vertex2 }
PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+3 }
VertexPersCorr{ vertex2, vertex2 }
ScaleVertexToGSFormat{ scale, vertex2 }
AddSTOffset{ stq2, stOffset }
PerformTexturePerspectiveCorrection{ outputStq2, stq2 }
;--- Calculate vertex3
MatrixMultiplyVertex{ vertex3, mvp, vertex3 }
PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+6 }
VertexPersCorr{ vertex3, vertex3 }
ScaleVertexToGSFormat{ scale, vertex3 }
AddSTOffset{ stq3, stOffset }
PerformTexturePerspectiveCorrection{ outputStq3, stq3 }
;--- Store vertex1
sq outputStq1, STQ_STORE_OFFSET(destAddress)
sq color, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputStq2, STQ_STORE_OFFSET+3(destAddress)
sq color, RGBA_STORE_OFFSET+3(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress)
;--- Store vertex3
sq outputStq3, STQ_STORE_OFFSET+6(destAddress)
sq color, RGBA_STORE_OFFSET+6(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress)
iaddiu vertexData, vertexData, 3
iaddiu stqData, stqData, 3
iaddiu destAddress, destAddress, 9
iaddi vertexCounter, vertexCounter, -3
ibne vertexCounter, buffer, vertexLoop
; End of vertex loop
--barrier
xgkick kickAddress
iaddiu blockData, blockData, VU1_MCPIP_CULL_QWORDS_PER_BLOCK
iaddi blockCounter, blockCounter, -1
ibne blockCounter, buffer, blocksLoop
; End of block loop
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,28 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.hpp"
extern u32 VU1BlocksCull_CodeStart __attribute__((section(".vudata")));
extern u32 VU1BlocksCull_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
McpipCullVU1Program::McpipCullVU1Program()
: McpipProgram(McpipProgramName::McPipCull, &VU1BlocksCull_CodeStart,
&VU1BlocksCull_CodeEnd) {}
McpipCullVU1Program::~McpipCullVU1Program() {}
std::string McpipCullVU1Program::getStringName() const {
return std::string("Cull");
}
} // namespace Tyra
@@ -0,0 +1,23 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp"
namespace Tyra {
McpipProgram::McpipProgram(const McpipProgramName& t_name, u32* t_start,
u32* t_end)
: VU1Program(t_start, t_end), name(t_name) {}
McpipProgram::~McpipProgram() {}
const McpipProgramName& McpipProgram::getName() const { return name; }
} // namespace Tyra
@@ -0,0 +1,130 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.hpp"
namespace Tyra {
BlockizerProgramsManager::BlockizerProgramsManager() {
lastProgramName = UndefinedMcpipProgram;
context = 0;
vu1BlockData = BlockNotUploaded;
dynamicPackets[0] = packet2_create(100, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
dynamicPackets[1] = packet2_create(100, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
staticPacket = packet2_create(2, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
setProgramsCache();
}
BlockizerProgramsManager::~BlockizerProgramsManager() {
packet2_free(dynamicPackets[0]);
packet2_free(dynamicPackets[1]);
packet2_free(staticPacket);
packet2_free(programsPacket);
}
void BlockizerProgramsManager::init(RendererCore* core) {
culler.init(core, &singleTexBlockData);
clipper.init(core, &singleTexBlockData, &multiTexBlockData);
}
void BlockizerProgramsManager::setProgramsCache() {
VU1Program** programs = new VU1Program*[2];
programs[0] = repo.getProgram(McpipProgramName::McPipCull);
programs[1] = repo.getProgram(McpipProgramName::McPipAsIs);
programsPacket =
renderer->core.getPath1()->createProgramsCache(programs, 2, 0);
delete[] programs;
}
void BlockizerProgramsManager::uploadVU1Programs() {
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(programsPacket, DMA_CHANNEL_VIF1, true);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
lastProgramName = UndefinedMcpipProgram;
vu1BlockData = BlockNotUploaded;
}
void BlockizerProgramsManager::uploadBlock(bool isMulti) {
if (vu1BlockData != BlockNotUploaded) {
if (isMulti && vu1BlockData == BlockMultiUploaded) {
return;
}
} else {
if (!isMulti && vu1BlockData == BlockSingleUploaded) {
return;
}
}
packet2_reset(staticPacket, false);
const McpipBlockData& blockData =
isMulti ? static_cast<McpipBlockData>(multiTexBlockData)
: static_cast<McpipBlockData>(singleTexBlockData);
packet2_utils_vu_add_unpack_data(
staticPacket, VU1_MCPIP_CULL_STATIC_VERTEX_DATA, blockData.comboData,
blockData.getComboCount(), false);
packet2_utils_vu_add_end_tag(staticPacket);
vu1BlockData = isMulti ? BlockMultiUploaded : BlockSingleUploaded;
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(staticPacket, DMA_CHANNEL_VIF1, true);
}
void BlockizerProgramsManager::cull(McpipBlock** blockPointerArray,
u32 blockPointerArrayCount,
RendererCoreTextureBuffers* texBuffers,
const bool& isMulti) {
uploadBlock(isMulti);
auto* currentPacket = dynamicPackets[context];
auto* program = repo.getProgram(McpipProgramName::McPipCull);
culler.addData(currentPacket, blockPointerArray, blockPointerArrayCount,
texBuffers, isMulti);
sendPacket(program);
}
void BlockizerProgramsManager::clip(McpipBlock* block,
RendererCoreTextureBuffers* texBuffers,
const bool& isMulti) {
vu1BlockData = BlockNotUploaded;
auto* currentPacket = dynamicPackets[context];
auto* program = repo.getProgram(McpipProgramName::McPipAsIs);
clipper.addData(block, isMulti, texBuffers, currentPacket, context);
sendPacket(program);
}
void BlockizerProgramsManager::sendPacket(McpipProgram* program) {
auto* currentPacket = dynamicPackets[context];
if (lastProgramName != program->getName()) {
packet2_utils_vu_add_start_program(currentPacket,
program->getDestinationAddress());
lastProgramName = program->getName();
} else {
packet2_utils_vu_add_continue_program(currentPacket);
}
packet2_utils_vu_add_end_tag(currentPacket);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(currentPacket, DMA_CHANNEL_VIF1, true);
context = !context;
}
} // namespace Tyra
@@ -0,0 +1,33 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.hpp"
namespace Tyra {
McpipProgramsRepository::McpipProgramsRepository() {}
McpipProgramsRepository::~McpipProgramsRepository() {}
McpipProgram* McpipProgramsRepository::getProgram(
const McpipProgramName& name) {
switch (name) {
case McpipProgramName::McPipCull:
return &cull;
case McpipProgramName::McPipAsIs:
return &asIs;
default:
TYRA_TRAP("Unknown VU1 program name");
return &cull;
}
}
} // namespace Tyra
@@ -0,0 +1,89 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tamtypes.h>
#include <string>
#include <sstream>
#include "renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.hpp"
namespace Tyra {
StdpipBagPackage::StdpipBagPackage() {
size = 0;
bag = nullptr;
vertices = nullptr;
sts = nullptr;
normals = nullptr;
colors = nullptr;
}
StdpipBagPackage::~StdpipBagPackage() {}
void StdpipBagPackage::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void StdpipBagPackage::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string StdpipBagPackage::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "StdpipBagPackage(";
}
res << std::fixed << std::setprecision(2);
res << std::endl;
res << "Size: " << static_cast<int>(size) << std::endl;
res << "Vectors: " << std::endl;
for (u32 i = 0; i < size; i++) {
res << i << ": " << vertices[i].getPrint() << std::endl;
}
if (sts != nullptr) {
res << "STs: " << std::endl;
for (u32 i = 0; i < size; i++)
res << i << ": " << sts[i].getPrint() << std::endl;
}
if (colors != nullptr) {
res << "Colors: " << std::endl;
for (u32 i = 0; i < size; i++)
res << i << ": " << colors[i].getPrint() << std::endl;
}
if (normals != nullptr) {
res << "Normals: " << std::endl;
for (u32 i = 0; i < size; i++)
res << i << ": " << normals[i].getPrint() << std::endl;
}
res << "Is in frustum: ";
switch (isInFrustum) {
case CoreBBoxFrustum::IN_FRUSTUM:
res << "Yes";
break;
case CoreBBoxFrustum::OUTSIDE_FRUSTUM:
res << "No";
break;
case CoreBBoxFrustum::PARTIALLY_IN_FRUSTUM:
res << "Partially";
break;
}
res << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,119 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tamtypes.h>
#include <math.h>
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.hpp"
namespace Tyra {
StdpipBagPackager::StdpipBagPackager() {}
StdpipBagPackager::~StdpipBagPackager() {}
void StdpipBagPackager::init(Renderer3DFrustumPlanes* t_frustumPlanes) {
frustumPlanes = t_frustumPlanes;
}
/**
* @brief Create render packages from provided render data
*
* @param size Max maxVertCount verts (VU1 buffer size)
*/
StdpipBagPackage* StdpipBagPackager::create(u16* o_size, StdpipBag* data,
u16 size) {
TYRA_ASSERT(size <= maxVertCount, "StdpipBagPackage can have max ",
maxVertCount, " verts. Provided \"", size, "\"");
*o_size = ceil(data->count / static_cast<float>(size));
StdpipBagPackage* result = new StdpipBagPackage[*o_size];
for (u16 i = 0; i < *o_size; i++) {
result[i].bag = data;
result[i].vertices = &data->vertices[i * size];
if (data->texture) result[i].sts = &data->texture->coordinates[i * size];
if (data->color->many)
result[i].colors = reinterpret_cast<Vec4*>(&data->color->many[i * size]);
if (data->lighting) result[i].normals = &data->lighting->normals[i * size];
result[i].indexOf1By3BBox = (i * size) / (maxVertCount / 3);
if (i == *o_size - 1) {
result[i].size = data->count - i * size;
} else {
result[i].size = size;
}
result[i].isInFrustum = checkFrustum(result[i]);
}
return result;
}
/**
* @brief Split render package to smaller packages
*
* @param size Max maxVertCount verts (VU1 buffer size)
*/
StdpipBagPackage* StdpipBagPackager::create(u16* o_count,
const StdpipBagPackage& pkg,
u16 size) {
TYRA_ASSERT(size <= maxVertCount, "StdpipBagPackage can have max ",
maxVertCount, " verts. Provided \"", size, "\"");
*o_count = ceil(pkg.size / static_cast<float>(size));
auto* result = new StdpipBagPackage[*o_count];
for (u16 i = 0; i < *o_count; i++) {
result[i].bag = pkg.bag;
result[i].vertices = &pkg.vertices[i * size];
if (pkg.bag->texture) result[i].sts = &pkg.sts[i * size];
if (pkg.bag->color->many) result[i].colors = &pkg.colors[i * size];
if (pkg.bag->lighting) result[i].normals = &pkg.normals[i * size];
result[i].indexOf1By3BBox =
pkg.indexOf1By3BBox + ((i * size) / (maxVertCount / 3));
if (i == *o_count - 1) {
result[i].size = pkg.size - i * size;
} else {
result[i].size = size;
}
result[i].isInFrustum = checkFrustum(result[i]);
}
return result;
}
CoreBBoxFrustum StdpipBagPackager::checkFrustum(const StdpipBagPackage& pkg) {
if (pkg.size <= (maxVertCount / 3)) { // Is subpackage
auto& bbox = renderBBox->getChildBBox1By3(pkg.indexOf1By3BBox);
return bbox.clipIsInFrustum(frustumPlanes->getAll(), *pkg.bag->info->model);
} else { // Is package
auto bbox = renderBBox->createChildBBox(
pkg.indexOf1By3BBox,
ceil(pkg.size / static_cast<float>(maxVertCount / 3)));
return bbox.clipIsInFrustum(frustumPlanes->getAll(), *pkg.bag->info->model);
}
}
void StdpipBagPackager::setMaxVertCount(const u32& count) {
maxVertCount = count;
}
} // namespace Tyra
@@ -0,0 +1,113 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tamtypes.h>
#include <string>
#include <sstream>
#include <vector>
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.hpp"
namespace Tyra {
StdpipBagPackagesBBox::StdpipBagPackagesBBox(Vec4* t_vertices, u32* t_faces,
const u32& t_facesCount,
const u32& t_maxVertCount) {
u32 splitPartSize = t_maxVertCount / 3;
vertexCount = t_facesCount;
partsCount = ceil(vertexCount / static_cast<float>(splitPartSize));
bboxParts = new std::vector<CoreBBox>;
for (u32 i = 0; i < partsCount; i++) {
u32 partSize =
i == partsCount - 1 ? t_facesCount - i * splitPartSize : splitPartSize;
bboxParts->push_back(
CoreBBox(t_vertices, t_faces + i * splitPartSize, partSize));
}
mainBBox = new RenderBBox(*bboxParts, 0, partsCount);
}
StdpipBagPackagesBBox::StdpipBagPackagesBBox(Vec4* t_vertices,
const u32& t_count,
const u32& t_maxVertCount) {
u32 splitPartSize = t_maxVertCount / 3;
vertexCount = t_count;
partsCount = ceil(vertexCount / static_cast<float>(splitPartSize));
bboxParts = new std::vector<CoreBBox>;
for (u32 i = 0; i < partsCount; i++) {
u32 partSize =
i == partsCount - 1 ? t_count - i * splitPartSize : splitPartSize;
bboxParts->push_back(RenderBBox(t_vertices + i * splitPartSize, partSize));
}
mainBBox = new RenderBBox(*bboxParts, 0, partsCount);
}
const RenderBBox& StdpipBagPackagesBBox::getChildBBox1By3(
const u32& index) const {
TYRA_ASSERT(index < partsCount,
"Index out of range. Provided index: ", index);
return static_cast<RenderBBox&>(bboxParts->at(index));
}
RenderBBox* StdpipBagPackagesBBox::getMainBBox() { return mainBBox; }
const u32& StdpipBagPackagesBBox::getPartsCount() const { return partsCount; }
const u32& StdpipBagPackagesBBox::getVertexCount() const { return vertexCount; }
RenderBBox StdpipBagPackagesBBox::createChildBBox(const u32& index,
const u16& partsSize) const {
return RenderBBox(*bboxParts, index, partsSize);
}
void StdpipBagPackagesBBox::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void StdpipBagPackagesBBox::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string StdpipBagPackagesBBox::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "StdpipBagPackagesBBox(";
}
res << std::fixed << std::setprecision(2);
res << std::endl;
res << "Vertices count: " << static_cast<int>(vertexCount) << std::endl;
res << "Main CoreBBox: " << std::endl;
res << mainBBox->getPrint() << std::endl;
res << "Child BBoxes: " << std::endl;
for (u32 i = 0; i < partsCount; i++) {
res << i << ": " << bboxParts->at(i).getPrint();
if (i != partsCount - 1) {
res << std::endl;
}
}
res << ")";
return res.str();
}
StdpipBagPackagesBBox::~StdpipBagPackagesBBox() {
delete bboxParts;
delete mainBBox;
}
} // namespace Tyra
@@ -0,0 +1,84 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <string>
#include "renderer/3d/pipeline/std/core/bag/stdpip_bag.hpp"
namespace Tyra {
StdpipBag::StdpipBag() {
info = nullptr;
color = nullptr;
texture = nullptr;
lighting = nullptr;
}
StdpipBag::~StdpipBag() {}
StdpipBagPackagesBBox StdpipBag::calculateBbox(const u32& maxVertCount) {
TYRA_ASSERT(vertices != nullptr, "Vertices are required to calculate bbox");
TYRA_ASSERT(count > 0, "Count must be greater than 0 to calculate bbox");
return StdpipBagPackagesBBox(vertices, count, maxVertCount);
}
void StdpipBag::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void StdpipBag::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string StdpipBag::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "StdpipBag(";
}
res << std::fixed << std::setprecision(4);
res << std::endl;
res << "Count: " << count << ", " << std::endl;
res << "Vertices present: " << (vertices != nullptr ? "Yes" : "No") << ", "
<< std::endl;
res << "Info present: " << (info != nullptr ? "Yes" : "No") << ", "
<< std::endl;
res << "Color present: " << (color != nullptr ? "Yes" : "No") << ", "
<< std::endl;
res << "Texture present: " << (texture != nullptr ? "Yes" : "No") << ", "
<< std::endl;
res << "Lighting present: " << (lighting != nullptr ? "Yes" : "No") << ", "
<< std::endl;
res << "Model matrix: " << info->model->getPrint() << ", " << std::endl;
if (color->single) {
res << "Color single: " << color->single->getPrint() << ", " << std::endl;
} else {
res << "Color many: " << color->many->getPrint() << ", " << std::endl;
}
if (texture) {
res << "Texture coords present: "
<< (texture->coordinates != nullptr ? "Yes" : "No") << ", "
<< std::endl;
res << "Texture: " << texture->texture->getPrint() << ", " << std::endl;
}
if (lighting) {
res << "Lighting normals present: " << (lighting->normals ? "Yes" : "No")
<< ", " << std::endl;
res << "Lighting matrix: " << lighting->lightMatrix->getPrint() << ", "
<< std::endl;
}
res << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,22 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/bag/stdpip_color_bag.hpp"
namespace Tyra {
StdpipColorBag::StdpipColorBag() {
single = nullptr;
many = nullptr;
}
StdpipColorBag::~StdpipColorBag() {}
} // namespace Tyra
@@ -0,0 +1,24 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/bag/stdpip_info_bag.hpp"
namespace Tyra {
StdpipInfoBag::StdpipInfoBag() {
shadingType = StdpipShadingFlat;
blendingEnabled = true;
antiAliasingEnabled = false;
model = nullptr;
}
StdpipInfoBag::~StdpipInfoBag() {}
} // namespace Tyra
@@ -0,0 +1,122 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.hpp"
namespace Tyra {
StdpipLightingBag::StdpipLightingBag(const bool& manual) {
isAllocated = false;
mode = Auto;
normals = nullptr;
lightMatrix = nullptr;
lightColors = nullptr;
lightDirections = nullptr;
if (manual) {
mode = Manual;
} else {
allocate();
}
}
StdpipLightingBag::~StdpipLightingBag() { deallocate(); }
void StdpipLightingBag::setAmbientColor(const Color& color) {
TYRA_ASSERT(mode != Manual, "Ambient color cannot be set in manual mode");
lightColors[3].set(reinterpret_cast<const Vec4&>(color));
}
void StdpipLightingBag::setDirectionalLightColors(Color* colors,
const u8& count) {
for (u8 i = 0; i < count; i++) setDirectionalLightColor(colors[i], i);
}
void StdpipLightingBag::setDirectionalLightDirections(Vec4* directions,
const u8& count) {
for (u8 i = 0; i < count; i++) setDirectionalLightDirection(directions[i], i);
}
void StdpipLightingBag::setDirectionalLightColor(const Color& color,
const u8& index) {
TYRA_ASSERT(mode != Manual,
"Directional lights cannot be set in manual mode");
TYRA_ASSERT(index < 3, "There are max 3 directional lights");
lightColors[index].set(reinterpret_cast<const Vec4&>(color));
}
void StdpipLightingBag::setDirectionalLightDirection(const Vec4& direction,
const u8& index) {
TYRA_ASSERT(mode != Manual,
"Directional lights cannot be set in manual mode");
TYRA_ASSERT(index < 3, "There are max 3 directional lights");
lightDirections[index].set(direction);
}
void StdpipLightingBag::setLightsManually(Vec4* colors, Vec4* directions) {
deallocate();
lightColors = colors;
lightDirections = directions;
mode = Manual;
}
void StdpipLightingBag::disableManualMode() {
allocate();
mode = Auto;
}
void StdpipLightingBag::allocate() {
if (isAllocated) return;
lightColors = new Vec4[4];
lightDirections = new Vec4[3];
for (u8 i = 0; i < 3; i++) {
lightColors[i].set(0.0F, 0.0F, 0.0F, 1.0F);
}
lightColors[3].set(.5F, .5F, .5F, 1.0F); // Ambient
for (u8 i = 0; i < 3; i++) {
lightDirections[i].set(1.0F, 1.0F, 1.0F, 1.0F);
}
isAllocated = true;
}
void StdpipLightingBag::deallocate() {
if (!isAllocated) return;
forceDeallocate();
}
void StdpipLightingBag::forceDeallocate() {
forceDeallocateColors();
forceDeallocateDirections();
isAllocated = false;
}
void StdpipLightingBag::forceDeallocateColors() {
if (lightColors != nullptr) {
delete[] lightColors;
lightColors = nullptr;
}
}
void StdpipLightingBag::forceDeallocateDirections() {
if (lightDirections != nullptr) {
delete[] lightDirections;
lightDirections = nullptr;
}
}
} // namespace Tyra
@@ -0,0 +1,22 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.hpp"
namespace Tyra {
StdpipTextureBag::StdpipTextureBag() {
coordinates = nullptr;
texture = nullptr;
}
StdpipTextureBag::~StdpipTextureBag() {}
} // namespace Tyra
@@ -0,0 +1,125 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; AsIs = NO TRANSFORM
; Colors
;---------------------------------------------------------------
.syntax new
.name StdpipVU1As_Is_C
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define RGBA_STORE_OFFSET 0
#define XYZ2_STORE_OFFSET 1
--enter
--endenter
#vuprog StdpipVU1AsIsC
LoadTyraStaticData{ gifSetTag }
LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd colorData, vertexData, vertexCount
iblez singleColorEnabled, setDestAddrMultiColor
iadd kickAddress, vertexData, vertexCount
b setDestAddr
setDestAddrMultiColor:
iadd kickAddress, colorData, vertexCount
setDestAddr:
iaddiu destAddress, kickAddress, 0
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
iblez singleColorEnabled, multiColor
;--- Load vertices single color
add color1, vf00, singleColor
add color2, vf00, singleColor
add color3, vf00, singleColor
b processing
multiColor:
;--- Load vertices colors
lq color1, (colorData)
lq color2, 1(colorData)
lq color3, 2(colorData)
processing:
;--- Load vertex1
lq.xyz vertex1, (vertexData)
;--- Load vertex2
lq.xyz vertex2, 1(vertexData)
;--- Load vertex3
lq.xyz vertex3, 2(vertexData)
;--- Calculate vertex1
ScaleVertexToGSFormat{ scale, vertex1 }
FixColor{ color1 }
;--- Calculate vertex2
ScaleVertexToGSFormat{ scale, vertex2 }
FixColor{ color2 }
;--- Calculate vertex3
ScaleVertexToGSFormat{ scale, vertex3 }
FixColor{ color3 }
;--- Store vertex1
sq color1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq color2, RGBA_STORE_OFFSET+2(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress)
;--- Store vertex3
sq color3, RGBA_STORE_OFFSET+4(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu colorData, colorData, 3
iaddiu destAddress, destAddress, 6
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,47 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.hpp"
extern u32 StdpipVU1As_Is_C_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1As_Is_C_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipAsIsCVU1Program::StdpipAsIsCVU1Program()
: StdpipVU1Program(StdpipAsIsColor, &StdpipVU1As_Is_C_CodeStart,
&StdpipVU1As_Is_C_CodeEnd,
((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2,
2) {}
StdpipAsIsCVU1Program::~StdpipAsIsCVU1Program() {}
std::string StdpipAsIsCVU1Program::getStringName() const {
return std::string("As is - C");
}
void StdpipAsIsCVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,111 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; AsIs = NO TRANSFORM
; Directional lights
;---------------------------------------------------------------
.syntax new
.name StdpipVU1As_Is_D
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define RGBA_STORE_OFFSET 0
#define XYZ2_STORE_OFFSET 1
--enter
--endenter
#vuprog StdpipVU1AsIsD
LoadTyraStaticData{ gifSetTag }
LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd normalData, vertexData, vertexCount
iadd kickAddress, normalData, vertexCount
iadd destAddress, normalData, vertexCount
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
;--- Load vertex1
lq.xyz vertex1, (vertexData)
lq.xyz normal1, (normalData)
;--- Load vertex2
lq.xyz vertex2, 1(vertexData)
lq.xyz normal2, 1(normalData)
;--- Load vertex3
lq.xyz vertex3, 2(vertexData)
lq.xyz normal3, 2(normalData)
;--- Calculate vertex1
ScaleVertexToGSFormat{ scale, vertex1 }
CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor1 }
;--- Calculate vertex2
ScaleVertexToGSFormat{ scale, vertex2 }
CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor2 }
;--- Calculate vertex3
ScaleVertexToGSFormat{ scale, vertex3 }
CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor3 }
;--- Store vertex1
sq outputColor1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputColor2, RGBA_STORE_OFFSET+2(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress)
;--- Store vertex3
sq outputColor3, RGBA_STORE_OFFSET+4(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu normalData, normalData, 3
iaddiu destAddress, destAddress, 6
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,52 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.hpp"
extern u32 StdpipVU1As_Is_D_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1As_Is_D_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipAsIsDVU1Program::StdpipAsIsDVU1Program()
: StdpipVU1Program(StdpipAsIsDirLights, &StdpipVU1As_Is_D_CodeStart,
&StdpipVU1As_Is_D_CodeEnd,
((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2,
3) {}
StdpipAsIsDVU1Program::~StdpipAsIsDVU1Program() {}
std::string StdpipAsIsDVU1Program::getStringName() const {
return std::string("As is - LC");
}
void StdpipAsIsDVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
addr += qbuffer->size;
// Add normal
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals,
qbuffer->size, true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,140 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; AsIs = NO TRANSFORM
; Texture, colors
;---------------------------------------------------------------
.syntax new
.name StdpipVU1As_Is_TC
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define STQ_STORE_OFFSET 0
#define RGBA_STORE_OFFSET 1
#define XYZ2_STORE_OFFSET 2
--enter
--endenter
#vuprog StdpipVU1AsIsTC
LoadTyraStaticData{ gifSetTag }
LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd stqData, vertexData, vertexCount
iadd colorData, stqData, vertexCount
iblez singleColorEnabled, setDestAddrMultiColor
iadd kickAddress, stqData, vertexCount
b setDestAddr
setDestAddrMultiColor:
iadd kickAddress, colorData, vertexCount
setDestAddr:
iaddiu destAddress, kickAddress, 0
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
iblez singleColorEnabled, multiColor
;--- Load vertices single color
add color1, vf00, singleColor
add color2, vf00, singleColor
add color3, vf00, singleColor
b processing
multiColor:
;--- Load vertices colors
lq color1, (colorData)
lq color2, 1(colorData)
lq color3, 2(colorData)
processing:
;--- Load vertex1
lq vertex1, (vertexData)
lq stq1, (stqData)
;--- Load vertex2
lq vertex2, 1(vertexData)
lq stq2, 1(stqData)
;--- Load vertex3
lq vertex3, 2(vertexData)
lq stq3, 2(stqData)
;--- Calculate vertex1
ScaleVertexToGSFormat{ scale, vertex1 }
div q, vf00[w], vertex1[w]
PerformTexturePerspectiveCorrection{ outputStq1, stq1 }
FixColor{ color1 }
;--- Calculate vertex2
ScaleVertexToGSFormat{ scale, vertex2 }
div q, vf00[w], vertex2[w]
PerformTexturePerspectiveCorrection{ outputStq2, stq2 }
FixColor{ color2 }
;--- Calculate vertex3
ScaleVertexToGSFormat{ scale, vertex3 }
div q, vf00[w], vertex3[w]
PerformTexturePerspectiveCorrection{ outputStq3, stq3 }
FixColor{ color3 }
;--- Store vertex1
sq outputStq1, STQ_STORE_OFFSET(destAddress)
sq color1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputStq2, STQ_STORE_OFFSET+3(destAddress)
sq color2, RGBA_STORE_OFFSET+3(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress)
;--- Store vertex3
sq outputStq3, STQ_STORE_OFFSET+6(destAddress)
sq color3, RGBA_STORE_OFFSET+6(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu stqData, stqData, 3
iaddiu colorData, colorData, 3
iaddiu destAddress, destAddress, 9
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,53 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.hpp"
extern u32 StdpipVU1As_Is_TC_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1As_Is_TC_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipAsIsTCVU1Program::StdpipAsIsTCVU1Program()
: StdpipVU1Program(StdpipAsIsTextureColor, &StdpipVU1As_Is_TC_CodeStart,
&StdpipVU1As_Is_TC_CodeEnd,
((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 |
((u64)GIF_REG_XYZ2) << 8,
3, 3) {}
StdpipAsIsTCVU1Program::~StdpipAsIsTCVU1Program() {}
std::string StdpipAsIsTCVU1Program::getStringName() const {
return std::string("As is - TC");
}
void StdpipAsIsTCVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
addr += qbuffer->size;
// Add sts
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size,
true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,126 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; AsIs = NO TRANSFORM
; Texture, directional lights
;---------------------------------------------------------------
.syntax new
.name StdpipVU1As_Is_TD
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define STQ_STORE_OFFSET 0
#define RGBA_STORE_OFFSET 1
#define XYZ2_STORE_OFFSET 2
--enter
--endenter
#vuprog StdpipVU1AsIsTD
LoadTyraStaticData{ gifSetTag }
LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd stqData, vertexData, vertexCount
iadd normalData, stqData, vertexCount
iadd kickAddress, normalData, vertexCount
iadd destAddress, normalData, vertexCount
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
;--- Load vertex1
lq vertex1, (vertexData)
lq stq1, (stqData)
lq.xyz normal1, (normalData)
;--- Load vertex2
lq vertex2, 1(vertexData)
lq stq2, 1(stqData)
lq.xyz normal2, 1(normalData)
;--- Load vertex3
lq vertex3, 2(vertexData)
lq stq3, 2(stqData)
lq.xyz normal3, 2(normalData)
;--- Calculate vertex1
ScaleVertexToGSFormat{ scale, vertex1 }
div q, vf00[w], vertex1[w]
PerformTexturePerspectiveCorrection{ outputStq1, stq1 }
CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor1 }
;--- Calculate vertex2
ScaleVertexToGSFormat{ scale, vertex2 }
div q, vf00[w], vertex2[w]
PerformTexturePerspectiveCorrection{ outputStq2, stq2 }
CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor2 }
;--- Calculate vertex3
ScaleVertexToGSFormat{ scale, vertex3 }
div q, vf00[w], vertex3[w]
PerformTexturePerspectiveCorrection{ outputStq3, stq3 }
CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor3 }
;--- Store vertex1
sq outputStq1, STQ_STORE_OFFSET(destAddress)
sq outputColor1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputStq2, STQ_STORE_OFFSET+3(destAddress)
sq outputColor2, RGBA_STORE_OFFSET+3(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress)
;--- Store vertex3
sq outputStq3, STQ_STORE_OFFSET+6(destAddress)
sq outputColor3, RGBA_STORE_OFFSET+6(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu stqData, stqData, 3
iaddiu normalData, normalData, 3
iaddiu destAddress, destAddress, 9
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,58 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.hpp"
extern u32 StdpipVU1As_Is_TD_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1As_Is_TD_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipAsIsTDVU1Program::StdpipAsIsTDVU1Program()
: StdpipVU1Program(StdpipAsIsTextureDirLights, &StdpipVU1As_Is_TD_CodeStart,
&StdpipVU1As_Is_TD_CodeEnd,
((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 |
((u64)GIF_REG_XYZ2) << 8,
3, 4) {}
StdpipAsIsTDVU1Program::~StdpipAsIsTDVU1Program() {}
std::string StdpipAsIsTDVU1Program::getStringName() const {
return std::string("As is - LTC");
}
void StdpipAsIsTDVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
addr += qbuffer->size;
// Add sts
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size,
true);
addr += qbuffer->size;
// Add normal
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals,
qbuffer->size, true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,136 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; Cull = Standard PS2 way. clipw polys are culled.
; Volors
;---------------------------------------------------------------
.syntax new
.name StdpipVU1Cull_C
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define RGBA_STORE_OFFSET 0
#define XYZ2_STORE_OFFSET 1
--enter
--endenter
#vuprog StdpipVU1CullC
ResetClipFlags{ }
LoadTyraStaticData{ gifSetTag }
MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 }
LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd colorData, vertexData, vertexCount
iblez singleColorEnabled, setDestAddrMultiColor
iadd kickAddress, vertexData, vertexCount
b setDestAddr
setDestAddrMultiColor:
iadd kickAddress, colorData, vertexCount
setDestAddr:
iaddiu destAddress, kickAddress, 0
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
iblez singleColorEnabled, multiColor
;--- Load vertices single color
add color1, vf00, singleColor
add color2, vf00, singleColor
add color3, vf00, singleColor
b processing
multiColor:
;--- Load vertices colors
lq color1, (colorData)
lq color2, 1(colorData)
lq color3, 2(colorData)
processing:
;--- Load vertex1
lq vertex1, (vertexData)
;--- Load vertex2
lq vertex2, 1(vertexData)
;--- Load vertex3
lq vertex3, 2(vertexData)
;--- Calculate vertex1
MatrixMultiplyVertex{ vertex1, mvp, vertex1 }
PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET }
VertexPersCorr{ vertex1, vertex1 }
ScaleVertexToGSFormat{ scale, vertex1 }
FixColor{ color1 }
;--- Calculate vertex2
MatrixMultiplyVertex{ vertex2, mvp, vertex2 }
PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+2 }
VertexPersCorr{ vertex2, vertex2 }
ScaleVertexToGSFormat{ scale, vertex2 }
FixColor{ color2 }
;--- Calculate vertex3
MatrixMultiplyVertex{ vertex3, mvp, vertex3 }
PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+4 }
VertexPersCorr{ vertex3, vertex3 }
ScaleVertexToGSFormat{ scale, vertex3 }
FixColor{ color3 }
;--- Store vertex1
sq color1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq color2, RGBA_STORE_OFFSET+2(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress)
;--- Store vertex3
sq color3, RGBA_STORE_OFFSET+4(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu colorData, colorData, 3
iaddiu destAddress, destAddress, 6
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,46 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.hpp"
extern u32 StdpipVU1Cull_C_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1Cull_C_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipCullCVU1Program::StdpipCullCVU1Program()
: StdpipVU1Program(
StdpipCullColor, &StdpipVU1Cull_C_CodeStart, &StdpipVU1Cull_C_CodeEnd,
((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2, 2) {}
StdpipCullCVU1Program::~StdpipCullCVU1Program() {}
std::string StdpipCullCVU1Program::getStringName() const {
return std::string("Cull - C");
}
void StdpipCullCVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,122 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; Cull = Standard PS2 way. clipw polys are culled.
; Directional lights
;---------------------------------------------------------------
.syntax new
.name StdpipVU1Cull_D
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define RGBA_STORE_OFFSET 0
#define XYZ2_STORE_OFFSET 1
--enter
--endenter
#vuprog StdpipVU1CullD
ResetClipFlags{ }
LoadTyraStaticData{ gifSetTag }
MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 }
LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd normalData, vertexData, vertexCount
iadd kickAddress, normalData, vertexCount
iadd destAddress, normalData, vertexCount
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
;--- Load vertex1
lq vertex1, (vertexData)
lq.xyz normal1, (normalData)
;--- Load vertex2
lq vertex2, 1(vertexData)
lq.xyz normal2, 1(normalData)
;--- Load vertex3
lq vertex3, 2(vertexData)
lq.xyz normal3, 2(normalData)
;--- Calculate vertex1
MatrixMultiplyVertex{ vertex1, mvp, vertex1 }
PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET }
VertexPersCorr{ vertex1, vertex1 }
ScaleVertexToGSFormat{ scale, vertex1 }
CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor1 }
;--- Calculate vertex2
MatrixMultiplyVertex{ vertex2, mvp, vertex2 }
PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+2 }
VertexPersCorr{ vertex2, vertex2 }
ScaleVertexToGSFormat{ scale, vertex2 }
CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor2 }
;--- Calculate vertex3
MatrixMultiplyVertex{ vertex3, mvp, vertex3 }
PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+4 }
VertexPersCorr{ vertex3, vertex3 }
ScaleVertexToGSFormat{ scale, vertex3 }
CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor3 }
;--- Store vertex1
sq outputColor1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputColor2, RGBA_STORE_OFFSET+2(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress)
;--- Store vertex3
sq outputColor3, RGBA_STORE_OFFSET+4(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu normalData, normalData, 3
iaddiu destAddress, destAddress, 6
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,52 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.hpp"
extern u32 StdpipVU1Cull_D_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1Cull_D_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipCullDVU1Program::StdpipCullDVU1Program()
: StdpipVU1Program(StdpipCullDirLights, &StdpipVU1Cull_D_CodeStart,
&StdpipVU1Cull_D_CodeEnd,
((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2,
3) {}
StdpipCullDVU1Program::~StdpipCullDVU1Program() {}
std::string StdpipCullDVU1Program::getStringName() const {
return std::string("Cull - LC");
}
void StdpipCullDVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
addr += qbuffer->size;
// Add normal
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals,
qbuffer->size, true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,148 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; Cull = Standard PS2 way. clipw polys are culled.
; Lighting, texture, colors
;---------------------------------------------------------------
.syntax new
.name StdpipVU1Cull_TC
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define STQ_STORE_OFFSET 0
#define RGBA_STORE_OFFSET 1
#define XYZ2_STORE_OFFSET 2
--enter
--endenter
#vuprog StdpipVU1CullTC
ResetClipFlags{ }
LoadTyraStaticData{ gifSetTag }
MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 }
LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd stqData, vertexData, vertexCount
iadd colorData, stqData, vertexCount
iblez singleColorEnabled, setDestAddrMultiColor
iadd kickAddress, stqData, vertexCount
b setDestAddr
setDestAddrMultiColor:
iadd kickAddress, colorData, vertexCount
setDestAddr:
iaddiu destAddress, kickAddress, 0
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
iblez singleColorEnabled, multiColor
;--- Load vertices single color
add color1, vf00, singleColor
add color2, vf00, singleColor
add color3, vf00, singleColor
b processing
multiColor:
;--- Load vertices colors
lq color1, (colorData)
lq color2, 1(colorData)
lq color3, 2(colorData)
processing:
;--- Load vertex1
lq vertex1, (vertexData)
lq stq1, (stqData)
;--- Load vertex2
lq vertex2, 1(vertexData)
lq stq2, 1(stqData)
;--- Load vertex3
lq vertex3, 2(vertexData)
lq stq3, 2(stqData)
;--- Calculate vertex1
MatrixMultiplyVertex{ vertex1, mvp, vertex1 }
PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET }
VertexPersCorr{ vertex1, vertex1 }
ScaleVertexToGSFormat{ scale, vertex1 }
PerformTexturePerspectiveCorrection{ outputStq1, stq1 }
FixColor{ color1 }
;--- Calculate vertex2
MatrixMultiplyVertex{ vertex2, mvp, vertex2 }
PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+3 }
VertexPersCorr{ vertex2, vertex2 }
ScaleVertexToGSFormat{ scale, vertex2 }
PerformTexturePerspectiveCorrection{ outputStq2, stq2 }
FixColor{ color2 }
;--- Calculate vertex3
MatrixMultiplyVertex{ vertex3, mvp, vertex3 }
PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+6 }
VertexPersCorr{ vertex3, vertex3 }
ScaleVertexToGSFormat{ scale, vertex3 }
PerformTexturePerspectiveCorrection{ outputStq3, stq3 }
FixColor{ color3 }
;--- Store vertex1
sq outputStq1, STQ_STORE_OFFSET(destAddress)
sq color1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputStq2, STQ_STORE_OFFSET+3(destAddress)
sq color2, RGBA_STORE_OFFSET+3(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress)
;--- Store vertex3
sq outputStq3, STQ_STORE_OFFSET+6(destAddress)
sq color3, RGBA_STORE_OFFSET+6(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu stqData, stqData, 3
iaddiu colorData, colorData, 3
iaddiu destAddress, destAddress, 9
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,53 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.hpp"
extern u32 StdpipVU1Cull_TC_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1Cull_TC_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipCullTCVU1Program::StdpipCullTCVU1Program()
: StdpipVU1Program(StdpipCullTextureColor, &StdpipVU1Cull_TC_CodeStart,
&StdpipVU1Cull_TC_CodeEnd,
((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 |
((u64)GIF_REG_XYZ2) << 8,
3, 3) {}
StdpipCullTCVU1Program::~StdpipCullTCVU1Program() {}
std::string StdpipCullTCVU1Program::getStringName() const {
return std::string("Cull - TC");
}
void StdpipCullTCVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
addr += qbuffer->size;
// Add sts
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size,
true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,134 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Triangle list
; Cull = Standard PS2 way. clipw polys are culled.
; Texture, directional lights
;---------------------------------------------------------------
.syntax new
.name StdpipVU1Cull_TD
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
#define STQ_STORE_OFFSET 0
#define RGBA_STORE_OFFSET 1
#define XYZ2_STORE_OFFSET 2
--enter
--endenter
#vuprog StdpipVU1CullTD
ResetClipFlags{ }
LoadTyraStaticData{ gifSetTag }
MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 }
LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR }
LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR }
begin:
xtop buffer
LoadTyraBufferTags{ scale, primTag, buffer }
iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR
ilw.w vertexCount, 0(buffer)
iadd stqData, vertexData, vertexCount
iadd normalData, stqData, vertexCount
iadd kickAddress, normalData, vertexCount
iadd destAddress, normalData, vertexCount
StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress }
;--- Loop
iadd vertexCounter, buffer, vertexCount
vertexLoop:
;--- Load vertex1
lq vertex1, (vertexData)
lq stq1, (stqData)
lq.xyz normal1, (normalData)
;--- Load vertex2
lq vertex2, 1(vertexData)
lq stq2, 1(stqData)
lq.xyz normal2, 1(normalData)
;--- Load vertex3
lq vertex3, 2(vertexData)
lq stq3, 2(stqData)
lq.xyz normal3, 2(normalData)
;--- Calculate vertex1
MatrixMultiplyVertex{ vertex1, mvp, vertex1 }
PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET }
VertexPersCorr{ vertex1, vertex1 }
ScaleVertexToGSFormat{ scale, vertex1 }
PerformTexturePerspectiveCorrection{ outputStq1, stq1 }
CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor1 }
;--- Calculate vertex2
MatrixMultiplyVertex{ vertex2, mvp, vertex2 }
PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+3 }
VertexPersCorr{ vertex2, vertex2 }
ScaleVertexToGSFormat{ scale, vertex2 }
PerformTexturePerspectiveCorrection{ outputStq2, stq2 }
CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor2 }
;--- Calculate vertex3
MatrixMultiplyVertex{ vertex3, mvp, vertex3 }
PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+6 }
VertexPersCorr{ vertex3, vertex3 }
ScaleVertexToGSFormat{ scale, vertex3 }
PerformTexturePerspectiveCorrection{ outputStq3, stq3 }
CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor }
FixColor{ outputColor3 }
;--- Store vertex1
sq outputStq1, STQ_STORE_OFFSET(destAddress)
sq outputColor1, RGBA_STORE_OFFSET(destAddress)
sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress)
;--- Store vertex2
sq outputStq2, STQ_STORE_OFFSET+3(destAddress)
sq outputColor2, RGBA_STORE_OFFSET+3(destAddress)
sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress)
;--- Store vertex3
sq outputStq3, STQ_STORE_OFFSET+6(destAddress)
sq outputColor3, RGBA_STORE_OFFSET+6(destAddress)
sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress)
;-------------------------------
iaddiu vertexData, vertexData, 3
iaddiu stqData, stqData, 3
iaddiu normalData, normalData, 3
iaddiu destAddress, destAddress, 9
;--- Fix loop
iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter
ibne vertexCounter, buffer, vertexLoop ; and repeat if needed
xgkick kickAddress ; dispatch to the GS rasterizer.
--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it...
--cont
b begin
#endvuprog
--exit
--endexit
@@ -0,0 +1,58 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.hpp"
extern u32 StdpipVU1Cull_TD_CodeStart __attribute__((section(".vudata")));
extern u32 StdpipVU1Cull_TD_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
StdpipCullTDVU1Program::StdpipCullTDVU1Program()
: StdpipVU1Program(StdpipCullTextureDirLights, &StdpipVU1Cull_TD_CodeStart,
&StdpipVU1Cull_TD_CodeEnd,
((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 |
((u64)GIF_REG_XYZ2) << 8,
3, 4) {}
StdpipCullTDVU1Program::~StdpipCullTDVU1Program() {}
std::string StdpipCullTDVU1Program::getStringName() const {
return std::string("Cull - LTC");
}
void StdpipCullTDVU1Program::addProgramQBufferDataToPacket(
packet2_t* packet, StdpipQBuffer* qbuffer) const {
u32 addr = VU1_VERT_DATA_ADDR;
// Add vertices
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices,
qbuffer->size, true);
addr += qbuffer->size;
// Add sts
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size,
true);
addr += qbuffer->size;
// Add normal
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals,
qbuffer->size, true);
// Add colors
if (qbuffer->bag->color->single == nullptr) {
addr += qbuffer->size;
packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors,
qbuffer->size, true);
}
}
} // namespace Tyra
@@ -0,0 +1,87 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/path1/stdpip_clipper.hpp"
namespace Tyra {
StdpipClipper::StdpipClipper() {}
StdpipClipper::~StdpipClipper() {}
void StdpipClipper::setMVP(M4x4* t_mvp) { mvp = t_mvp; }
void StdpipClipper::init(const RendererSettings& settings) {
algorithm.init(settings);
}
void StdpipClipper::setMaxVertCount(const u32& count) { maxVertCount = count; }
void StdpipClipper::clip(StdpipQBuffer* buffer) {
TYRA_ASSERT(buffer->size <= maxVertCount / 3, "Buffer should have max ",
maxVertCount / 3, " verts if we want to clip it.");
Path1EEClipAlgorithmSettings algoSettings = {
buffer->bag->lighting != nullptr, buffer->bag->texture != nullptr,
buffer->bag->color->many != nullptr};
std::vector<Path1ClipVertex> clippedVertices;
for (u32 i = 0; i < buffer->size / 3; i++) {
std::vector<Path1ClipVertex> inputTriangle;
for (u8 j = 0; j < 3; j++) {
Path1ClipVertex vert = {
*mvp * buffer->vertices[i * 3 + j],
buffer->bag->lighting ? buffer->normals[i * 3 + j] : Vec4(),
buffer->bag->texture ? buffer->sts[i * 3 + j] : Vec4(),
buffer->bag->color->many ? buffer->colors[i * 3 + j] : Vec4()};
inputTriangle.push_back(vert);
}
std::vector<Path1ClipVertex> clippedTriangle;
algorithm.clip(&clippedTriangle, inputTriangle, algoSettings);
if (clippedTriangle.size() == 0) continue;
auto va = clippedTriangle.at(0);
for (u32 j = 1; j <= clippedTriangle.size() - 2; j++) {
auto vb = clippedTriangle.at(j);
auto vc = clippedTriangle.at((j + 1) % clippedTriangle.size());
clippedVertices.push_back(va);
clippedVertices.push_back(vb);
clippedVertices.push_back(vc);
}
}
perspectiveDivide(&clippedVertices);
moveDataToBuffer(clippedVertices, buffer);
}
void StdpipClipper::perspectiveDivide(std::vector<Path1ClipVertex>* vertices) {
for (u32 i = 0; i < vertices->size(); i++) {
(*vertices)[i].position /= (*vertices)[i].position.w;
}
}
void StdpipClipper::moveDataToBuffer(
const std::vector<Path1ClipVertex>& vertices, StdpipQBuffer* buffer) {
buffer->reallocateManually(vertices.size());
for (u32 i = 0; i < vertices.size(); i++) {
auto& vertex = vertices.at(i);
buffer->vertices[i] = vertex.position;
if (buffer->bag->texture) buffer->sts[i] = vertex.st;
if (buffer->bag->color->many) buffer->colors[i] = vertex.color;
if (buffer->bag->lighting) buffer->normals[i] = vertex.normal;
}
}
} // namespace Tyra
@@ -0,0 +1,48 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.hpp"
namespace Tyra {
StdpipProgramsRepository::StdpipProgramsRepository() {}
StdpipProgramsRepository::~StdpipProgramsRepository() {}
StdpipVU1Program* StdpipProgramsRepository::getProgram(
const StdpipProgramName& name) {
switch (name) {
case StdpipProgramName::StdpipAsIsColor:
return &asIsColor;
case StdpipProgramName::StdpipCullColor:
return &cullColor;
case StdpipProgramName::StdpipAsIsDirLights:
return &asIsLightingColor;
case StdpipProgramName::StdpipCullDirLights:
return &cullLightingColor;
case StdpipProgramName::StdpipAsIsTextureDirLights:
return &asIsLightingTextureColor;
case StdpipProgramName::StdpipCullTextureDirLights:
return &cullLightingTextureColor;
case StdpipProgramName::StdpipAsIsTextureColor:
return &asIsTextureColor;
case StdpipProgramName::StdpipCullTextureColor:
return &cullTextureColor;
default:
TYRA_TRAP("Unknown VU1 program name");
return &cullLightingTextureColor;
}
}
} // namespace Tyra
@@ -0,0 +1,265 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.hpp"
namespace Tyra {
StdpipQBuffer::StdpipQBuffer() {
size = 0;
_isDynamicallyAllocated = false;
_stAllocated = false;
_colorAllocated = false;
_normalAllocated = false;
vertices = nullptr;
colors = nullptr;
sts = nullptr;
normals = nullptr;
}
StdpipQBuffer::~StdpipQBuffer() { deallocateDynamicData(); }
void StdpipQBuffer::setMaxVertCount(const u32& count) { maxVertCount = count; }
void StdpipQBuffer::fillByPointer(const StdpipBagPackage& pkg) {
TYRA_ASSERT(pkg.size <= maxVertCount, "VU1 buffer supports only ",
maxVertCount, " verts. Provided: ", pkg.size);
deallocateDynamicData();
vertices = pkg.vertices;
sts = pkg.sts;
colors = pkg.colors;
normals = pkg.normals;
size = pkg.size;
bag = pkg.bag;
}
void StdpipQBuffer::fillByCopyMax(const StdpipBagPackage& pkg1,
const StdpipBagPackage& pkg2,
const StdpipBagPackage& pkg3) {
TYRA_ASSERT(pkg1.size <= maxVertCount / 3,
"Wrong package size (1). Provided: ", pkg1.size);
TYRA_ASSERT(pkg2.size <= maxVertCount / 3,
"Wrong package size (2). Provided: ", pkg2.size);
TYRA_ASSERT(pkg3.size <= maxVertCount / 3,
"Wrong package size (3). Provided: ", pkg3.size);
deallocateDynamicData();
size = pkg1.size + pkg2.size + pkg3.size;
allocateDynamicData(size, pkg1.bag);
for (u16 i = 0; i < pkg1.size; i++) {
vertices[i].set(pkg1.vertices[i]);
if (pkg1.bag->texture) sts[i].set(pkg1.sts[i]);
if (pkg1.bag->color->many)
colors[i].set(reinterpret_cast<const Vec4&>(pkg1.colors[i]));
if (pkg1.bag->lighting) normals[i].set(pkg1.normals[i]);
}
for (u16 i = 0; i < pkg2.size; i++) {
vertices[i + pkg1.size].set(pkg2.vertices[i]);
if (pkg1.bag->texture) sts[i + pkg1.size].set(pkg2.sts[i]);
if (pkg1.bag->color->many)
colors[i + pkg1.size].set(reinterpret_cast<const Vec4&>(pkg2.colors[i]));
if (pkg1.bag->lighting) normals[i + pkg1.size].set(pkg2.normals[i]);
}
for (u16 i = 0; i < pkg3.size; i++) {
vertices[i + pkg1.size + pkg2.size].set(pkg3.vertices[i]);
if (pkg1.bag->texture) sts[i + pkg1.size + pkg2.size].set(pkg3.sts[i]);
if (pkg1.bag->color->many)
colors[i + pkg1.size + pkg2.size].set(
reinterpret_cast<const Vec4&>(pkg3.colors[i]));
if (pkg1.bag->lighting)
normals[i + pkg1.size + pkg2.size].set(pkg3.normals[i]);
}
bag = pkg1.bag;
}
void StdpipQBuffer::fillByCopy1By2(const StdpipBagPackage& pkg1,
const StdpipBagPackage& pkg2) {
TYRA_ASSERT(pkg1.size <= maxVertCount / 3,
"Wrong package size (1). Provided: ", pkg1.size);
TYRA_ASSERT(pkg2.size <= maxVertCount / 3,
"Wrong package size (2). Provided: ", pkg2.size);
deallocateDynamicData();
size = pkg1.size + pkg2.size;
allocateDynamicData(size, pkg1.bag);
for (u16 i = 0; i < pkg1.size; i++) {
vertices[i].set(pkg1.vertices[i]);
if (pkg1.bag->texture) sts[i].set(pkg1.sts[i]);
if (pkg1.bag->color->many)
colors[i].set(reinterpret_cast<const Vec4&>(pkg1.colors[i]));
if (pkg1.bag->lighting) normals[i].set(pkg1.normals[i]);
}
for (u16 i = 0; i < pkg2.size; i++) {
vertices[i + pkg1.size].set(pkg2.vertices[i]);
if (pkg1.bag->texture) sts[i + pkg1.size].set(pkg2.sts[i]);
if (pkg1.bag->color->many)
colors[i + pkg1.size].set(reinterpret_cast<const Vec4&>(pkg2.colors[i]));
if (pkg1.bag->lighting) normals[i + pkg1.size].set(pkg2.normals[i]);
}
bag = pkg1.bag;
}
void StdpipQBuffer::fillByCopy1By3(const StdpipBagPackage& pkg) {
TYRA_ASSERT(pkg.size <= maxVertCount / 3,
"Wrong package size (1). Provided: ", pkg.size);
deallocateDynamicData();
size = pkg.size;
allocateDynamicData(size, pkg.bag);
for (u16 i = 0; i < pkg.size; i++) {
vertices[i].set(pkg.vertices[i]);
if (pkg.bag->texture) sts[i].set(pkg.sts[i]);
if (pkg.bag->color->many)
colors[i].set(reinterpret_cast<const Vec4&>(pkg.colors[i]));
if (pkg.bag->lighting) normals[i].set(pkg.normals[i]);
}
bag = pkg.bag;
}
void StdpipQBuffer::reallocateManually(const u16& t_size) {
deallocateDynamicData();
allocateDynamicData(t_size, bag);
size = t_size;
}
void StdpipQBuffer::deallocateDynamicData() {
if (_isDynamicallyAllocated) {
delete[] vertices;
if (_stAllocated) {
delete[] sts;
_stAllocated = false;
}
if (_colorAllocated) {
delete[] colors;
_colorAllocated = false;
}
if (_normalAllocated) {
delete[] normals;
_normalAllocated = false;
}
_isDynamicallyAllocated = false;
}
}
/** When we not receive maxVertCount vertices, we must align it by ourself.
* Too bad - not efficient. */
void StdpipQBuffer::allocateDynamicData(u16 size, StdpipBag* bag) {
TYRA_ASSERT(size <= maxVertCount, "Wrong size. Max buffer size in VU1 is ",
maxVertCount, ". Provided: ", size);
TYRA_ASSERT(!_isDynamicallyAllocated, "Buffer is already allocated");
vertices = new (std::align_val_t(sizeof(VECTOR))) Vec4[size];
if (bag->texture != nullptr) {
sts = new (std::align_val_t(sizeof(VECTOR))) Vec4[size];
_stAllocated = true;
}
if (bag->color->many != nullptr) {
colors = new (std::align_val_t(sizeof(VECTOR))) Vec4[size];
_colorAllocated = true;
}
if (bag->lighting != nullptr) {
normals = new (std::align_val_t(sizeof(VECTOR))) Vec4[size];
_normalAllocated = true;
}
_isDynamicallyAllocated = true;
}
bool StdpipQBuffer::any() const { return size > 0; }
void StdpipQBuffer::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void StdpipQBuffer::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string StdpipQBuffer::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "Path1Buffer(";
}
res << std::fixed << std::setprecision(2);
res << std::endl;
res << "Size: " << static_cast<int>(size) << std::endl;
res << "Vectors: " << std::endl;
for (u32 i = 0; i < size; i++)
res << i << ": " << vertices[i].getPrint() << std::endl;
if (bag->texture != nullptr) {
res << "STs: " << std::endl;
for (u32 i = 0; i < size; i++)
res << i << ": " << sts[i].getPrint() << std::endl;
}
if (bag->color->many != nullptr) {
res << "Colors: " << std::endl;
for (u32 i = 0; i < size; i++)
res << i << ": " << colors[i].getPrint() << std::endl;
}
if (bag->lighting != nullptr) {
res << "Normals: " << std::endl;
for (u32 i = 0; i < size; i++) {
res << i << ": " << normals[i].getPrint();
if (i < size - 1) {
res << std::endl;
}
}
}
res << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,320 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.hpp"
#include "renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
namespace Tyra {
/**
* VU1 = 1000 vert
*
* Quadbuffering:
* 2 main buffers = 1000 / 2 = 500 vert
* 2 kick buffers = 500 / 2 = 250 vert
*
* Vert data:
* Pos + Normal + ST + Color = 4
* = 4 * 48 = 192
*
* Other data:
* mvp matrix, light matrix, tags = 14
* 20 light vectors, light intesities = 25
* = 14 + 25 = 39
*
* All data:
* = 192 + 39 = 231
*
*/
StdpipQBufferRenderer::StdpipQBufferRenderer() {
context = 0;
lastProgramName = StdipUndefinedProgram;
staticDataPacket = packet2_create(3, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
objectDataPacket = packet2_create(16, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
programsPacket = nullptr;
}
StdpipQBufferRenderer::~StdpipQBufferRenderer() {
packet2_free(packets[0]);
packet2_free(packets[1]);
packet2_free(staticDataPacket);
packet2_free(objectDataPacket);
if (programsPacket) packet2_free(programsPacket);
}
void StdpipQBufferRenderer::init(RendererCore* t_core) {
path1 = t_core->getPath1();
clipper.init(t_core->getSettings());
rendererCore = t_core;
dma_channel_initialize(DMA_CHANNEL_VIF1, NULL, 0);
dma_channel_fast_waits(DMA_CHANNEL_VIF1);
const u32 VU1_PACKET_SIZE = 16;
packets[0] =
packet2_create(VU1_PACKET_SIZE, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
packets[1] =
packet2_create(VU1_PACKET_SIZE, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
setProgramsCache();
reinitVU1();
TYRA_LOG("Renderer3DQBufferRenderer initialized");
}
void StdpipQBufferRenderer::reinitVU1() {
sendStaticData();
uploadPrograms();
setDoubleBuffer();
}
StdpipQBuffer* StdpipQBufferRenderer::getBuffer() { return &buffers[context]; }
void StdpipQBufferRenderer::sendObjectData(
StdpipBag* bag, M4x4* mvp, RendererCoreTextureBuffers* texBuffers) const {
packet2_reset(objectDataPacket, false);
packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_MVP_MATRIX_ADDR,
mvp->data, 4, false);
if (bag->lighting) {
packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_LIGHTS_MATRIX_ADDR,
bag->lighting->lightMatrix, 3, false);
packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_LIGHTS_DIRS_ADDR,
bag->lighting->getLightDirections(), 3,
false);
packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_LIGHTS_COLORS_ADDR,
bag->lighting->getLightColors(), 4, false);
}
u8 singleColorEnabled = bag->color->single != nullptr;
if (singleColorEnabled) // Color is placed in 4th slot of
// VU1_LIGHTS_MATRIX_ADDR
packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_SINGLE_COLOR_ADDR,
bag->color->single->rgba, 1, false);
packet2_utils_vu_open_unpack(objectDataPacket, VU1_OPTIONS_ADDR, false);
{
packet2_add_u32(objectDataPacket,
singleColorEnabled); // Single color enabled.
packet2_add_u32(objectDataPacket, 0); // not used, padding
packet2_add_u32(objectDataPacket, 0); // not used, padding
packet2_add_u32(objectDataPacket, 0); // not used, padding
packet2_utils_gs_add_lod(objectDataPacket, &rendererCore->gs.lod);
if (texBuffers != nullptr) {
packet2_utils_gs_add_texbuff_clut(objectDataPacket, texBuffers->core,
&rendererCore->texture.clut);
rendererCore->texture.updateClutBuffer(texBuffers->clut);
}
}
packet2_utils_vu_close_unpack(objectDataPacket);
packet2_utils_vu_add_end_tag(objectDataPacket);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(objectDataPacket, DMA_CHANNEL_VIF1, true);
}
void StdpipQBufferRenderer::setInfo(StdpipInfoBag* bag) {
rendererCore->gs.prim.antialiasing = bag->antiAliasingEnabled;
rendererCore->gs.prim.blending = bag->blendingEnabled;
rendererCore->gs.prim.shading = bag->shadingType;
}
void StdpipQBufferRenderer::sendStaticData() const {
packet2_reset(staticDataPacket, false);
packet2_utils_vu_open_unpack(staticDataPacket, VU1_SET_GIFTAG_ADDR, false);
{ packet2_utils_gif_add_set(staticDataPacket, 1); }
packet2_utils_vu_close_unpack(staticDataPacket);
packet2_utils_vu_add_end_tag(staticDataPacket);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(staticDataPacket, DMA_CHANNEL_VIF1, true);
}
void StdpipQBufferRenderer::setProgramsCache() {
VU1Program** programs = new VU1Program*[8];
programs[0] = repository.getProgram(StdpipCullColor);
programs[1] = repository.getProgram(StdpipAsIsColor);
programs[2] = repository.getProgram(StdpipCullDirLights);
programs[3] = repository.getProgram(StdpipAsIsDirLights);
programs[4] = repository.getProgram(StdpipCullTextureDirLights);
programs[5] = repository.getProgram(StdpipAsIsTextureDirLights);
programs[6] = repository.getProgram(StdpipCullTextureColor);
programs[7] = repository.getProgram(StdpipAsIsTextureColor);
programsPacket = path1->createProgramsCache(programs, 8, 0);
delete[] programs;
}
void StdpipQBufferRenderer::uploadPrograms() {
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(programsPacket, DMA_CHANNEL_VIF1, true);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
}
void StdpipQBufferRenderer::setDoubleBuffer() {
u16 startingAddr = VU1_LAST_ITEM_ADDR + 1;
const u16 bufferMaxSize = 1000;
bufferSize = (bufferMaxSize - startingAddr) / 2;
path1->setDoubleBuffer(startingAddr, bufferSize);
bufferSize -= 1; // Because we don't want to upload anything from first
// buffer, to first addr of second buffer
}
void StdpipQBufferRenderer::cull(StdpipQBuffer* buffer) {
if (buffer->size == 0) {
return;
}
auto program = getCullProgramByBag(buffer->bag);
addBufferDataToPacket(program, buffer);
sendPacket();
}
// void StdpipQBufferRenderer::sendFinishTag() {
// StdpipQBufferRenderer way proposition
// auto program = path1->getProgramByName(Draw_Finish);
// addBufferDataToPacket(program, nullptr);
// sendPacket();
// packet2_t* packet2 = packet2_create(8, P2_TYPE_NORMAL, P2_MODE_CHAIN,
// true); auto program =
// static_cast<VU1DrawFinish*>(getProgramByName(Draw_Finish));
// program->addTag(packet2, prim);
// packet2_utils_vu_add_start_program(packet2,
// program->getDestinationAddress()); packet2_utils_vu_add_end_tag(packet2);
// dma_channel_wait(DMA_CHANNEL_VIF1, 0);
// dma_channel_send_packet2(packet2, DMA_CHANNEL_VIF1, true);
// packet2_free(packet2);
// }
void StdpipQBufferRenderer::clip(StdpipQBuffer* buffer) {
if (buffer->size == 0) {
return;
}
auto program = getAsIsProgramByBag(buffer->bag);
clipper.clip(buffer);
if (buffer->any()) {
if (buffer) addBufferDataToPacket(program, buffer);
sendPacket();
}
}
void StdpipQBufferRenderer::clearLastProgramName() {
lastProgramName = StdipUndefinedProgram;
}
void StdpipQBufferRenderer::addBufferDataToPacket(StdpipVU1Program* program,
StdpipQBuffer* buffer) {
currentPacket = packets[context];
packet2_reset(currentPacket, false);
program->addBufferDataToPacket(currentPacket, buffer, &rendererCore->gs.prim);
if (lastProgramName != program->getName()) {
packet2_utils_vu_add_start_program(currentPacket,
program->getDestinationAddress());
lastProgramName = program->getName();
} else {
packet2_utils_vu_add_continue_program(currentPacket);
}
packet2_utils_vu_add_end_tag(currentPacket);
}
void StdpipQBufferRenderer::sendPacket() {
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(currentPacket, DMA_CHANNEL_VIF1, true);
// Switch packet, so we can proceed during DMA transfer
context = !context;
}
void StdpipQBufferRenderer::setMaxVertCount(const u32& count) {
buffers[0].setMaxVertCount(count);
buffers[1].setMaxVertCount(count);
clipper.setMaxVertCount(count);
}
StdpipVU1Program* StdpipQBufferRenderer::getAsIsProgramByBag(
const StdpipBag* bag) {
auto programType = getDrawProgramTypeByBag(bag);
if (programType == StdpipVU1TextureDirLights)
return getProgramByName(StdpipAsIsTextureDirLights);
else if (programType == StdpipVU1DirLights)
return getProgramByName(StdpipAsIsDirLights);
else if (programType == StdpipVU1TextureColor)
return getProgramByName(StdpipAsIsTextureColor);
else
return getProgramByName(StdpipAsIsColor);
}
StdpipVU1Program* StdpipQBufferRenderer::getCullProgramByBag(
const StdpipBag* bag) {
auto programType = getDrawProgramTypeByBag(bag);
return getCullProgramByType(programType);
}
StdpipVU1Program* StdpipQBufferRenderer::getProgramByName(
const StdpipProgramName& name) {
return repository.getProgram(name);
}
StdpipVU1Program* StdpipQBufferRenderer::getCullProgramByParams(
const bool& isLightingEnabled, const bool& isTextureEnabled) {
auto type = getDrawProgramTypeByParams(isLightingEnabled, isTextureEnabled);
return getCullProgramByType(type);
}
StdpipVU1Program* StdpipQBufferRenderer::getCullProgramByType(
const StdpipProgramType& programType) {
if (programType == StdpipVU1TextureDirLights)
return getProgramByName(StdpipCullTextureDirLights);
else if (programType == StdpipVU1DirLights)
return getProgramByName(StdpipCullDirLights);
else if (programType == StdpipVU1TextureColor)
return getProgramByName(StdpipCullTextureColor);
else
return getProgramByName(StdpipCullColor);
}
StdpipProgramType StdpipQBufferRenderer::getDrawProgramTypeByBag(
const StdpipBag* bag) const {
auto isLightingEnabled = bag->lighting != nullptr;
auto isTextureEnabled = bag->texture != nullptr;
return getDrawProgramTypeByParams(isLightingEnabled, isTextureEnabled);
}
StdpipProgramType StdpipQBufferRenderer::getDrawProgramTypeByParams(
const bool& isLightingEnabled, const bool& isTextureEnabled) const {
if (isLightingEnabled && isTextureEnabled)
return StdpipVU1TextureDirLights;
else if (isLightingEnabled)
return StdpipVU1DirLights;
else if (isTextureEnabled)
return StdpipVU1TextureColor;
else
return StdpipVU1Color;
}
} // namespace Tyra
@@ -0,0 +1,83 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.hpp"
namespace Tyra {
StdpipVU1Program::StdpipVU1Program(const StdpipProgramName& t_name,
u32* t_start, u32* t_end,
const u32& t_reglist,
const u8& t_reglistCount,
const u8& t_elementsPerVertex)
: VU1Program(t_start, t_end),
name(t_name),
reglistCount(t_reglistCount),
elementsPerVertex(t_elementsPerVertex),
reglist(t_reglist) {
packetSize = packet2_utils_get_packet_size_for_program(start, end);
programSize = calculateProgramSize();
}
StdpipVU1Program::~StdpipVU1Program() {}
const StdpipProgramName& StdpipVU1Program::getName() const { return name; }
u32& StdpipVU1Program::getReglist() { return reglist; }
void StdpipVU1Program::addBufferDataToPacket(packet2_t* packet,
StdpipQBuffer* buffer,
prim_t* prim) {
addStandardBufferDataToPacket(packet, buffer, prim);
addProgramQBufferDataToPacket(packet, buffer);
}
void StdpipVU1Program::addStandardBufferDataToPacket(packet2_t* packet,
StdpipQBuffer* buffer,
prim_t* prim) {
if (buffer->bag->texture)
prim->mapping = 1;
else
prim->mapping = 0;
packet2_utils_vu_open_unpack(packet, 0, true);
{
packet2_add_float(packet, 2048.0F); // scale
packet2_add_float(packet, 2048.0F); // scale
packet2_add_float(packet,
static_cast<float>(0xFFFFFF) / 32.0F); // scale
packet2_add_u32(packet, buffer->size); // vertex count
packet2_utils_gs_add_prim_giftag(packet, prim, buffer->size, reglist,
reglistCount, 0);
}
packet2_utils_vu_close_unpack(packet);
}
u16 StdpipVU1Program::getMaxVertCount(const bool& singleColorEnabled,
const u16& bufferSize) const {
u16 res = bufferSize - 4;
u8 colorElementsPerVertex =
singleColorEnabled ? (elementsPerVertex - 1) : elementsPerVertex;
res /= (colorElementsPerVertex + reglistCount);
// Buffer size = VU1 double buffer size (xtop)
// res = qbuffer size (directly inside VU1)
// Must be dividable by 3 and the result also dividable by 3. Why?
// 1st dividable reason - triangle, and packaging system in 3d rendering
// 2nd dividable reason - subpackaging system. We are splitting packages into
// 3 subpackages in 3d renderer.
res = res / 3 / 3;
res = res * 3 * 3;
return res;
}
} // namespace Tyra
@@ -0,0 +1,227 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/core/std_pipeline_core.hpp"
#include "renderer/core/renderer_core.hpp"
#include "thread/threading.hpp"
// #define TYRA_RENDERER_VERBOSE_LOG 1
#ifdef TYRA_RENDERER_VERBOSE_LOG
#define Verbose(...) Debug::writeLines("VRB: ", ##__VA_ARGS__, "\n")
#else
#define Verbose(...) ((void)0)
#endif
namespace Tyra {
StdPipelineCore::StdPipelineCore() { maxVertCount = 0; }
StdPipelineCore::~StdPipelineCore() {}
void StdPipelineCore::init(RendererCore* t_core) {
rendererCore = t_core;
qbufferRenderer.init(t_core);
packager.init(&rendererCore->renderer3D.frustumPlanes);
}
void StdPipelineCore::reinitStandardVU1Programs() {
qbufferRenderer.reinitVU1();
}
u32 StdPipelineCore::getMaxVertCountByBag(const StdpipBag* bag) {
return qbufferRenderer.getCullProgramByBag(bag)->getMaxVertCount(
bag->color->many == nullptr, qbufferRenderer.getBufferSize());
}
u32 StdPipelineCore::getMaxVertCountByParams(const bool& isSingleColor,
const bool& isLightingEnabled,
const bool& isTextureEnabled) {
return qbufferRenderer
.getCullProgramByParams(isLightingEnabled, isTextureEnabled)
->getMaxVertCount(isSingleColor, qbufferRenderer.getBufferSize());
}
void StdPipelineCore::render(StdpipBag* bag, StdpipBagPackagesBBox* bbox) {
if (bag->count <= 0) return;
TYRA_ASSERT(bag->vertices != nullptr,
"Vertices are required in 3D render bag!");
TYRA_ASSERT(bag->info != nullptr, "Info bag is required in 3D render bag!");
TYRA_ASSERT(bag->info->model != nullptr,
"Info bag's model pointer is empty!");
TYRA_ASSERT(bag->color != nullptr, "Color bag is required in 3D render bag!");
TYRA_ASSERT(bag->color->single || bag->color->many,
"At least one color is required in 3D render bag!");
TYRA_ASSERT((!bag->color->many && !bag->lighting) ||
(bag->color->many && !bag->lighting) ||
(!bag->color->many && bag->lighting),
"Multicolor is not supported with lighting, please choose one!");
TYRA_ASSERT(
!bag->lighting || (bag->lighting->lightMatrix && bag->lighting->normals),
"If you want lighting, please provide light matrix and normals!");
TYRA_ASSERT(
!bag->texture || (bag->texture->texture && bag->texture->coordinates),
"If you want texture, please provide texture and coordinates!");
StdpipBagPackagesBBox* renderBbox;
u32 maxVertCount = getMaxVertCountByBag(bag);
setMaxVertCount(maxVertCount);
if (!bbox)
renderBbox =
new StdpipBagPackagesBBox(bag->vertices, bag->count, maxVertCount);
else
renderBbox = bbox;
auto frustumCheck = renderBbox->getMainBBox()->clipIsInFrustum(
rendererCore->renderer3D.frustumPlanes.getAll(), *bag->info->model);
auto mvp = rendererCore->renderer3D.getViewProj() * *bag->info->model;
if (frustumCheck == OUTSIDE_FRUSTUM) return;
RendererCoreTextureBuffers* texBuffers = nullptr;
if (bag->texture) {
auto temp = rendererCore->texture.useTexture(bag->texture->texture);
texBuffers = new RendererCoreTextureBuffers{temp.id, temp.core, temp.clut};
}
qbufferRenderer.clearLastProgramName();
qbufferRenderer.sendObjectData(bag, &mvp, texBuffers);
packager.setRenderBBox(renderBbox);
qbufferRenderer.setClipperMVP(&mvp);
qbufferRenderer.setInfo(bag->info);
if (frustumCheck == IN_FRUSTUM ||
(frustumCheck == PARTIALLY_IN_FRUSTUM && bag->info->noClipChecks)) {
u16 packagesCount = 0;
auto biggerPkgs = packager.create(&packagesCount, bag, maxVertCount);
Verbose("Material - in frustum. Pkgs: ", packagesCount,
" size: ", static_cast<int>(biggerPkgs[0].size));
for (u16 i = 0; i < packagesCount; i++) {
Verbose(i, " package - cull by data pointer");
auto buffer = qbufferRenderer.getBuffer();
buffer->fillByPointer(biggerPkgs[i]);
qbufferRenderer.cull(buffer);
}
delete[] biggerPkgs;
} else if (frustumCheck == PARTIALLY_IN_FRUSTUM) {
u16 packagesCount = 0;
if (bag->count >= maxVertCount * 2) {
auto packages = packager.create(&packagesCount, bag, maxVertCount);
Verbose("Material - partial. Packages: ", packagesCount);
renderPkgs(packages, packagesCount);
delete[] packages;
} else {
auto subpkgs = packager.create(&packagesCount, bag, maxVertCount / 3);
Verbose("Material - partial. Subpackages: ", packagesCount);
renderSubpkgs(subpkgs, packagesCount);
delete[] subpkgs;
}
}
if (!bbox) delete renderBbox;
if (texBuffers) delete texBuffers;
Threading::switchThread();
Verbose("Render finished");
}
void StdPipelineCore::renderPkgs(StdpipBagPackage* packages, u16 count) {
for (u16 i = 0; i < count; i++) {
if (packages[i].isInFrustum == IN_FRUSTUM) {
Verbose(i, " - package in frustum -> cull");
auto buffer = qbufferRenderer.getBuffer();
buffer->fillByPointer(packages[i]);
qbufferRenderer.cull(buffer);
} else if (packages[i].isInFrustum == PARTIALLY_IN_FRUSTUM) {
u16 subpkgsSize = 0;
auto packages1By3 =
packager.create(&subpkgsSize, packages[i], maxVertCount / 3);
Verbose(i, " - partial package. Created subpkgs: ", subpkgsSize);
renderSubpkgs(packages1By3, subpkgsSize);
delete[] packages1By3;
}
Verbose(i, " - package skipped (outside)");
}
}
void StdPipelineCore::renderSubpkgs(StdpipBagPackage* subpkgs, u16 count) {
std::vector<u16> doneIndexes;
std::vector<u16> loadedIndexes;
// Check if some subpkgs are full in frustum
for (u16 i = 0; i < count; i++) {
if (subpkgs[i].isInFrustum == IN_FRUSTUM) {
if (loadedIndexes.size() <= 1) {
Verbose(i, " - subpackage in frustum -> load");
loadedIndexes.push_back(i);
} else { // Hmm, this will never happen?
Verbose(i, " - subpackage in frustum, cull all 3 subpkgs");
auto buffer = qbufferRenderer.getBuffer();
buffer->fillByCopyMax(subpkgs[loadedIndexes[0]],
subpkgs[loadedIndexes[1]], subpkgs[i]);
qbufferRenderer.cull(buffer);
doneIndexes.push_back(loadedIndexes[0]);
doneIndexes.push_back(loadedIndexes[1]);
doneIndexes.push_back(i);
loadedIndexes.clear();
}
}
}
if (loadedIndexes.size() == 2) {
Verbose("2 in frustum subpkgs left -> cull them");
auto buffer = qbufferRenderer.getBuffer();
buffer->fillByCopy1By2(subpkgs[loadedIndexes[0]],
subpkgs[loadedIndexes[1]]);
qbufferRenderer.cull(buffer);
doneIndexes.push_back(loadedIndexes[0]);
doneIndexes.push_back(loadedIndexes[1]);
} else if (loadedIndexes.size() == 1) {
Verbose("1 in frustum subpkg left -> cull it");
auto buffer = qbufferRenderer.getBuffer();
buffer->fillByPointer(subpkgs[loadedIndexes[0]]);
qbufferRenderer.cull(buffer);
doneIndexes.push_back(loadedIndexes[0]);
}
for (u16 i = 0; i < count; i++) {
bool isSkip = subpkgs[i].isInFrustum == OUTSIDE_FRUSTUM ||
std::find(doneIndexes.begin(), doneIndexes.end(), i) !=
doneIndexes.end();
if (isSkip) {
Verbose(i, " - subpkg skipped, already rendered/outside");
continue;
}
auto buffer = qbufferRenderer.getBuffer();
buffer->fillByCopy1By3(subpkgs[i]);
Verbose(i, " - subpkg out/partial -> send to clipper");
qbufferRenderer.clip(buffer);
}
}
void StdPipelineCore::setMaxVertCount(const u32& count) {
maxVertCount = count;
packager.setMaxVertCount(count);
qbufferRenderer.setMaxVertCount(count);
}
} // namespace Tyra
@@ -0,0 +1,221 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/pipeline/std/std_pipeline.hpp"
namespace Tyra {
StdPipeline::StdPipeline() { colorsCache = new Vec4[4]; }
StdPipeline::~StdPipeline() { delete[] colorsCache; }
void StdPipeline::init(RendererCore* t_core) {
rendererCore = t_core;
core.init(t_core);
}
void StdPipeline::onUse() { core.reinitStandardVU1Programs(); }
void StdPipeline::render(Mesh* mesh, const StdpipOptions* options) {
auto model = mesh->getModelMatrix();
MeshFrame* frameFrom = mesh->getFramesCount() > 0
? mesh->getFrame(mesh->getCurrentAnimationFrame())
: mesh->getFrame(0);
MeshFrame* frameTo = mesh->getFramesCount() > 0
? mesh->getFrame(mesh->getNextAnimationFrame())
: nullptr;
auto* infoBag = getInfoBag(mesh, options, &model);
if (options->lighting) setLightingColorsCache(options->lighting);
for (u32 i = 0; i < mesh->getMaterialsCount(); i++) {
auto* material = mesh->getMaterial(i);
// 2x bufory[maxVertCount*2] -> pętla po mniejszych częściach i czestsze
// rendery
// TODO: Double buffering in future
// auto maxVertCount = core->renderer3D.getMaxVertCountByParams(
// material->isSingleColorActivated(), material->getNormalFaces(),
// material->getTextureCoordFaces());
StdpipBag bag;
addVertices(mesh, material, &bag, frameFrom, frameTo);
bag.info = infoBag;
bag.color = getColorBag(mesh, material, frameFrom, frameTo);
bag.texture = getTextureBag(mesh, material, frameFrom, frameTo);
bag.lighting =
getLightingBag(mesh, material, &model, frameFrom, frameTo, options);
core.render(&bag);
deallocDrawBags(&bag, material);
}
delete infoBag;
}
void StdPipeline::addVertices(Mesh* mesh, MeshMaterial* material,
StdpipBag* bag, MeshFrame* frameFrom,
MeshFrame* frameTo) const {
bag->count = material->getFacesCount();
bag->vertices = new Vec4[bag->count];
for (u32 i = 0; i < bag->count; i++) {
auto& face = material->getVertexFaces()[i];
if (frameTo == nullptr) {
bag->vertices[i] = frameFrom->getVertices()[face];
} else {
Vec4::setLerp(&bag->vertices[i], frameFrom->getVertices()[face],
frameTo->getVertices()[face],
mesh->getAnimState().interpolation);
}
}
}
StdpipInfoBag* StdPipeline::getInfoBag(Mesh* mesh, const StdpipOptions* options,
M4x4* model) const {
auto* result = new StdpipInfoBag();
if (options) {
result->antiAliasingEnabled = options->antiAliasingEnabled;
result->blendingEnabled = options->blendingEnabled;
result->shadingType = options->shadingType;
result->noClipChecks = options->noClipChecks;
} else {
result->antiAliasingEnabled = false;
result->blendingEnabled = true;
result->shadingType = StdpipShadingFlat;
result->noClipChecks = true;
}
result->model = model;
return result;
}
StdpipColorBag* StdPipeline::getColorBag(Mesh* mesh, MeshMaterial* material,
MeshFrame* frameFrom,
MeshFrame* frameTo) const {
auto* result = new StdpipColorBag();
if (material->isSingleColorActivated()) {
result->single = &material->singleColor;
} else {
result->many = new Color[material->getFacesCount()];
for (u32 i = 0; i < material->getFacesCount(); i++) {
auto& face = material->getColorFaces()[i];
if (frameTo == nullptr) {
result->many[i] = frameFrom->getColors()[face];
} else {
Vec4::setLerp(
reinterpret_cast<Vec4*>(&result->many[i]),
reinterpret_cast<const Vec4&>(frameFrom->getColors()[face]),
reinterpret_cast<const Vec4&>(frameTo->getColors()[face]),
mesh->getAnimState().interpolation);
}
}
}
return result;
}
StdpipTextureBag* StdPipeline::getTextureBag(Mesh* mesh, MeshMaterial* material,
MeshFrame* frameFrom,
MeshFrame* frameTo) {
if (!material->getTextureCoordFaces()) return nullptr;
auto* result = new StdpipTextureBag();
result->texture =
rendererCore->texture.repository.getBySpriteOrMesh(material->getId());
TYRA_ASSERT(result->texture, "Texture for material id: ", material->getId(),
" was not found in texture repository!");
result->coordinates = new Vec4[material->getFacesCount()];
for (u32 i = 0; i < material->getFacesCount(); i++) {
auto& face = material->getTextureCoordFaces()[i];
if (frameTo == nullptr) {
result->coordinates[i] = frameFrom->getTextureCoords()[face];
} else {
Vec4::setLerp(&result->coordinates[i],
frameFrom->getTextureCoords()[face],
frameTo->getTextureCoords()[face],
mesh->getAnimState().interpolation);
}
}
return result;
}
StdpipLightingBag* StdPipeline::getLightingBag(
Mesh* mesh, MeshMaterial* material, M4x4* model, MeshFrame* frameFrom,
MeshFrame* frameTo, const StdpipOptions* options) const {
if (!material->getNormalFaces() || options == nullptr ||
options->lighting == nullptr)
return nullptr;
auto* result = new StdpipLightingBag(true);
result->lightMatrix = model;
result->setLightsManually(colorsCache,
options->lighting->directionalDirections);
result->normals = new Vec4[material->getFacesCount()];
for (u32 i = 0; i < material->getFacesCount(); i++) {
auto& face = material->getNormalFaces()[i];
if (frameTo == nullptr) {
result->normals[i] = frameFrom->getNormals()[face];
} else {
Vec4::setLerp(&result->normals[i], frameFrom->getNormals()[face],
frameTo->getNormals()[face],
mesh->getAnimState().interpolation);
}
}
return result;
}
void StdPipeline::setLightingColorsCache(
StdpipLightingOptions* lightingOptions) {
for (int i = 0; i < 3; i++) {
colorsCache[i] =
reinterpret_cast<Vec4&>(lightingOptions->directionalColors[i]);
}
colorsCache[3] = reinterpret_cast<Vec4&>(*lightingOptions->ambientColor);
}
void StdPipeline::deallocDrawBags(StdpipBag* bag,
MeshMaterial* material) const {
if (bag->color->many) {
delete[] bag->color->many;
}
if (bag->texture) {
delete[] bag->texture->coordinates;
delete bag->texture;
}
if (bag->lighting) {
delete[] bag->lighting->normals;
delete bag->lighting;
}
delete[] bag->vertices;
delete bag->color;
}
} // namespace Tyra
+27
View File
@@ -0,0 +1,27 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/3d/renderer_3d.hpp"
namespace Tyra {
Renderer3D::Renderer3D() { currentPipeline = nullptr; }
Renderer3D::~Renderer3D() {}
void Renderer3D::usePipeline(Renderer3DPipeline* pipeline) {
if (currentPipeline != pipeline) {
currentPipeline = pipeline;
currentPipeline->onUse();
}
}
} // namespace Tyra
@@ -0,0 +1,104 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/2d/renderer_core_2d.hpp"
#include <dma.h>
#include <draw.h>
namespace Tyra {
RendererCore2D::RendererCore2D() {
context = 0;
packets[0] = packet2_create(16, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0);
packets[1] = packet2_create(16, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0);
rects[0] = new texrect_t;
rects[1] = new texrect_t;
}
RendererCore2D::~RendererCore2D() {
packet2_free(packets[0]);
packet2_free(packets[1]);
delete rects[0];
delete rects[1];
}
const float RendererCore2D::GS_CENTER = 4096.0F;
const float RendererCore2D::SCREEN_CENTER = 4096.0F / 2.0F;
void RendererCore2D::init(RendererSettings* t_settings,
clutbuffer_t* t_clutBuffer) {
settings = t_settings;
clutBuffer = t_clutBuffer;
}
void RendererCore2D::render(Sprite* sprite,
const RendererCoreTextureBuffers& texBuffers,
Texture* texture) {
auto* rect = rects[context];
float sizeX, sizeY;
if (sprite->getMode() == MODE_REPEAT) {
sizeX = sprite->size.x;
sizeY = sprite->size.y;
} else {
sizeX = static_cast<float>(texture->getWidth());
sizeY = static_cast<float>(texture->getHeight());
}
float texS, texT;
float texMax = texT = texS = sizeX > sizeY ? sizeX : sizeY;
if (sizeX > sizeY)
texT = texMax / (sizeX / sizeY);
else if (sizeY > sizeX)
texS = texMax / (sizeY / sizeX);
rect->t0.s = sprite->isFlippedHorizontally() ? texS : 0.0F;
rect->t0.t = sprite->isFlippedVertically() ? texT : 0.0F;
rect->t1.s = sprite->isFlippedHorizontally() ? 0.0F : texS;
rect->t1.t = sprite->isFlippedVertically() ? 0.0F : texT;
rect->color.r = sprite->color.r;
rect->color.g = sprite->color.g;
rect->color.b = sprite->color.b;
rect->color.a = sprite->color.a;
rect->color.q = 0;
rect->v0.x = sprite->position.x;
rect->v0.y = sprite->position.y;
rect->v0.z = (u32)-1;
rect->v1.x = (sprite->size.x * sprite->scale) + sprite->position.x;
rect->v1.y = (sprite->size.y * sprite->scale) + sprite->position.y;
rect->v1.z = (u32)-1;
auto* packet = packets[context];
packet2_reset(packet, false);
packet2_update(packet, draw_primitive_xyoffset(packet->base, 0, SCREEN_CENTER,
SCREEN_CENTER));
packet2_utils_gif_add_set(packet, 1);
packet2_utils_gs_add_texbuff_clut(packet, texBuffers.core, clutBuffer);
draw_enable_blending();
packet2_update(packet, draw_rect_textured(packet->next, 0, rect));
packet2_update(packet, draw_primitive_xyoffset(
packet->next, 0,
SCREEN_CENTER - (settings->getWidth() / 2.0F),
SCREEN_CENTER - (settings->getHeight() / 2.0F)));
draw_disable_blending();
packet2_update(packet, draw_finish(packet->next));
dma_channel_wait(DMA_CHANNEL_GIF, 0);
dma_channel_send_packet2(packet, DMA_CHANNEL_GIF, true);
context = !context;
}
} // namespace Tyra
@@ -0,0 +1,39 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/2d/sprite/sprite.hpp"
namespace Tyra {
Sprite::Sprite() {
id = rand() % 1000000;
_flipH = false;
_flipV = false;
size.set(32.0F, 32.0F);
position.set(100.0F, 100.0F);
scale = 1.0F;
mode = MODE_REPEAT;
setDefaultColor();
}
Sprite::~Sprite() {}
// ----
// Methods
// ----
void Sprite::setDefaultColor() {
color.r = 128;
color.g = 128;
color.b = 128;
color.a = 128;
}
} // namespace Tyra
@@ -0,0 +1,169 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <string>
#include <sstream>
#include "renderer/core/3d/bbox/core_bbox.hpp"
namespace Tyra {
CoreBBox::CoreBBox(CoreBBox** t_bboxes, const u32& count) {
float lowX = t_bboxes[0]->_vertices[0].x;
float lowY = t_bboxes[0]->_vertices[0].y;
float lowZ = t_bboxes[0]->_vertices[0].z;
float hiX = t_bboxes[0]->_vertices[7].x;
float hiY = t_bboxes[0]->_vertices[7].y;
float hiZ = t_bboxes[0]->_vertices[7].z;
for (u32 i = 0; i < count; i++) {
if (lowX > t_bboxes[i]->_vertices[0].x) lowX = t_bboxes[i]->_vertices[0].x;
if (hiX < t_bboxes[i]->_vertices[7].x) hiX = t_bboxes[i]->_vertices[7].x;
if (lowY > t_bboxes[i]->_vertices[0].y) lowY = t_bboxes[i]->_vertices[0].y;
if (hiY < t_bboxes[i]->_vertices[7].y) hiY = t_bboxes[i]->_vertices[7].y;
if (lowZ > t_bboxes[i]->_vertices[0].z) lowZ = t_bboxes[i]->_vertices[0].z;
if (hiZ < t_bboxes[i]->_vertices[7].z) hiZ = t_bboxes[i]->_vertices[7].z;
}
_vertices[0].set(lowX, lowY, lowZ);
_vertices[1].set(lowX, lowY, hiZ);
_vertices[2].set(lowX, hiY, lowZ);
_vertices[3].set(lowX, hiY, hiZ);
_vertices[4].set(hiX, lowY, lowZ);
_vertices[5].set(hiX, lowY, hiZ);
_vertices[6].set(hiX, hiY, lowZ);
_vertices[7].set(hiX, hiY, hiZ);
}
CoreBBox::CoreBBox(const std::vector<CoreBBox>& t_bboxes, const u32& startIndex,
const u32& stopIndex) {
float lowX = t_bboxes[startIndex]._vertices[0].x;
float lowY = t_bboxes[startIndex]._vertices[0].y;
float lowZ = t_bboxes[startIndex]._vertices[0].z;
float hiX = t_bboxes[startIndex]._vertices[7].x;
float hiY = t_bboxes[startIndex]._vertices[7].y;
float hiZ = t_bboxes[startIndex]._vertices[7].z;
for (u32 i = startIndex; i < stopIndex; i++) {
if (lowX > t_bboxes[i]._vertices[0].x) lowX = t_bboxes[i]._vertices[0].x;
if (hiX < t_bboxes[i]._vertices[7].x) hiX = t_bboxes[i]._vertices[7].x;
if (lowY > t_bboxes[i]._vertices[0].y) lowY = t_bboxes[i]._vertices[0].y;
if (hiY < t_bboxes[i]._vertices[7].y) hiY = t_bboxes[i]._vertices[7].y;
if (lowZ > t_bboxes[i]._vertices[0].z) lowZ = t_bboxes[i]._vertices[0].z;
if (hiZ < t_bboxes[i]._vertices[7].z) hiZ = t_bboxes[i]._vertices[7].z;
}
_vertices[0].set(lowX, lowY, lowZ);
_vertices[1].set(lowX, lowY, hiZ);
_vertices[2].set(lowX, hiY, lowZ);
_vertices[3].set(lowX, hiY, hiZ);
_vertices[4].set(hiX, lowY, lowZ);
_vertices[5].set(hiX, lowY, hiZ);
_vertices[6].set(hiX, hiY, lowZ);
_vertices[7].set(hiX, hiY, hiZ);
}
CoreBBox::CoreBBox(Vec4* t_vertices, u32* faces, u32 count) {
float lowX, lowY, lowZ, hiX, hiY, hiZ;
lowX = hiX = t_vertices[faces[0]].x;
lowY = hiY = t_vertices[faces[0]].y;
lowZ = hiZ = t_vertices[faces[0]].z;
for (u32 i = 0; i < count; i++) {
if (lowX > t_vertices[faces[i]].x) lowX = t_vertices[faces[i]].x;
if (hiX < t_vertices[faces[i]].x) hiX = t_vertices[faces[i]].x;
if (lowY > t_vertices[faces[i]].y) lowY = t_vertices[faces[i]].y;
if (hiY < t_vertices[faces[i]].y) hiY = t_vertices[faces[i]].y;
if (lowZ > t_vertices[faces[i]].z) lowZ = t_vertices[faces[i]].z;
if (hiZ < t_vertices[faces[i]].z) hiZ = t_vertices[faces[i]].z;
}
_vertices[0].set(lowX, lowY, lowZ);
_vertices[1].set(lowX, lowY, hiZ);
_vertices[2].set(lowX, hiY, lowZ);
_vertices[3].set(lowX, hiY, hiZ);
_vertices[4].set(hiX, lowY, lowZ);
_vertices[5].set(hiX, lowY, hiZ);
_vertices[6].set(hiX, hiY, lowZ);
_vertices[7].set(hiX, hiY, hiZ);
}
CoreBBox::CoreBBox(Vec4* t_vertices, u32 count) {
float lowX, lowY, lowZ, hiX, hiY, hiZ;
lowX = hiX = t_vertices[0].x;
lowY = hiY = t_vertices[0].y;
lowZ = hiZ = t_vertices[0].z;
for (u32 i = 0; i < count; i++) {
if (lowX > t_vertices[i].x) lowX = t_vertices[i].x;
if (hiX < t_vertices[i].x) hiX = t_vertices[i].x;
if (lowY > t_vertices[i].y) lowY = t_vertices[i].y;
if (hiY < t_vertices[i].y) hiY = t_vertices[i].y;
if (lowZ > t_vertices[i].z) lowZ = t_vertices[i].z;
if (hiZ < t_vertices[i].z) hiZ = t_vertices[i].z;
}
_vertices[0].set(lowX, lowY, lowZ);
_vertices[1].set(lowX, lowY, hiZ);
_vertices[2].set(lowX, hiY, lowZ);
_vertices[3].set(lowX, hiY, hiZ);
_vertices[4].set(hiX, lowY, lowZ);
_vertices[5].set(hiX, lowY, hiZ);
_vertices[6].set(hiX, hiY, lowZ);
_vertices[7].set(hiX, hiY, hiZ);
}
CoreBBox::CoreBBox(Vec4* t_vertices) {
for (auto i = 0; i < 8; i++) Vec4::copy(&_vertices[i], t_vertices[i].xyzw);
}
void CoreBBox::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void CoreBBox::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string CoreBBox::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "CoreBBox(";
}
res << std::fixed << std::setprecision(4);
res << std::endl;
for (auto i = 0; i < 8; i++) {
res << i << ": " << _vertices[i].x << ", " << _vertices[i].y << ", "
<< _vertices[i].z << ", " << _vertices[i].w;
if (i != 7)
res << std::endl;
else
res << ")";
}
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,94 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <string>
#include <sstream>
#include "renderer/core/3d/bbox/render_bbox.hpp"
namespace Tyra {
RenderBBox::RenderBBox(CoreBBox** t_bboxes, const u32& count)
: CoreBBox(t_bboxes, count) {}
RenderBBox::RenderBBox(const std::vector<CoreBBox>& t_bboxes,
const u32& startIndex, const u32& stopIndex)
: CoreBBox(t_bboxes, startIndex, stopIndex) {}
RenderBBox::RenderBBox(Vec4* t_vertices, u32* faces, u32 count)
: CoreBBox(t_vertices, faces, count) {}
RenderBBox::RenderBBox(Vec4* t_vertices, u32 count)
: CoreBBox(t_vertices, count) {}
RenderBBox::RenderBBox(Vec4* t_vertices) : CoreBBox(t_vertices) {}
/**
* @brief Frustum checker for renderer.
* Background: We want to really put as low as possible polys to clipper.
* So we are doing magic trick. If BBox is partially inside frustum (clipper),
* we are adding some margins, and checking again if it really needs clipping,
* because "Cull" renderer can handle easy clip cases and its faster.
*/
CoreBBoxFrustum RenderBBox::clipIsInFrustum(const Plane* frustumPlanes,
const M4x4& model) const {
auto result = isInFrustum(frustumPlanes, model);
if (result != PARTIALLY_IN_FRUSTUM) {
return result;
}
// Oh no, it probably needs clipping
float margins[6]; // This probably needs more calibration
margins[0] = -15.0F; // Top
margins[1] = -10.0F; // BOTTOM
margins[2] = -25.0F; // LEFT
margins[3] = -25.0F; // RIGHT
margins[4] = -10.0F; // NEAR
margins[5] = -10.0F; // FAR
return isInFrustum(frustumPlanes, model, margins); // Let's check it again
}
CoreBBoxFrustum RenderBBox::isInFrustum(const Plane* frustumPlanes,
const M4x4& model,
const float* margins) const {
CoreBBoxFrustum result = IN_FRUSTUM;
Vec4 boxCalcTemp;
u8 boxIn = 0, boxOut = 0;
for (u8 i = 0; i < 6; i++) {
const auto margin = margins == nullptr ? 0.0F : margins[i];
boxOut = 0;
boxIn = 0;
// for each corner of the box do ...
// get out of the cycle as soon as a box as corners
// both inside and out of the frustum
for (u8 y = 0; y < 8 && (boxIn == 0 || boxOut == 0); y++) {
boxCalcTemp = model * _vertices[y];
auto isOut = frustumPlanes[i].distanceTo(boxCalcTemp) < margin;
if (isOut)
boxOut++;
else
boxIn++;
}
// if all corners are out
if (!boxIn)
return OUTSIDE_FRUSTUM;
else if (boxOut)
result = PARTIALLY_IN_FRUSTUM;
}
return result;
}
} // namespace Tyra
@@ -0,0 +1,30 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/3d/camera_info_3d.hpp"
namespace Tyra {
Vec4 CameraInfo3D::defaultCameraUp = Vec4(0.0F, 1.0F, 0.0F);
CameraInfo3D::CameraInfo3D(Vec4* t_cameraPosition, Vec4* t_cameraLooksAt,
Vec4* t_cameraUp) {
position = t_cameraPosition;
looksAt = t_cameraLooksAt;
if (t_cameraUp == nullptr) {
up = &defaultCameraUp;
} else {
up = t_cameraUp;
}
}
CameraInfo3D::~CameraInfo3D() {}
} // namespace Tyra
@@ -0,0 +1,107 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <string>
#include "debug/debug.hpp"
#include "renderer/core/3d/renderer_3d_frustum_planes.hpp"
namespace Tyra {
Renderer3DFrustumPlanes::Renderer3DFrustumPlanes() { lastFov = 0.0F; }
Renderer3DFrustumPlanes::~Renderer3DFrustumPlanes() {}
void Renderer3DFrustumPlanes::init(RendererSettings* t_settings,
const float& fov) {
settings = t_settings;
computeStaticData(fov);
TYRA_LOG("Frustum planes initialized!");
}
void Renderer3DFrustumPlanes::update(const CameraInfo3D& cameraInfo,
const float& fov) {
computeStaticData(fov);
// compute the Z axis of camera
Z = *cameraInfo.position - *cameraInfo.looksAt;
Z.normalize();
// X axis of camera of given "up" vector and Z axis
X = cameraInfo.up->cross(Z);
X.normalize();
// the real "up" vector is the cross product of Z and X
Y = Z.cross(X);
// compute the center of the near and far planes
nearCenter = *cameraInfo.position - Z * settings->getNear();
farCenter = *cameraInfo.position - Z * settings->getFar();
// compute the 8 corners of the frustum
ntl = nearCenter + Y * nearHeight - X * nearWidth;
ntr = nearCenter + Y * nearHeight + X * nearWidth;
nbl = nearCenter - Y * nearHeight - X * nearWidth;
nbr = nearCenter - Y * nearHeight + X * nearWidth;
ftl = farCenter + Y * farHeight - X * farWidth;
fbr = farCenter - Y * farHeight + X * farWidth;
ftr = farCenter + Y * farHeight + X * farWidth;
fbl = farCenter - Y * farHeight - X * farWidth;
frustumPlanes[0].update(ntr, ntl, ftl); // Top
frustumPlanes[1].update(nbl, nbr, fbr); // BOTTOM
frustumPlanes[2].update(ntl, nbl, fbl); // LEFT
frustumPlanes[3].update(nbr, ntr, fbr); // RIGHT
frustumPlanes[4].update(ntl, ntr, nbr); // NEAR
frustumPlanes[5].update(ftr, ftl, fbl); // FAR
}
void Renderer3DFrustumPlanes::computeStaticData(const float& fov) {
if (fabs(fov - lastFov) < 0.00001F) return;
lastFov = fov;
float tang = tanf(fov * Math::HALF_ANG2RAD);
nearHeight = tang * settings->getNear();
nearWidth = nearHeight * settings->getAspectRatio();
farHeight = tang * settings->getFar();
farWidth = farHeight * settings->getAspectRatio();
}
void Renderer3DFrustumPlanes::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void Renderer3DFrustumPlanes::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string Renderer3DFrustumPlanes::getPrint(const char* name) const {
std::stringstream res;
if (name) {
res << name << "(";
} else {
res << "Renderer3DFrustumPlanes(";
}
res << std::fixed << std::setprecision(2);
res << std::endl;
res << frustumPlanes[0].getPrint("Top") << std::endl;
res << frustumPlanes[1].getPrint("Bottom") << std::endl;
res << frustumPlanes[2].getPrint("Left") << std::endl;
res << frustumPlanes[3].getPrint("Right") << std::endl;
res << frustumPlanes[4].getPrint("Near") << std::endl;
res << frustumPlanes[5].getPrint("Far") << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,54 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/3d/renderer_core_3d.hpp"
namespace Tyra {
RendererCore3D::RendererCore3D() { fov = 60.0F; }
RendererCore3D::~RendererCore3D() {}
void RendererCore3D::update(const CameraInfo3D& cameraInfo) {
frustumPlanes.update(cameraInfo, fov);
M4x4::lookAt(&view, *cameraInfo.position, *cameraInfo.looksAt);
viewProj = projection * view;
}
void RendererCore3D::init(RendererSettings* t_settings, Path1* t_path1) {
settings = t_settings;
path1 = t_path1;
frustumPlanes.init(settings, fov);
setProjection();
TYRA_LOG("RendererCore3D initialized!");
}
// TODO, usunac albo private core? przesunac set/get fov?
void RendererCore3D::setFov(const float& t_fov) {
fov = t_fov;
setProjection();
}
void RendererCore3D::setProjection() {
projection = M4x4::perspective(
fov, settings->getWidth(), settings->getHeight(),
settings->getProjectionScale(), settings->getAspectRatio(),
settings->getNear(), settings->getFar());
}
u32 RendererCore3D::uploadVU1Program(VU1Program* program, const u32& address) {
return path1->uploadProgram(program, address);
}
void RendererCore3D::setVU1DoubleBuffers(const u16& startingAddress,
const u16& bufferSize) {
path1->setDoubleBuffer(startingAddress, bufferSize);
}
} // namespace Tyra
@@ -0,0 +1,147 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <dma.h>
#include <tamtypes.h>
#include <draw.h>
#include <graph.h>
#include <gs_privileged.h>
#include <gs_psm.h>
#include <packet2.h>
#include <packet2_utils.h>
#include <packet2_chain.h>
#include <packet2_types.h>
#include "debug/debug.hpp"
#include "renderer/core/gs/renderer_core_gs.hpp"
namespace Tyra {
RendererCoreGS::RendererCoreGS() {
context = 0;
spaceOccupiedByFrameBuffers = 0;
spaceOccupiedByZBuffer = 0;
}
RendererCoreGS::~RendererCoreGS() {
if (flipPacket) {
packet2_free(flipPacket);
}
}
void RendererCoreGS::init(RendererSettings* t_settings) {
settings = t_settings;
initChannels();
flipPacket = packet2_create(4, P2_TYPE_UNCACHED_ACCL, P2_MODE_NORMAL, 0);
allocateBuffers();
initDrawingEnvironment();
setPrim();
setLod();
TYRA_LOG("Renderer core initialized!");
}
void RendererCoreGS::initChannels() {
dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0);
}
void RendererCoreGS::allocateBuffers() {
const u16 psm = 24;
frameBuffers[0].width = static_cast<unsigned int>(settings->getWidth());
frameBuffers[0].height = static_cast<unsigned int>(settings->getHeight());
frameBuffers[0].mask = 0;
frameBuffers[0].psm = GS_PSM_24;
frameBuffers[0].address =
graph_vram_allocate(frameBuffers[0].width, frameBuffers[0].height,
frameBuffers[0].psm, GRAPH_ALIGN_PAGE);
frameBuffers[1].width = frameBuffers[0].width;
frameBuffers[1].height = frameBuffers[0].height;
frameBuffers[1].mask = frameBuffers[0].mask;
frameBuffers[1].psm = frameBuffers[0].psm;
frameBuffers[1].address =
graph_vram_allocate(frameBuffers[1].width, frameBuffers[1].height,
frameBuffers[1].psm, GRAPH_ALIGN_PAGE);
zBuffer.enable = DRAW_ENABLE;
zBuffer.mask = 0;
zBuffer.method = ZTEST_METHOD_GREATER_EQUAL;
zBuffer.zsm = GS_ZBUF_24;
zBuffer.address =
graph_vram_allocate(frameBuffers[0].width, frameBuffers[0].height,
zBuffer.zsm, GRAPH_ALIGN_PAGE);
TYRA_LOG("Framebuffers, zBuffer set and allocated!");
// Initialize the screen and tie the first framebuffer to the read circuits.
graph_initialize(frameBuffers[1].address, frameBuffers[1].width,
frameBuffers[1].height, frameBuffers[1].psm, 0, 0);
spaceOccupiedByFrameBuffers =
((frameBuffers[0].width / 100.0F) * (frameBuffers[0].height / 100.0F) *
(psm / 100.0F)) /
8.0F;
spaceOccupiedByZBuffer = spaceOccupiedByFrameBuffers;
spaceOccupiedByFrameBuffers *= 2;
}
void RendererCoreGS::initDrawingEnvironment() {
packet2_t* packet2 = packet2_create(20, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0);
packet2_update(packet2, draw_setup_environment(packet2->base, 0, frameBuffers,
&zBuffer));
packet2_update(packet2, draw_primitive_xyoffset(
packet2->next, 0,
screenCenter - (settings->getWidth() / 2.0F),
screenCenter - (settings->getHeight() / 2.0F)));
packet2_update(packet2, draw_finish(packet2->next));
dma_channel_send_packet2(packet2, DMA_CHANNEL_GIF, true);
dma_channel_wait(DMA_CHANNEL_GIF, 0);
packet2_free(packet2);
TYRA_LOG("Drawing environment initialized!");
}
void RendererCoreGS::setPrim() {
prim.type = PRIM_TRIANGLE;
prim.shading = PRIM_SHADE_GOURAUD;
prim.mapping = DRAW_ENABLE;
prim.fogging = DRAW_DISABLE;
prim.blending = DRAW_ENABLE;
prim.antialiasing = DRAW_DISABLE;
prim.mapping_type = PRIM_MAP_ST;
prim.colorfix = PRIM_UNFIXED;
TYRA_LOG("Prim set!");
}
void RendererCoreGS::setLod() {
lod.calculation = LOD_USE_K;
lod.max_level = 0;
lod.mag_filter = LOD_MAG_NEAREST;
lod.min_filter = LOD_MIN_NEAREST;
lod.l = 0;
lod.k = 0.0F;
TYRA_LOG("Lod set!");
}
void RendererCoreGS::flipBuffers() {
graph_set_framebuffer_filtered(frameBuffers[context].address,
frameBuffers[context].width,
frameBuffers[context].psm, 0, 0);
context ^= 1;
packet2_update(flipPacket,
draw_framebuffer(flipPacket->base, 0, &frameBuffers[context]));
packet2_update(flipPacket, draw_finish(flipPacket->next));
dma_channel_wait(DMA_CHANNEL_GIF, 0);
dma_channel_send_packet2(flipPacket, DMA_CHANNEL_GIF, true);
draw_wait_finish();
}
} // namespace Tyra
@@ -0,0 +1,114 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp"
namespace Tyra {
Path1EEClipAlgorithm::Path1EEClipAlgorithm() {}
Path1EEClipAlgorithm::~Path1EEClipAlgorithm() {}
float Path1EEClipAlgorithm::clipMargin = -10.0F;
void Path1EEClipAlgorithm::init(const RendererSettings& settings) {
halfWidth = settings.getWidth() / 2;
halfHeight = settings.getHeight() / 2;
near = settings.getNear() - (-clipMargin);
far = -settings.getFar();
}
void Path1EEClipAlgorithm::clip(std::vector<Path1ClipVertex>* o_vertices,
const std::vector<Path1ClipVertex>& vertices,
const Path1EEClipAlgorithmSettings& settings) {
tempVertices.clear();
for (u32 i = 0; i < vertices.size(); i++) {
o_vertices->push_back(vertices.at(i));
}
clipAgainstPlane(*o_vertices, &tempVertices, 1, halfWidth, settings);
clipAgainstPlane(tempVertices, o_vertices, 1, -halfWidth, settings);
clipAgainstPlane(*o_vertices, &tempVertices, 2, halfHeight, settings);
clipAgainstPlane(tempVertices, o_vertices, 2, -halfHeight, settings);
clipAgainstPlane(*o_vertices, &tempVertices, 3, near, settings);
clipAgainstPlane(tempVertices, o_vertices, 4, far, settings);
}
float Path1EEClipAlgorithm::getValueByPlane(const Path1ClipVertex& v,
const int& plane) {
switch (plane) {
case 1:
return v.position.x; // x plane
case 2:
return v.position.y; // y plane
case 3: // z near
case 4:
return v.position.z; // z far
default:
return 0;
}
}
bool Path1EEClipAlgorithm::isInside(const int& plane, const float& v,
const float& w,
const float& planeLimitValue) {
switch (plane) {
case 3:
return v <= planeLimitValue; // near z plane
case 4:
return v >= planeLimitValue; // far z plane
default:
return (planeLimitValue < 0) ? (v >= planeLimitValue * w)
: (v <= planeLimitValue * w);
}
}
void Path1EEClipAlgorithm::clipAgainstPlane(
const std::vector<Path1ClipVertex>& original,
std::vector<Path1ClipVertex>* clipped, const int& plane,
const float& planeLimitValue,
const Path1EEClipAlgorithmSettings& settings) {
clipped->clear();
for (u32 i = 0; i < original.size(); i++) {
auto a = original.at(i);
auto b = original.at((i + 1) % original.size());
auto apx = getValueByPlane(a, plane);
auto bpx = getValueByPlane(b, plane);
auto aIsInside = isInside(plane, apx, a.position.w, planeLimitValue);
auto bIsInside = isInside(plane, bpx, b.position.w, planeLimitValue);
if (aIsInside) {
clipped->push_back(a);
}
if (aIsInside != bIsInside) {
auto p =
(plane >= 3)
? (planeLimitValue - a.position.z) / (b.position.z - a.position.z)
: (-a.position.w * planeLimitValue + apx) /
((b.position.w - a.position.w) * planeLimitValue -
(bpx - apx));
Path1ClipVertex nb = {
Vec4::getByLerp(a.position, b.position, p),
settings.lerpNormals ? Vec4::getByLerp(a.normal, b.normal, p)
: Vec4(),
settings.lerpTexCoords ? Vec4::getByLerp(a.st, b.st, p) : Vec4(),
settings.lerpColors ? Vec4::getByLerp(a.color, b.color, p) : Vec4(),
};
clipped->push_back(nb);
}
}
}
} // namespace Tyra
@@ -0,0 +1,82 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/paths/path1/path1.hpp"
namespace Tyra {
Path1::Path1() {
doubleBufferPacket = packet2_create(2, P2_TYPE_NORMAL, P2_MODE_CHAIN, true);
}
Path1::~Path1() { packet2_free(doubleBufferPacket); }
u32 Path1::uploadProgram(VU1Program* program, const u32& address) {
// TYRA_LOG("Uploading VU1 program. Size: ", program->getProgramSize(),
// ", name: ", program->getStringName(), ", address:", address);
auto packetSize = program->getPacketSize() + 1; // + end tag
packet2_t* packet2 =
packet2_create(packetSize, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1);
packet2_vif_add_micro_program(packet2, address, program->getStart(),
program->getEnd());
packet2_utils_vu_add_end_tag(packet2);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
dma_channel_send_packet2(packet2, DMA_CHANNEL_VIF1, true);
dma_channel_wait(DMA_CHANNEL_VIF1, 0);
program->setDestinationAddress(address);
packet2_free(packet2);
return address + program->getProgramSize();
}
packet2_t* Path1::createProgramsCache(VU1Program** programs, const u32& count,
const u32& address) {
u32 packetSize = 1; // + end tag
for (u32 i = 0; i < count; i++) {
packetSize += programs[i]->getPacketSize();
}
packet2_t* packet2 =
packet2_create(packetSize, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1);
u32 currentAddr = address;
for (u32 i = 0; i < count; i++) {
programs[i]->setDestinationAddress(currentAddr);
packet2_vif_add_micro_program(packet2, currentAddr, programs[i]->getStart(),
programs[i]->getEnd());
currentAddr += programs[i]->getProgramSize() + 1;
}
packet2_utils_vu_add_end_tag(packet2);
return packet2;
}
/** Set double buffer settings */
void Path1::setDoubleBuffer(const u16& startingAddress, const u16& bufferSize) {
packet2_reset(doubleBufferPacket, false);
packet2_utils_vu_add_double_buffer(doubleBufferPacket, startingAddress,
bufferSize);
// TYRA_LOG("VU1 double buffer: starting addr: ", startingAddress,
// ", offset: ", startingAddress + bufferSize, ", size: ",
// bufferSize);
packet2_utils_vu_add_end_tag(doubleBufferPacket);
dma_channel_send_packet2(doubleBufferPacket, DMA_CHANNEL_VIF1, true);
}
} // namespace Tyra
@@ -0,0 +1,45 @@
; ______ ____ ___
; | \/ ____| |___|
; | | | \ | |
;---------------------------------------------------------------
; Copyright 2022, tyra - https://github.com/h4570/tyra
; Licenced under Apache License 2.0
; Sandro Sobczyński <sandro.sobczynski@gmail.com>
;
;---------------------------------------------------------------
; Needed for synchronization with draw_wait_finish()
;---------------------------------------------------------------
.syntax new
.name VU1DrawFinish
.vu
.init_vf_all
.init_vi_all
#include "src/renderer/core/paths/path1/programs/vcl_sml.i"
#include "src/renderer/core/paths/path1/programs/tyra_macros.i"
#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h"
--enter
--endenter
#vuprog VU1DrawFinish
LoadTyraStaticData{ gifSetTag }
xtop buffer
lq drawFinishTag, 0(buffer)
lq primTag, 1(buffer)
iaddiu kickAddress, buffer, 2
sq gifSetTag, 0(kickAddress)
sq drawFinishTag, 1(kickAddress)
sq primTag, 2(kickAddress)
xgkick kickAddress
#endvuprog
--exit
--endexit
@@ -0,0 +1,37 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "debug/debug.hpp"
#include "renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.hpp"
extern u32 VU1DrawFinish_CodeStart __attribute__((section(".vudata")));
extern u32 VU1DrawFinish_CodeEnd __attribute__((section(".vudata")));
namespace Tyra {
VU1DrawFinish::VU1DrawFinish()
: VU1Program(&VU1DrawFinish_CodeStart, &VU1DrawFinish_CodeEnd) {}
VU1DrawFinish::~VU1DrawFinish() {}
std::string VU1DrawFinish::getStringName() const {
return std::string("Draw finish");
}
void VU1DrawFinish::addTag(packet2_t* packet, prim_t* prim) const {
packet2_utils_vu_open_unpack(packet, 0, true);
packet2_utils_gs_add_draw_finish_giftag(packet);
packet2_utils_gs_add_prim_giftag(
packet, prim, 1, ((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2,
0);
packet2_utils_vu_close_unpack(packet);
}
} // namespace Tyra
@@ -0,0 +1,99 @@
;//--------------------------------------------------------------------------------
;// Tyra's standard macros library
;//--------------------------------------------------------------------------------
;//---------------------------------------------------------
;// LoadTyraStaticData - Load "Set" gif tag
;//---------------------------------------------------------
#macro LoadTyraStaticData: t_gifSetTagName
lq t_gifSetTagName, VU1_SET_GIFTAG_ADDR(vi00)
#endmacro
;//---------------------------------------------------------
;// LoadTyraLightMatrix - Loads single color. Color is placed in 4th slot of Lights matrix
;//---------------------------------------------------------
#macro LoadTyraSingleColor: t_singleColor, t_singleColorEnabled, t_singleColorAddr, t_optionsAddr
lq t_singleColor, t_singleColorAddr(vi00)
ilw.x t_singleColorEnabled, t_optionsAddr(vi00)
#endmacro
;//---------------------------------------------------------
;// LoadTyraTags - Load lod, texture buffer and clut
;// 1 - GIF tag - texture LOD
;// 2 - GIF tag - texture buffer & CLUT
;//---------------------------------------------------------
#macro LoadTyraTags: t_lodGifTag, t_texBufferClutGifTag, t_lodAddr, t_ClutAddr
lq t_lodGifTag, t_lodAddr(vi00)
lq t_texBufferClutGifTag, t_ClutAddr(vi00)
#endmacro
;//---------------------------------------------------------
;// LoadTyraBufferTags - Load scales and prim tag
;// 1 - float : X, Y, Z - scale vector that we will use to scale the verts after projecting them, float : W - vert count.
;// 2 - GIF tag - tell GS how many data we will send
;//---------------------------------------------------------
#macro LoadTyraBufferTags: t_scale, t_primTag, t_buffer
lq.xyz t_scale, 0(t_buffer)
lq t_primTag, 1(t_buffer)
#endmacro
;//---------------------------------------------------------
;// LoadTyraDirectionalLights - Load directions and colors. All are always present
;// - 3 directional lights directions
;// - 3 directional lights colors + ambient color
;//---------------------------------------------------------
#macro LoadTyraDirectionalLights: t_lightMatrix, t_lightDirections, t_lightsColors, t_ambientColor, t_dirOffset, t_colorOffset, t_matrixOffset
lq.xyz t_lightMatrix[0], t_matrixOffset+0(vi00)
lq.xyz t_lightMatrix[1], t_matrixOffset+1(vi00)
lq.xyz t_lightMatrix[2], t_matrixOffset+2(vi00)
lq.xyz t_lightDirections[0], t_dirOffset(vi00)
lq.xyz t_lightDirections[1], t_dirOffset+1(vi00)
lq.xyz t_lightDirections[2], t_dirOffset+2(vi00)
lq.xyz t_lightsColors[0], t_colorOffset(vi00)
lq.xyz t_lightsColors[1], t_colorOffset+1(vi00)
lq.xyz t_lightsColors[2], t_colorOffset+2(vi00)
lq.xyz t_ambientColor, t_colorOffset+3(vi00)
#endmacro
;//---------------------------------------------------------
;// StoreTyraGifTags - Store gif tags.
;// Not using sqi instruction, because VCL cannot optimize it.
;// Primtag contains information about how many polys we will send
;//---------------------------------------------------------
#macro StoreTyraGifTags: t_gifSetTag, t_lodGifTag, t_texBufferClutGifTag, t_primTag, t_destAddress
sq t_gifSetTag, 0(t_destAddress)
sq t_lodGifTag, 1(t_destAddress)
sq t_gifSetTag, 2(t_destAddress)
sq t_texBufferClutGifTag, 3(t_destAddress)
sq t_primTag, 4(t_destAddress)
iaddiu t_destAddress, t_destAddress, 5
#endmacro
;//---------------------------------------------------------
;// CalculateLights - Based on Dr Fortuna's work
;//
;// 1. Transform by the rotation part of the world matrix
;// 2. "Transform" the normal by the light direction matrix
;// 3. Four intensities, one for each light.
;// 4. Clamp the intensity to 0..1
;// 5. Transform the intensities by the light colour matrix
;// 6. Load 128 and put it into the alpha value
;// 7. Clamp result to 0-128 values
;// 8. And write to the output buffer
;//---------------------------------------------------------
#macro CalculateTyraDirectionalLights: t_outputColor, t_normal, t_lightDirections, t_lightColors, t_lightMatrix, t_ambientColor
mul.xyz acc, t_lightMatrix[0], t_normal[x]
madd.xyz acc, t_lightMatrix[1], t_normal[y]
madd.xyz t_normal, t_lightMatrix[2], t_normal[z]
mula.xyz acc, t_lightDirections[0], t_normal[x]
madd.xyz acc, t_lightDirections[1], t_normal[y]
madd.xyz t_outputColor, t_lightDirections[2], t_normal[z]
mini.xyz t_outputColor, t_outputColor, vf00[w]
max.xyz t_outputColor, t_outputColor, vf00[x]
mula.xyz acc, t_lightColors[0], t_outputColor[x]
madda.xyz acc, t_lightColors[1], t_outputColor[y]
madda.xyz acc, t_lightColors[2], t_outputColor[z]
madd.xyz t_outputColor, t_ambientColor, vf00[w]
loi 128
addi.w t_outputColor, vf00, i
#endmacro
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/paths/path1/vu1_program.hpp"
namespace Tyra {
VU1Program::VU1Program(u32* t_start, u32* t_end) : start(t_start), end(t_end) {
packetSize = packet2_utils_get_packet_size_for_program(start, end);
programSize = calculateProgramSize();
}
VU1Program::~VU1Program() {}
const u32& VU1Program::getPacketSize() const { return packetSize; }
const u32& VU1Program::getProgramSize() const { return programSize; }
const u32& VU1Program::getDestinationAddress() const {
return destinationAddress;
}
void VU1Program::setDestinationAddress(const u32& addr) {
destinationAddress = addr;
}
u32* VU1Program::getStart() const { return start; }
u32* VU1Program::getEnd() const { return end; }
u32 VU1Program::calculateProgramSize() const {
u32 count = (getEnd() - getStart()) / 2;
if (count & 1) count++;
return count;
}
} // namespace Tyra
@@ -0,0 +1,94 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/paths/path3/path3.hpp"
namespace Tyra {
Path3::Path3() {
drawFinishPacket = packet2_create(3, P2_TYPE_NORMAL, P2_MODE_CHAIN, false);
clearScreenPacket = packet2_create(36, P2_TYPE_NORMAL, P2_MODE_CHAIN, false);
texturePacket = packet2_create(128, P2_TYPE_NORMAL, P2_MODE_CHAIN, false);
packet2_chain_open_end(drawFinishPacket, 0, 0);
packet2_update(drawFinishPacket, draw_finish(drawFinishPacket->next));
packet2_chain_close_tag(drawFinishPacket);
}
Path3::~Path3() {
packet2_free(drawFinishPacket);
packet2_free(clearScreenPacket);
packet2_free(texturePacket);
}
void Path3::init(RendererSettings* t_settings) {
settings = t_settings;
dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0);
TYRA_LOG("Path3 initialized");
}
void Path3::addDrawFinishTag() {
dma_channel_wait(DMA_CHANNEL_GIF, 0);
dma_channel_send_packet2(drawFinishPacket, DMA_CHANNEL_GIF, true);
}
void Path3::clearScreen(zbuffer_t* z, const Color& color) {
packet2_reset(clearScreenPacket, false);
packet2_chain_open_end(clearScreenPacket, 0, 0);
packet2_update(clearScreenPacket,
draw_disable_tests(clearScreenPacket->next, 0, z));
packet2_update(
clearScreenPacket,
draw_clear(clearScreenPacket->next, 0,
2048.0F - (settings->getWidth() / 2),
2048.0F - (settings->getHeight() / 2), settings->getWidth(),
settings->getHeight(), static_cast<int>(color.r),
static_cast<int>(color.g), static_cast<int>(color.b)));
packet2_update(clearScreenPacket,
draw_enable_tests(clearScreenPacket->next, 0, z));
packet2_update(clearScreenPacket, draw_finish(clearScreenPacket->next));
packet2_chain_close_tag(clearScreenPacket);
dma_channel_wait(DMA_CHANNEL_GIF, 0);
dma_channel_send_packet2(clearScreenPacket, DMA_CHANNEL_GIF, true);
}
void Path3::sendTexture(Texture* texture,
const RendererCoreTextureBuffers& texBuffers) {
packet2_reset(texturePacket, false);
packet2_update(
texturePacket,
draw_texture_transfer(texturePacket->base, texture->getCoreData().data,
texture->getWidth(), texture->getHeight(),
texture->getCoreData().psm,
texBuffers.core->address, texBuffers.core->width));
if (texBuffers.clut != nullptr) {
auto* clut = texture->getClutData();
packet2_update(texturePacket, draw_texture_transfer(
texturePacket->next, clut->data,
clut->width, clut->height, clut->psm,
texBuffers.clut->address, clut->width));
}
packet2_chain_open_cnt(texturePacket, 0, 0, 0);
packet2_update(texturePacket,
draw_texture_wrapping(texturePacket->next, 0,
texture->getWrapSettings()));
packet2_chain_close_tag(texturePacket);
packet2_update(texturePacket, draw_texture_flush(texturePacket->next));
dma_channel_wait(DMA_CHANNEL_GIF, 0);
dma_channel_send_packet2(texturePacket, DMA_CHANNEL_GIF, true);
}
} // namespace Tyra
@@ -0,0 +1,43 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/renderer_core.hpp"
#include "thread/threading.hpp"
namespace Tyra {
RendererCore::RendererCore() { isFrameLimitOn = true; }
RendererCore::~RendererCore() {}
void RendererCore::init() {
gs.init(&settings);
texture.init(&gs, &path3);
path3.init(&settings);
renderer3D.init(&settings, &path1);
renderer2D.init(&settings, &texture.clut);
sync.init(&path3);
}
void RendererCore::setClearScreenColor(const Color& color) { bgColor = color; }
void RendererCore::beginFrame(const CameraInfo3D& cameraInfo) {
renderer3D.update(cameraInfo);
texture.onFrameChange();
Threading::switchThread();
path3.clearScreen(&gs.zBuffer, bgColor);
}
void RendererCore::endFrame() {
Threading::switchThread();
if (isFrameLimitOn) graph_wait_vsync();
gs.flipBuffers();
}
} // namespace Tyra
@@ -0,0 +1,38 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/renderer_core_sync.hpp"
namespace Tyra {
RendererCoreSync::RendererCoreSync() {}
RendererCoreSync::~RendererCoreSync() {}
void RendererCoreSync::init(Path3* t_path3) { path3 = t_path3; }
void RendererCoreSync::align() {
clear();
add();
waitAndClear();
}
void RendererCoreSync::add() { path3->addDrawFinishTag(); }
u8 RendererCoreSync::check() { return *GS_REG_CSR & 2; }
void RendererCoreSync::clear() { *GS_REG_CSR |= 2; }
void RendererCoreSync::waitAndClear() {
while (!check()) {
}
clear();
}
} // namespace Tyra
@@ -0,0 +1,74 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/texture/cache_manager/renderer_texture_cache_manager.hpp"
namespace Tyra {
RendererTextureCacheManager::RendererTextureCacheManager() {}
RendererTextureCacheManager::~RendererTextureCacheManager() {}
void RendererTextureCacheManager::onFrameChange() { statistics.changeFrame(); }
void RendererTextureCacheManager::addRequestedTexture(Texture* texture) {
statistics.addNewRequest(texture->getId());
}
u32 RendererTextureCacheManager::getTextureIdToDealloc(
const std::vector<RendererCoreTextureBuffers>& currentAllocs) {
if (!statistics.isReady() || statistics.isProbablyNotCorrect()) {
return currentAllocs.size() > 0 ? currentAllocs.at(0).id : 0;
}
u8 tuner = currentAllocs.size() > 254 ? 254 : currentAllocs.size();
if (tuner >= 6) {
tuner /= 3;
} else if (tuner >= 4) {
tuner /= 2;
} else {
tuner = 1;
}
std::vector<u32> temp;
auto probableTextureIds = statistics.getTopTextureIdsForNextChanges(tuner);
tryAddIdThatNotExistInAnalysis(&temp, currentAllocs, probableTextureIds);
if (temp.size() == 0) {
addIdThatExistInAnalysisButIsOnTheBottom(&temp, currentAllocs,
probableTextureIds);
}
return temp.at(0);
}
void RendererTextureCacheManager::tryAddIdThatNotExistInAnalysis(
std::vector<u32>* result,
const std::vector<RendererCoreTextureBuffers>& currentAllocs,
const std::vector<u32>& analysis) {
for (u32 i = 0; i < currentAllocs.size(); i++) {
if (std::find(analysis.begin(), analysis.end(), currentAllocs.at(i).id) ==
analysis.end()) {
result->push_back(currentAllocs.at(i).id);
break;
}
}
}
void RendererTextureCacheManager::addIdThatExistInAnalysisButIsOnTheBottom(
std::vector<u32>* result,
const std::vector<RendererCoreTextureBuffers>& currentAllocs,
const std::vector<u32>& analysis) {
u32 lastId = analysis.at(analysis.size() - 1);
result->push_back(lastId);
}
} // namespace Tyra
@@ -0,0 +1,152 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/texture/cache_manager/renderer_texture_cm_analysis.hpp"
namespace Tyra {
RendererTextureCMAnalysis::RendererTextureCMAnalysis() {
last = &buffers[0];
current = &buffers[1];
currentIndex = 0;
}
RendererTextureCMAnalysis::~RendererTextureCMAnalysis() {}
void RendererTextureCMAnalysis::changeFrame() {
flipBuffers();
current->clear();
currentIndex = 0;
}
void RendererTextureCMAnalysis::addNewRequest(const u32& id) {
current->push_back(id);
currentIndex++;
}
std::vector<u32> RendererTextureCMAnalysis::getTopTextureIdsForNextChanges(
const u8& count) {
TYRA_ASSERT(!isProbablyNotCorrect(),
"Analysis not available, because its probably not correct - "
"internal error");
auto nextTextureIds = getNextTextureIds(count);
auto occurences = getOccurencesForTextureIds(nextTextureIds);
auto uniqueTextureIds = getTextureIdsUnique(nextTextureIds);
auto weights = getWeights(uniqueTextureIds, occurences);
auto result = weightsToResult(&weights);
return result;
}
std::vector<u32> RendererTextureCMAnalysis::getNextTextureIds(const u8& count) {
std::vector<u32> result;
for (u32 i = 0; i < count; i++) {
u32 index = i + currentIndex;
u32 offset = index >= last->size() ? index - last->size() : index;
result.push_back(last->at(offset));
}
return result;
}
std::vector<RendererTextureCMAnalysisOccurence>
RendererTextureCMAnalysis::getOccurencesForTextureIds(
const std::vector<u32>& textureIds) {
std::vector<RendererTextureCMAnalysisOccurence> result;
for (u8 i = 0; i < textureIds.size(); i++) {
// If not exist, add with 1 occurence
if (std::find_if(result.begin(), result.end(),
[textureIds,
i](const RendererTextureCMAnalysisOccurence& occurence) {
return occurence.id == textureIds[i];
}) == result.end()) {
result.push_back({textureIds[i], 1});
} else {
// If exist, increase occurence
auto occurence = std::find_if(
result.begin(), result.end(),
[textureIds, i](const RendererTextureCMAnalysisOccurence& occurence) {
return occurence.id == textureIds[i];
});
occurence->occurence++;
}
}
return result;
}
std::vector<u32> RendererTextureCMAnalysis::getTextureIdsUnique(
const std::vector<u32>& textureIds) {
std::vector<u32> result;
for (u8 i = 0; i < textureIds.size(); i++) {
if (std::find(result.begin(), result.end(), textureIds[i]) ==
result.end()) {
result.push_back(textureIds[i]);
}
}
return result;
}
std::vector<RendererTextureCMAnalysisWeight>
RendererTextureCMAnalysis::getWeights(
const std::vector<u32>& uniqueTexIds,
const std::vector<RendererTextureCMAnalysisOccurence>& occurencies) {
std::vector<RendererTextureCMAnalysisWeight> result;
for (u8 i = 0; i < uniqueTexIds.size(); i++) {
u16 weight = uniqueTexIds.size() - i;
for (u8 j = 0; j < occurencies.size(); j++) {
if (occurencies[j].id == uniqueTexIds[i]) {
weight *= occurencies[j].occurence;
break;
}
}
result.push_back({uniqueTexIds[i], weight});
}
return result;
}
std::vector<u32> RendererTextureCMAnalysis::weightsToResult(
std::vector<RendererTextureCMAnalysisWeight>* weights) {
std::sort(weights->begin(), weights->end(),
[](const auto& a, const auto& b) { return a.weight > b.weight; });
std::vector<u32> result;
for (u8 i = 0; i < weights->size(); i++) {
result.push_back(weights->at(i).id);
}
return result;
}
bool RendererTextureCMAnalysis::isReady() { return last->size() != 0; }
bool RendererTextureCMAnalysis::isProbablyNotCorrect() {
return currentIndex > last->size();
}
void RendererTextureCMAnalysis::flipBuffers() {
if (current == &buffers[0]) {
current = &buffers[1];
last = &buffers[0];
} else {
current = &buffers[0];
last = &buffers[1];
}
}
} // namespace Tyra
@@ -0,0 +1,173 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <iomanip>
#include <draw_buffers.h>
#include <gs_psm.h>
#include <stdlib.h>
#include <string>
#include <sstream>
#include "renderer/core/texture/models/texture.hpp"
namespace Tyra {
Texture::Texture(TextureBuilderData* t_data) {
id = rand() % 1000000;
name = t_data->name;
core = new TextureData(t_data->data, t_data->bpp, t_data->gsComponents,
t_data->width, t_data->height);
clut =
new TextureData(t_data->clut, t_data->clutBpp, t_data->clutGsComponents,
t_data->clutWidth, t_data->clutHeight);
setDefaultWrapSettings();
}
Texture::~Texture() {
if (getTextureLinksCount() > 0) links.clear();
if (core) delete core;
if (clut) delete clut;
}
float Texture::getSizeInMB() const {
return (core->width / 100.0F) * (core->height / 100.0F) *
(core->bpp / 100.0F) / 8.0F;
}
/** Based on gsKit code, thank you guys! */
u32 Texture::getTextureSize() const {
int widthBlocks, heightBlocks;
int widthAlign, heightAlign;
// Calculate the number of blocks width and height
// A block is 256 bytes in size
switch (core->bpp) {
case bpp32:
case bpp24:
// 1 block = 8x8 pixels
widthBlocks = (core->width + 7) / 8;
heightBlocks = (core->height + 7) / 8;
break;
case bpp8:
// 1 block = 16x16 pixels
widthBlocks = (core->width + 15) / 16;
heightBlocks = (core->height + 15) / 16;
break;
case bpp4:
// 1 block = 32x16 pixels
widthBlocks = (core->width + 31) / 32;
heightBlocks = (core->height + 15) / 16;
break;
default:
TYRA_TRAP("Unknown texture bpp");
return -1;
}
// Calculate the minimum block alignment
if (core->bpp == bpp32 || core->bpp == bpp24 || core->bpp == bpp8) {
// 8x4 blocks in a page.
// block traversing order:
// 0145....
// 2367....
// ........
// ........
if (widthBlocks <= 2 && heightBlocks <= 1) {
widthAlign = 1;
heightAlign = 1;
} else if (widthBlocks <= 4 && heightBlocks <= 2) {
widthAlign = 2;
heightAlign = 2;
} else if (widthBlocks <= 8 && heightBlocks <= 4) {
widthAlign = 4;
heightAlign = 4;
} else {
widthAlign = 8;
heightAlign = 4;
}
} else {
if (widthBlocks <= 1 && heightBlocks <= 2) {
widthAlign = 1;
heightAlign = 1;
} else if (widthBlocks <= 2 && heightBlocks <= 2) {
widthAlign = 2;
heightAlign = 2;
} else if (widthBlocks <= 2 && heightBlocks <= 8) {
widthAlign = 2;
heightAlign = 8;
} else {
widthAlign = 4;
heightAlign = 8;
}
}
widthBlocks = (-widthAlign) & (widthBlocks + widthAlign - 1);
heightBlocks = (-heightAlign) & (heightBlocks + heightAlign - 1);
return widthBlocks * heightBlocks * 256;
}
void Texture::setDefaultWrapSettings() {
wrap.horizontal = WRAP_REPEAT;
wrap.vertical = WRAP_REPEAT;
wrap.maxu = 0;
wrap.maxv = 0;
wrap.minu = 0;
wrap.minv = 0;
}
void Texture::setWrapSettings(const TextureWrap t_horizontal,
const TextureWrap t_vertical) {
wrap.horizontal = t_horizontal;
wrap.vertical = t_vertical;
}
void Texture::addLink(const u32& t_id) {
TextureLink link;
link.id = t_id;
links.push_back(link);
}
void Texture::print() const {
auto text = getPrint(nullptr);
printf("%s\n", text.c_str());
}
void Texture::print(const char* name) const {
auto text = getPrint(name);
printf("%s\n", text.c_str());
}
std::string Texture::getPrint(const char* objectName) const {
std::stringstream res;
if (objectName) {
res << objectName << "(";
} else {
res << "Texture(";
}
std::string wrapString;
if (wrap.horizontal == WRAP_REPEAT)
wrapString = "WRAP_REPEAT";
else if (wrap.horizontal == WRAP_CLAMP)
wrapString = "WRAP_CLAMP";
res << "id: " << id << ", ";
res << "name: " << name << ", ";
res << "core: " << core->getPrint() << ", ";
if (clut != nullptr) res << "clut: " << clut->getPrint() << ", ";
res << "wrap: " << wrapString << ")";
return res.str();
}
} // namespace Tyra
@@ -0,0 +1,95 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/texture/renderer_core_texture.hpp"
namespace Tyra {
RendererCoreTexture::RendererCoreTexture() {}
RendererCoreTexture::~RendererCoreTexture() {}
void RendererCoreTexture::onFrameChange() { cacheManager.onFrameChange(); }
void RendererCoreTexture::init(RendererCoreGS* t_gs, Path3* t_path3) {
sender.init(t_path3, t_gs);
path3 = t_path3;
initClut();
}
void RendererCoreTexture::updateClutBuffer(texbuffer_t* clutBuffer) {
if (clutBuffer == nullptr) {
clut.psm = 0;
clut.load_method = CLUT_NO_LOAD;
clut.address = 0;
} else {
clut.psm = clutBuffer->psm;
clut.load_method = CLUT_LOAD;
clut.address = clutBuffer->address;
}
}
RendererCoreTextureBuffers RendererCoreTexture::useTexture(Texture* t_tex) {
TYRA_ASSERT(t_tex != nullptr, "Provided nullptr texture!");
cacheManager.addRequestedTexture(t_tex);
auto allocated = getAllocatedBuffersByTextureId(t_tex->getId());
if (allocated.id != 0) return allocated;
while (t_tex->getSizeInMB() > sender.getFreeVRamInMB()) {
auto idToDealloc = cacheManager.getTextureIdToDealloc(currentAllocations);
auto buffToDealloc = getAllocatedBuffersByTextureId(idToDealloc);
sender.deallocate(buffToDealloc);
unregisterAllocation(idToDealloc);
}
auto newTexBuffer = sender.allocate(t_tex);
path3->sendTexture(t_tex, newTexBuffer);
registerAllocation(newTexBuffer);
return newTexBuffer;
}
RendererCoreTextureBuffers RendererCoreTexture::getAllocatedBuffersByTextureId(
const u32& t_id) {
for (u32 i = 0; i < currentAllocations.size(); i++)
if (currentAllocations[i].id == t_id) return currentAllocations[i];
return {0, nullptr, nullptr};
}
void RendererCoreTexture::registerAllocation(
const RendererCoreTextureBuffers& t_buffers) {
currentAllocations.push_back(t_buffers);
}
void RendererCoreTexture::unregisterAllocation(const u32& textureId) {
u32 foundIndex;
for (u32 i = 0; i < currentAllocations.size(); i++) {
if (currentAllocations[i].id == textureId) {
foundIndex = i;
break;
}
}
currentAllocations.erase(currentAllocations.begin() + foundIndex);
}
void RendererCoreTexture::initClut() {
clut.storage_mode = CLUT_STORAGE_MODE1;
clut.start = 0;
clut.psm = 0;
clut.load_method = CLUT_NO_LOAD;
clut.address = 0;
TYRA_LOG("Clut set!");
}
} // namespace Tyra
@@ -0,0 +1,120 @@
/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "renderer/core/texture/renderer_core_texture_sender.hpp"
#include <gs_psm.h>
namespace Tyra {
RendererCoreTextureSender::RendererCoreTextureSender() {
isTextureVRAMAllocated = false;
allocatedVRamMemForTextures = 0;
}
RendererCoreTextureSender::~RendererCoreTextureSender() {}
void RendererCoreTextureSender::init(Path3* t_path3, RendererCoreGS* t_gs) {
gs = t_gs;
path3 = t_path3;
TYRA_LOG("Renderer texture initialized!");
}
RendererCoreTextureBuffers RendererCoreTextureSender::allocate(
Texture* t_texture) {
texbuffer_t* core = allocateTextureCore(t_texture);
texbuffer_t* clut = nullptr;
if (t_texture->getClutData() != nullptr) {
clut = allocateTextureClut(t_texture);
}
return {t_texture->getId(), core, clut};
}
float RendererCoreTextureSender::getSizeInMB(texbuffer_t* texBuffer) {
auto bpp = getBppByPsm(texBuffer->psm);
auto width = pow(2, texBuffer->info.width);
auto height = pow(2, texBuffer->info.height);
return (width / 100.0F) * (height / 100.0F) * (bpp / 100.0F) / 8.0F;
}
void RendererCoreTextureSender::deallocate(
const RendererCoreTextureBuffers& texBuffers) {
graph_vram_free(texBuffers.core->address);
delete texBuffers.core;
if (texBuffers.clut != nullptr) {
graph_vram_free(texBuffers.clut->address);
delete texBuffers.clut;
}
allocatedVRamMemForTextures -= getSizeInMB(texBuffers.core);
}
texbuffer_t* RendererCoreTextureSender::allocateTextureCore(
Texture* t_texture) {
auto* result = new texbuffer_t;
result->width = t_texture->getWidth();
result->psm = t_texture->getCoreData().psm;
result->info.components = t_texture->getCoreData().components;
TYRA_ASSERT(t_texture->getSizeInMB() <= getFreeVRamInMB(),
"Not enough VRAM memory for texture!");
result->address =
graph_vram_allocate(t_texture->getWidth(), t_texture->getHeight(),
result->psm, GRAPH_ALIGN_BLOCK);
TYRA_ASSERT(result->address > 0,
"Texture buffer allocation error. No memory!");
allocatedVRamMemForTextures += t_texture->getSizeInMB();
result->info.width = draw_log2(t_texture->getWidth());
result->info.height = draw_log2(t_texture->getHeight());
result->info.function = TEXTURE_FUNCTION_MODULATE;
return result;
}
texbuffer_t* RendererCoreTextureSender::allocateTextureClut(
Texture* t_texture) {
auto* result = new texbuffer_t;
const auto* clut = t_texture->getClutData();
result->width = clut->width;
result->psm = clut->psm;
result->info.components = clut->components;
result->address = graph_vram_allocate(clut->width, clut->height, result->psm,
GRAPH_ALIGN_BLOCK);
result->info.width = draw_log2(clut->width);
result->info.height = draw_log2(clut->height);
result->info.function = TEXTURE_FUNCTION_MODULATE;
return result;
}
float RendererCoreTextureSender::getFreeVRamInMB() {
return gs->getVRamFreeSpaceInMB() - allocatedVRamMemForTextures;
}
TextureBpp RendererCoreTextureSender::getBppByPsm(const u32& psm) {
if (psm == GS_PSM_32) {
return bpp32;
} else if (psm == GS_PSM_24) {
return bpp24;
} else if (psm == GS_PSM_8) {
return bpp8;
} else if (psm == GS_PSM_4) {
return bpp4;
} else {
TYRA_TRAP("Unknown bpp!");
return bpp32;
}
}
} // namespace Tyra

Some files were not shown because too many files have changed in this diff Show More