Licar mod that adds a simple visual effect, a kind of ribbon light trail. diff --git a/game.h b/game.h index b1db32c..e9db9be 100644 --- a/game.h +++ b/game.h @@ -335,6 +335,7 @@ struct uint32_t frame; ///< Current frame number. uint32_t nextRenderFrameTime; ///< At which frame to render next frame. uint32_t nextRacingTickTime; ///< When to simulate next physics tick. + uint32_t nextRibbonTime; uint8_t cameraMode; uint8_t musicOn; uint8_t keyStates[LCR_KEYS_TOTAL]; /**< Assures unchanging key states @@ -1102,6 +1103,7 @@ void LCR_gameInit(int argc, const char **argv) LCR_game.musicOn = LCR_SETTING_MUSIC; LCR_game.nextRenderFrameTime = 0; LCR_game.nextRacingTickTime = 0; + LCR_game.nextRibbonTime = 0; LCR_game.cameraMode = LCR_CAMERA_MODE_DRIVE; LCR_currentMap.blockCount = 0; // means no map loaded @@ -1771,6 +1773,9 @@ uint8_t LCR_gameStep(uint32_t time) uint32_t events = paused ? 0 : LCR_racingStep(input); + if (LCR_racing.tick % LCR_SETTING_RIBBON_INTERVAL == 0) + LCR_rendererRibbonUpdate(); + #if LCR_SETTING_PARTICLES LCR_rendererSetParticles(0); diff --git a/renderer.h b/renderer.h index 735644e..6941398 100644 --- a/renderer.h +++ b/renderer.h @@ -153,6 +153,9 @@ struct #if LCR_SETTING_PARTICLES uint_fast16_t particleColor; ///< 0x0000 means no particles active. #endif + + int16_t ribbonPoints[LCR_SETTING_RIBBON_SEGMENTS * 3]; + uint8_t ribbonLastSegment; } LCR_renderer; /** @@ -200,6 +203,23 @@ void LCR_rendererSetGhostTransform(LCR_GameUnit position[3], _LCR_rendererSetModelTransform(LCR_renderer.ghostModel,position,rotation); } +void LCR_rendererRibbonUpdate(void) +{ + LCR_renderer.ribbonLastSegment = + (LCR_renderer.ribbonLastSegment + 1) % LCR_SETTING_RIBBON_SEGMENTS; + + unsigned int n = 3 * LCR_renderer.ribbonLastSegment; + + LCR_renderer.ribbonPoints[n] = + LCR_renderer.carModel->transform.translation.x / 2; + n++; + LCR_renderer.ribbonPoints[n] = + LCR_renderer.carModel->transform.translation.y / 2; + n++; + LCR_renderer.ribbonPoints[n] = + LCR_renderer.carModel->transform.translation.z / 2; +} + void LCR_rendererSetCarVisibility(uint8_t visible) { LCR_renderer.carModel->config.visible = visible; @@ -2154,6 +2174,59 @@ void LCR_rendererSetWheelState(LCR_GameUnit rotation, LCR_GameUnit steer) #endif } +void LCR_rendererDrawRibbon(void) +{ + S3L_Vec4 p, r; + int16_t *s = // this will go backwards + LCR_renderer.ribbonPoints + 3 * LCR_renderer.ribbonLastSegment + 2; + int16_t prev[3]; // previous values for interpolation: screen x, y and size + + p.x = LCR_renderer.carModel->transform.translation.x; + p.y = LCR_renderer.carModel->transform.translation.y; + p.z = LCR_renderer.carModel->transform.translation.z; + p.w = (LCR_SETTING_RIBBON_SIZE * LCR_SETTING_RESOLUTION_X) / 100; + + S3L_project3DPointToScreen(p,LCR_renderer.scene.camera,&r); + + prev[0] = r.x; + prev[1] = r.y; + prev[2] = r.w; + + for (int i = 0; i < LCR_SETTING_RIBBON_SEGMENTS; ++i) + { + p.z = *s * 2; s--; + p.y = *s * 2; s--; + p.x = *s * 2; s--; + p.w = (LCR_SETTING_RIBBON_SIZE * LCR_SETTING_RESOLUTION_X) / 100; + + // reducing size towards the tail: + p.w = (p.w * (LCR_SETTING_RIBBON_SEGMENTS - 1 - i)) + / LCR_SETTING_RIBBON_SEGMENTS; + + if (s <= LCR_renderer.ribbonPoints) // wrap to the end + s = LCR_renderer.ribbonPoints + (LCR_SETTING_RIBBON_SEGMENTS - 1) * 3 + 2; + + S3L_project3DPointToScreen(p,LCR_renderer.scene.camera,&r); + + for (int j = 0; j < LCR_SETTING_RIBBON_SUBDIV; ++j) + { + int w = prev[2] + (j * (r.w - prev[2])) / LCR_SETTING_RIBBON_SUBDIV; + int x = prev[0] + (j * (r.x - prev[0])) / LCR_SETTING_RIBBON_SUBDIV + - w / 2; + int y = prev[1] + (j * (r.y - prev[1])) / LCR_SETTING_RIBBON_SUBDIV + - w / 2; + + if (w > 0 && x > 0 && y > 0 && + x < LCR_EFFECTIVE_RESOLUTION_X && y < LCR_EFFECTIVE_RESOLUTION_Y) + LCR_rendererDrawRect(x,y,w,w,LCR_SETTING_RIBBON_COLOR,1); + } + + prev[0] = r.x; + prev[1] = r.y; + prev[2] = r.w; + } +} + void LCR_rendererDraw3D(void) { LCR_LOG2("rendering frame (start)"); @@ -2304,6 +2377,8 @@ void LCR_rendererDraw3D(void) #endif + LCR_rendererDrawRibbon(); + LCR_LOG2("3D rendering (end)"); LCR_LOG2("rendering frame (end)"); } diff --git a/settings.h b/settings.h index 332df45..47e27f8 100644 --- a/settings.h +++ b/settings.h @@ -200,6 +200,32 @@ #define LCR_SETTING_COUNTDOWN_MS 2200 #endif +#ifndef LCR_SETTING_RIBBON_SEGMENTS + /** Ribbon mod: how many points to spawn. */ + #define LCR_SETTING_RIBBON_SEGMENTS 10 +#endif + +#ifndef LCR_SETTING_RIBBON_SUBDIV + /** Ribbon mod: number of splats to draw per segment, it's good to keep this a + power of 2. */ + #define LCR_SETTING_RIBBON_SUBDIV 16 +#endif + +#ifndef LCR_SETTING_RIBBON_INTERVAL + /** Ribbon mod: how often to spawn a new ribbon point (in physics ticks). */ + #define LCR_SETTING_RIBBON_INTERVAL 4 +#endif + +#ifndef LCR_SETTING_RIBBON_SIZE + /** Ribbon mod: ribbon splat size (in percents of screen width). */ + #define LCR_SETTING_RIBBON_SIZE 6 +#endif + +#ifndef LCR_SETTING_RIBBON_COLOR + /** Ribbon mod: ribbon color. */ + #define LCR_SETTING_RIBBON_COLOR 0xffe6 +#endif + #ifndef LCR_SETTING_MAP_CHUNK_RELOAD_INTERVAL /** Interval in rendering frames of reloading map chunks, should ideally be kept a power of two, can't be 0. */