#include "koopatlas/map.h" #include "koopatlas/camera.h" //#define TILE_DEBUGGING //#define BOUND_DEBUGGING //#define DOODAD_DEBUGGING #ifdef TILE_DEBUGGING #define TileReport OSReport #else inline void TileReport(const char *str, ...) { } #endif #ifdef BOUND_DEBUGGING #define BoundReport OSReport #else inline void BoundReport(const char *str, ...) { } #endif #ifdef DOODAD_DEBUGGING #define DoodadReport OSReport #else inline void DoodadReport(const char *str, ...) { } #endif dWMMap_c *dWMMap_c::instance = 0; dWMMap_c *dWMMap_c::build() { OSReport("Creating WM_Map\n"); void *buffer = AllocFromGameHeap1(sizeof(dWMMap_c)); dWMMap_c *c = new(buffer) dWMMap_c; OSReport("Created WM_Map @ %p\n", c); instance = c; return c; } dWMMap_c::dWMMap_c() { } int dWMMap_c::onCreate() { renderer.allocator.setup(GameHeaps[0], 0x20); OSReport("Setting up Renderer\n"); bool result = renderer.setup(&renderer.allocator); if (result) OSReport("Set up Renderer\n"); else OSReport(":(\n"); return true; } int dWMMap_c::onDelete() { return true; } int dWMMap_c::onExecute() { return true; } int dWMMap_c::onDraw() { renderer.scheduleForDrawing(); return true; } void dWMMap_c::renderer_c::drawOpa() { //drawLayers(); } void dWMMap_c::renderer_c::drawXlu() { drawLayers(); } void dWMMap_c::renderer_c::drawLayers() { dKPMapData_c *dataCls = &dScKoopatlas_c::instance->mapData; dKPMapFile_s *data = dataCls->data; baseZ = 1000 - (10 * data->layerCount); beginRendering(); for (int iLayer = data->layerCount - 1; iLayer >= 0; iLayer--) { dKPLayer_s *layer = data->layers[iLayer]; renderMtx[2][3] += 10; TileReport("Checking layer %d with type %d\n", iLayer, layer->type); if (layer->type == dKPLayer_s::OBJECTS) renderTileLayer(layer, data->sectors); else if (layer->type == dKPLayer_s::DOODADS) renderDoodadLayer(layer); } endRendering(); } void dWMMap_c::renderer_c::beginRendering() { currentTexture = 0; nw4r::g3d::Camera cam3d(GetCameraByID(0)); cam3d.GetCameraMtx(&renderMtx); MTXTransApply(renderMtx, renderMtx, 0, 0, baseZ); /*OSReport("MTX:\n"); OSReport("%f, %f, %f, %f:\n", renderMtx[0][0], renderMtx[0][1], renderMtx[0][2], renderMtx[0][3]); OSReport("%f, %f, %f, %f:\n", renderMtx[1][0], renderMtx[1][1], renderMtx[1][2], renderMtx[1][3]); OSReport("%f, %f, %f, %f:\n", renderMtx[2][0], renderMtx[2][1], renderMtx[2][2], renderMtx[2][3]);//*/ GXSetCurrentMtx(GX_PNMTX0); dWorldCamera_c *camObj = dWorldCamera_c::instance; minX = ((int)camObj->screenLeft) / 16; minY = ((int)(-camObj->screenTop) - 15) / 16; maxX = (((int)(camObj->screenLeft + camObj->screenWidth)) + 15) / 16; maxY = ((int)(-camObj->screenTop + camObj->screenHeight)) / 16; GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U8, 8); GXSetVtxAttrFmt(GX_VTXFMT1, GX_VA_POS, GX_POS_XY, GX_F32, 0); GXSetVtxAttrFmt(GX_VTXFMT1, GX_VA_TEX0, GX_TEX_ST, GX_U8, 8); GXSetNumIndStages(0); for (int i = 0; i < 0x10; i++) GXSetTevDirect((GXTevStageID)i); GXSetNumChans(0); GXSetNumTexGens(1); GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); GXSetNumTevStages(1); GXSetNumIndStages(0); GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_C1, GX_CC_C0, GX_CC_TEXC, GX_CC_ZERO); GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_A0, GX_CA_TEXA, GX_CA_ZERO); GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetZCompLoc(GX_FALSE); GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET); GXSetZMode(GX_TRUE, GX_LEQUAL, GX_FALSE); GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_GREATER, 0); GXSetFog(GX_FOG_NONE, 0, 0, 0, 0, (GXColor){0,0,0,0}); GXSetFogRangeAdj(GX_FALSE, 0, 0); GXSetCullMode(GX_CULL_NONE); GXSetDither(GX_TRUE); GXSetTevColor(GX_TEVREG0, (GXColor){255,255,255,255}); GXSetTevColor(GX_TEVREG1, (GXColor){0,0,0,255}); } void dWMMap_c::renderer_c::renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector_s *sectors) { //TileReport("Rendering layer %p\n", layer); // don't render it if we don't need to if (maxX < layer->left || minX > layer->right) return; if (maxY < layer->top || minY > layer->bottom) return; // set up loadCamera(); loadTexture(layer->tileset); // figure out -what- to render BoundReport("Regular render area: %d,%d to %d,%d\n", minX, minY, maxX, maxY); BoundReport("Layer bounds: %d,%d to %d,%d\n", layer->left, layer->top, layer->right, layer->bottom); int toRenderMinX = max(minX, layer->left); int toRenderMinY = max(minY, layer->top); int toRenderMaxX = min(maxX, layer->right); int toRenderMaxY = min(maxY, layer->bottom); int sectorBaseX = layer->sectorLeft; int sectorBaseY = layer->sectorTop; int sectorIndexStride = (layer->sectorRight - layer->sectorLeft + 1); int sectorMinX = toRenderMinX / 16; int sectorMinY = toRenderMinY / 16; int sectorMaxX = toRenderMaxX / 16; int sectorMaxY = toRenderMaxY / 16; BoundReport("To render: %d,%d to %d,%d\n", toRenderMinX, toRenderMinY, toRenderMaxX, toRenderMaxY); BoundReport("sectors %d,%d to %d,%d\n", sectorMinX, sectorMinY, sectorMaxX, sectorMaxY); for (int sectorY = sectorMinY; sectorY <= sectorMaxY; sectorY++) { int baseIndex = (sectorY - sectorBaseY) * sectorIndexStride; int iMinY = (sectorY == sectorMinY) ? (toRenderMinY & 0xF) : 0; int iMaxY = (sectorY == sectorMaxY) ? (toRenderMaxY & 0xF) : 15; int worldSectorY = sectorY << 4; for (int sectorX = sectorMinX; sectorX <= sectorMaxX; sectorX++) { u16 index = layer->indices[baseIndex + sectorX - sectorBaseX]; TileReport("Sector index @ %d,%d: %d\n", sectorX, sectorY, index); if (index == 0xFFFF) continue; dKPLayer_s::sector_s *sector = §ors[index]; int iMinX = (sectorX == sectorMinX) ? (toRenderMinX & 0xF) : 0; int iMaxX = (sectorX == sectorMaxX) ? (toRenderMaxX & 0xF) : 15; TileReport("Min/Max: X: %d,%d Y: %d,%d\n", iMinX, iMaxX, iMinY, iMaxY); int worldSectorX = sectorX << 4; for (int inY = iMinY; inY <= iMaxY; inY++) { for (int inX = iMinX; inX <= iMaxX; inX++) { u16 tileID = (*sector)[inY][inX]; if (tileID == 0xFFFF) continue; s16 worldX = (worldSectorX | inX) * 24; s16 worldY = -((worldSectorY | inY) * 24); u8 tileX = (tileID & 0x1F) * 8; u8 tileY = ((tileID & 0x1E0) / 32) * 16; TileReport("Drawing tile %d at %d,%d\n", tileID, worldX, worldY); GXBegin(GX_QUADS, GX_VTXFMT0, 4); GXPosition2s16(worldX + 24, worldY - 24); GXTexCoord2u8(tileX + 7, tileY + 14); GXPosition2s16(worldX + 24, worldY); GXTexCoord2u8(tileX + 7, tileY + 2); GXPosition2s16(worldX, worldY); GXTexCoord2u8(tileX + 1, tileY + 2); GXPosition2s16(worldX, worldY - 24); GXTexCoord2u8(tileX + 1, tileY + 14);//*/ GXEnd(); } } TileReport("Sector complete\n"); } } //TileReport("Layer complete\n"); } void dWMMap_c::renderer_c::renderDoodadLayer(dKPLayer_s *layer) { for (int i = 0; i < layer->doodadCount; i++) { dKPDoodad_s *doodad = layer->doodads[i]; DoodadReport("Doodad @ %f,%f sized %f,%f with angle %f\n", doodad->x, doodad->y, doodad->width, doodad->height, doodad->angle); // ANIMATE THE FUCKER float effectiveX = doodad->x, effectiveY = doodad->y; float effectiveWidth = doodad->width, effectiveHeight = doodad->height; float effectiveAngle = doodad->angle; if (doodad->animationCount > 0) { for (int j = 0; j < doodad->animationCount; j++) { dKPDoodad_s::animation_s *anim = &doodad->animations[j]; u32 baseTick = anim->baseTick; if (baseTick == 0) { anim->baseTick = baseTick = GlobalTickCount; } u32 elapsed = GlobalTickCount - baseTick; if (anim->isReversed) elapsed = anim->frameCount - 1 - elapsed; if (elapsed >= anim->frameCount) { // we've reached the end switch (anim->loop) { case dKPDoodad_s::animation_s::CONTIGUOUS: // Stop here elapsed = anim->frameCount - 1; break; case dKPDoodad_s::animation_s::LOOP: // Start over elapsed = 0; anim->baseTick = GlobalTickCount; break; case dKPDoodad_s::animation_s::REVERSE_LOOP: // Change direction anim->isReversed = !anim->isReversed; elapsed = (anim->isReversed) ? (anim->frameCount - 1) : 0; anim->baseTick = GlobalTickCount; break; } } // now calculate the thing float progress = elapsed / (float)anim->frameCount; float value; switch (anim->curve) { case dKPDoodad_s::animation_s::LINEAR: value = progress; break; case dKPDoodad_s::animation_s::SIN: value = (sin(((progress * M_PI * 2)) - M_PI_2) + 1) / 2; break; case dKPDoodad_s::animation_s::COS: value = (cos(((progress * M_PI * 2)) - M_PI_2) + 1) / 2; break; } float delta = anim->end - anim->start; float frame; if (anim->isReversed) frame = anim->start + ceil(delta * value); else frame = anim->start + (delta * value); // and apply it! switch (anim->type) { case dKPDoodad_s::animation_s::X_POS: effectiveX += frame; break; case dKPDoodad_s::animation_s::Y_POS: effectiveY += frame; break; case dKPDoodad_s::animation_s::ANGLE: effectiveAngle += frame; break; case dKPDoodad_s::animation_s::X_SCALE: effectiveWidth = (effectiveWidth * frame / 100.0); break; case dKPDoodad_s::animation_s::Y_SCALE: effectiveHeight = (effectiveHeight * frame / 100.0); break; case dKPDoodad_s::animation_s::OPACITY: // TODO break; } } } float halfW = effectiveWidth * 0.5f, halfH = effectiveHeight * 0.5f; Mtx doodadMtx; MTXTransApply(renderMtx, doodadMtx, effectiveX + halfW, -effectiveY - halfH, 0); Mtx rotMtx; MTXRotDeg(rotMtx, 'z', -effectiveAngle); MTXConcat(doodadMtx, rotMtx, doodadMtx); loadCamera(doodadMtx); loadTexture(doodad->texObj); GXBegin(GX_QUADS, GX_VTXFMT1, 4); GXPosition2f32(halfW, -halfH); GXTexCoord2u8(255, 255); GXPosition2f32(halfW, halfH); GXTexCoord2u8(255, 0); GXPosition2f32(-halfW, halfH); GXTexCoord2u8(0, 0); GXPosition2f32(-halfW, -halfH); GXTexCoord2u8(0, 255); GXEnd(); } } void dWMMap_c::renderer_c::endRendering() { } void dWMMap_c::renderer_c::loadTexture(GXTexObj *obj) { if (currentTexture == obj) return; GXLoadTexObj(obj, GX_TEXMAP0); currentTexture = obj; } void dWMMap_c::renderer_c::loadCamera() { GXLoadPosMtxImm(renderMtx, GX_PNMTX0); } void dWMMap_c::renderer_c::loadCamera(Mtx m) { GXLoadPosMtxImm(m, GX_PNMTX0); }