From 5db7dc544aaedb6adc8e497469f8f15634bb1df0 Mon Sep 17 00:00:00 2001 From: James Lambert Date: Sun, 13 Nov 2022 19:48:40 -0700 Subject: [PATCH] Finish out render plan code --- Makefile | 2 + src/scene/portal.c | 36 +++--- src/scene/portal.h | 22 +++- src/scene/render_plan.c | 249 ++++++++++++++++++++++++++++++++++++++++ src/scene/render_plan.h | 17 +++ src/scene/scene.c | 2 +- 6 files changed, 309 insertions(+), 19 deletions(-) create mode 100644 src/scene/render_plan.c create mode 100644 src/scene/render_plan.h diff --git a/Makefile b/Makefile index cefcfe1..5b08f3c 100644 --- a/Makefile +++ b/Makefile @@ -201,6 +201,8 @@ build/src/scene/box_dropper.o: $(MODEL_HEADERS) build/src/scene/pedestal.o: $(MODEL_HEADERS) +build/src/scene/render_plan.o: $(MODEL_HEADERS) + build/anims.ld: $(ANIM_LIST) tools/generate_animation_ld.js @mkdir -p $(@D) node tools/generate_animation_ld.js $@ $(ANIM_LIST) diff --git a/src/scene/portal.c b/src/scene/portal.c index 14447a3..3460fba 100644 --- a/src/scene/portal.c +++ b/src/scene/portal.c @@ -57,7 +57,7 @@ void renderPropsInit(struct RenderProps* props, struct Camera* camera, float asp cameraApplyMatrices(renderState, &props->cameraMatrixInfo); props->viewport = &fullscreenViewport; props->currentDepth = STARTING_RENDER_DEPTH; - props->fromPortalIndex = NO_PORTAL; + props->exitPortalIndex = NO_PORTAL; props->fromRoom = roomIndex; props->clippingPortalIndex = -1; @@ -98,6 +98,10 @@ void renderPropsInit(struct RenderProps* props, struct Camera* camera, float asp props->minY = 0; props->maxX = SCREEN_WD; props->maxY = SCREEN_HT; + + props->previousProperties = NULL; + + props->portalRenderType = 0; } #define MIN_VP_WIDTH 64 @@ -213,9 +217,8 @@ int renderPropsNext(struct RenderProps* current, struct RenderProps* next, struc } next->clippingPortalIndex = -1; - next->fromPortalIndex = toPortal < fromPortal ? 0 : 1; - // Gross - next->fromRoom = gCollisionScene.portalRooms[toPortal == gCollisionScene.portalTransforms[0] ? 0 : 1]; + next->exitPortalIndex = toPortal < fromPortal ? 0 : 1; + next->fromRoom = gCollisionScene.portalRooms[next->exitPortalIndex]; gSPViewport(renderState->dl++, viewport); gDPSetScissor(renderState->dl++, G_SC_NON_INTERLACE, next->minX, next->minY, next->maxX, next->maxY); @@ -332,6 +335,19 @@ void portalRenderScreenCover(struct Vector2s16* points, int pointCount, struct R gDPSetRenderMode(renderState->dl++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2); } +void portalDetermineTransform(struct Portal* portal, float portalTransform[4][4]) { + struct Transform finalTransform; + finalTransform = portal->transform; + + if (portal->flags & PortalFlagsOddParity) { + quatMultiply(&portal->transform.rotation, &gVerticalFlip, &finalTransform.rotation); + } + + vector3Scale(&gOneVec, &finalTransform.scale, portal->scale); + + transformToMatrix(&finalTransform, portalTransform, SCENE_SCALE); +} + void portalRenderCover(struct Portal* portal, float portalTransform[4][4], struct RenderState* renderState) { Mtx* matrix = renderStateRequestMatrices(renderState, 1); @@ -369,17 +385,7 @@ void portalRender(struct Portal* portal, struct Portal* otherPortal, struct Rend struct ScreenClipper clipper; float portalTransform[4][4]; - struct Transform finalTransform; - - finalTransform = portal->transform; - - if (portal->flags & PortalFlagsOddParity) { - quatMultiply(&portal->transform.rotation, &gVerticalFlip, &finalTransform.rotation); - } - - vector3Scale(&gOneVec, &finalTransform.scale, portal->scale); - - transformToMatrix(&finalTransform, portalTransform, SCENE_SCALE); + portalDetermineTransform(portal, portalTransform); if (props->currentDepth == 0 || !otherPortal) { portalRenderCover(portal, portalTransform, renderState); diff --git a/src/scene/portal.h b/src/scene/portal.h index 9e23636..2aa71d0 100644 --- a/src/scene/portal.h +++ b/src/scene/portal.h @@ -8,6 +8,7 @@ #include "camera.h" #include "static_scene.h" #include "./portal_surface.h" +#include "../graphics/screen_clipper.h" #define PORTAL_LOOP_SIZE 8 @@ -34,6 +35,14 @@ typedef void SceneRenderCallback(void* data, struct RenderProps* properties, str #define NO_PORTAL 0xFF +#define PORTAL_RENDER_TYPE_VISIBLE_0 (1 << 0) +#define PORTAL_RENDER_TYPE_VISIBLE_1 (1 << 1) +#define PORTAL_RENDER_TYPE_ENABLED_0 (1 << 2) +#define PORTAL_RENDER_TYPE_ENABLED_1 (1 << 3) + +#define PORTAL_RENDER_TYPE_VISIBLE(portalIndex) (PORTAL_RENDER_TYPE_VISIBLE_0 << (portalIndex)) +#define PORTAL_RENDER_TYPE_ENABLED(portalIndex) (PORTAL_RENDER_TYPE_ENABLED_0 << (portalIndex)) + struct RenderProps { struct Camera camera; float aspectRatio; @@ -43,7 +52,9 @@ struct RenderProps { Vp* viewport; u8 currentDepth; - u8 fromPortalIndex; + u8 exitPortalIndex; + s8 clippingPortalIndex; + u8 portalRenderType; u16 fromRoom; @@ -51,8 +62,11 @@ struct RenderProps { short minY; short maxX; short maxY; - - s8 clippingPortalIndex; + + u64 visiblerooms; + + struct RenderProps* previousProperties; + struct ScreenClipper clipper; }; void renderPropsInit(struct RenderProps* props, struct Camera* camera, float aspectRatio, struct RenderState* renderState, u16 roomIndex); @@ -62,6 +76,8 @@ void portalInit(struct Portal* portal, enum PortalFlags flags); void portalUpdate(struct Portal* portal, int isOpen); void portalRender(struct Portal* portal, struct Portal* otherPortal, struct RenderProps* props, SceneRenderCallback sceneRenderer, void* data, struct RenderState* renderState); +void portalDetermineTransform(struct Portal* portal, float portalTransform[4][4]); + int portalAttachToSurface(struct Portal* portal, struct PortalSurface* surface, int surfaceIndex, struct Transform* portalAt); void portalCheckForHoles(struct Portal* portals); diff --git a/src/scene/render_plan.c b/src/scene/render_plan.c new file mode 100644 index 0000000..b0a8081 --- /dev/null +++ b/src/scene/render_plan.c @@ -0,0 +1,249 @@ + +#include "render_plan.h" + +#include "../graphics/screen_clipper.h" +#include "../physics/collision_scene.h" +#include "../levels/static_render.h" + +#include "../build/assets/models/portal/portal_blue.h" +#include "../build/assets/models/portal/portal_blue_filled.h" +#include "../build/assets/models/portal/portal_blue_face.h" +#include "../build/assets/models/portal/portal_orange.h" +#include "../build/assets/models/portal/portal_orange_face.h" +#include "../build/assets/models/portal/portal_orange_filled.h" + +Vp* renderPropsBuildViewport(struct RenderProps* props, struct RenderState* renderState); +void renderPlanFinishView(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderProps* properties, struct RenderState* renderState); +void portalRenderScreenCover(struct Vector2s16* points, int pointCount, struct RenderProps* props, struct RenderState* renderState); +void portalRenderCover(struct Portal* portal, float portalTransform[4][4], struct RenderState* renderState); +extern struct Quaternion gVerticalFlip; +extern struct Vector3 gPortalOutline[PORTAL_LOOP_SIZE]; + +#define CALC_SCREEN_SPACE(clip_space, screen_size) ((clip_space + 1.0f) * ((screen_size) / 2)) + +int renderPlanPortal(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderProps* current, int exitPortalIndex, struct RenderState* renderState) { + struct Portal* portal = &scene->portals[1 - exitPortalIndex]; + + struct Vector3 forward = gForward; + if (!(portal->flags & PortalFlagsOddParity)) { + forward.z = -1.0f; + } + + struct Vector3 worldForward; + quatMultVector(&portal->transform.rotation, &forward, &worldForward); + + struct Vector3 offsetFromCamera; + vector3Sub(¤t->camera.transform.position, &portal->transform.position, &offsetFromCamera); + + // don't render the portal if it is facing the wrong way + if (vector3Dot(&worldForward, &offsetFromCamera) < 0.0f) { + return 0; + } + + float portalTransform[4][4]; + + struct Transform finalTransform; + + finalTransform = portal->transform; + + if (portal->flags & PortalFlagsOddParity) { + quatMultiply(&portal->transform.rotation, &gVerticalFlip, &finalTransform.rotation); + } + + vector3Scale(&gOneVec, &finalTransform.scale, portal->scale); + + transformToMatrix(&finalTransform, portalTransform, SCENE_SCALE); + + int portalIndex = 1 - exitPortalIndex; + + int flags = PORTAL_RENDER_TYPE_VISIBLE(portalIndex); + + if (current->currentDepth == 0 || !collisionSceneIsPortalOpen() || renderPlan->stageCount >= MAX_PORTAL_STEPS) { + return flags; + } + + struct RenderProps* next = &renderPlan->stageProps[renderPlan->stageCount]; + + screenClipperInitWithCamera(&next->clipper, ¤t->camera, (float)SCREEN_WD / (float)SCREEN_HT, portalTransform); + struct Box2D clippingBounds; + screenClipperBoundingPoints(&next->clipper, gPortalOutline, sizeof(gPortalOutline) / sizeof(*gPortalOutline), &clippingBounds); + + if (current->clippingPortalIndex == portalIndex) { + next->minX = 0; + next->maxX = SCREEN_WD; + next->minY = 0; + next->maxY = SCREEN_HT; + } else { + next->minX = CALC_SCREEN_SPACE(clippingBounds.min.x, SCREEN_WD); + next->maxX = CALC_SCREEN_SPACE(clippingBounds.max.x, SCREEN_WD); + next->minY = CALC_SCREEN_SPACE(-clippingBounds.max.y, SCREEN_HT); + next->maxY = CALC_SCREEN_SPACE(-clippingBounds.min.y, SCREEN_HT); + + next->minX = MAX(next->minX, current->minX); + next->maxX = MIN(next->maxX, current->maxX); + next->minY = MAX(next->minY, current->minY); + next->maxY = MIN(next->maxY, current->maxY); + } + + if (next->minX >= next->maxX || next->minY >= next->maxY) { + return 0; + } + + struct Transform* exitPortal = &scene->portals[exitPortalIndex].transform; + struct Transform* intoPortal = &scene->portals[1 - exitPortalIndex].transform; + + struct Transform otherInverse; + transformInvert(exitPortal, &otherInverse); + struct Transform portalCombined; + transformConcat(intoPortal, &otherInverse, &portalCombined); + + next->camera = current->camera; + next->aspectRatio = current->aspectRatio; + transformConcat(&portalCombined, ¤t->camera.transform, &next->camera.transform); + + struct Vector3 portalOffset; + vector3Sub(&intoPortal->position, &next->camera.transform.position, &portalOffset); + + struct Vector3 cameraForward; + quatMultVector(&next->camera.transform.rotation, &gForward, &cameraForward); + + next->camera.nearPlane = (-vector3Dot(&portalOffset, &cameraForward)) * SCENE_SCALE; + + if (next->camera.nearPlane < current->camera.nearPlane) { + next->camera.nearPlane = current->camera.nearPlane; + + if (next->camera.nearPlane > next->camera.farPlane) { + next->camera.nearPlane = next->camera.farPlane; + } + } + + next->currentDepth = current->currentDepth - 1; + next->viewport = renderPropsBuildViewport(next, renderState); + + if (!next->viewport) { + return flags; + } + + if (!cameraSetupMatrices(&next->camera, renderState, next->aspectRatio, next->viewport, &next->cameraMatrixInfo)) { + return flags; + } + + if (current->clippingPortalIndex == -1) { + // set the near clipping plane to be the exit portal surface + quatMultVector(&intoPortal->rotation, &gForward, &next->cameraMatrixInfo.cullingInformation.clippingPlanes[4].normal); + if (intoPortal < exitPortal) { + vector3Negate(&next->cameraMatrixInfo.cullingInformation.clippingPlanes[4].normal, &next->cameraMatrixInfo.cullingInformation.clippingPlanes[4].normal); + } + next->cameraMatrixInfo.cullingInformation.clippingPlanes[4].d = -vector3Dot(&next->cameraMatrixInfo.cullingInformation.clippingPlanes[4].normal, &intoPortal->position) * SCENE_SCALE; + } + next->clippingPortalIndex = -1; + + next->exitPortalIndex = exitPortalIndex; + next->fromRoom = gCollisionScene.portalRooms[next->exitPortalIndex]; + + ++renderPlan->stageCount; + + next->previousProperties = current; + + next->portalRenderType = 0; + + renderPlanFinishView(renderPlan, scene, next, renderState); + + return flags | PORTAL_RENDER_TYPE_ENABLED(portalIndex); +} + +void renderPlanFinishView(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderProps* properties, struct RenderState* renderState) { + + staticRenderDetermineVisibleRooms(&properties->cameraMatrixInfo.cullingInformation, properties->fromRoom, &properties->visiblerooms); + + int closerPortal = vector3DistSqrd(&properties->camera.transform.position, &scene->portals[0].transform.position) > vector3DistSqrd(&properties->camera.transform.position, &scene->portals[1].transform.position) ? 0 : 1; + int otherPortal = 1 - closerPortal; + + for (int i = 0; i < 2; ++i) { + if (gCollisionScene.portalTransforms[closerPortal] && + properties->exitPortalIndex != closerPortal && + staticRenderIsRoomVisible(properties->visiblerooms, gCollisionScene.portalRooms[closerPortal])) { + properties->portalRenderType |= renderPlanPortal( + renderPlan, + scene, + properties, + closerPortal, + renderState + ); + } + + closerPortal = 1 - closerPortal; + otherPortal = 1 - otherPortal; + } +} + +void renderPlanBuild(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderState* renderState) { + renderPropsInit(&renderPlan->stageProps[0], &scene->camera, (float)SCREEN_WD / (float)SCREEN_HT, renderState, scene->player.body.currentRoom); + renderPlan->stageProps[0].camera = scene->camera; + renderPlan->stageCount = 1; + + renderPlanFinishView(renderPlan, scene, &renderPlan->stageProps[0], renderState); +} + +void renderPlanExecute(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderState* renderState) { + for (int i = renderPlan->stageCount - 1; i >= 0; --i) { + struct RenderProps* current = &renderPlan->stageProps[i]; + + if (!cameraApplyMatrices(renderState, ¤t->cameraMatrixInfo)) { + return; + } + + gSPViewport(renderState->dl++, current->viewport); + gDPSetScissor(renderState->dl++, G_SC_NON_INTERLACE, current->minX, current->minY, current->maxX, current->maxY); + + for (int i = 0; i < 2; ++i) { + if ((current->portalRenderType & (PORTAL_RENDER_TYPE_VISIBLE(i) | PORTAL_RENDER_TYPE_ENABLED(i))) == PORTAL_RENDER_TYPE_VISIBLE(i)) { + float portalTransform[4][4]; + struct Portal* portal = &scene->portals[i]; + portalDetermineTransform(portal, portalTransform); + portalRenderCover(portal, portalTransform, renderState); + } + } + + staticRender(¤t->camera.transform, ¤t->cameraMatrixInfo.cullingInformation, current->visiblerooms, renderState); + + if (current->previousProperties && (current->previousProperties->portalRenderType & PORTAL_RENDER_TYPE_VISIBLE(1 - current->exitPortalIndex))) { + gSPMatrix(renderState->dl++, osVirtualToPhysical(current->previousProperties->cameraMatrixInfo.projectionView), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + gSPPerspNormalize(renderState->dl++, current->previousProperties->cameraMatrixInfo.perspectiveNormalize); + gDPSetScissor(renderState->dl++, G_SC_NON_INTERLACE, current->previousProperties->minX, current->previousProperties->minY, current->previousProperties->maxX, current->previousProperties->maxY); + gSPViewport(renderState->dl++, current->previousProperties->viewport); + + float portalTransform[4][4]; + struct Portal* portal = &scene->portals[i]; + portalDetermineTransform(portal, portalTransform); + + // render the front portal cover + Mtx* matrix = renderStateRequestMatrices(renderState, 1); + + if (!matrix) { + return; + } + + guMtxF2L(portalTransform, matrix); + gSPMatrix(renderState->dl++, matrix, G_MTX_MODELVIEW | G_MTX_PUSH | G_MTX_MUL); + + gDPSetEnvColor(renderState->dl++, 255, 255, 255, portal->opacity < 0.0f ? 0 : (portal->opacity > 1.0f ? 255 : (u8)(portal->opacity * 255.0f))); + + if (portal->flags & PortalFlagsOddParity) { + gSPDisplayList(renderState->dl++, portal_portal_blue_face_model_gfx); + portalRenderScreenCover(current->clipper.nearPolygon, current->clipper.nearPolygonCount, current, renderState); + gDPPipeSync(renderState->dl++); + + gSPDisplayList(renderState->dl++, portal_portal_blue_model_gfx); + } else { + gSPDisplayList(renderState->dl++, portal_portal_orange_face_model_gfx); + portalRenderScreenCover(current->clipper.nearPolygon, current->clipper.nearPolygonCount, current, renderState); + gDPPipeSync(renderState->dl++); + + gSPDisplayList(renderState->dl++, portal_portal_orange_model_gfx); + } + + gSPPopMatrix(renderState->dl++, G_MTX_MODELVIEW); + } + } +} \ No newline at end of file diff --git a/src/scene/render_plan.h b/src/scene/render_plan.h new file mode 100644 index 0000000..c3c5f3b --- /dev/null +++ b/src/scene/render_plan.h @@ -0,0 +1,17 @@ +#ifndef __SCENE_RENDER_PLAN_H__ +#define __SCENE_RENDER_PLAN_H__ + +#include "./scene.h" + +#define MAX_PORTAL_STEPS 6 + +struct RenderPlan { + struct RenderProps stageProps[MAX_PORTAL_STEPS]; + short stageCount; +}; + +void renderPlanBuild(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderState* renderState); + +void renderPlanExecute(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderState* renderState); + +#endif \ No newline at end of file diff --git a/src/scene/scene.c b/src/scene/scene.c index 69b47a4..206052d 100644 --- a/src/scene/scene.c +++ b/src/scene/scene.c @@ -130,7 +130,7 @@ void sceneRenderWithProperties(void* data, struct RenderProps* properties, struc for (int i = 0; i < 2; ++i) { if (gCollisionScene.portalTransforms[closerPortal] && - properties->fromPortalIndex != closerPortal && + properties->exitPortalIndex != closerPortal && staticRenderIsRoomVisible(visibleRooms, gCollisionScene.portalRooms[closerPortal])) { portalRender( &scene->portals[closerPortal],