From 03b703d454c34257a1113961a27cd3fc4437a492 Mon Sep 17 00:00:00 2001 From: Miloslav Ciz Date: Mon, 9 Sep 2024 19:16:51 +0200 Subject: [PATCH] Add animation smoothing --- assets.h | 2 +- game.h | 106 ++++++++++++++++---------- racing.h | 170 ++++++++++++++++++++++++++++++++++-------- renderer.h | 214 ++++++++++++++++++++++++++--------------------------- settings.h | 16 +++- 5 files changed, 326 insertions(+), 182 deletions(-) diff --git a/assets.h b/assets.h index 2212201..5b6b117 100644 --- a/assets.h +++ b/assets.h @@ -6194,7 +6194,7 @@ void LCR_imageChangeBrightness(int up) { if (up) for (int i = 0; i < 256; ++i) - _LCR_currentImagePalette[i] |= 0x1042; + _LCR_currentImagePalette[i] |= 0x18e3; else for (int i = 0; i < 256; ++i) _LCR_currentImagePalette[i] = diff --git a/game.h b/game.h index 95dc6f7..f8c4500 100644 --- a/game.h +++ b/game.h @@ -12,10 +12,10 @@ square is LCR_GAME_UNITs long, a full angle is also LCR_GAME_UNITs. - LCR_GAME_UNIT: Size of one game square and full angle in LCR_GameUnits. - S3L_Unit: data type, small3dlib's unit. May change with renderer change. - - S3L_FRACTIONS_PER_UNIT: small3dlib's value representing 1.0. + - S3L_F: small3dlib's value representing 1.0. - LCR_RENDERER_UNIT: for the renderer one map square is this many S3L_Units. - TPE_Unit: tinyphysicsengine's unit. May change with phys. engine change. - - TPE_FRACTIONS_PER_UNIT: tinyphysicsengine's value representing value 1.0. + - TPE_F: tinyphysicsengine's value representing value 1.0. - LCR_PHYSICS_UNIT: for the phys. eng. one map square is this many TPE_Units. COORDINATE SYSTEM AND ROTATIONS: The game itself (racing module) is @@ -96,10 +96,15 @@ uint8_t LCR_gameStep(uint32_t timeMs); //------------------------------------------------------------------------------ +#define LCR_CONTROL_MODE_FREECAM 0x00 +#define LCR_CONTROL_MODE_DRIVE 0x01 + struct { uint32_t nextRenderFrameTime; uint32_t nextRacingTickTime; + uint8_t controlMode; + uint8_t debugDraw; } LCR_game; void LCR_drawPixelXYUnsafe(unsigned int x, unsigned int y, @@ -118,7 +123,9 @@ static inline void LCR_drawPixelXYSafe(unsigned int x, unsigned int y, #include "renderer.h" uint8_t LCR_keyStates[LCR_KEYS_TOTAL]; /**< Assures unchanging key states - during a single frame. */ + during a single frame, holds + number of frames for which the key + has been continuously held. */ void LCR_drawPixelXYUnsafe(unsigned int x, unsigned int y, uint16_t color) @@ -150,6 +157,9 @@ void LCR_gameInit(void) LCR_game.nextRenderFrameTime = 0; LCR_game.nextRacingTickTime = 0; + + LCR_game.controlMode = LCR_CONTROL_MODE_FREECAM; + LCR_game.debugDraw = 0; } void LCR_gameEnd(void) @@ -159,15 +169,22 @@ void LCR_gameEnd(void) uint8_t LCR_gameStep(uint32_t time) { for (int i = 0; i < LCR_KEYS_TOTAL; ++i) - LCR_keyStates[i] = LCR_keyPressed(i); + LCR_keyStates[i] = LCR_keyPressed(i) ? + (LCR_keyStates[i] < 255 ? LCR_keyStates[i] + 1 : 255) : 0; uint32_t sleep = 0; + if (LCR_keyStates[LCR_KEY_B] == 1) + LCR_game.controlMode = LCR_game.controlMode == LCR_CONTROL_MODE_FREECAM ? + LCR_CONTROL_MODE_DRIVE : LCR_CONTROL_MODE_FREECAM; + else if (LCR_keyStates[LCR_KEY_B] == 30) + LCR_game.debugDraw = !LCR_game.debugDraw; + while (time >= LCR_game.nextRacingTickTime) { unsigned int input = 0; - if (LCR_keyStates[LCR_KEY_B]) + if (LCR_game.controlMode != LCR_CONTROL_MODE_FREECAM) input = (LCR_keyStates[LCR_KEY_UP] ? LCR_RACING_INPUT_FORW : 0) | (LCR_keyStates[LCR_KEY_RIGHT] ? LCR_RACING_INPUT_RIGHT : 0) | @@ -178,56 +195,67 @@ uint8_t LCR_gameStep(uint32_t time) LCR_game.nextRacingTickTime += LCR_RACING_TICK_MS; } - if (time >= LCR_game.nextRenderFrameTime) { + + +LCR_GameUnit physicsInterpolationParam = LCR_GAME_UNIT - + ((LCR_game.nextRacingTickTime - time) * LCR_GAME_UNIT) / + LCR_RACING_TICK_MS; + LCR_GameUnit carTransform[6]; - LCR_racingGetCarTransform(carTransform,carTransform + 3); + LCR_racingGetCarTransform(carTransform,carTransform + 3, + physicsInterpolationParam); LCR_rendererSetCarTransform(carTransform,carTransform + 3); - - - LCR_rendererDraw(); - - -LCR_GameUnit sss[7]; -LCR_rendererGetCameraTransform(sss,sss + 3,sss + 6); -LCR_physicsDebugDraw(sss,sss + 3,sss[6]); - - - LCR_game.nextRenderFrameTime += 1000 / LCR_SETTING_FPS; + while (time >= LCR_game.nextRenderFrameTime) + LCR_game.nextRenderFrameTime += 1000 / LCR_SETTING_FPS; LCR_GameUnit offsets[5]; for (int i = 0; i < 5; ++i) offsets[i] = 0; - if (LCR_keyStates[LCR_KEY_A]) + if (LCR_game.controlMode == LCR_CONTROL_MODE_FREECAM) { - if (LCR_keyStates[LCR_KEY_UP]) - offsets[4] = LCR_FREE_CAMERA_TURN_STEP; - else if (LCR_keyStates[LCR_KEY_DOWN]) - offsets[4] -= LCR_FREE_CAMERA_TURN_STEP; + if (LCR_keyStates[LCR_KEY_A]) + { + if (LCR_keyStates[LCR_KEY_UP]) + offsets[4] = LCR_FREE_CAMERA_TURN_STEP; + else if (LCR_keyStates[LCR_KEY_DOWN]) + offsets[4] -= LCR_FREE_CAMERA_TURN_STEP; - if (LCR_keyStates[LCR_KEY_RIGHT]) - offsets[3] -= LCR_FREE_CAMERA_TURN_STEP; - else if (LCR_keyStates[LCR_KEY_LEFT]) - offsets[3] = LCR_FREE_CAMERA_TURN_STEP; + if (LCR_keyStates[LCR_KEY_RIGHT]) + offsets[3] -= LCR_FREE_CAMERA_TURN_STEP; + else if (LCR_keyStates[LCR_KEY_LEFT]) + offsets[3] = LCR_FREE_CAMERA_TURN_STEP; + } + else + { + if (LCR_keyStates[LCR_KEY_UP]) + offsets[0] = LCR_FREE_CAMERA_STEP; + else if (LCR_keyStates[LCR_KEY_DOWN]) + offsets[0] -= LCR_FREE_CAMERA_STEP; + + if (LCR_keyStates[LCR_KEY_RIGHT]) + offsets[1] = LCR_FREE_CAMERA_STEP; + else if (LCR_keyStates[LCR_KEY_LEFT]) + offsets[1] -= LCR_FREE_CAMERA_STEP; + } + + LCR_rendererMoveCamera(offsets,offsets + 3); } - else if (!LCR_keyStates[LCR_KEY_B]) + else + LCR_rendererCameraFollow(); + + LCR_rendererDraw(); + + if (LCR_game.debugDraw) { - if (LCR_keyStates[LCR_KEY_UP]) - offsets[0] = LCR_FREE_CAMERA_STEP; - else if (LCR_keyStates[LCR_KEY_DOWN]) - offsets[0] -= LCR_FREE_CAMERA_STEP; - - if (LCR_keyStates[LCR_KEY_RIGHT]) - offsets[1] = LCR_FREE_CAMERA_STEP; - else if (LCR_keyStates[LCR_KEY_LEFT]) - offsets[1] -= LCR_FREE_CAMERA_STEP; + LCR_GameUnit camTr[7]; + LCR_rendererGetCameraTransform(camTr,camTr + 3,camTr + 6); + LCR_physicsDebugDraw(camTr,camTr + 3,camTr[6]); } - - LCR_rendererMoveCamera(offsets,offsets + 3); } else sleep = LCR_game.nextRenderFrameTime - time; diff --git a/racing.h b/racing.h index bf3954f..e2f2a15 100644 --- a/racing.h +++ b/racing.h @@ -5,16 +5,16 @@ #ifndef _LCR_RACING_H #define _LCR_RACING_H -typedef int32_t LCR_GameUnit; ///< abstract game unit +typedef int32_t LCR_GameUnit; ///< abstract game unit -#define LCR_GAME_UNIT 1024 ///< length of map square in LCR_GameUnits +#define LCR_GAME_UNIT 1024 ///< length of map square in LCR_GameUnits #define LCR_RACING_INPUT_FORW 0x01 #define LCR_RACING_INPUT_RIGHT 0x02 #define LCR_RACING_INPUT_BACK 0x04 #define LCR_RACING_INPUT_LEFT 0x08 -#define LCR_PHYSICS_UNIT 1024 ///< length of map square for physics engine +#define LCR_PHYSICS_UNIT 512 ///< length of map square for physics engine #include "map.h" #include "tinyphysicsengine.h" @@ -22,15 +22,35 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit #define LCR_CAR_JOINTS 5 #define LCR_CAR_CONNECTIONS 10 +#define LCR_CAR_FORWARD_FRICTION (TPE_F / 14) +#define LCR_CAR_TURN_FRICTION (3 * TPE_F / 4) +#define LCR_CAR_ELASTICITY (TPE_F / 100) + struct { TPE_World physicsWorld; TPE_Body carBody; TPE_Joint carJoints[LCR_CAR_JOINTS]; TPE_Connection carConnections[LCR_CAR_CONNECTIONS]; + uint8_t wheelCollisions; /**< In individual bits records for each car wheel + whether it's currently touching the ground. + Lower bits record current collisions, higher + bits the previous state (for averaging). */ + + + TPE_Vec3 carPositions[2]; ///* Current and previous position. + } LCR_racing; -TPE_Vec3 LCR_racingEnvironmentFunction(TPE_Vec3 point, TPE_Unit maxDist) +TPE_Vec3 _LCR_TPE_vec3DividePlain(TPE_Vec3 v, TPE_Unit d) +{ + v.x /= d; + v.y /= d; + v.z /= d; + return v; +} + +TPE_Vec3 _LCR_racingEnvironmentFunction(TPE_Vec3 point, TPE_Unit maxDist) { return TPE_envAABoxInside(point,TPE_vec3(0,0,0),TPE_vec3( LCR_PHYSICS_UNIT * LCR_MAP_SIZE_BLOCKS, @@ -38,6 +58,31 @@ TPE_Vec3 LCR_racingEnvironmentFunction(TPE_Vec3 point, TPE_Unit maxDist) LCR_PHYSICS_UNIT * LCR_MAP_SIZE_BLOCKS)); } +uint8_t _LCR_racingCollisionHandler(uint16_t b1, uint16_t j1, uint16_t b2, + uint16_t j2, TPE_Vec3 p) +{ + // check which wheels are touching the ground. + + if (j1 < 4) // wheel joint? + LCR_racing.wheelCollisions |= 0x01 << j1; + + return 1; +} + +/** + Initializes new run. +*/ +void LCR_racingRestart(void) +{ + LCR_racing.wheelCollisions = 0; + TPE_bodyDeactivate(&(LCR_racing.carBody)); + + // TODO +} + +/** + Initializes the racing module, only call once. +*/ void LCR_racingInit(void) { LCR_log("initializing racing engine"); @@ -49,42 +94,57 @@ void LCR_racingInit(void) (LCR_PHYSICS_UNIT * 3) / 4, LCR_PHYSICS_UNIT / 8); - LCR_racing.carJoints[4].position.y += LCR_PHYSICS_UNIT / 8; + LCR_racing.carJoints[4].position.y += LCR_PHYSICS_UNIT / 6; LCR_racing.carJoints[4].sizeDivided *= 3; LCR_racing.carJoints[4].sizeDivided /= 2; TPE_bodyInit(&(LCR_racing.carBody), LCR_racing.carJoints,LCR_CAR_JOINTS, LCR_racing.carConnections,LCR_CAR_CONNECTIONS, - TPE_FRACTIONS_PER_UNIT); + TPE_F); + + LCR_racing.carBody.friction = LCR_CAR_FORWARD_FRICTION; + LCR_racing.carBody.elasticity = LCR_CAR_ELASTICITY; TPE_worldInit(&(LCR_racing.physicsWorld), - &(LCR_racing.carBody),1,LCR_racingEnvironmentFunction); + &(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction); - TPE_bodyDeactivate(&(LCR_racing.carBody)); + LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler; + + LCR_racingRestart(); } void LCR_racingGetCarTransform(LCR_GameUnit position[3], - LCR_GameUnit rotation[3]) + LCR_GameUnit rotation[3], LCR_GameUnit interpolationParam) { - #define AVERAGE(c) \ - (((((LCR_racing.carBody.joints[0].position.c + \ - LCR_racing.carBody.joints[1].position.c + \ - LCR_racing.carBody.joints[2].position.c + \ - LCR_racing.carBody.joints[3].position.c) / 4) + \ - LCR_racing.carBody.joints[4].position.c) / 2) * \ - LCR_GAME_UNIT) / LCR_PHYSICS_UNIT - position[0] = AVERAGE(x); - position[1] = AVERAGE(y); - position[2] = AVERAGE(z); - #undef AVERAGE +#if LCR_SETTING_SMOOTH_ANIMATIONS + TPE_Vec3 v = TPE_vec3Plus( + LCR_racing.carPositions[1], + _LCR_TPE_vec3DividePlain( + TPE_vec3TimesPlain( + TPE_vec3Minus( + LCR_racing.carPositions[0],LCR_racing.carPositions[1]), + interpolationParam),LCR_GAME_UNIT)); - TPE_Vec3 rot = TPE_bodyGetRotation(&(LCR_racing.carBody),0,2,1); + position[0] = v.x; + position[1] = v.y; + position[2] = v.z; +#else + TPE_Vec3 v; - rotation[0] = (rot.x * LCR_GAME_UNIT) / TPE_FRACTIONS_PER_UNIT; - rotation[1] = (rot.y * LCR_GAME_UNIT) / TPE_FRACTIONS_PER_UNIT; - rotation[2] = (rot.z * LCR_GAME_UNIT) / TPE_FRACTIONS_PER_UNIT; + position[0] = LCR_racing.carPositions[0].x; + position[1] = LCR_racing.carPositions[0].y; + position[2] = LCR_racing.carPositions[0].z; +#endif + + v = TPE_bodyGetRotation(&(LCR_racing.carBody),0,2,1); + + rotation[0] = (v.x * LCR_GAME_UNIT) / TPE_F; + rotation[1] = (v.y * LCR_GAME_UNIT) / TPE_F; + rotation[2] = (v.z * LCR_GAME_UNIT) / TPE_F; + + // TODO: also smooth out rotation? } void _LCR_drawPhysicsDebugPixel(uint16_t x, uint16_t y, uint8_t color) @@ -100,18 +160,24 @@ void _LCR_drawPhysicsDebugPixel(uint16_t x, uint16_t y, uint8_t color) } } -// TODO: input +int LCR_racingCarWheelTouchesGround(int wheel) +{ + return ((LCR_racing.wheelCollisions & (LCR_racing.wheelCollisions >> 4)) + >> wheel) & 0x01; +} + void LCR_racingStep(unsigned int input) { + TPE_Vec3 carForw, carRight, carUp; TPE_Vec3 vel = TPE_vec3(0,0,0); if (input) { if (input & LCR_RACING_INPUT_FORW) - vel.z = LCR_PHYSICS_UNIT / 32; + vel.y = LCR_PHYSICS_UNIT / 8; if (input & LCR_RACING_INPUT_BACK) - vel.y = LCR_PHYSICS_UNIT / 32; + vel.z = LCR_PHYSICS_UNIT / 32; if (input & LCR_RACING_INPUT_RIGHT) vel.x = LCR_PHYSICS_UNIT / 32; @@ -123,10 +189,52 @@ void LCR_racingStep(unsigned int input) } TPE_bodyApplyGravity(&(LCR_racing.carBody), - TPE_FRACTIONS_PER_UNIT / 32 + TPE_F / 32 ); + LCR_racing.wheelCollisions <<= 4; TPE_worldStep(&(LCR_racing.physicsWorld)); + + carForw = TPE_vec3Normalized(TPE_vec3Plus( + TPE_vec3Minus(LCR_racing.carBody.joints[2].position, + LCR_racing.carBody.joints[0].position), + TPE_vec3Minus(LCR_racing.carBody.joints[3].position, + LCR_racing.carBody.joints[1].position))); + + carRight = TPE_vec3Normalized(TPE_vec3Plus( + TPE_vec3Minus(LCR_racing.carBody.joints[1].position, + LCR_racing.carBody.joints[0].position), + TPE_vec3Minus(LCR_racing.carBody.joints[3].position, + LCR_racing.carBody.joints[2].position))); + + carUp = TPE_vec3Cross(carForw,carRight); + + if (TPE_vec3Dot(carUp,TPE_vec3Minus(LCR_racing.carBody.joints[4].position, + LCR_racing.carBody.joints[0].position)) < 0) + { + /* if the car falls on its roof the center joint may flip to the other + side, here we fix it */ + +// LCR_log("car flipped over, fixing"); + + LCR_racing.carBody.joints[4].position = TPE_vec3Plus(TPE_vec3Times(carUp, + LCR_GAME_UNIT / 4),LCR_racing.carBody.joints[4].position); + } + + LCR_racing.carPositions[1] = LCR_racing.carPositions[0]; + + #define AVERAGE(c) \ + (((((LCR_racing.carBody.joints[0].position.c + \ + LCR_racing.carBody.joints[1].position.c + \ + LCR_racing.carBody.joints[2].position.c + \ + LCR_racing.carBody.joints[3].position.c) / 4) + \ + LCR_racing.carBody.joints[4].position.c) / 2) * \ + LCR_GAME_UNIT) / LCR_PHYSICS_UNIT + + LCR_racing.carPositions[0].x = AVERAGE(x); + LCR_racing.carPositions[0].y = AVERAGE(y); + LCR_racing.carPositions[0].z = AVERAGE(z); + #undef AVERAGE } void LCR_physicsDebugDraw(LCR_GameUnit camPos[3], LCR_GameUnit camRot[2], @@ -138,13 +246,13 @@ void LCR_physicsDebugDraw(LCR_GameUnit camPos[3], LCR_GameUnit camRot[2], cPos.y = (camPos[1] * LCR_PHYSICS_UNIT) / LCR_GAME_UNIT; cPos.z = (camPos[2] * LCR_PHYSICS_UNIT) / LCR_GAME_UNIT; - cRot.x = (camRot[0] * TPE_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT; - cRot.y = (camRot[1] * TPE_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT; + cRot.x = (camRot[0] * TPE_F) / LCR_GAME_UNIT; + cRot.y = (camRot[1] * TPE_F) / LCR_GAME_UNIT; cRot.z = 0; cView.x = LCR_EFFECTIVE_RESOLUTION_X; cView.y = LCR_EFFECTIVE_RESOLUTION_Y; - cView.z = (camFov * TPE_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT; + cView.z = (camFov * TPE_F) / LCR_GAME_UNIT; TPE_worldDebugDraw(&(LCR_racing.physicsWorld),_LCR_drawPhysicsDebugPixel, cPos,cRot,cView,16,2 * LCR_PHYSICS_UNIT); diff --git a/renderer.h b/renderer.h index 88f5214..8212702 100644 --- a/renderer.h +++ b/renderer.h @@ -17,8 +17,8 @@ #include "small3dlib.h" /// Renderer specific unit, length of one map square. -#define LCR_RENDERER_UNIT (S3L_FRACTIONS_PER_UNIT / 2) - // ^ just S3L_FRACTIONS_PER_UNIT leaves some tris bugging +#define LCR_RENDERER_UNIT (S3L_F / 2) + // ^ just S3L_F leaves some tris bugging #define LCR_RENDERER_CHUNK_RESOLUTION 4 // do not change #define LCR_RENDERER_LOD_BLOCKS 64 // do not change @@ -29,7 +29,7 @@ #define LCR_RENDERER_CHUNKS_TOTAL (LCR_RENDERER_CHUNK_RESOLUTION * \ LCR_RENDERER_CHUNK_RESOLUTION * LCR_RENDERER_CHUNK_RESOLUTION) -#define LCR_RENDERER_MODEL_COUNT 9 +#define LCR_RENDERER_MODEL_COUNT 10 #define LCR_RENDERER_CAR_SCALE (LCR_RENDERER_UNIT / 4) @@ -37,7 +37,8 @@ struct { S3L_Scene scene; S3L_Model3D mapModel; ///< whole map model - S3L_Model3D *carModel; + S3L_Model3D *carModel; + S3L_Model3D *ghostModel; // TODO: ghostModel @@ -45,6 +46,7 @@ struct The scene model array. 0, 1, 2, 3, 4, 5, 6, 7: nearest map chunk models 8: car model + 9: ghost model */ S3L_Model3D models[LCR_RENDERER_MODEL_COUNT]; @@ -70,11 +72,6 @@ struct */ uint8_t gridOfLODs[LCR_RENDERER_LOD_BLOCKS]; - // pixel function precomputed values: - uint32_t previousTriID; - int triUVs[6]; - uint8_t texSubsampleCount; - #if LCR_ANIMATE_CAR S3L_Unit wheelRotation; S3L_Unit wheelTurn; @@ -82,6 +79,13 @@ struct S3L_Unit animatedCarVerts[LCR_CAR_VERTEX_COUNT * 3]; #endif + // pixel function precomputed values: + uint32_t previousTriID; + int triUVs[6]; + int texSubsampleCount; + unsigned int flatAndTransparent; /**< If non-zero, transparent (dithered) + polygons will be drawn without texture, + with color stored in this variable. */ } LCR_renderer; void LCR_rendererSetCarTransform(LCR_GameUnit position[3], @@ -95,11 +99,11 @@ void LCR_rendererSetCarTransform(LCR_GameUnit position[3], (position[2] * LCR_RENDERER_UNIT) / LCR_GAME_UNIT; LCR_renderer.carModel->transform.rotation.x = S3L_wrap((rotation[0] * - S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT,S3L_FRACTIONS_PER_UNIT); + S3L_F) / LCR_GAME_UNIT,S3L_F); LCR_renderer.carModel->transform.rotation.y = S3L_wrap((rotation[1] * - S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT,S3L_FRACTIONS_PER_UNIT); + S3L_F) / LCR_GAME_UNIT,S3L_F); LCR_renderer.carModel->transform.rotation.z = S3L_wrap((rotation[2] * - S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT,S3L_FRACTIONS_PER_UNIT); + S3L_F) / LCR_GAME_UNIT,S3L_F); } void _LCR_pixelFuncc3D(S3L_PixelInfo *pixel) @@ -108,12 +112,18 @@ void _LCR_pixelFuncc3D(S3L_PixelInfo *pixel) if (pixel->triangleID != LCR_renderer.previousTriID) { LCR_renderer.previousTriID = pixel->triangleID; + LCR_renderer.flatAndTransparent = 0; #if LCR_SETTING_TEXTURE_SUBSAMPLE != 0 LCR_renderer.texSubsampleCount = 0; #endif - if (pixel->modelIndex == 8) + if (pixel->modelIndex == 9) + { + // car ghost model + LCR_renderer.flatAndTransparent = LCR_SETTING_GHOST_COLOR; + } + else if (pixel->modelIndex == 8) { // car model @@ -197,8 +207,15 @@ void _LCR_pixelFuncc3D(S3L_PixelInfo *pixel) } } - uint16_t color; + if (LCR_renderer.flatAndTransparent) + { + if (pixel->x % 2 == pixel->y % 2) + LCR_drawPixelXYUnsafe(pixel->x,pixel->y,LCR_renderer.flatAndTransparent); + + return; + } + uint16_t color; #if LCR_SETTING_TEXTURE_SUBSAMPLE != 0 if (LCR_renderer.texSubsampleCount == 0) @@ -214,11 +231,11 @@ void _LCR_pixelFuncc3D(S3L_PixelInfo *pixel) (barycentric[0] * LCR_renderer.triUVs[0] + barycentric[1] * LCR_renderer.triUVs[2] + barycentric[2] * LCR_renderer.triUVs[4]) - / (S3L_FRACTIONS_PER_UNIT / 8), + / (S3L_F / 8), (barycentric[0] * LCR_renderer.triUVs[1] + barycentric[1] * LCR_renderer.triUVs[3] + barycentric[2] * LCR_renderer.triUVs[5]) - / (S3L_FRACTIONS_PER_UNIT / 8)); + / (S3L_F / 8)); #if LCR_SETTING_TEXTURE_SUBSAMPLE != 0 LCR_renderer.texSubsampleCount = LCR_SETTING_TEXTURE_SUBSAMPLE; @@ -755,6 +772,7 @@ uint8_t LCR_rendererInit(void) _LCR_rendererComputeLOD(); LCR_renderer.carModel = LCR_renderer.models + 8; + LCR_renderer.ghostModel = LCR_renderer.models + 9; S3L_model3DInit( #if LCR_ANIMATE_CAR @@ -766,6 +784,20 @@ uint8_t LCR_rendererInit(void) LCR_carTriangles,LCR_CAR_TRIANGLE_COUNT, LCR_renderer.carModel); + S3L_vec4Set(&(LCR_renderer.carModel->transform.scale), + LCR_RENDERER_CAR_SCALE,LCR_RENDERER_CAR_SCALE,LCR_RENDERER_CAR_SCALE,0); + + S3L_model3DInit( + LCR_carVertices + ,LCR_CAR_VERTEX_COUNT, + LCR_carTriangles,LCR_CAR_TRIANGLE_COUNT, + LCR_renderer.ghostModel); + + LCR_renderer.ghostModel->transform.scale = + LCR_renderer.carModel->transform.scale; + +LCR_renderer.ghostModel->transform.translation.x -= LCR_GAME_UNIT / 4; + #if LCR_ANIMATE_CAR for (int i = 0; i < LCR_CAR_VERTEX_COUNT * 3; ++i) LCR_renderer.animatedCarVerts[i] = LCR_carVertices[i]; @@ -799,10 +831,6 @@ uint8_t LCR_rendererInit(void) LCR_renderer.wheelTurn = 0; #endif - LCR_renderer.carModel->transform.scale.x = LCR_RENDERER_CAR_SCALE; - LCR_renderer.carModel->transform.scale.y = LCR_RENDERER_CAR_SCALE; - LCR_renderer.carModel->transform.scale.z = LCR_RENDERER_CAR_SCALE; - S3L_sceneInit( LCR_renderer.models,LCR_RENDERER_MODEL_COUNT,&LCR_renderer.scene); @@ -820,14 +848,14 @@ void LCR_rendererGetCameraTransform(LCR_GameUnit position[3], LCR_GAME_UNIT) / LCR_RENDERER_UNIT; rotation[0] = (LCR_renderer.scene.camera.transform.rotation.x * - LCR_GAME_UNIT) / S3L_FRACTIONS_PER_UNIT; + LCR_GAME_UNIT) / S3L_F; rotation[1] = (LCR_renderer.scene.camera.transform.rotation.y * - LCR_GAME_UNIT) / S3L_FRACTIONS_PER_UNIT; + LCR_GAME_UNIT) / S3L_F; rotation[2] = (LCR_renderer.scene.camera.transform.rotation.z * - LCR_GAME_UNIT) / S3L_FRACTIONS_PER_UNIT; + LCR_GAME_UNIT) / S3L_F; *fov = (LCR_renderer.scene.camera.focalLength * LCR_GAME_UNIT) - / S3L_FRACTIONS_PER_UNIT; + / S3L_F; } void LCR_rendererMoveCamera(LCR_GameUnit forwRightUpOffset[3], @@ -836,29 +864,29 @@ void LCR_rendererMoveCamera(LCR_GameUnit forwRightUpOffset[3], S3L_Vec4 f, r, u; S3L_rotationToDirections(LCR_renderer.scene.camera.transform.rotation, - S3L_FRACTIONS_PER_UNIT,&f,&r,&u); + S3L_F,&f,&r,&u); LCR_renderer.scene.camera.transform.translation.x += ((f.x * forwRightUpOffset[0] + r.x * forwRightUpOffset[1] + - u.x * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT; + u.x * forwRightUpOffset[2]) * S3L_F) / LCR_GAME_UNIT; LCR_renderer.scene.camera.transform.translation.y += ((f.y * forwRightUpOffset[0] + r.y * forwRightUpOffset[1] + - u.y * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT; + u.y * forwRightUpOffset[2]) * S3L_F) / LCR_GAME_UNIT; LCR_renderer.scene.camera.transform.translation.z += ((f.z * forwRightUpOffset[0] + r.z * forwRightUpOffset[1] + - u.z * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT; + u.z * forwRightUpOffset[2]) * S3L_F) / LCR_GAME_UNIT; LCR_renderer.scene.camera.transform.rotation.y = S3L_wrap( LCR_renderer.scene.camera.transform.rotation.y + - (yawPitchOffset[0] * S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT, - S3L_FRACTIONS_PER_UNIT); + (yawPitchOffset[0] * S3L_F) / LCR_GAME_UNIT, + S3L_F); LCR_renderer.scene.camera.transform.rotation.x = S3L_clamp( LCR_renderer.scene.camera.transform.rotation.x + - (yawPitchOffset[1] * S3L_FRACTIONS_PER_UNIT) / LCR_GAME_UNIT, - -1 * S3L_FRACTIONS_PER_UNIT / 4,S3L_FRACTIONS_PER_UNIT / 4); + (yawPitchOffset[1] * S3L_F) / LCR_GAME_UNIT, + -1 * S3L_F / 4,S3L_F / 4); #define chk(o,c,l) \ if (LCR_renderer.scene.camera.transform.translation.c o l) \ @@ -976,7 +1004,7 @@ void _LCR_rendererDrawLODBlock(int blockX, int blockY, int blockZ, unsigned int /** Draws background sky, offsets are in multiples of screen dimensions - (e.g. S3L_FRACTIONS_PER_UNIT / 2 for offsetH means half the screen width). + (e.g. S3L_F / 2 for offsetH means half the screen width). */ void LCR_rendererDrawSky(int sky, S3L_Unit offsetH, S3L_Unit offsetV) { @@ -993,7 +1021,7 @@ void LCR_rendererDrawSky(int sky, S3L_Unit offsetH, S3L_Unit offsetV) bottomColor = LCR_sampleImage(LCR_IMAGE_SIZE - 1,LCR_IMAGE_SIZE - 1); anchorPoint[0] = ((LCR_EFFECTIVE_RESOLUTION_X * offsetH) - / S3L_FRACTIONS_PER_UNIT) % + / S3L_F) % (2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE); if (anchorPoint[0] < 0) @@ -1001,7 +1029,7 @@ void LCR_rendererDrawSky(int sky, S3L_Unit offsetH, S3L_Unit offsetV) anchorPoint[1] = (LCR_EFFECTIVE_RESOLUTION_Y) / 3 - // 3: we place the center a bit more up - (LCR_EFFECTIVE_RESOLUTION_Y * offsetV) / S3L_FRACTIONS_PER_UNIT + (LCR_EFFECTIVE_RESOLUTION_Y * offsetV) / S3L_F - LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE; pixelIndex = 0; @@ -1266,8 +1294,8 @@ void _LCR_rendererAnimateCar(void) LCR_renderer.wheelRotationCenters[offset + 1]; tmp = v[0]; - v[0] = (v[0] * c - v[1] * s) / S3L_FRACTIONS_PER_UNIT; - v[1] = (tmp * s + v[1] * c) / S3L_FRACTIONS_PER_UNIT; + v[0] = (v[0] * c - v[1] * s) / S3L_F; + v[1] = (tmp * s + v[1] * c) / S3L_F; LCR_renderer.animatedCarVerts[index + 2] = v[0] + LCR_renderer.wheelRotationCenters[offset]; @@ -1282,12 +1310,12 @@ void _LCR_rendererAnimateCar(void) LCR_renderer.animatedCarVerts[index + 2] -= (LCR_renderer.animatedCarVerts[index] * LCR_renderer.wheelTurn) - / (8 * S3L_FRACTIONS_PER_UNIT); + / (8 * S3L_F); LCR_renderer.animatedCarVerts[index] += ((LCR_renderer.animatedCarVerts[index + 2] - LCR_renderer.wheelRotationCenters[0]) * LCR_renderer.wheelTurn) - / (2 * S3L_FRACTIONS_PER_UNIT); + / (2 * S3L_F); } } } @@ -1296,86 +1324,57 @@ void _LCR_rendererAnimateCar(void) void LCR_rendererCameraFollow(void) { + S3L_Transform3D transPrev = + LCR_renderer.scene.camera.transform; - -/* LCR_renderer.scene.camera.transform.translation.y = - LCR_renderer.carModel->transform.translation.y + LCR_RENDERER_UNIT; -*/ - -/* - TPE_Vec3 cPos = TPE_vec3KeepWithinDistanceBand( - TPE_vec3( - s3l_scene.camera.transform.translation.x, - s3l_scene.camera.transform.translation.y, - s3l_scene.camera.transform.translation.z - ),carBody->joints[4].position,4 * TPE_F,6 * TPE_F); - - s3l_scene.camera.transform.translation.x = cPos.x; - s3l_scene.camera.transform.translation.y = cPos.y; - s3l_scene.camera.transform.translation.z = cPos.z; -*/ - - -/* - S3L_Unit tmp = - LCR_renderer.scene.camera.transform.translation.y - - LCR_renderer.carModel->transform.translation.y; - - tmp -= S3L_clamp(tmp, - (LCR_SETTING_CAMERA_HEIGHT - - LCR_SETTING_CAMERA_HEIGHT_BAND) * LCR_GAME_UNIT / 8, - (LCR_SETTING_CAMERA_HEIGHT + - LCR_SETTING_CAMERA_HEIGHT_BAND) * LCR_GAME_UNIT / 8); - - LCR_renderer.scene.camera.transform.translation.y -= tmp; -*/ - - LCR_renderer.scene.camera.transform.translation.y = - S3L_clamp( - LCR_renderer.scene.camera.transform.translation.y, - LCR_renderer.carModel->transform.translation.y + - (LCR_SETTING_CAMERA_HEIGHT - LCR_SETTING_CAMERA_HEIGHT_BAND) * - LCR_GAME_UNIT / 8, - LCR_renderer.carModel->transform.translation.y + - (LCR_SETTING_CAMERA_HEIGHT + LCR_SETTING_CAMERA_HEIGHT_BAND) * - LCR_GAME_UNIT / 8); - - LCR_renderer.scene.camera.transform.translation.x = - S3L_clamp( - LCR_renderer.scene.camera.transform.translation.x, - LCR_renderer.carModel->transform.translation.x - - LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_GAME_UNIT / 4, - LCR_renderer.carModel->transform.translation.x + - LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_GAME_UNIT / 4); - - LCR_renderer.scene.camera.transform.translation.z = - S3L_clamp( - LCR_renderer.scene.camera.transform.translation.z, - LCR_renderer.carModel->transform.translation.z - - LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_GAME_UNIT / 4, - LCR_renderer.carModel->transform.translation.z + - LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_GAME_UNIT / 4); - + S3L_clamp( + LCR_renderer.scene.camera.transform.translation.y, + LCR_renderer.carModel->transform.translation.y + + (LCR_SETTING_CAMERA_HEIGHT - LCR_SETTING_CAMERA_HEIGHT_BAND) * + LCR_RENDERER_UNIT / 8, + LCR_renderer.carModel->transform.translation.y + + (LCR_SETTING_CAMERA_HEIGHT + LCR_SETTING_CAMERA_HEIGHT_BAND) * + LCR_RENDERER_UNIT / 8); + LCR_renderer.scene.camera.transform.translation.x = + S3L_clamp( + LCR_renderer.scene.camera.transform.translation.x, + LCR_renderer.carModel->transform.translation.x - + LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_RENDERER_UNIT / 4, + LCR_renderer.carModel->transform.translation.x + + LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_RENDERER_UNIT / 4); + LCR_renderer.scene.camera.transform.translation.z = + S3L_clamp( + LCR_renderer.scene.camera.transform.translation.z, + LCR_renderer.carModel->transform.translation.z - + LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_RENDERER_UNIT / 4, + LCR_renderer.carModel->transform.translation.z + + LCR_SETTING_CAMERA_MAX_DISTANCE * LCR_RENDERER_UNIT / 4); S3L_lookAt(LCR_renderer.carModel->transform.translation, &(LCR_renderer.scene.camera.transform)); +#if LCR_SETTING_SMOOTH_ANIMATIONS + // now average with previous transform to smooth the animation out: + S3L_vec3Add(&(LCR_renderer.scene.camera.transform.translation), + transPrev.translation); -/* - TPE_Unit angleDiff = s3l_scene.camera.transform.rotation.y - - (TPE_vec2Angle(toCar.x,toCar.z) - 128); + LCR_renderer.scene.camera.transform.translation.x /= 2; + LCR_renderer.scene.camera.transform.translation.y /= 2; + LCR_renderer.scene.camera.transform.translation.z /= 2; - s3l_scene.camera.transform.rotation.y -= - (angleDiff < 100 && angleDiff > -100) ? angleDiff / 2 : angleDiff; -*/ + transPrev.rotation.x -= LCR_renderer.scene.camera.transform.rotation.x; + transPrev.rotation.y -= LCR_renderer.scene.camera.transform.rotation.y; + if (S3L_abs(transPrev.rotation.x) < S3L_F / 4) + LCR_renderer.scene.camera.transform.rotation.x += transPrev.rotation.x / 2; + + LCR_renderer.scene.camera.transform.rotation.y += transPrev.rotation.y / 2; +#endif } - - void LCR_rendererDraw(void) { LCR_renderer.previousTriID = -1; @@ -1394,12 +1393,11 @@ LCR_renderer.wheelTurn = S3L_sin(LCR_renderer.frame * 4); _LCR_rendererAnimateCar(); #endif -LCR_rendererCameraFollow(); - LCR_drawLevelFloor(); LCR_rendererDrawLOD(); S3L_drawScene(LCR_renderer.scene); + LCR_renderer.frame++; } diff --git a/settings.h b/settings.h index 6b81e7c..8d8fbe0 100644 --- a/settings.h +++ b/settings.h @@ -102,18 +102,28 @@ #ifndef LCR_SETTING_CAMERA_HEIGHT /** Base height of the car follow camera, in 4ths of map block height. */ - #define LCR_SETTING_CAMERA_HEIGHT 2 + #define LCR_SETTING_CAMERA_HEIGHT 4 #endif #ifndef LCR_SETTING_CAMERA_HEIGHT_BAND /** Size of height band of the follow camera, in same units as base height. */ - #define LCR_SETTING_CAMERA_HEIGHT_BAND 1 + #define LCR_SETTING_CAMERA_HEIGHT_BAND 2 #endif #ifndef LCR_SETTING_CAMERA_MAX_DISTANCE /** Maximum horizontal distance of the car follow camera, in 4ths of map block width. */ - #define LCR_SETTING_CAMERA_MAX_DISTANCE 2 + #define LCR_SETTING_CAMERA_MAX_DISTANCE 6 +#endif + +#ifndef LCR_SETTING_GHOST_COLOR + /** Color of the ghost car (in RGB565). */ + #define LCR_SETTING_GHOST_COLOR 0xff00 +#endif + +#ifndef LCR_SETTING_SMOOTH_ANIMATIONS + /** Whether to smooth out animations (car physics, camera movement etc.). */ + #define LCR_SETTING_SMOOTH_ANIMATIONS 1 #endif #endif // guard