diff --git a/Makefile b/Makefile index cdb736a..9238fa9 100644 --- a/Makefile +++ b/Makefile @@ -95,6 +95,7 @@ src/models/sphere.h src/models/sphere_geo.inc.h: assets/fbx/Sphere.fbx portal_pak_dir: vpk/portal_pak_dir.vpk vpk -x portal_pak_dir vpk/portal_pak_dir.vpk + vpk -x portal_pak_dir vpk/hl2/hl2_sound_misc_dir.vpk TEXTURE_SCRIPTS = $(shell find assets/ -type f -name '*.ims') @@ -164,9 +165,11 @@ MODEL_LIST = assets/models/cube/cube.blend \ assets/models/props/round_elevator_collision.blend \ assets/models/portal/portal_blue.blend \ assets/models/portal/portal_blue_filled.blend \ + assets/models/portal/portal_blue_face.blend \ assets/models/portal/portal_orange.blend \ assets/models/portal/portal_orange_filled.blend \ - assets/models/portal/portal_face.blend + assets/models/portal/portal_orange_face.blend + MODEL_HEADERS = $(MODEL_LIST:%.blend=build/%.h) MODEL_OBJECTS = $(MODEL_LIST:%.blend=build/%_geo.o) diff --git a/README.md b/README.md index 7226985..84d145a 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,14 @@ Where `/home/james/Blender/blender-2.93.1-linux-x64` is the folder where Blender ## Current TODO list -- [x] Implement "Elevator" +- [ ] Turn level indicator board into a game object +- [ ] Presort portal gun polygon order - [ ] Implement level transitions - Implement loading levels from the cartridge -- [x] Implement "Emancipation grid" - [ ] Change the way player standing logic works -- [x] Cut holes in portal walls - [ ] Cube dispenser - [ ] NAN in overlap -- [ ] Get an optimized build working +- [x] Implement "Elevator" +- [x] Implement "Emancipation grid" +- [x] Cut holes in portal walls +- [x] Get an optimized build working diff --git a/assets/materials/static.skm.yaml b/assets/materials/static.skm.yaml index c8617ed..a44cd7e 100644 --- a/assets/materials/static.skm.yaml +++ b/assets/materials/static.skm.yaml @@ -364,10 +364,14 @@ materials: clear: [G_CULL_BACK, G_CULL_FRONT] z_update: + gDPSetCycleType: G_CYC_1CYCLE gDPSetCombineMode: - color: ["0", "0", "0", "PRIMITIVE"] - alpha: ["0", "0", "0", "PRIMITIVE"] + color: ["0", "0", "0", "SHADE"] + alpha: ["0", "0", "0", "ENVIRONMENT"] + gSPGeometryMode: + clear: [G_LIGHTING] + set: [G_SHADE] gDPSetRenderMode: flags: - Z_CMP diff --git a/assets/models/portal/portal_blue_face.blend b/assets/models/portal/portal_blue_face.blend new file mode 100644 index 0000000..6205914 Binary files /dev/null and b/assets/models/portal/portal_blue_face.blend differ diff --git a/assets/models/portal/portal_face.flags b/assets/models/portal/portal_blue_face.flags similarity index 100% rename from assets/models/portal/portal_face.flags rename to assets/models/portal/portal_blue_face.flags diff --git a/assets/models/portal/portal_face.blend b/assets/models/portal/portal_face.blend deleted file mode 100644 index b1e8ec7..0000000 Binary files a/assets/models/portal/portal_face.blend and /dev/null differ diff --git a/assets/models/portal/portal_orange_face.blend b/assets/models/portal/portal_orange_face.blend new file mode 100644 index 0000000..1df333f Binary files /dev/null and b/assets/models/portal/portal_orange_face.blend differ diff --git a/assets/models/portal/portal_orange_face.flags b/assets/models/portal/portal_orange_face.flags new file mode 100644 index 0000000..46ea02d --- /dev/null +++ b/assets/models/portal/portal_orange_face.flags @@ -0,0 +1 @@ +-m assets/materials/static.skm.yaml \ No newline at end of file diff --git a/src/graphics/screen_clipper.c b/src/graphics/screen_clipper.c index a989fa0..c36fa7c 100644 --- a/src/graphics/screen_clipper.c +++ b/src/graphics/screen_clipper.c @@ -3,6 +3,7 @@ #include "../scene/camera.h" #include "../math/vector4.h" #include "../math/matrix.h" +#include "../math/mathf.h" #include "./graphics.h" #include @@ -86,6 +87,9 @@ unsigned screenClipperClipBoundary(struct ScreenClipper* clipper, struct Vector4 return outputPointCount; } +// 6 is actually 1.5 pixels since it is a fixed point number with 2 decimal points +#define PIXEL_EXPAND_COUNT 6 + void screenClipperBoundingPoints(struct ScreenClipper* clipper, struct Vector3* input, unsigned pointCount, struct Box2D* output) { vector2Scale(&gOneVec2, -1.0f, &output->max); output->min = gOneVec2; @@ -115,7 +119,32 @@ void screenClipperBoundingPoints(struct ScreenClipper* clipper, struct Vector3* clipper->nearPolygon[i].y = (short)(point->y * invW * (SCREEN_HT << 1) + (SCREEN_HT << 1)); } - // TODO expand the polygon a few pixels + // expand the polygon a few pixels + for (int i = 0; i < clipper->nearPolygonCount; ++i) { + struct Vector2s16* curr = &clipper->nearPolygon[i]; + struct Vector2s16* next = &clipper->nearPolygon[(i + 1) % clipper->nearPolygonCount]; + + struct Vector2s16 offset; + vector2s16Sub(next, curr, &offset); + + if (abs(offset.x) > abs(offset.y)) { + if (curr->x < next->x) { + curr->x -= PIXEL_EXPAND_COUNT; + next->x += PIXEL_EXPAND_COUNT; + } else { + curr->x += PIXEL_EXPAND_COUNT; + next->y -= PIXEL_EXPAND_COUNT; + } + } else { + if (curr->y < next->y) { + curr->y -= PIXEL_EXPAND_COUNT; + next->y += PIXEL_EXPAND_COUNT; + } else { + curr->y += PIXEL_EXPAND_COUNT; + curr->y -= PIXEL_EXPAND_COUNT; + } + } + } for (unsigned i = 0; i < pointCount; ++i) { screenClipperIncludePoint(&clipBuffer[i], output); diff --git a/src/levels/static_render.c b/src/levels/static_render.c index c6ad158..a7d329d 100644 --- a/src/levels/static_render.c +++ b/src/levels/static_render.c @@ -6,8 +6,6 @@ #include "../graphics/render_scene.h" #include "../math/mathf.h" -int gForceRenderStaticIndex = -1; - void staticRenderPopulateRooms(struct FrustrumCullingInformation* cullingInfo, struct RenderScene* renderScene) { int currentRoom = 0; @@ -20,7 +18,7 @@ void staticRenderPopulateRooms(struct FrustrumCullingInformation* cullingInfo, s for (int i = staticRange.min; i < staticRange.max; ++i) { struct BoundingBoxs16* box = &gCurrentLevel->staticBoundingBoxes[i]; - if (i != gForceRenderStaticIndex && isOutsideFrustrum(cullingInfo, box)) { + if (isOutsideFrustrum(cullingInfo, box)) { continue; } diff --git a/src/levels/static_render.h b/src/levels/static_render.h index e390beb..3065f19 100644 --- a/src/levels/static_render.h +++ b/src/levels/static_render.h @@ -6,8 +6,6 @@ #include "scene/camera.h" #include "../scene/dynamic_scene.h" -extern int gForceRenderStaticIndex; - void staticRenderDetermineVisibleRooms(struct FrustrumCullingInformation* cullingInfo, u16 currentRoom, u64* visitedRooms); int staticRenderIsRoomVisible(u64 visibleRooms, u16 roomIndex); void staticRender(struct Transform* cameraTransform, struct FrustrumCullingInformation* cullingInfo, u64 visibleRooms, struct RenderState* renderState); diff --git a/src/player/player.c b/src/player/player.c index dee6128..0aa9545 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -32,7 +32,7 @@ struct CollisionQuad gPlayerColliderFaces[8]; struct CollisionCylinder gPlayerCollider = { 0.25f, - 0.7f, + 0.5f, gPlayerColliderEdgeVectors, sizeof(gPlayerColliderEdgeVectors) / sizeof(*gPlayerColliderEdgeVectors), gPlayerColliderFaces, diff --git a/src/scene/portal.c b/src/scene/portal.c index df5d461..81aeb30 100644 --- a/src/scene/portal.c +++ b/src/scene/portal.c @@ -8,19 +8,18 @@ #include "../physics/collision_scene.h" #include "../math/mathf.h" #include "../math/vector2s16.h" +#include "../util/time.h" #include "../build/assets/models/portal/portal_blue.h" -#include "../build/assets/models/portal/portal_orange.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" -#include "../build/assets/models/portal/portal_face.h" #define CALC_SCREEN_SPACE(clip_space, screen_size) ((clip_space + 1.0f) * ((screen_size) / 2)) -#define PORTAL_SCALE_Y 0.8f -#define PORTAL_SCALE_X 0.95f - #define PORTAL_COVER_HEIGHT 0.708084f #define PORTAL_COVER_WIDTH 0.84085f @@ -35,15 +34,18 @@ struct Vector3 gPortalOutline[PORTAL_LOOP_SIZE] = { {-0.353553f * SCENE_SCALE * PORTAL_COVER_WIDTH, 0.707107f * SCENE_SCALE * PORTAL_COVER_HEIGHT, 0}, }; +#define PORTAL_HOLE_SCALE_X 0.945f +#define PORTAL_HOLE_SCALE_Y 0.795f + struct Vector3 gPortalOutlineWorld[PORTAL_LOOP_SIZE] = { - {-0.353553f * PORTAL_SCALE_X, 0.707107f * PORTAL_SCALE_Y, 0.0f}, - {-0.5f * PORTAL_SCALE_X, 0.0f, 0.0f}, - {-0.353553f * PORTAL_SCALE_X, -0.707107f * PORTAL_SCALE_Y, 0.0f}, - {0.0f, -1.0f * PORTAL_SCALE_Y, 0.0f}, - {0.353553f * PORTAL_SCALE_X, -0.707107f * PORTAL_SCALE_Y, 0.0f}, - {0.5f * PORTAL_SCALE_X, 0.0f, 0.0f}, - {0.353553f * PORTAL_SCALE_X, 0.707107f * PORTAL_SCALE_Y, 0.0f}, - {0.0f, 1.0f * PORTAL_SCALE_Y, 0.0f}, + {-0.353553f * PORTAL_HOLE_SCALE_X, 0.707107f * PORTAL_HOLE_SCALE_Y, 0.0f}, + {-0.5f * PORTAL_HOLE_SCALE_X, 0.0f, 0.0f}, + {-0.353553f * PORTAL_HOLE_SCALE_X, -0.707107f * PORTAL_HOLE_SCALE_Y, 0.0f}, + {0.0f, -1.0f * PORTAL_HOLE_SCALE_Y, 0.0f}, + {0.353553f * PORTAL_HOLE_SCALE_X, -0.707107f * PORTAL_HOLE_SCALE_Y, 0.0f}, + {0.5f * PORTAL_HOLE_SCALE_X, 0.0f, 0.0f}, + {0.353553f * PORTAL_HOLE_SCALE_X, 0.707107f * PORTAL_HOLE_SCALE_Y, 0.0f}, + {0.0f, 1.0f * PORTAL_HOLE_SCALE_Y, 0.0f}, }; #define SHOW_EXTERNAL_VIEW 0 @@ -63,6 +65,11 @@ struct Quaternion gVerticalFlip = {0.0f, 1.0f, 0.0f, 0.0f}; #define CAMERA_CLIPPING_RADIUS 0.2f +#define PORTAL_CLIPPING_OFFSET 0.1f + +#define PORTAL_OPACITY_FADE_TIME 0.6f +#define PORTAL_GROW_TIME 0.3f + void renderPropsInit(struct RenderProps* props, struct Camera* camera, float aspectRatio, struct RenderState* renderState, u16 roomIndex) { props->camera = *camera; props->aspectRatio = aspectRatio; @@ -107,7 +114,7 @@ void renderPropsInit(struct RenderProps* props, struct Camera* camera, float asp if (collisionSceneIsTouchingSinglePortal(&projectedPoint, &portalNormal, gCollisionScene.portalTransforms[i], i)) { planeInitWithNormalAndPoint(&props->cullingInfo.clippingPlanes[5], &portalNormal, &gCollisionScene.portalTransforms[i]->position); - props->cullingInfo.clippingPlanes[5].d *= SCENE_SCALE; + props->cullingInfo.clippingPlanes[5].d = (props->cullingInfo.clippingPlanes[5].d + PORTAL_CLIPPING_OFFSET) * SCENE_SCALE; ++props->cullingInfo.usedClippingPlaneCount; props->clippingPortalIndex = i; break; @@ -249,6 +256,28 @@ void renderPropsNext(struct RenderProps* current, struct RenderProps* next, stru void portalInit(struct Portal* portal, enum PortalFlags flags) { transformInitIdentity(&portal->transform); portal->flags = flags; + portal->opacity = 1.0f; + portal->scale = 0.0f; +} + +void portalUpdate(struct Portal* portal, int isOpen) { + if (isOpen && portal->opacity > 0.0f) { + portal->opacity -= FIXED_DELTA_TIME * (1.0f / PORTAL_OPACITY_FADE_TIME); + + if (portal->opacity < 0.0f) { + portal->opacity = 0.0f; + } + } else if (!isOpen) { + portal->opacity = 1.0f; + } + + if (portal->scale < 1.0f) { + portal->scale += FIXED_DELTA_TIME * (1.0f / PORTAL_GROW_TIME); + + if (portal->scale > 1.0f) { + portal->scale = 1.0f; + } + } } void portalRenderScreenCover(struct Vector2s16* points, int pointCount, struct RenderProps* props, struct RenderState* renderState) { @@ -336,6 +365,8 @@ void portalRender(struct Portal* portal, struct Portal* otherPortal, struct Rend quatMultiply(&portal->transform.rotation, &gVerticalFlip, &finalTransform.rotation); } + vector3Scale(&gOneVec, &finalTransform.scale, portal->scale); + transformToMatrix(&finalTransform, portalTransform, SCENE_SCALE); if (props->currentDepth == 0 || !otherPortal) { @@ -391,14 +422,20 @@ void portalRender(struct Portal* portal, struct Portal* otherPortal, struct Rend guMtxF2L(portalTransform, matrix); gSPMatrix(renderState->dl++, matrix, G_MTX_MODELVIEW | G_MTX_PUSH | G_MTX_MUL); - - gSPDisplayList(renderState->dl++, portal_portal_face_model_gfx); - portalRenderScreenCover(clipper.nearPolygon, clipper.nearPolygonCount, props, renderState); - gDPPipeSync(renderState->dl++); + 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(clipper.nearPolygon, clipper.nearPolygonCount, props, renderState); + gDPPipeSync(renderState->dl++); + gSPDisplayList(renderState->dl++, portal_portal_blue_model_gfx); } else { + gSPDisplayList(renderState->dl++, portal_portal_orange_face_model_gfx); + portalRenderScreenCover(clipper.nearPolygon, clipper.nearPolygonCount, props, renderState); + gDPPipeSync(renderState->dl++); + gSPDisplayList(renderState->dl++, portal_portal_orange_model_gfx); } diff --git a/src/scene/portal.h b/src/scene/portal.h index 6589439..fe50af7 100644 --- a/src/scene/portal.h +++ b/src/scene/portal.h @@ -20,6 +20,8 @@ enum PortalFlags { struct Portal { struct Transform transform; enum PortalFlags flags; + float opacity; + float scale; }; struct RenderProps; @@ -53,7 +55,7 @@ void renderPropsInit(struct RenderProps* props, struct Camera* camera, float asp void renderPropsNext(struct RenderProps* current, struct RenderProps* next, struct Transform* fromPortal, struct Transform* toPortal, struct RenderState* renderState); 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); #endif \ No newline at end of file diff --git a/src/scene/scene.c b/src/scene/scene.c index edee521..bd3709a 100644 --- a/src/scene/scene.c +++ b/src/scene/scene.c @@ -118,13 +118,7 @@ void sceneRenderWithProperties(void* data, struct RenderProps* properties, struc otherPortal = 1 - otherPortal; } - if (properties->clippingPortalIndex != -1) { - gForceRenderStaticIndex = portalSurfaceStaticIndexForReplacement(properties->clippingPortalIndex); - } - staticRender(&properties->camera.transform, &properties->cullingInfo, visibleRooms, renderState); - - gForceRenderStaticIndex = -1; } #define SOLID_COLOR 0, 0, 0, ENVIRONMENT, 0, 0, 0, ENVIRONMENT @@ -211,6 +205,11 @@ void sceneCheckPortals(struct Scene* scene) { sceneClosePortal(scene, 1); scene->player.body.flags &= ~RigidBodyFizzled; } + + int isOpen = collisionSceneIsPortalOpen(); + + portalUpdate(&scene->portals[0], isOpen); + portalUpdate(&scene->portals[1], isOpen); } void sceneUpdatePortalListener(struct Scene* scene, int portalIndex, int listenerIndex) { @@ -294,6 +293,13 @@ int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, gCollisionScene.portalTransforms[portalIndex] = &scene->portals[portalIndex].transform; gCollisionScene.portalRooms[portalIndex] = roomIndex; + if (collisionSceneIsPortalOpen()) { + // the second portal is fully transparent right away + scene->portals[portalIndex].opacity = 0.0f; + } + + scene->portals[portalIndex].scale = 0.0f; + contactSolverCheckPortalContacts(&gContactSolver, &gCurrentLevel->collisionQuads[quadIndex]); return 1; }