/* # ______ ____ ___ # | \/ ____| |___| # | | | \ | | #----------------------------------------------------------------------- # Copyright 2020, tyra - https://github.com/h4570/tyra # Licenced under Apache License 2.0 # Sandro Sobczyński */ #include "../include/modules/gif_sender.hpp" #include "../include/utils/math.hpp" #include "../include/utils/debug.hpp" #include "../include/modules/light.hpp" #include #include #include #include #include #include #include // ---- // Constructors/Destructors // ---- /** Initializes vars and creates data transfer packets * @param packetSize Size of data packet, should be increased when more data will be rendered */ GifSender::GifSender(u32 t_packetSize, ScreenSettings *t_screen) : screen(t_screen) { PRINT_LOG("Initializing GifSender"); packetSize = t_packetSize; packets[0] = spacket_create(t_packetSize, SPACKET_NORMAL); packets[1] = spacket_create(t_packetSize, SPACKET_NORMAL); PRINT_LOG("GifSender initialized!"); } /** Releases packets memory */ GifSender::~GifSender() { spacket_free(packets[0]); spacket_free(packets[1]); } // ---- // Methods // ---- #include #include /** Send texture via GIF */ void GifSender::sendTexture(MeshTexture &texture, texbuffer_t *t_texBuffer) { spacket_t *spacket = spacket_create(15, SPACKET_NORMAL); spacket_update( spacket, draw_texture_transfer( spacket->base, texture.getData(), texture.getWidth(), texture.getHeight(), GS_PSM_24, t_texBuffer->address, t_texBuffer->width)); spacket_chain_open_cnt(spacket, 0, 0, 0); spacket_update(spacket, draw_texture_wrapping(spacket->next, 0, texture.getWrapSettings())); spacket_chain_close_tag(spacket); spacket_update(spacket, draw_texture_flush(spacket->next)); spacket_send_chain(spacket, DMA_CHANNEL_GIF, true, true); spacket_free(spacket); } void GifSender::sendClear(zbuffer_t *t_zBuffer) { spacket_t *spacket = spacket_create(36, SPACKET_NORMAL); spacket_chain_open_end(spacket, 0, 0); spacket_update(spacket, draw_disable_tests(spacket->next, 0, t_zBuffer)); spacket_update(spacket, draw_clear(spacket->next, 0, 2048.0F - (screen->width / 2), 2048.0F - (screen->height / 2), screen->width, screen->height, 0x10, 0x10, 0x10)); spacket_update(spacket, draw_enable_tests(spacket->next, 0, t_zBuffer)); spacket_update(spacket, draw_finish(spacket->next)); spacket_chain_close_tag(spacket); spacket_send_chain(spacket, DMA_CHANNEL_GIF, true, true); spacket_free(spacket); } /** Used in game loop. * Switches current packet to next one */ void GifSender::initPacket(u8 t_context) { currentPacket = packets[t_context]; spacket_reset(currentPacket, false); isAnyObjectAdded = false; } /** Sends packet to GIF. */ void GifSender::sendPacket() { if (isAnyObjectAdded) { spacket_chain_open_end(currentPacket, 0, 0); spacket_update(currentPacket, draw_finish(currentPacket->next)); spacket_chain_close_tag(currentPacket); } spacket_send_chain(currentPacket, DMA_CHANNEL_GIF, true, true); } /** Adds clear screen to current packet */ void GifSender::addClear(zbuffer_t *t_zBuffer) { spacket_chain_open_cnt(currentPacket, 0, 0, 0); spacket_update(currentPacket, draw_disable_tests(currentPacket->next, 0, t_zBuffer)); spacket_update(currentPacket, draw_clear(currentPacket->next, 0, 2048.0F - (screen->width / 2), 2048.0F - (screen->height / 2), screen->width, screen->height, 0x10, 0x10, 0x10)); spacket_update(currentPacket, draw_enable_tests(currentPacket->next, 0, t_zBuffer)); spacket_chain_close_tag(currentPacket); } /** Adds 3D objects to current packet * @param worldView Matrix * @param perspective Matrix with .setPerpsective() used [clone of gluPerspective] * @param objects3D Array of 3D objects pointers * @param amount Amount of 3D objects */ void GifSender::addObject(RenderData *t_renderData, Mesh &t_mesh, u32 vertexCount, VECTOR *vertices, VECTOR *normals, VECTOR *coordinates, LightBulb *t_bulbs, u16 t_bulbsCount, texbuffer_t *textureBuffer) { spacket_chain_open_cnt(currentPacket, 0, 0, 0); spacket_update(currentPacket, draw_texture_sampling(currentPacket->next, 0, &t_mesh.lod)); spacket_update(currentPacket, draw_texturebuffer(currentPacket->next, 0, textureBuffer, &t_mesh.clut)); spacket_update(currentPacket, draw_prim_start(currentPacket->next, 0, t_renderData->prim, &t_mesh.color)); xyz = new xyz_t[vertexCount]; rgbaq = new color_t[vertexCount]; st = new texel_t[vertexCount]; calc3DObject(*t_renderData->perspective, t_mesh, vertexCount, vertices, normals, coordinates, t_renderData, t_bulbs, t_bulbsCount); addCurrentCalcs(vertexCount); delete[] xyz; delete[] rgbaq; delete[] st; spacket_update(currentPacket, draw_prim_end(currentPacket->next, 3, DRAW_STQ_REGLIST)); spacket_chain_close_tag(currentPacket); } /** Calculates 3D object data into xyz, rgbq, st * After it addCurrentSTQ() can be done * @param worldView Matrix * @param perspective Matrix with .setPerpsective() used [clone of gluPerspective] * @param mesh 3D object */ void GifSender::calc3DObject(Matrix t_perspective, Mesh &t_mesh, u32 vertexCount, VECTOR *vertices, VECTOR *normals, VECTOR *coordinates, RenderData *t_renderData, LightBulb *t_bulbs, u16 t_bulbsCount) { VECTOR *colors = new VECTOR[vertexCount]; VECTOR position, rotation; vec3ToNative(position, t_mesh.position, 1.0F); vec3ToNative(rotation, t_mesh.rotation, 1.0F); create_local_world(localWorld, position, rotation); const u8 SHOULD_BE_LIGHTED = t_bulbs != NULL && t_mesh.shouldBeLighted; if (SHOULD_BE_LIGHTED) create_local_light(localLight, rotation); // I cant put perspective from renderData here. ee-gcc bug? create_local_screen(localScreen, localWorld, t_renderData->worldView->data, t_perspective.data); if (SHOULD_BE_LIGHTED) { const u16 lightsCount = Light::getLightsCount(t_bulbsCount); VECTOR *lightDirections = new VECTOR[lightsCount]; VECTOR *lightColors = new VECTOR[lightsCount]; int *lightTypes = new int[lightsCount]; VECTOR *lights = new VECTOR[vertexCount]; calculate_normals(normals, vertexCount, normals, localLight); Light::calculateLight(lightDirections, lightColors, lightTypes, t_bulbs, t_bulbsCount, t_mesh.position); calculate_lights(lights, vertexCount, normals, lightDirections, lightColors, lightTypes, lightsCount); for (u32 i = 0; i < vertexCount; i++) { // Apply the light value to the colour. colors[i][0] = (t_mesh.color.r * lights[i][0] / 128.0F); colors[i][1] = (t_mesh.color.g * lights[i][1] / 128.0F); colors[i][2] = (t_mesh.color.b * lights[i][2] / 128.0F); vector_clamp(colors[i], colors[i], 0.00F, 1.99F); } delete[] lightDirections; delete[] lightColors; delete[] lightTypes; delete[] lights; } else for (u32 i = 0; i < vertexCount; i++) { colors[i][0] = t_mesh.color.r / 128.0F; colors[i][1] = t_mesh.color.g / 128.0F; colors[i][2] = t_mesh.color.b / 128.0F; } calculate_vertices(vertices, vertexCount, vertices, localScreen); convertCalcs(vertexCount, vertices, colors, coordinates, t_mesh.color); delete[] colors; } void GifSender::convertCalcs(u32 t_vertexCount, VECTOR *t_vertices, VECTOR *t_colors, VECTOR *t_sts, color_t &t_color) { // TODO get this via screensettings const s32 centerX = ftoi4(2048); const s32 centerY = ftoi4(2048); const u32 maxZ = ftoi4(((float)0xFFFFFF) / 32.0F); float q = 1.00F; for (u32 i = 0; i < t_vertexCount; i++) { xyz[i].x = (u16)((t_vertices[i][0] + 1.0F) * centerX); xyz[i].y = (u16)((t_vertices[i][1] + 1.0F) * centerY); xyz[i].z = (u32)((t_vertices[i][2] + 1.0F) * maxZ); if (t_vertices[i][3]) q = 1 / t_vertices[i][3]; st[i].s = t_sts[i][0] * q; st[i].t = t_sts[i][1] * q; rgbaq[i].r = (u8)(t_colors[i][0] * 128.0F); rgbaq[i].g = (u8)(t_colors[i][1] * 128.0F); rgbaq[i].b = (u8)(t_colors[i][2] * 128.0F); rgbaq[i].a = t_color.a; rgbaq[i].q = q; } } /** Update q's double words. It may look strange, but this workaround * use's a 64-bit pointer to simplify adding data to the packet. */ void GifSender::addCurrentCalcs(u32 &t_vertexCount) { for (u32 i = 0; i < t_vertexCount; i++) { spacket_add_dword(currentPacket, rgbaq[i].rgbaq); spacket_add_dword(currentPacket, st[i].uv); spacket_add_dword(currentPacket, xyz[i].xyz); } spacket_align_to_qw(currentPacket); }