Files
tyra-linux/src/engine/modules/renderer.cpp
T
2020-12-28 14:23:51 +01:00

388 lines
14 KiB
C++

/*
# ______ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2020, tyra - https://github.com/h4570/tyra
# Licenced under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "../include/modules/renderer.hpp"
#include <dma.h>
#include <graph.h>
#include <packet.h>
#include "../include/utils/debug.hpp"
#include "../include/utils/math.hpp"
// ----
// Constructors/Destructors
// ----
static const float GS_CENTER = 4096.0F;
static const float SCREEN_CENTER = GS_CENTER / 2.0F;
/** Initialize DMA<->GIF channel
* Allocate buffers
* Initialize screen
* Initialize drawing environment
* Load/setup textures
* @param screenW Half of screen width
* @param screenH Half of screen height
*/
Renderer::Renderer(u32 t_packetSize, ScreenSettings *t_screen)
{
PRINT_LOG("Initializing renderer");
dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); // Initialize DMA to enable data transfer
dma_channel_fast_waits(DMA_CHANNEL_GIF);
screen = t_screen;
context = 0;
isTextureVRAMAllocated = false;
isVSyncEnabled = true;
isFrameEmpty = false;
lastTextureId = 0;
flipPacket = packet2_create(4, P2_TYPE_UNCACHED_ACCL, P2_MODE_NORMAL, 0);
allocateBuffers((int)t_screen->width, (int)t_screen->height);
initDrawingEnv();
setPrim();
worldColor.r = 0x10;
worldColor.g = 0x10;
worldColor.b = 0x10;
gifSender = new GifSender(t_packetSize, t_screen, &light);
vifSender = new VifSender(&light);
perspective.setPerspective(*t_screen);
renderData.projection = &perspective;
PRINT_LOG("Renderer initialized!");
}
Renderer::~Renderer() {}
// ----
// Methods
// ----
/** Configure and allocate vRAM for texture buffer */
void Renderer::allocateTextureBuffer(Texture *t_texture)
{
textureBuffer.width = t_texture->getWidth();
textureBuffer.psm = t_texture->getType();
textureBuffer.info.components = textureBuffer.psm == TEX_TYPE_RGBA ? TEXTURE_COMPONENTS_RGBA : TEXTURE_COMPONENTS_RGB;
textureBuffer.address = graph_vram_allocate(t_texture->getWidth(), t_texture->getHeight(), textureBuffer.psm, GRAPH_ALIGN_BLOCK);
if (textureBuffer.address <= 1)
PRINT_ERR("Texture buffer allocation error. No memory!");
textureBuffer.info.width = draw_log2(t_texture->getWidth());
textureBuffer.info.height = draw_log2(t_texture->getHeight());
textureBuffer.info.function = TEXTURE_FUNCTION_MODULATE;
isTextureVRAMAllocated = true;
}
/** Configure and allocate vRAM for texture buffer */
void Renderer::deallocateTextureBuffer()
{
if (isTextureVRAMAllocated)
{
graph_vram_free(textureBuffer.address);
isTextureVRAMAllocated = false;
}
}
void Renderer::changeTexture(Texture *t_tex)
{
if (t_tex != NULL)
{
if (t_tex->getId() != lastTextureId)
{
lastTextureId = t_tex->getId();
deallocateTextureBuffer();
allocateTextureBuffer(t_tex);
GifSender::sendTexture(*t_tex, &textureBuffer);
}
}
else
PRINT_ERR("Texture was not found in texture repository!");
}
void Renderer::draw(Sprite &t_sprite)
{
Texture *texture = textureRepo.getBySpriteOrMesh(t_sprite.getId());
texrect_t rect;
float sizeX, sizeY;
if (t_sprite.getMode() == MODE_REPEAT)
{
sizeX = t_sprite.size.x;
sizeY = t_sprite.size.y;
}
else
{
sizeX = (float)texture->getWidth();
sizeY = (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 = t_sprite.isFlippedHorizontally() ? texS : 0.0F;
rect.t0.t = t_sprite.isFlippedVertically() ? texT : 0.0F;
rect.t1.s = t_sprite.isFlippedHorizontally() ? 0.0F : texS;
rect.t1.t = t_sprite.isFlippedVertically() ? 0.0F : texT;
rect.color.r = t_sprite.color.r;
rect.color.g = t_sprite.color.g;
rect.color.b = t_sprite.color.b;
rect.color.a = t_sprite.color.a;
rect.color.q = 0;
rect.v0.x = t_sprite.position.x;
rect.v0.y = t_sprite.position.y;
rect.v0.z = (u32)-1;
rect.v1.x = (t_sprite.size.x * t_sprite.scale) + t_sprite.position.x;
rect.v1.y = (t_sprite.size.y * t_sprite.scale) + t_sprite.position.y;
rect.v1.z = (u32)-1;
beginFrameIfNeeded();
changeTexture(texture);
packet2_t *packet2 = packet2_create(12, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0);
packet2_update(packet2, draw_primitive_xyoffset(packet2->next, 0, SCREEN_CENTER, SCREEN_CENTER));
packet2_utils_gif_add_set(packet2, 1);
packet2_utils_gs_add_texbuff_clut(packet2, &textureBuffer, &t_sprite.clut);
draw_enable_blending();
packet2_update(packet2, draw_rect_textured(packet2->next, 0, &rect));
packet2_update(packet2,
draw_primitive_xyoffset(
packet2->next,
0,
SCREEN_CENTER - (screen->width / 2.0F),
SCREEN_CENTER - (screen->height / 2.0F)));
draw_disable_blending();
packet2_update(packet2, draw_finish(packet2->next));
dma_channel_wait(DMA_CHANNEL_GIF, 0);
dma_channel_send_packet2(packet2, DMA_CHANNEL_GIF, true);
packet2_free(packet2);
}
/** Initializes drawing environment (1st app packet) */
void Renderer::initDrawingEnv()
{
PRINT_LOG("Initializing drawing environment");
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,
SCREEN_CENTER - (screen->width / 2.0F),
SCREEN_CENTER - (screen->height / 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);
PRINT_LOG("Drawing environment initialized!");
}
/** Sets drawing prim for all 3D objects */
void Renderer::setPrim()
{
prim.type = PRIM_TRIANGLE;
prim.shading = PRIM_SHADE_FLAT;
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;
renderData.prim = &prim;
PRINT_LOG("Prim set!");
}
void Renderer::setWorldColor(const color_t &t_rgb)
{
worldColor.r = t_rgb.r;
worldColor.g = t_rgb.g;
worldColor.b = t_rgb.b;
}
/** Defines and allocates framebuffers and zbuffer */
void Renderer::allocateBuffers(int t_screenW, int t_screenH)
{
frameBuffers[0].width = (unsigned int)t_screenW;
frameBuffers[0].height = (unsigned int)t_screenH;
frameBuffers[0].mask = 0;
frameBuffers[0].psm = GS_PSM_32;
frameBuffers[0].address = graph_vram_allocate(t_screenW, t_screenH, frameBuffers[0].psm, GRAPH_ALIGN_PAGE);
frameBuffers[1].width = (unsigned int)t_screenW;
frameBuffers[1].height = (unsigned int)t_screenH;
frameBuffers[1].mask = 0;
frameBuffers[1].psm = GS_PSM_32;
frameBuffers[1].address = graph_vram_allocate(t_screenW, t_screenH, 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(t_screenW, t_screenH, zBuffer.zsm, GRAPH_ALIGN_PAGE);
PRINT_LOG("Framebuffers, zBuffer set and allocated!");
// Initialize the screen and tie the first framebuffer to the read circuits.
graph_initialize(frameBuffers[0].address, frameBuffers[0].width, frameBuffers[0].height, frameBuffers[0].psm, 0, 0);
}
/// --- Draw: PATH3
// Obsolete
// void Renderer::drawByPath3(Mesh *t_meshes, u16 t_amount, LightBulb *t_bulbs, u16 t_bulbsCount)
// {
// for (u16 i = 0; i < t_amount; i++)
// drawByPath3(t_meshes[i], t_bulbs, t_bulbsCount);
// }
// Obsolete
// void Renderer::drawByPath3(Mesh &t_mesh, LightBulb *t_bulbs, u16 t_bulbsCount)
// {
// beginFrameIfNeeded();
// if (!t_mesh.isDataLoaded())
// PRINT_ERR("Can't draw, because no mesh data was loaded!");
// if (t_mesh.getCurrentAnimationFrame() != t_mesh.getNextAnimationFrame())
// t_mesh.animate();
// for (u32 i = 0; i < t_mesh.getMaterialsCount(); i++)
// {
// MeshMaterial *material = &t_mesh.getMaterial(i);
// if (t_mesh.shouldBeFrustumCulled && !material->isInFrustum(renderData.frustumPlanes, t_mesh.position))
// return;
// Texture *tex = textureRepo.getBySpriteOrMesh(material->getId());
// changeTexture(tex);
// gifSender->initPacket(context);
// u32 vertCount = material->getFacesCount();
// VECTOR *vertices = new VECTOR[vertCount];
// VECTOR *normals = new VECTOR[vertCount];
// VECTOR *coordinates = new VECTOR[vertCount];
// vertCount = t_mesh.getDrawData(i, vertices, normals, coordinates, *renderData.cameraPosition);
// gifSender->addObject(&renderData, t_mesh, vertCount, vertices, normals, coordinates, t_bulbs, t_bulbsCount, &textureBuffer, &material->color);
// gifSender->sendPacket();
// delete[] vertices;
// delete[] normals;
// delete[] coordinates;
// }
// }
// Obsolete
// void Renderer::drawByPath3(Mesh *t_meshes, u16 t_amount) { drawByPath3(t_meshes, t_amount, NULL, 0); }
// Obsolete
// void Renderer::drawByPath3(Mesh &t_mesh) { drawByPath3(t_mesh, NULL, 0); }
/// --- Draw: PATH1
void Renderer::draw(Mesh **t_meshes, u16 t_amount, LightBulb *t_bulbs, u16 t_bulbsCount)
{
beginFrameIfNeeded();
if (!t_meshes[0]->isDataLoaded())
PRINT_ERR("Can't draw, because no mesh data was loaded!");
else if (
t_amount >= 3 &&
!t_meshes[0]->shouldBeBackfaceCulled &&
t_meshes[0]->getFramesCount() == 1 &&
t_meshes[0]->getMaterialsCount() == 1 &&
t_meshes[0]->getFrame(0).getVertexCount() <= 96)
{
vifSender->disableWait();
Mesh **meshesInFrustum = new Mesh *[t_amount];
u16 addedMeshes = 0;
for (u16 i = 0; i < t_amount; i++)
if (t_meshes[i]->getMaterial(0).isInFrustum(renderData.frustumPlanes, t_meshes[i]->position))
meshesInFrustum[addedMeshes++] = t_meshes[i];
draw(*meshesInFrustum[0], t_bulbs, t_bulbsCount);
draw(*meshesInFrustum[1], t_bulbs, t_bulbsCount);
vifSender->enableWait();
resetWaitFlag();
vifSender->drawTheSameWithOtherMatrices(renderData, meshesInFrustum, 2, addedMeshes);
if (isWaitFlagSet())
resetWaitFlag();
else
waitForRender();
delete[] meshesInFrustum;
}
else
for (u16 i = 0; i < t_amount; i++)
draw(*t_meshes[i], t_bulbs, t_bulbsCount);
}
void Renderer::draw(Mesh &t_mesh, LightBulb *t_bulbs, u16 t_bulbsCount)
{
beginFrameIfNeeded();
vifSender->calcMatrix(renderData, t_mesh.position, t_mesh.rotation);
if (!t_mesh.isDataLoaded())
PRINT_ERR("Can't draw, because no mesh data was loaded!");
camRotation.identity();
camRotation.rotate(-t_mesh.rotation);
Vector3 rotatedCamera = Vector3(camRotation * *renderData.cameraPosition);
if (t_mesh.getCurrentAnimationFrame() != t_mesh.getNextAnimationFrame())
t_mesh.animate();
for (u32 i = 0; i < t_mesh.getMaterialsCount(); i++)
{
MeshMaterial *material = &t_mesh.getMaterial(i);
if (t_mesh.shouldBeFrustumCulled && !material->isInFrustum(renderData.frustumPlanes, t_mesh.position))
return;
u32 vertCount = material->getFacesCount();
VECTOR vertices[vertCount] __attribute__((aligned(16)));
VECTOR normals[vertCount] __attribute__((aligned(16)));
VECTOR coordinates[vertCount] __attribute__((aligned(16)));
Texture *tex = textureRepo.getBySpriteOrMesh(material->getId());
changeTexture(tex);
vertCount = t_mesh.getDrawData(i, vertices, normals, coordinates, rotatedCamera);
vifSender->drawMesh(&renderData, perspective, vertCount, vertices, normals, coordinates, t_mesh, t_bulbs, t_bulbsCount, &textureBuffer, &material->color);
}
}
void Renderer::draw(Mesh **t_meshes, u16 t_amount) { draw(t_meshes, t_amount, NULL, 0); }
void Renderer::draw(Mesh &t_mesh) { draw(t_mesh, NULL, 0); }
/// ---
void Renderer::setCameraDefinitions(Matrix *t_worldView, Vector3 *t_cameraPos, Plane *t_planes)
{
renderData.view = t_worldView;
renderData.cameraPosition = t_cameraPos;
renderData.frustumPlanes = t_planes;
}
void Renderer::beginFrameIfNeeded()
{
if (isFrameEmpty)
{
isFrameEmpty = false;
gifSender->sendClear(&zBuffer, &worldColor);
}
}
void Renderer::endFrame(float fps)
{
if (!isFrameEmpty)
{
if (fps > 49.0F && isVSyncEnabled)
graph_wait_vsync();
flipBuffers();
}
}
/** We need to flip buffers outside of the chain, for some reason,
* so we use a separate small packet
* Do not use this method. This is called via packetManager
*/
void Renderer::flipBuffers()
{
graph_set_framebuffer_filtered(
frameBuffers[context].address,
frameBuffers[context].width,
frameBuffers[context].psm,
0,
0);
context ^= 1;
isFrameEmpty = 1;
packet2_update(flipPacket, draw_framebuffer(flipPacket->base, 0, &frameBuffers[context]));
packet2_update(flipPacket, draw_finish(flipPacket->next));
dma_channel_send_packet2(flipPacket, DMA_CHANNEL_GIF, true);
draw_wait_finish();
}