Licar/racing.h

615 lines
18 KiB
C
Raw Normal View History

2023-09-16 22:52:03 +02:00
/**
racing module: implements the racing physics and logic.
*/
#ifndef _LCR_RACING_H
#define _LCR_RACING_H
2024-09-09 19:16:51 +02:00
typedef int32_t LCR_GameUnit; ///< abstract game unit
2023-09-16 22:52:03 +02:00
2024-09-09 19:16:51 +02:00
#define LCR_GAME_UNIT 1024 ///< length of map square in LCR_GameUnits
2024-09-04 23:26:05 +02:00
2024-09-05 22:51:11 +02:00
#define LCR_RACING_INPUT_FORW 0x01
#define LCR_RACING_INPUT_RIGHT 0x02
#define LCR_RACING_INPUT_BACK 0x04
#define LCR_RACING_INPUT_LEFT 0x08
2024-09-23 23:31:30 +02:00
#define LCR_PHYSICS_UNIT 2048 ///< length of map square for physics engine
2023-09-16 22:52:03 +02:00
#include "map.h"
#include "tinyphysicsengine.h"
2024-09-23 23:31:30 +02:00
// TODO: move some of this to constants?
2024-09-26 21:29:36 +02:00
#if 0
2024-09-10 15:30:07 +02:00
#define LCR_GRAVITY (LCR_PHYSICS_UNIT / 100)
2024-09-10 21:49:23 +02:00
#define LCR_CAR_FORWARD_FRICTION (TPE_F / 11)
#define LCR_CAR_TURN_FRICTION (4 * TPE_F / 4)
#define LCR_CAR_ELASTICITY (TPE_F / 100)
#define LCR_CAR_ACCELERATION (LCR_PHYSICS_UNIT / 15)
2024-09-11 02:12:04 +02:00
#define LCR_CAR_TURN_SPEED (LCR_GAME_UNIT / 20)
2024-09-10 21:49:23 +02:00
#define LCR_CAR_TURN_MAX (LCR_GAME_UNIT / 4)
2024-09-26 21:29:36 +02:00
#else
#define LCR_GRAVITY (LCR_PHYSICS_UNIT / 140)
#define LCR_CAR_FORWARD_FRICTION (TPE_F / 9)
#define LCR_CAR_TURN_FRICTION (4 * TPE_F / 4)
#define LCR_CAR_ELASTICITY (TPE_F / 110)
#define LCR_CAR_ACCELERATION (LCR_PHYSICS_UNIT / 16)
#define LCR_CAR_TURN_SPEED (LCR_GAME_UNIT / 20)
#define LCR_CAR_TURN_MAX (LCR_GAME_UNIT / 4)
#endif
2024-09-23 23:31:30 +02:00
#define LCR_CAR_JOINTS 5
#define LCR_CAR_CONNECTIONS 10
2024-09-09 19:16:51 +02:00
2024-09-04 23:26:05 +02:00
struct
{
TPE_World physicsWorld;
TPE_Body carBody;
2024-09-06 00:58:32 +02:00
TPE_Joint carJoints[LCR_CAR_JOINTS];
TPE_Connection carConnections[LCR_CAR_CONNECTIONS];
2024-09-10 21:49:23 +02:00
uint32_t tick;
2024-09-09 19:16:51 +02:00
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.
2024-09-22 20:19:43 +02:00
TPE_Vec3 carRotations[2]; ///* Current and previous rotation.
2024-09-09 19:16:51 +02:00
2024-09-10 15:30:07 +02:00
LCR_GameUnit wheelRotation;
LCR_GameUnit wheelSteer;
2024-09-04 23:26:05 +02:00
} LCR_racing;
2024-09-09 19:16:51 +02:00
TPE_Vec3 _LCR_TPE_vec3DividePlain(TPE_Vec3 v, TPE_Unit d)
{
2024-09-23 23:31:30 +02:00
v.x /= d; v.y /= d; v.z /= d;
2024-09-09 19:16:51 +02:00
return v;
}
2024-09-23 20:21:08 +02:00
TPE_Vec3 _LCR_racingBlockEnvFunc(TPE_Vec3 point, const uint8_t *block)
{
uint8_t bx, by, bz;
LCR_mapBlockGetCoords(block,&bx,&by,&bz);
2024-09-26 21:29:36 +02:00
2024-09-26 14:56:39 +02:00
TPE_Vec3 blockOffset = TPE_vec3(
(((int) bx) - LCR_MAP_SIZE_BLOCKS / 2) * LCR_PHYSICS_UNIT,
(((int) by) - LCR_MAP_SIZE_BLOCKS / 2) * (LCR_PHYSICS_UNIT / 2),
(((int) bz) - LCR_MAP_SIZE_BLOCKS / 2) * LCR_PHYSICS_UNIT);
2024-09-23 20:21:08 +02:00
2024-09-26 14:56:39 +02:00
point = TPE_vec3Minus(point,blockOffset); // shift to origin
2024-09-23 20:21:08 +02:00
2024-09-26 14:56:39 +02:00
uint8_t transform =
LCR_mapBlockOppositeTransform(LCR_mapBlockGetTransform(block));
2024-09-24 14:48:45 +02:00
2024-09-26 21:29:36 +02:00
LCR_TRANSFORM_COORDS(transform,point.x,point.y,point.z,LCR_PHYSICS_UNIT,
(LCR_PHYSICS_UNIT / 2))
2024-09-26 14:56:39 +02:00
2024-09-26 21:29:36 +02:00
point = TPE_vec3Minus(point,
TPE_vec3(LCR_PHYSICS_UNIT / 2,LCR_PHYSICS_UNIT / 4,LCR_PHYSICS_UNIT / 2));
2024-09-24 14:48:45 +02:00
switch (block[0])
{
case LCR_BLOCK_FULL:
case LCR_BLOCK_BOTTOM:
case LCR_BLOCK_LEFT:
case LCR_BLOCK_BOTTOM_LEFT:
case LCR_BLOCK_BOTTOM_LEFT_FRONT:
case LCR_BLOCK_FULL_ACCEL:
case LCR_BLOCK_FULL_FAN:
{
TPE_Vec3
offset = TPE_vec3(0,0,0),
size = TPE_vec3(LCR_PHYSICS_UNIT / 2,LCR_PHYSICS_UNIT / 4,
LCR_PHYSICS_UNIT / 2);
if (block[0] == LCR_BLOCK_BOTTOM ||
block[0] == LCR_BLOCK_BOTTOM_LEFT ||
block[0] == LCR_BLOCK_BOTTOM_LEFT_FRONT)
{
offset.y -= LCR_PHYSICS_UNIT / 8;
size.y = LCR_PHYSICS_UNIT / 8;
}
if (block[0] == LCR_BLOCK_LEFT ||
block[0] == LCR_BLOCK_BOTTOM_LEFT ||
block[0] == LCR_BLOCK_BOTTOM_LEFT_FRONT)
{
offset.x -= LCR_PHYSICS_UNIT / 4;
size.x = LCR_PHYSICS_UNIT / 4;
}
if (block[0] == LCR_BLOCK_BOTTOM_LEFT_FRONT)
{
offset.z -= LCR_PHYSICS_UNIT / 4;
size.z = LCR_PHYSICS_UNIT / 4;
}
point = TPE_envAABox(point,offset,size);
break;
}
case LCR_BLOCK_RAMP:
case LCR_BLOCK_RAMP_34:
case LCR_BLOCK_RAMP_12:
case LCR_BLOCK_RAMP_14:
{
TPE_Unit sides[6];
2024-09-26 14:56:39 +02:00
sides[0] = -1 * LCR_PHYSICS_UNIT / 2;
sides[1] = -1 * LCR_PHYSICS_UNIT / 4;
sides[2] = LCR_PHYSICS_UNIT / 2;
sides[3] = -1 * LCR_PHYSICS_UNIT / 4;
sides[4] = LCR_PHYSICS_UNIT / 2;
sides[5] = -1 * LCR_PHYSICS_UNIT / 4 +
LCR_rampHeight4ths(block[0]) * (LCR_PHYSICS_UNIT / 8);
2024-09-24 14:48:45 +02:00
point = TPE_envAATriPrism(point,TPE_vec3(0,0,0),sides,LCR_PHYSICS_UNIT,2);
break;
}
default:
point = TPE_vec3(0,0,LCR_MAP_SIZE_BLOCKS * LCR_PHYSICS_UNIT);
break;
}
2024-09-26 14:56:39 +02:00
point = TPE_vec3Plus(point,
TPE_vec3(LCR_PHYSICS_UNIT / 2,LCR_PHYSICS_UNIT / 4,LCR_PHYSICS_UNIT / 2));
2024-09-24 14:48:45 +02:00
2024-09-26 14:56:39 +02:00
transform = LCR_mapBlockOppositeTransform(transform);
2024-09-24 14:48:45 +02:00
2024-09-26 14:56:39 +02:00
LCR_TRANSFORM_COORDS(transform,point.x,point.y,point.z,LCR_PHYSICS_UNIT,
(LCR_PHYSICS_UNIT / 2))
2024-09-24 14:48:45 +02:00
2024-09-26 14:56:39 +02:00
point = TPE_vec3Plus(point,blockOffset); // shift back
return point;
2024-09-23 20:21:08 +02:00
}
2024-09-09 19:16:51 +02:00
TPE_Vec3 _LCR_racingEnvironmentFunction(TPE_Vec3 point, TPE_Unit maxDist)
2024-09-04 23:26:05 +02:00
{
2024-09-23 20:21:08 +02:00
// start with the map outside walls:
TPE_ENV_START(TPE_envAABoxInside(point,TPE_vec3(0,0,0),TPE_vec3(
2024-09-23 23:31:30 +02:00
LCR_PHYSICS_UNIT * LCR_MAP_SIZE_BLOCKS,
(LCR_PHYSICS_UNIT * LCR_MAP_SIZE_BLOCKS) / 2,
LCR_PHYSICS_UNIT * LCR_MAP_SIZE_BLOCKS)),point)
2024-09-23 20:21:08 +02:00
2024-09-26 14:56:39 +02:00
// without this check we might try to get block outside the map
if (_pBest.x == point.x && _pBest.y == point.y && _pBest.z == point.z)
return _pBest;
2024-09-23 20:21:08 +02:00
if (maxDist <= LCR_PHYSICS_UNIT / 4) // considering half of square height
{
/* Here we only check the 8 closest blocks => relatively fast. */
TPE_Vec3 pointShifted = TPE_vec3Plus(point,TPE_vec3(
(LCR_MAP_SIZE_BLOCKS / 2) * LCR_PHYSICS_UNIT,
(LCR_MAP_SIZE_BLOCKS / 4) * LCR_PHYSICS_UNIT,
(LCR_MAP_SIZE_BLOCKS / 2) * LCR_PHYSICS_UNIT));
uint8_t coords[6]; // x_low, x_high, y_low, y_high, z_low, z_high
coords[0] = (pointShifted.x / LCR_PHYSICS_UNIT);
coords[1] = (pointShifted.x % LCR_PHYSICS_UNIT < LCR_PHYSICS_UNIT / 2);
coords[2] = (pointShifted.y / (LCR_PHYSICS_UNIT / 2));
coords[3] =
(pointShifted.y % (LCR_PHYSICS_UNIT / 2) < LCR_PHYSICS_UNIT / 4);
coords[4] = (pointShifted.z / LCR_PHYSICS_UNIT);
coords[5] = (pointShifted.z % LCR_PHYSICS_UNIT < LCR_PHYSICS_UNIT / 2);
for (int i = 0; i < 6; i += 2)
if (coords[i + 1])
{
coords[i + 1] = coords[i];
coords[i] = coords[i] > 0 ? coords[i] - 1 : 0;
}
else
coords[i + 1] = coords[i] < 63 ? coords[i] + 1 : 63;
int start = 0, end = LCR_currentMap.blockCount - 1;
for (uint8_t i = 0; i < 8; ++i)
{
/* Black magic: here we make it so that we the lowest coord numbers
(0,0,0), then the highest (1,1,1), then second lowest (1,0,0), then
second highest (0,1,1) etc. This way we are narrowing the range (start,
end) for the binary search. */
int blockNum = LCR_mapGetBlockAtFast(
coords[0] + ((i ^ (i >> 1)) & 0x01),
coords[2] + ((i ^ (i >> 2)) & 0x01),
coords[4] + (i & 0x01),start,end);
if (blockNum >= 0) // is there a block at the coords?
{
TPE_ENV_NEXT(_LCR_racingBlockEnvFunc(point, // check it
LCR_currentMap.blocks + blockNum * LCR_BLOCK_SIZE),point)
// narrow the search range:
if (i % 2 == 0 && blockNum > start)
start = blockNum;
if (i % 2 && blockNum < end)
end = blockNum;
}
}
}
else
{
printf("oof\n");
2024-09-23 23:31:30 +02:00
const uint8_t *block = LCR_currentMap.blocks;
2024-09-23 20:21:08 +02:00
/* Full check of all map blocks, slow, shouldn't happen often! */
for (int j = 0; j < LCR_currentMap.blockCount; ++j)
{
TPE_ENV_NEXT(_LCR_racingBlockEnvFunc(point,block),point)
block += LCR_BLOCK_SIZE;
}
}
TPE_ENV_END
2024-09-04 23:26:05 +02:00
}
2024-09-10 15:30:07 +02:00
LCR_GameUnit LCR_racingGetCarSpeed(void)
{
return (TPE_vec3Len(TPE_vec3(
LCR_racing.carBody.joints[4].velocity[0],
LCR_racing.carBody.joints[4].velocity[1],
LCR_racing.carBody.joints[4].velocity[2])) * LCR_GAME_UNIT)
/ LCR_PHYSICS_UNIT;
}
2024-09-09 19:16:51 +02:00
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;
}
2024-09-22 20:19:43 +02:00
LCR_GameUnit _LCR_racingSmoothRot(LCR_GameUnit angleNew, LCR_GameUnit angleOld)
{
/* We'll smooth small rotations by averaging the last two angles; bigger
rotations won't be smoothed -- firstly this removes lag for fast rotations
and also deals with the issue of averaging e.g. 1 and 359 degrees. */
LCR_GameUnit diff = angleNew - angleOld;
2024-09-23 23:31:30 +02:00
if (diff > LCR_GAME_UNIT / 8 || diff < -1 * LCR_GAME_UNIT / 8)
2024-09-22 20:19:43 +02:00
return angleNew;
return angleOld + diff / 2;
}
2024-09-09 19:16:51 +02:00
/**
Initializes new run.
*/
void LCR_racingRestart(void)
{
2024-09-10 21:49:23 +02:00
LCR_racing.tick = 0;
2024-09-10 15:30:07 +02:00
TPE_bodyActivate(&(LCR_racing.carBody));
2024-09-09 19:16:51 +02:00
LCR_racing.wheelCollisions = 0;
2024-09-10 15:30:07 +02:00
LCR_racing.wheelRotation = 0;
LCR_racing.wheelSteer = 0;
2024-09-09 19:16:51 +02:00
2024-09-22 20:19:43 +02:00
LCR_racing.carPositions[0] = TPE_vec3(0,0,0);
LCR_racing.carPositions[1] = LCR_racing.carPositions[0];
LCR_racing.carRotations[0] = TPE_vec3(0,0,0);
LCR_racing.carRotations[1] = LCR_racing.carRotations[0];
2024-09-09 19:16:51 +02:00
// TODO
}
/**
Initializes the racing module, only call once.
*/
2024-09-04 23:26:05 +02:00
void LCR_racingInit(void)
{
LCR_log("initializing racing engine");
2024-09-05 22:51:11 +02:00
// make the car body:
2024-09-06 00:58:32 +02:00
TPE_makeCenterRectFull(LCR_racing.carJoints,
2024-09-05 22:51:11 +02:00
LCR_racing.carConnections,
2024-09-06 00:58:32 +02:00
LCR_PHYSICS_UNIT / 2,
(LCR_PHYSICS_UNIT * 3) / 4,
LCR_PHYSICS_UNIT / 8);
2024-09-05 22:51:11 +02:00
2024-09-09 19:16:51 +02:00
LCR_racing.carJoints[4].position.y += LCR_PHYSICS_UNIT / 6;
2024-09-05 22:51:11 +02:00
LCR_racing.carJoints[4].sizeDivided *= 3;
LCR_racing.carJoints[4].sizeDivided /= 2;
2024-09-23 23:31:30 +02:00
TPE_bodyInit(&(LCR_racing.carBody),LCR_racing.carJoints,LCR_CAR_JOINTS,
LCR_racing.carConnections,LCR_CAR_CONNECTIONS,TPE_F);
2024-09-09 19:16:51 +02:00
2024-09-04 23:26:05 +02:00
TPE_worldInit(&(LCR_racing.physicsWorld),
2024-09-09 19:16:51 +02:00
&(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction);
2024-09-06 00:58:32 +02:00
2024-09-09 19:16:51 +02:00
LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler;
2024-09-09 21:25:22 +02:00
LCR_racing.carBody.friction = LCR_CAR_FORWARD_FRICTION;
LCR_racing.carBody.elasticity = LCR_CAR_ELASTICITY;
2024-09-10 15:30:07 +02:00
LCR_racing.carBody.flags |= TPE_BODY_FLAG_ALWAYS_ACTIVE;
2024-09-09 21:25:22 +02:00
2024-09-23 20:21:08 +02:00
/* We disable bounding sphere checks because that would lead to calling env.
function with large min. distance which would lead to slow iteration over
all map blocks. */
LCR_racing.carBody.flags |= TPE_BODY_FLAG_NO_BSPHERE;
2024-09-09 19:16:51 +02:00
LCR_racingRestart();
2024-09-04 23:26:05 +02:00
}
2024-09-22 20:19:43 +02:00
/**
Gets current car transformation intended for rendering, i.e. potentially with
smoothing and interpolation applied to the underlying internal state in the
physics engine.
*/
2024-09-05 22:51:11 +02:00
void LCR_racingGetCarTransform(LCR_GameUnit position[3],
2024-09-09 19:16:51 +02:00
LCR_GameUnit rotation[3], LCR_GameUnit interpolationParam)
2024-09-05 22:51:11 +02:00
{
2024-09-20 15:22:18 +02:00
TPE_Vec3 v;
2024-09-09 19:16:51 +02:00
#if LCR_SETTING_SMOOTH_ANIMATIONS
2024-09-23 23:31:30 +02:00
v = TPE_vec3Plus(LCR_racing.carPositions[1], // LERP previous and current pos
_LCR_TPE_vec3DividePlain(
TPE_vec3TimesPlain(TPE_vec3Minus(
LCR_racing.carPositions[0],LCR_racing.carPositions[1]),
interpolationParam),LCR_GAME_UNIT));
2024-09-09 19:16:51 +02:00
position[0] = v.x;
position[1] = v.y;
position[2] = v.z;
2024-09-22 20:19:43 +02:00
rotation[0] = _LCR_racingSmoothRot(LCR_racing.carRotations[0].x,
LCR_racing.carRotations[1].x);
rotation[1] = _LCR_racingSmoothRot(LCR_racing.carRotations[0].y,
LCR_racing.carRotations[1].y);
rotation[2] = _LCR_racingSmoothRot(LCR_racing.carRotations[0].z,
LCR_racing.carRotations[1].z);
2024-09-09 19:16:51 +02:00
#else
position[0] = LCR_racing.carPositions[0].x;
position[1] = LCR_racing.carPositions[0].y;
position[2] = LCR_racing.carPositions[0].z;
2024-09-22 20:19:43 +02:00
rotation[0] = LCR_racing.carRotations[0].x;
rotation[1] = LCR_racing.carRotations[0].y;
rotation[2] = LCR_racing.carRotations[0].z;
#endif
2024-09-05 22:51:11 +02:00
}
2024-09-04 23:26:05 +02:00
void _LCR_drawPhysicsDebugPixel(uint16_t x, uint16_t y, uint8_t color)
{
2024-09-05 22:51:11 +02:00
if (x > 1 && x < LCR_EFFECTIVE_RESOLUTION_X - 2 &&
y > 1 && y < LCR_EFFECTIVE_RESOLUTION_Y - 2)
2024-09-04 23:26:05 +02:00
{
2024-09-05 22:51:11 +02:00
uint16_t c = 0x8101 | (0x8f1f << (2 * color));
2024-09-04 23:26:05 +02:00
2024-09-05 22:51:11 +02:00
for (int j = -1; j <= 2; ++j)
for (int i = -1; i <= 2; ++i)
LCR_drawPixelXYUnsafe(x + i,y + j,c);
2024-09-04 23:26:05 +02:00
}
}
2024-09-09 19:16:51 +02:00
int LCR_racingCarWheelTouchesGround(int wheel)
{
return ((LCR_racing.wheelCollisions & (LCR_racing.wheelCollisions >> 4))
>> wheel) & 0x01;
}
2024-09-10 15:30:07 +02:00
LCR_GameUnit LCR_racingGetWheelRotation(void)
{
return LCR_racing.wheelRotation;
}
LCR_GameUnit LCR_racingGetWheelSteer(void)
{
return LCR_racing.wheelSteer;
}
void _LCR_racingWheelAccelerate(unsigned int wheel, TPE_Vec3 dir)
{
LCR_racing.carBody.joints[wheel].velocity[0] +=
(dir.x * LCR_CAR_ACCELERATION) / TPE_F;
LCR_racing.carBody.joints[wheel].velocity[1] +=
(dir.y * LCR_CAR_ACCELERATION) / TPE_F;
LCR_racing.carBody.joints[wheel].velocity[2] +=
(dir.z * LCR_CAR_ACCELERATION) / TPE_F;
}
2024-09-09 19:16:51 +02:00
2024-09-10 17:29:22 +02:00
/**
Updates the racing physics world, call every LCR_RACING_TICK_MS milliseconds.
*/
2024-09-05 22:51:11 +02:00
void LCR_racingStep(unsigned int input)
{
2024-09-09 19:16:51 +02:00
TPE_Vec3 carForw, carRight, carUp;
2024-09-10 15:30:07 +02:00
carForw = TPE_vec3Normalized(TPE_vec3Plus(
TPE_vec3Minus(LCR_racing.carBody.joints[0].position,
LCR_racing.carBody.joints[2].position),
TPE_vec3Minus(LCR_racing.carBody.joints[1].position,
LCR_racing.carBody.joints[3].position)));
carRight = TPE_vec3Normalized(TPE_vec3Plus(
TPE_vec3Minus(LCR_racing.carBody.joints[0].position,
LCR_racing.carBody.joints[1].position),
TPE_vec3Minus(LCR_racing.carBody.joints[2].position,
LCR_racing.carBody.joints[3].position)));
carUp = TPE_vec3Cross(carForw,carRight);
2024-09-05 22:51:11 +02:00
if (input)
{
2024-09-10 17:29:22 +02:00
unsigned char steering = 0;
2024-09-10 15:30:07 +02:00
// TODO: magic constants
2024-09-10 17:29:22 +02:00
if (input & (LCR_RACING_INPUT_FORW | LCR_RACING_INPUT_BACK))
{
2024-09-10 21:49:23 +02:00
LCR_GameUnit rotateBy =
(LCR_racing.wheelCollisions & 0x0f) ? // on ground slow down wheel rot.
(LCR_racingGetCarSpeed() / 16) : LCR_GAME_UNIT / 32;
2024-09-10 17:29:22 +02:00
2024-09-10 21:49:23 +02:00
if (!(input & LCR_RACING_INPUT_BACK))
rotateBy *= -1;
LCR_racing.wheelRotation =
(LCR_racing.wheelRotation + rotateBy) % LCR_GAME_UNIT;
2024-09-10 15:30:07 +02:00
2024-09-10 17:29:22 +02:00
if (LCR_racing.wheelRotation < 0)
2024-09-10 15:30:07 +02:00
LCR_racing.wheelRotation += LCR_GAME_UNIT;
2024-09-10 17:29:22 +02:00
}
2024-09-05 22:51:11 +02:00
if (input & LCR_RACING_INPUT_RIGHT)
2024-09-10 17:29:22 +02:00
{
steering = 2;
2024-09-10 21:49:23 +02:00
2024-09-10 15:30:07 +02:00
LCR_racing.wheelSteer = TPE_min(
2024-09-10 17:29:22 +02:00
LCR_racing.wheelSteer + LCR_CAR_TURN_SPEED,
LCR_CAR_TURN_MAX);
}
else if (input & LCR_RACING_INPUT_LEFT)
{
steering = 1;
2024-09-10 21:49:23 +02:00
2024-09-10 15:30:07 +02:00
LCR_racing.wheelSteer = TPE_max(
2024-09-10 17:29:22 +02:00
LCR_racing.wheelSteer - LCR_CAR_TURN_SPEED,
-1 * LCR_CAR_TURN_MAX);
}
2024-09-10 15:30:07 +02:00
2024-09-10 17:29:22 +02:00
if ((LCR_racing.wheelCollisions & 0x0c)) // back wheel on ground?
2024-09-10 15:30:07 +02:00
{
if (input & LCR_RACING_INPUT_FORW)
{
_LCR_racingWheelAccelerate(0,carForw);
_LCR_racingWheelAccelerate(1,carForw);
}
else if (input & LCR_RACING_INPUT_BACK)
{
_LCR_racingWheelAccelerate(0,TPE_vec3TimesPlain(carForw,-1));
_LCR_racingWheelAccelerate(1,TPE_vec3TimesPlain(carForw,-1));
}
}
2024-09-10 17:29:22 +02:00
for (int i = 0; i < 4; ++i)
if (LCR_racing.wheelCollisions & (0x01 << i)) // wheel on ground?
{
TPE_Vec3 jv = TPE_vec3( // joint velocity
LCR_racing.carBody.joints[i].velocity[0],
LCR_racing.carBody.joints[i].velocity[1],
LCR_racing.carBody.joints[i].velocity[2]);
TPE_Vec3 ja = carRight; // wheel axis of rotation
if (i >= 2 && steering)
{
// for front wheels with turning we tilt the wheel axis 45 degrees
TPE_Unit steer =
(LCR_racing.wheelSteer * TPE_F) / LCR_GAME_UNIT;
2024-09-20 15:22:18 +02:00
2024-09-10 17:29:22 +02:00
ja = TPE_vec3Normalized(
TPE_vec3Plus(TPE_vec3Times(carForw,steer),carRight));
}
/* friction is in the direction if the axis and its magnitude is
determined by the dot product (angle) of the axis and velocity */
TPE_Vec3 fric = TPE_vec3Times(ja,(TPE_vec3Dot(ja,jv) *
LCR_CAR_TURN_FRICTION) / TPE_F);
jv = TPE_vec3Minus(jv,fric); // subtract the friction
LCR_racing.carBody.joints[i].velocity[0] = jv.x;
LCR_racing.carBody.joints[i].velocity[1] = jv.y;
LCR_racing.carBody.joints[i].velocity[2] = jv.z;
}
2024-09-05 22:51:11 +02:00
}
2024-09-10 15:30:07 +02:00
if ((!(input & LCR_RACING_INPUT_LEFT)) &&
(!(input & LCR_RACING_INPUT_RIGHT)))
LCR_racing.wheelSteer /= 2;
2024-09-09 19:16:51 +02:00
2024-09-10 15:30:07 +02:00
if ((LCR_racing.wheelCollisions & 0x0f) != 0x0f) // EXPERIMENTAL: don't apply gravity with all wheels on ground
TPE_bodyApplyGravity(&(LCR_racing.carBody),LCR_GRAVITY);
2024-09-09 19:16:51 +02:00
2024-09-10 15:30:07 +02:00
LCR_racing.wheelCollisions <<= 4;
TPE_worldStep(&(LCR_racing.physicsWorld));
2024-09-09 19:16:51 +02:00
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_racing.carBody.joints[4].position = TPE_vec3Plus(TPE_vec3Times(carUp,
LCR_GAME_UNIT / 4),LCR_racing.carBody.joints[4].position);
}
2024-09-09 21:25:22 +02:00
TPE_Vec3 tmpVec = LCR_racing.carPositions[0];
2024-09-23 23:31:30 +02:00
LCR_racing.carPositions[0] = // average position of 4 wheels to get car pos
_LCR_TPE_vec3DividePlain(
TPE_vec3TimesPlain(
2024-09-20 15:22:18 +02:00
TPE_vec3Plus(
2024-09-23 23:31:30 +02:00
TPE_vec3Plus(
2024-09-20 15:22:18 +02:00
LCR_racing.carBody.joints[0].position,
LCR_racing.carBody.joints[1].position),
2024-09-23 23:31:30 +02:00
TPE_vec3Plus(
2024-09-20 15:22:18 +02:00
LCR_racing.carBody.joints[2].position,
LCR_racing.carBody.joints[3].position)
2024-09-23 23:31:30 +02:00
),LCR_GAME_UNIT),4 * LCR_PHYSICS_UNIT);
LCR_racing.carPositions[0] = // smooth the position
TPE_vec3KeepWithinBox(LCR_racing.carPositions[1],LCR_racing.carPositions[0],
TPE_vec3(
LCR_PHYSICS_UNIT / 64, // TODO: constant
LCR_PHYSICS_UNIT / 64,
LCR_PHYSICS_UNIT / 64));
2024-09-09 21:25:22 +02:00
LCR_racing.carPositions[1] = tmpVec;
2024-09-10 21:49:23 +02:00
2024-09-23 23:31:30 +02:00
tmpVec = _LCR_TPE_vec3DividePlain(TPE_vec3TimesPlain(TPE_bodyGetRotation(
&(LCR_racing.carBody),0,2,1),LCR_GAME_UNIT),TPE_F);
2024-09-22 20:19:43 +02:00
LCR_racing.carRotations[1] = LCR_racing.carRotations[0];
LCR_racing.carRotations[0] = tmpVec;
2024-09-10 21:49:23 +02:00
LCR_racing.tick++;
2024-09-05 22:51:11 +02:00
}
2024-09-04 23:26:05 +02:00
void LCR_physicsDebugDraw(LCR_GameUnit camPos[3], LCR_GameUnit camRot[2],
LCR_GameUnit camFov)
{
TPE_Vec3 cPos, cRot, cView;
cPos.x = (camPos[0] * LCR_PHYSICS_UNIT) / LCR_GAME_UNIT;
cPos.y = (camPos[1] * LCR_PHYSICS_UNIT) / LCR_GAME_UNIT;
cPos.z = (camPos[2] * LCR_PHYSICS_UNIT) / LCR_GAME_UNIT;
2024-09-09 19:16:51 +02:00
cRot.x = (camRot[0] * TPE_F) / LCR_GAME_UNIT;
cRot.y = (camRot[1] * TPE_F) / LCR_GAME_UNIT;
2024-09-04 23:26:05 +02:00
cRot.z = 0;
cView.x = LCR_EFFECTIVE_RESOLUTION_X;
cView.y = LCR_EFFECTIVE_RESOLUTION_Y;
2024-09-09 19:16:51 +02:00
cView.z = (camFov * TPE_F) / LCR_GAME_UNIT;
2024-09-04 23:26:05 +02:00
TPE_worldDebugDraw(&(LCR_racing.physicsWorld),_LCR_drawPhysicsDebugPixel,
2024-09-24 14:48:45 +02:00
cPos,cRot,cView,16,LCR_PHYSICS_UNIT / 4,LCR_racing.tick * 4);
2024-09-04 23:26:05 +02:00
}
2023-09-16 22:52:03 +02:00
#endif // guard