#include "global.h" #include "PR/gs2dex.h" #include "debug.h" void Room_Noop(PlayState* play, Room* room, Input* input, s32 arg3) { } void Room_DrawNone(PlayState* play, Room* room, u32 flags) { } static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; void Room_DrawNormal(PlayState* play, Room* room, u32 flags) { RoomShapeNormal* roomShape; RoomShapeDListsEntry* entry; s32 i; OPEN_DISPS(play->state.gfxCtx); if (flags & ROOM_DRAW_OPA) { func_800BCBF4(&sZeroVec, play); gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); func_8012C268(&play->state); gSPMatrix(POLY_OPA_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); } if (flags & ROOM_DRAW_XLU) { func_800BCC68(&sZeroVec, play); gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); Gfx_SetupDL25_Xlu(play->state.gfxCtx); gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); } roomShape = &room->roomShape->normal; entry = Lib_SegmentedToVirtual(roomShape->entries); for (i = 0; i < roomShape->numEntries; i++) { if ((flags & ROOM_DRAW_OPA) && (entry->opa != NULL)) { gSPDisplayList(POLY_OPA_DISP++, entry->opa); } if ((flags & ROOM_DRAW_XLU) && (entry->xlu != NULL)) { gSPDisplayList(POLY_XLU_DISP++, entry->xlu); } entry++; } CLOSE_DISPS(play->state.gfxCtx); } typedef enum { /* 0 */ ROOM_CULL_DEBUG_MODE_OFF, /* 1 */ ROOM_CULL_DEBUG_MODE_UP_TO_TARGET, /* 2 */ ROOM_CULL_DEBUG_MODE_ONLY_TARGET } RoomCullableDebugMode; typedef struct RoomShapeCullableEntryLinked { /* 0x0 */ RoomShapeCullableEntry* entry; /* 0x4 */ f32 boundsNearZ; /* 0x8 */ struct RoomShapeCullableEntryLinked* prev; /* 0xC */ struct RoomShapeCullableEntryLinked* next; } RoomShapeCullableEntryLinked; // size = 0x10 #define ROOM_SHAPE_CULLABLE_MAX_ENTRIES 128 void Room_DrawCullable(PlayState* play, Room* room, u32 flags) { RoomShapeCullable* roomShape; RoomShapeCullableEntry* roomShapeCullableEntry; RoomShapeCullableEntryLinked linkedEntriesBuffer[ROOM_SHAPE_CULLABLE_MAX_ENTRIES]; RoomShapeCullableEntryLinked* head = NULL; RoomShapeCullableEntryLinked* tail = NULL; RoomShapeCullableEntryLinked* iter; Gfx* displayList; RoomShapeCullableEntryLinked* insert; f32 entryBoundsNearZ; s32 i; Vec3f pos; Vec3f projectedPos; RoomShapeCullableEntry* roomShapeCullableEntries; OPEN_DISPS(play->state.gfxCtx); if (flags & ROOM_DRAW_OPA) { func_800BCBF4(&sZeroVec, play); gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); if (play->roomCtx.unk74 != NULL) { gSPSegment(POLY_OPA_DISP++, 0x06, play->roomCtx.unk74); } func_8012C268(&play->state); gSPMatrix(POLY_OPA_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); } if (flags & ROOM_DRAW_XLU) { func_800BCC68(&sZeroVec, play); gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); if (play->roomCtx.unk74 != NULL) { gSPSegment(POLY_XLU_DISP++, 0x06, play->roomCtx.unk74); } Gfx_SetupDL25_Xlu(play->state.gfxCtx); gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); } if (room->enablePosLights || (MREG(93) != 0)) { gSPSetGeometryMode(POLY_OPA_DISP++, G_LIGHTING_POSITIONAL); gSPSetGeometryMode(POLY_XLU_DISP++, G_LIGHTING_POSITIONAL); } roomShape = &room->roomShape->cullable; roomShapeCullableEntry = Lib_SegmentedToVirtual(roomShape->entries); insert = linkedEntriesBuffer; roomShapeCullableEntries = roomShapeCullableEntry; if (play->roomCtx.unk78 < 0) { for (i = 0; i < roomShape->numEntries; i++, roomShapeCullableEntry++) { if (R_ROOM_CULL_DEBUG_MODE != 0) { if (((R_ROOM_CULL_DEBUG_MODE == ROOM_CULL_DEBUG_MODE_UP_TO_TARGET) && (i <= R_ROOM_CULL_DEBUG_TARGET)) || ((R_ROOM_CULL_DEBUG_MODE == ROOM_CULL_DEBUG_MODE_ONLY_TARGET) && (i == R_ROOM_CULL_DEBUG_TARGET))) { if (flags & ROOM_DRAW_OPA) { displayList = roomShapeCullableEntry->opa; if (displayList != NULL) { gSPDisplayList(POLY_OPA_DISP++, displayList); } } if (flags & ROOM_DRAW_XLU) { displayList = roomShapeCullableEntry->xlu; if (displayList != NULL) { gSPDisplayList(POLY_XLU_DISP++, displayList); } } } } else { if (flags & ROOM_DRAW_OPA) { displayList = roomShapeCullableEntry->opa; if (displayList != NULL) { gSPDisplayList(POLY_OPA_DISP++, displayList); } } if (flags & ROOM_DRAW_XLU) { displayList = roomShapeCullableEntry->xlu; if (displayList != NULL) { gSPDisplayList(POLY_XLU_DISP++, displayList); } } } } } else { f32 var_fa1 = 1.0f / play->projectionMtxFDiagonal.z; f32 var_fv1; s32 var_a1; // Pick and sort entries by depth for (i = 0; i < roomShape->numEntries; i++, roomShapeCullableEntry++) { // Project the entry position, to get the depth it is at. pos.x = roomShapeCullableEntry->boundsSphereCenter.x; pos.y = roomShapeCullableEntry->boundsSphereCenter.y; pos.z = roomShapeCullableEntry->boundsSphereCenter.z; SkinMatrix_Vec3fMtxFMultXYZ(&play->viewProjectionMtxF, &pos, &projectedPos); projectedPos.z *= var_fa1; var_fv1 = ABS_ALT(roomShapeCullableEntry->boundsSphereRadius); // If the entry bounding sphere isn't fully before the rendered depth range if (-var_fv1 < projectedPos.z) { // Compute the depth of the nearest point in the entry's bounding sphere entryBoundsNearZ = projectedPos.z - var_fv1; // If the entry bounding sphere isn't fully beyond the rendered depth range if (entryBoundsNearZ < play->lightCtx.zFar) { // This entry will be rendered insert->entry = roomShapeCullableEntry; if (roomShapeCullableEntry->boundsSphereRadius < 0) { insert->boundsNearZ = FLT_MAX; } else { insert->boundsNearZ = entryBoundsNearZ; } // Insert into the linked list, ordered by ascending depth of the nearest point in the bounding // sphere iter = head; if (iter == NULL) { head = tail = insert; insert->prev = insert->next = NULL; } else { do { if (insert->boundsNearZ < iter->boundsNearZ) { break; } iter = iter->next; } while (iter != NULL); if (iter == NULL) { insert->prev = tail; insert->next = NULL; tail->next = insert; tail = insert; } else { insert->prev = iter->prev; if (insert->prev == NULL) { head = insert; } else { insert->prev->next = insert; } iter->prev = insert; insert->next = iter; } } insert++; } } } //! FAKE: Similar trick used in OoT R_ROOM_CULL_NUM_ENTRIES = roomShape->numEntries & 0xFFFF & 0xFFFF & 0xFFFF; // Draw entries, from nearest to furthest i = 1; if (flags & ROOM_DRAW_OPA) { for (; head != NULL; head = head->next, i++) { s32 pad; roomShapeCullableEntry = head->entry; if (R_ROOM_CULL_DEBUG_MODE != ROOM_CULL_DEBUG_MODE_OFF) { // Debug mode drawing if (((R_ROOM_CULL_DEBUG_MODE == ROOM_CULL_DEBUG_MODE_UP_TO_TARGET) && (i <= R_ROOM_CULL_DEBUG_TARGET)) || ((R_ROOM_CULL_DEBUG_MODE == ROOM_CULL_DEBUG_MODE_ONLY_TARGET) && (i == R_ROOM_CULL_DEBUG_TARGET))) { displayList = roomShapeCullableEntry->opa; if (displayList != NULL) { gSPDisplayList(POLY_OPA_DISP++, displayList); } } } else { displayList = roomShapeCullableEntry->opa; if (displayList != NULL) { gSPDisplayList(POLY_OPA_DISP++, displayList); } } } } if (flags & ROOM_DRAW_XLU) { for (; tail != NULL; tail = tail->prev) { f32 temp_fv0; f32 temp_fv1; roomShapeCullableEntry = tail->entry; displayList = roomShapeCullableEntry->xlu; if (displayList != NULL) { if (roomShapeCullableEntry->boundsSphereRadius & 1) { temp_fv0 = tail->boundsNearZ - (f32)(iREG(93) + 0xBB8); temp_fv1 = iREG(94) + 0x7D0; if (temp_fv0 < temp_fv1) { if (temp_fv0 < 0.0f) { var_a1 = 255; } else { var_a1 = 255 - (s32)((temp_fv0 / temp_fv1) * 255.0f); } gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, var_a1); gSPDisplayList(POLY_XLU_DISP++, displayList); } } else { gSPDisplayList(POLY_XLU_DISP++, displayList); } } } } R_ROOM_CULL_USED_ENTRIES = i - 1; } CLOSE_DISPS(play->state.gfxCtx); } #define ROOM_IMAGE_NODRAW_BACKGROUND (1 << 0) #define ROOM_IMAGE_NODRAW_OPA (1 << 1) #define ROOM_IMAGE_NODRAW_XLU (1 << 2) void Room_DrawImageSingle(PlayState* play, Room* room, u32 flags) { Camera* activeCam; Gfx* gfx; RoomShapeImageSingle* roomShape; RoomShapeDListsEntry* entry; u32 isFixedCamera; u32 drawBackground; u32 drawOpa; u32 drawXlu; OPEN_DISPS(play->state.gfxCtx); activeCam = GET_ACTIVE_CAM(play); isFixedCamera = false; // Condition is inferred from OoT roomShape = &room->roomShape->image.single; entry = Lib_SegmentedToVirtual(roomShape->base.entry); drawBackground = (flags & ROOM_DRAW_OPA) && isFixedCamera && (roomShape->source != NULL) && !(R_ROOM_IMAGE_NODRAW_FLAGS & ROOM_IMAGE_NODRAW_BACKGROUND); drawOpa = (flags & ROOM_DRAW_OPA) && (entry->opa != NULL) && !(R_ROOM_IMAGE_NODRAW_FLAGS & ROOM_IMAGE_NODRAW_OPA); drawXlu = (flags & ROOM_DRAW_XLU) && (entry->xlu != NULL) && !(R_ROOM_IMAGE_NODRAW_FLAGS & ROOM_IMAGE_NODRAW_XLU); if (drawOpa || drawBackground) { gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); if (drawOpa) { Gfx_SetupDL25_Opa(play->state.gfxCtx); gSPMatrix(POLY_OPA_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_OPA_DISP++, entry->opa); } if (drawBackground) { gSPLoadUcodeL(POLY_OPA_DISP++, gspS2DEX2_fifo); gfx = POLY_OPA_DISP; { Vec3f quakeOffset; Camera_GetQuakeOffset(&quakeOffset, activeCam); Prerender_DrawBackground2D( &gfx, roomShape->source, roomShape->tlut, roomShape->width, roomShape->height, roomShape->fmt, roomShape->siz, roomShape->tlutMode, roomShape->tlutCount, (quakeOffset.x + quakeOffset.z) * 1.2f + quakeOffset.y * 0.6f, quakeOffset.y * 2.4f + (quakeOffset.x + quakeOffset.z) * 0.3f, 1.0f, 1.0f, 0); } POLY_OPA_DISP = gfx; gSPLoadUcode(POLY_OPA_DISP++, SysUcode_GetUCode(), SysUcode_GetUCodeData()); } } if (drawXlu) { gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); Gfx_SetupDL25_Xlu(play->state.gfxCtx); gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_XLU_DISP++, entry->xlu); } CLOSE_DISPS(play->state.gfxCtx); } RoomShapeImageMultiBgEntry* Room_GetImageMultiBgEntry(RoomShapeImageMulti* roomShapeImageMulti, PlayState* play) { Camera* activeCam = GET_ACTIVE_CAM(play); s32 bgCamIndex = activeCam->bgCamIndex; s16 overrideBgCamIndex; Player* player; RoomShapeImageMultiBgEntry* bgEntry; s32 i; overrideBgCamIndex = ((BgCamFuncData*)BgCheck_GetBgCamFuncDataImpl(&play->colCtx, bgCamIndex, BGCHECK_SCENE)) ->roomImageOverrideBgCamIndex; if (overrideBgCamIndex >= 0) { bgCamIndex = overrideBgCamIndex; } player = GET_PLAYER(play); player->actor.params = (player->actor.params & 0xFF00) | bgCamIndex; bgEntry = Lib_SegmentedToVirtual(roomShapeImageMulti->backgrounds); for (i = 0; i < roomShapeImageMulti->numBackgrounds; i++) { if (bgEntry->bgCamIndex == bgCamIndex) { return bgEntry; } bgEntry++; } _dbg_hungup("../z_room.c", 849); return NULL; } void Room_DrawImageMulti(PlayState* play, Room* room, u32 flags) { Camera* activeCam; Gfx* gfx; RoomShapeImageMulti* roomShape; RoomShapeImageMultiBgEntry* bgEntry; RoomShapeDListsEntry* dListsEntry; u32 isFixedCamera; u32 drawBackground; u32 drawOpa; u32 drawXlu; OPEN_DISPS(play->state.gfxCtx); activeCam = GET_ACTIVE_CAM(play); isFixedCamera = false; // Condition is inferred from OoT roomShape = &room->roomShape->image.multi; dListsEntry = Lib_SegmentedToVirtual(roomShape->base.entry); bgEntry = Room_GetImageMultiBgEntry(roomShape, play); drawBackground = (flags & ROOM_DRAW_OPA) && isFixedCamera && (bgEntry->source != NULL) && !(R_ROOM_IMAGE_NODRAW_FLAGS & ROOM_IMAGE_NODRAW_BACKGROUND); drawOpa = (flags & ROOM_DRAW_OPA) && (dListsEntry->opa != NULL) && !(R_ROOM_IMAGE_NODRAW_FLAGS & ROOM_IMAGE_NODRAW_OPA); drawXlu = (flags & ROOM_DRAW_XLU) && (dListsEntry->xlu != NULL) && !(R_ROOM_IMAGE_NODRAW_FLAGS & ROOM_IMAGE_NODRAW_XLU); if (drawOpa || drawBackground) { gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); if (drawOpa) { Gfx_SetupDL25_Opa(play->state.gfxCtx); gSPMatrix(POLY_OPA_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_OPA_DISP++, dListsEntry->opa); } if (drawBackground) { gSPLoadUcodeL(POLY_OPA_DISP++, gspS2DEX2_fifo); gfx = POLY_OPA_DISP; { Vec3f quakeOffset; Camera_GetQuakeOffset(&quakeOffset, activeCam); Prerender_DrawBackground2D(&gfx, bgEntry->source, bgEntry->tlut, bgEntry->width, bgEntry->height, bgEntry->fmt, bgEntry->siz, bgEntry->tlutMode, bgEntry->tlutCount, (quakeOffset.x + quakeOffset.z) * 1.2f + quakeOffset.y * 0.6f, quakeOffset.y * 2.4f + (quakeOffset.x + quakeOffset.z) * 0.3f, 1.0f, 1.0f, 0); } POLY_OPA_DISP = gfx; gSPLoadUcode(POLY_OPA_DISP++, SysUcode_GetUCode(), SysUcode_GetUCodeData()); } } if (drawXlu) { gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); Gfx_SetupDL25_Xlu(play->state.gfxCtx); gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_XLU_DISP++, dListsEntry->xlu); } CLOSE_DISPS(play->state.gfxCtx); } void Room_DrawImage(PlayState* play, Room* room, u32 flags) { RoomShapeImageBase* roomShape = &room->roomShape->image.base; if (roomShape->amountType == ROOM_SHAPE_IMAGE_AMOUNT_SINGLE) { Room_DrawImageSingle(play, room, flags); } else if (roomShape->amountType == ROOM_SHAPE_IMAGE_AMOUNT_MULTI) { Room_DrawImageMulti(play, room, flags); } else { _dbg_hungup("../z_room.c", 965); } } void Room_Init(PlayState* play, RoomContext* roomCtx) { s32 i; roomCtx->curRoom.num = -1; roomCtx->curRoom.segment = NULL; roomCtx->unk78 = 1; roomCtx->unk79 = 0; for (i = 0; i < ARRAY_COUNT(roomCtx->unk7A); i++) { roomCtx->unk7A[i] = 0; } } size_t Room_AllocateAndLoad(PlayState* play, RoomContext* roomCtx) { size_t maxRoomSize = 0; size_t roomSize; s32 i; s32 j; s32 frontRoom; s32 backRoom; size_t frontRoomSize; size_t backRoomSize; size_t cumulRoomSize; s32 pad[2]; { RomFile* roomList = play->roomList; for (i = 0; i < play->numRooms; i++) { roomSize = roomList[i].vromEnd - roomList[i].vromStart; maxRoomSize = MAX(roomSize, maxRoomSize); } } if ((u32)play->doorCtx.numTransitionActors != 0) { RomFile* roomList = play->roomList; TransitionActorEntry* transitionActor = &play->doorCtx.transitionActorList[0]; for (j = 0; j < play->doorCtx.numTransitionActors; j++) { frontRoom = transitionActor->sides[0].room; backRoom = transitionActor->sides[1].room; frontRoomSize = (frontRoom < 0) ? 0 : roomList[frontRoom].vromEnd - roomList[frontRoom].vromStart; backRoomSize = (backRoom < 0) ? 0 : roomList[backRoom].vromEnd - roomList[backRoom].vromStart; cumulRoomSize = (frontRoom != backRoom) ? frontRoomSize + backRoomSize : frontRoomSize; maxRoomSize = MAX(cumulRoomSize, maxRoomSize); transitionActor++; } } roomCtx->roomMemPages[0] = THA_AllocTailAlign16(&play->state.heap, maxRoomSize); if (roomCtx->roomMemPages[0] == NULL) { _dbg_hungup("../z_room.c", 1078); } roomCtx->roomMemPages[1] = (void*)((uintptr_t)roomCtx->roomMemPages[0] + maxRoomSize); roomCtx->activeMemPage = 0; roomCtx->status = 0; if ((gSaveContext.respawnFlag != 0) && (gSaveContext.respawnFlag != -2) && (gSaveContext.respawnFlag != -7)) { s32 respawnMode; if ((gSaveContext.respawnFlag == -8) || (gSaveContext.respawnFlag == -5) || (gSaveContext.respawnFlag == -4) || ((gSaveContext.respawnFlag < 0) && (gSaveContext.respawnFlag != -1) && (gSaveContext.respawnFlag != -6))) { respawnMode = RESPAWN_MODE_DOWN; } else if (gSaveContext.respawnFlag < 0) { respawnMode = RESPAWN_MODE_TOP; } else { respawnMode = gSaveContext.respawnFlag - 1; } frontRoom = gSaveContext.respawn[respawnMode].roomIndex; } else { frontRoom = play->setupEntranceList[play->curSpawn].room; } Room_StartRoomTransition(play, roomCtx, frontRoom); return maxRoomSize; } s32 Room_StartRoomTransition(PlayState* play, RoomContext* roomCtx, s32 index) { if (roomCtx->status == 0) { size_t size; roomCtx->prevRoom = roomCtx->curRoom; roomCtx->curRoom.num = index; roomCtx->curRoom.segment = NULL; roomCtx->status = 1; size = play->roomList[index].vromEnd - play->roomList[index].vromStart; roomCtx->activeRoomVram = (void*)(ALIGN16((uintptr_t)roomCtx->roomMemPages[roomCtx->activeMemPage] - (size + 8) * roomCtx->activeMemPage - 7)); osCreateMesgQueue(&roomCtx->loadQueue, roomCtx->loadMsg, ARRAY_COUNT(roomCtx->loadMsg)); DmaMgr_SendRequestImpl(&roomCtx->dmaRequest, roomCtx->activeRoomVram, play->roomList[index].vromStart, size, 0, &roomCtx->loadQueue, NULL); roomCtx->activeMemPage ^= 1; return 1; } return 0; } s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx) { if (roomCtx->status == 1) { if (osRecvMesg(&roomCtx->loadQueue, NULL, OS_MESG_NOBLOCK) == 0) { roomCtx->status = 0; roomCtx->curRoom.segment = roomCtx->activeRoomVram; gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->activeRoomVram); Scene_ExecuteCommands(play, roomCtx->curRoom.segment); func_80123140(play, GET_PLAYER(play)); Actor_SpawnTransitionActors(play, &play->actorCtx); if (((play->sceneId != SCENE_IKANA) || (roomCtx->curRoom.num != 1)) && (play->sceneId != SCENE_IKNINSIDE)) { play->envCtx.lightSettingOverride = 0xFF; play->envCtx.unk_E0 = 0; } func_800FEAB0(); if (!func_800FE4B8(play)) { func_800FD858(play); } } else { return 0; } } return 1; } RoomDrawHandler sRoomDrawHandlers[] = { Room_DrawNormal, // ROOM_SHAPE_TYPE_NORMAL Room_DrawImage, // ROOM_SHAPE_TYPE_IMAGE Room_DrawCullable, // ROOM_SHAPE_TYPE_CULLABLE Room_DrawNone, // ROOM_SHAPE_TYPE_NONE }; void Room_Draw(PlayState* play, Room* room, u32 flags) { if (room->segment != NULL) { gSegments[3] = VIRTUAL_TO_PHYSICAL(room->segment); sRoomDrawHandlers[room->roomShape->base.type](play, room, flags); } return; } void func_8012EBF8(PlayState* play, RoomContext* roomCtx) { roomCtx->prevRoom.num = -1; roomCtx->prevRoom.segment = NULL; func_800BA798(play, &play->actorCtx); Actor_SpawnTransitionActors(play, &play->actorCtx); if (roomCtx->curRoom.num > -1) { Map_InitRoomData(play, roomCtx->curRoom.num); Minimap_SavePlayerRoomInitInfo(play); } func_801A3CD8(play->roomCtx.curRoom.echo); }