Code cleanup
This commit is contained in:
parent
259c1d5d55
commit
3f417b1e24
17 changed files with 2156 additions and 2049 deletions
|
|
@ -135,7 +135,8 @@ API Changes
|
|||
threshold glow, blurred over a half-size chain before the tone curve.
|
||||
viewNew renders a second camera to a texture every frame (viewSetCamera
|
||||
points it) that materialSetView shows on any surface: monitors,
|
||||
mirrors, portals.
|
||||
mirrors, portals. Each view turns billboards to its own camera and
|
||||
draws transparency back to front from it.
|
||||
|
||||
- Particle extras. Trails (emitterSetTrail: a fading ribbon behind
|
||||
each particle, 2D and 3D), collisions with a floor or, by ray cast,
|
||||
|
|
|
|||
|
|
@ -1023,9 +1023,10 @@ a material: a security monitor, a rear-view mirror, a portal, a picture in
|
|||
picture. `viewNew(width, height)` makes one at that resolution,
|
||||
`viewSetCamera` gives it a camera node, and `materialSetView` puts its
|
||||
picture on any mesh. Views share the frame's shadows and skip bloom, and
|
||||
each one renders the scene again, so keep them few and small. A billboard
|
||||
(see <<scenesprites,Sprites and Text in the Scene>>) faces the window's
|
||||
camera, so a view sees it from the side.
|
||||
each one renders the scene again, so keep them few and small. Each view
|
||||
turns billboards (see <<scenesprites,Sprites and Text in the Scene>>) to
|
||||
its own camera and draws transparent materials back to front from it, so
|
||||
a monitor sees name tags face on and glass in the right order.
|
||||
|
||||
[source,lua]
|
||||
----
|
||||
|
|
@ -7820,8 +7821,8 @@ world up only, so the node stays upright (trees, health bars);
|
|||
`BILLBOARD_NONE`, the default, does not turn at all. The node's world
|
||||
position and scale are kept and its own rotation is ignored while a mode is
|
||||
set. Any other value aborts the script. It works on meshes as well as
|
||||
sprites and text, and follows the window's camera: a view sees the
|
||||
billboard from the side.
|
||||
sprites and text, and turns to whichever camera is drawing: a view sees
|
||||
the billboard face on too, while its shadow follows the window's camera.
|
||||
|
||||
*Since:* 3.00.
|
||||
*See also:* <<nodesetsprite,nodeSetSprite>>, <<nodesettext,nodeSetText>>, <<cameraset,cameraSet>>
|
||||
|
|
@ -12541,7 +12542,7 @@ end
|
|||
[#view]
|
||||
=== View
|
||||
|
||||
A view is a second camera rendered to a texture every frame, for a monitor, a mirror or a portal in the scene: `viewNew` makes one and returns an integer handle, `viewSetCamera` points it at a node, and `materialSetView` shows it on a material. Up to four exist at once; each renders the whole scene again at its own size, in overlay-independent pixels, with the main camera's projection, the frame's shadows and no bloom, so keep them few and small. Billboards (`nodeSetBillboard`) are turned to face the window's camera and blended draws are sorted back to front from it, once for every view, so a view whose camera looks from elsewhere sees billboards side on and may see transparent objects overlap in the wrong order. A bad handle raises an error. See <<scenes3d,3D Scenes>>.
|
||||
A view is a second camera rendered to a texture every frame, for a monitor, a mirror or a portal in the scene: `viewNew` makes one and returns an integer handle, `viewSetCamera` points it at a node, and `materialSetView` shows it on a material. Up to four exist at once; each renders the whole scene again at its own size, in overlay-independent pixels, with the main camera's projection, the frame's shadows and no bloom, so keep them few and small. Every view turns billboards (`nodeSetBillboard`) to its own camera and sorts blended draws back to front from it, so a view whose camera looks from elsewhere still sees billboards face on and transparent objects in the right order. A bad handle raises an error. See <<scenes3d,3D Scenes>>.
|
||||
|
||||
[#viewdelete]
|
||||
==== viewDelete
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@
|
|||
|
||||
|
||||
static bool _isRadiance(const uint8_t *bytes, size_t size);
|
||||
static float _linear(uint8_t value);
|
||||
static float *_loadRadiance(const uint8_t *bytes, size_t size, int32_t *width, int32_t *height);
|
||||
static float *_loadSurface(SDL_Surface *surface, int32_t *width, int32_t *height);
|
||||
static float _linear(uint8_t value);
|
||||
static bool _readScanline(const uint8_t *bytes, size_t size, size_t *offset, uint8_t *rgbe, int32_t width);
|
||||
static void _rgbeToFloat(const uint8_t *rgbe, float *out);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ typedef struct Ktx2ImageS {
|
|||
bool srgb; // The file says its colours are sRGB
|
||||
} Ktx2ImageT;
|
||||
|
||||
void ktx2Free(Ktx2ImageT *image);
|
||||
bool ktx2Is(const void *bytes, size_t size); // Starts with the KTX2 identifier
|
||||
bool ktx2Transcode(const void *bytes, size_t size, Ktx2FormatE wanted, Ktx2ImageT *out); // Every level; free with ktx2Free
|
||||
void ktx2Free(Ktx2ImageT *image);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
68
src/main.c
68
src/main.c
|
|
@ -185,12 +185,12 @@ static void _crashHandler(int signalNumber);
|
|||
#endif
|
||||
static void _launcher(const char *exeName, ConfigT *conf);
|
||||
static void _mainTrace(const ConfigT *conf, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
static bool _runTool(const ConfigT *conf);
|
||||
static bool _modeMatchesRatio(int32_t index, int32_t ratioIndex);
|
||||
static ConfigT *_parseArguments(const char *exeName, int32_t argc, char *argv[]);
|
||||
static bool _parseInteger(const char *text, int32_t *value);
|
||||
static void _requireRange(const char *exeName, int32_t value, int32_t min, int32_t max, const char *what, const char *unit);
|
||||
static void _resolveFiles(const char *exeName, ConfigT *conf);
|
||||
static bool _runTool(const ConfigT *conf);
|
||||
static void _showHeader(void);
|
||||
static void _showUsage(const char *name, const char *message) __attribute__((noreturn));
|
||||
static void _startSDL(void);
|
||||
|
|
@ -207,6 +207,22 @@ static char *_cloneString(const char *string) {
|
|||
}
|
||||
|
||||
|
||||
// Last words on a crash: where it happened, so a report can name the line. Async-signal-unsafe
|
||||
// calls are acceptable here; the process is already lost.
|
||||
#ifndef _WIN32
|
||||
static void _crashHandler(int signalNumber) {
|
||||
void *frames[CRASH_FRAMES_MAX];
|
||||
int32_t count = backtrace(frames, CRASH_FRAMES_MAX);
|
||||
|
||||
fprintf(stderr, "\nSinge crashed (signal %d). Backtrace:\n", signalNumber);
|
||||
backtrace_symbols_fd(frames, count, STDERR_FILENO);
|
||||
fprintf(stderr, "Run with --program and send trace.txt with this.\n");
|
||||
signal(signalNumber, SIG_DFL);
|
||||
raise(signalNumber);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Writes an embedded support file, or rewrites it when the installed copy differs from this build's.
|
||||
static bool _extractFile(const char *filename, const uint8_t *data, size_t length) {
|
||||
FILE *out = NULL;
|
||||
|
|
@ -278,22 +294,6 @@ static char *_findVideoFile(const char *baseName) {
|
|||
}
|
||||
|
||||
|
||||
// Last words on a crash: where it happened, so a report can name the line. Async-signal-unsafe
|
||||
// calls are acceptable here; the process is already lost.
|
||||
#ifndef _WIN32
|
||||
static void _crashHandler(int signalNumber) {
|
||||
void *frames[CRASH_FRAMES_MAX];
|
||||
int32_t count = backtrace(frames, CRASH_FRAMES_MAX);
|
||||
|
||||
fprintf(stderr, "\nSinge crashed (signal %d). Backtrace:\n", signalNumber);
|
||||
backtrace_symbols_fd(frames, count, STDERR_FILENO);
|
||||
fprintf(stderr, "Run with --program and send trace.txt with this.\n");
|
||||
signal(signalNumber, SIG_DFL);
|
||||
raise(signalNumber);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void _launcher(const char *exeName, ConfigT *conf) {
|
||||
int32_t x = 0;
|
||||
int32_t bestResIndex = -1;
|
||||
|
|
@ -887,23 +887,6 @@ static void _resolveFiles(const char *exeName, ConfigT *conf) {
|
|||
}
|
||||
|
||||
|
||||
static void _showHeader(void) {
|
||||
static bool shown = false;
|
||||
|
||||
if (!shown) {
|
||||
utilRedirectConsole();
|
||||
// 00000000011111111112222222222333333333344444444445555555555666666666677777777778
|
||||
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
utilSay(" ___ ___ _ _ ___ ___");
|
||||
utilSay("/ __|_ _| \\| |/ __| __| SINGE Is Not a Game Emulator %s", VERSION_STRING);
|
||||
utilSay("\\__ \\| || .` | (_ | _| Copyright (c) 2006-%s Scott C. Duensing", COPYRIGHT_END_YEAR);
|
||||
utilSay("|___/___|_|\\_|\\___|___| https://KangarooPunch.com https://SingeEngine.com");
|
||||
utilNewline();
|
||||
shown = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --pack, --unpack, and --patch: the option carries the source, the script argument the destination.
|
||||
static bool _runTool(const ConfigT *conf) {
|
||||
switch (conf->toolMode) {
|
||||
|
|
@ -922,6 +905,23 @@ static bool _runTool(const ConfigT *conf) {
|
|||
}
|
||||
|
||||
|
||||
static void _showHeader(void) {
|
||||
static bool shown = false;
|
||||
|
||||
if (!shown) {
|
||||
utilRedirectConsole();
|
||||
// 00000000011111111112222222222333333333344444444445555555555666666666677777777778
|
||||
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
utilSay(" ___ ___ _ _ ___ ___");
|
||||
utilSay("/ __|_ _| \\| |/ __| __| SINGE Is Not a Game Emulator %s", VERSION_STRING);
|
||||
utilSay("\\__ \\| || .` | (_ | _| Copyright (c) 2006-%s Scott C. Duensing", COPYRIGHT_END_YEAR);
|
||||
utilSay("|___/___|_|\\_|\\___|___| https://KangarooPunch.com https://SingeEngine.com");
|
||||
utilNewline();
|
||||
shown = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _showUsage(const char *name, const char *message) {
|
||||
const int32_t helpColumn = 6 + USAGE_OPTION_WIDTH; // " -x, " plus the padded long form
|
||||
const int32_t helpWidth = USAGE_LINE_WIDTH - helpColumn;
|
||||
|
|
|
|||
26
src/nav.h
26
src/nav.h
|
|
@ -42,32 +42,32 @@ extern "C" {
|
|||
#define NAV_MAX_CROWD_AGENTS 128 // Agents one mesh's crowd can steer
|
||||
|
||||
void navInit(void);
|
||||
int32_t navPollArrived(int32_t *agents, int32_t max); // Agents that reached their targets since last asked
|
||||
void navQuit(void);
|
||||
void navUpdate(bool advance); // Once per frame: moves the agents
|
||||
int32_t navPollArrived(int32_t *agents, int32_t max); // Agents that reached their targets since last asked
|
||||
|
||||
int32_t navNew(float agentRadius, float agentHeight, float maxSlopeDegrees, float maxStep);
|
||||
bool navAddNode(int32_t nav, int32_t node); // The node's mesh and its children's, where they stand now
|
||||
bool navBuild(int32_t nav); // Bakes what was added; may take a moment
|
||||
bool navDelete(int32_t nav);
|
||||
bool navValid(int32_t nav);
|
||||
int32_t navPath(int32_t nav, Vec3T from, Vec3T to, Vec3T *points, int32_t max); // Corners of the path, or -1 for none
|
||||
bool navNearest(int32_t nav, Vec3T point, Vec3T *out); // The closest point on the mesh
|
||||
bool navRaycast(int32_t nav, Vec3T from, Vec3T to, Vec3T *hit); // true when the walk from from to to is blocked, hit where
|
||||
bool navRandomPoint(int32_t nav, Vec3T *out);
|
||||
bool navSave(int32_t nav, const char *path); // The baked mesh, to reload with navLoad
|
||||
int32_t navLoad(const void *data, size_t size, float agentRadius, float agentHeight);
|
||||
int32_t navGetPolygons(int32_t nav, Vec3T *vertices, int32_t max); // Triangles of the baked mesh (3 per), for debug drawing
|
||||
int32_t navLoad(const void *data, size_t size, float agentRadius, float agentHeight);
|
||||
bool navNearest(int32_t nav, Vec3T point, Vec3T *out); // The closest point on the mesh
|
||||
int32_t navNew(float agentRadius, float agentHeight, float maxSlopeDegrees, float maxStep);
|
||||
int32_t navPath(int32_t nav, Vec3T from, Vec3T to, Vec3T *points, int32_t max); // Corners of the path, or -1 for none
|
||||
bool navRandomPoint(int32_t nav, Vec3T *out);
|
||||
bool navRaycast(int32_t nav, Vec3T from, Vec3T to, Vec3T *hit); // true when the walk from from to to is blocked, hit where
|
||||
bool navSave(int32_t nav, const char *path); // The baked mesh, to reload with navLoad
|
||||
bool navValid(int32_t nav);
|
||||
|
||||
int32_t navAgentNew(int32_t nav, int32_t node, float radius, float height, float speed);
|
||||
bool navAgentDelete(int32_t agent);
|
||||
bool navAgentMoveTo(int32_t agent, Vec3T target);
|
||||
bool navAgentStop(int32_t agent);
|
||||
int32_t navAgentGetNode(int32_t agent);
|
||||
bool navAgentGetVelocity(int32_t agent, Vec3T *velocity);
|
||||
bool navAgentIsArrived(int32_t agent);
|
||||
bool navAgentMoveTo(int32_t agent, Vec3T target);
|
||||
int32_t navAgentNew(int32_t nav, int32_t node, float radius, float height, float speed);
|
||||
bool navAgentSetPlayer(int32_t agent, bool player); // Steer a player controller on the node instead of placing it
|
||||
bool navAgentStop(int32_t agent);
|
||||
bool navAgentValid(int32_t agent);
|
||||
int32_t navAgentGetNode(int32_t agent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,46 +77,46 @@ typedef struct EmitterViewT {
|
|||
Vec3T trailOffset; // Added to trail points (a local emitter's origin)
|
||||
} EmitterViewT;
|
||||
|
||||
void particlesClearQueue2D(void);
|
||||
void particlesInit(void);
|
||||
void particlesQueue2D(int32_t emitter); // Draw this emitter this frame
|
||||
int32_t particlesQueued2D(ParticleLayerE layer, int32_t *emitters, int32_t max);
|
||||
void particlesQuit(void);
|
||||
void particlesUpdate(bool advance);
|
||||
int32_t particlesView3D(EmitterViewT *views, int32_t max); // Every 3D emitter with live particles, up to max; returns how many
|
||||
bool particlesViewEmitter(int32_t emitter, EmitterViewT *view);
|
||||
void particlesQueue2D(int32_t emitter); // Draw this emitter this frame
|
||||
int32_t particlesQueued2D(ParticleLayerE layer, int32_t *emitters, int32_t max);
|
||||
void particlesClearQueue2D(void);
|
||||
|
||||
int32_t emitterNew(int32_t node);
|
||||
bool emitterValid(int32_t emitter);
|
||||
void emitterBurst(int32_t emitter, int32_t count);
|
||||
void emitterClear(int32_t emitter);
|
||||
void emitterDelete(int32_t emitter);
|
||||
void emitterSetTexture(int32_t emitter, SDL_Surface **frames, int32_t frameCount); // Copies the surfaces; NULL restores the disc
|
||||
void emitterSetTrail(int32_t emitter, int32_t length, float width);
|
||||
void emitterSetFrames(int32_t emitter, int32_t first, int32_t last);
|
||||
int32_t emitterGetCount(int32_t emitter);
|
||||
bool emitterIs3D(int32_t emitter);
|
||||
bool emitterIsActive(int32_t emitter);
|
||||
int32_t emitterNew(int32_t node);
|
||||
void emitterSetBlend(int32_t emitter, ParticleBlendE blend);
|
||||
void emitterSetCollide(int32_t emitter, ParticleCollideE mode, float bounce, float friction, float floor);
|
||||
void emitterSetColor(int32_t emitter, const float *start, const float *finish);
|
||||
void emitterSetDirection(int32_t emitter, Vec3T direction);
|
||||
void emitterSetDrag(int32_t emitter, float perSecond);
|
||||
void emitterSetFrames(int32_t emitter, int32_t first, int32_t last);
|
||||
void emitterSetGravity(int32_t emitter, Vec3T acceleration);
|
||||
void emitterSetLayer(int32_t emitter, ParticleLayerE layer);
|
||||
void emitterSetRate(int32_t emitter, float perSecond);
|
||||
void emitterSetLife(int32_t emitter, float minSeconds, float maxSeconds);
|
||||
void emitterSetLit(int32_t emitter, bool lit);
|
||||
void emitterSetSpeed(int32_t emitter, float min, float max);
|
||||
void emitterSetDirection(int32_t emitter, Vec3T direction);
|
||||
void emitterSetSpread(int32_t emitter, float degrees);
|
||||
void emitterSetGravity(int32_t emitter, Vec3T acceleration);
|
||||
void emitterSetDrag(int32_t emitter, float perSecond);
|
||||
void emitterSetSize(int32_t emitter, float start, float finish, float variation);
|
||||
void emitterSetSoftness(int32_t emitter, float distance);
|
||||
void emitterSetColor(int32_t emitter, const float *start, const float *finish);
|
||||
void emitterSetSpin(int32_t emitter, float min, float max);
|
||||
void emitterSetRadius(int32_t emitter, float radius);
|
||||
void emitterSetLocal(int32_t emitter, bool local);
|
||||
void emitterSetMax(int32_t emitter, int32_t count);
|
||||
void emitterSetPosition(int32_t emitter, Vec3T position);
|
||||
void emitterSetRadius(int32_t emitter, float radius);
|
||||
void emitterSetRate(int32_t emitter, float perSecond);
|
||||
void emitterSetSize(int32_t emitter, float start, float finish, float variation);
|
||||
void emitterSetSoftness(int32_t emitter, float distance);
|
||||
void emitterSetSpeed(int32_t emitter, float min, float max);
|
||||
void emitterSetSpin(int32_t emitter, float min, float max);
|
||||
void emitterSetSpread(int32_t emitter, float degrees);
|
||||
void emitterSetTexture(int32_t emitter, SDL_Surface **frames, int32_t frameCount); // Copies the surfaces; NULL restores the disc
|
||||
void emitterSetTrail(int32_t emitter, int32_t length, float width);
|
||||
void emitterStart(int32_t emitter);
|
||||
void emitterStop(int32_t emitter);
|
||||
void emitterBurst(int32_t emitter, int32_t count);
|
||||
void emitterClear(int32_t emitter);
|
||||
int32_t emitterGetCount(int32_t emitter);
|
||||
bool emitterIsActive(int32_t emitter);
|
||||
bool emitterIs3D(int32_t emitter);
|
||||
bool emitterValid(int32_t emitter);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -132,6 +132,15 @@ int32_t jointNew(JointTypeE type, int32_t nodeA, int32_t nodeB, Vec3T anchor, Ve
|
|||
bool jointSetLimits(int32_t joint, float low, float high);
|
||||
bool jointValid(int32_t joint);
|
||||
bool physicsAvailable(void);
|
||||
int32_t physicsGetEvents(PhysicsEventT *events, int32_t maximum);
|
||||
bool physicsInit(void);
|
||||
void physicsQuit(void);
|
||||
bool physicsRaycast(Vec3T origin, Vec3T direction, float maxDistance, int32_t *node, Vec3T *point, Vec3T *normal);
|
||||
void physicsSet2D(bool planar);
|
||||
void physicsSetDebug(uint32_t mask);
|
||||
void physicsSetEnabled(bool enabled);
|
||||
void physicsSetGravity(Vec3T gravity);
|
||||
void physicsUpdate(bool advance);
|
||||
bool playerDelete(int32_t node);
|
||||
bool playerExists(int32_t node);
|
||||
int32_t playerGetGround(int32_t node, Vec3T *normal);
|
||||
|
|
@ -184,20 +193,11 @@ bool vehicleSetAntiRoll(int32_t node, float stiffness);
|
|||
bool vehicleSetBrakes(int32_t node, float brake, float handBrake);
|
||||
bool vehicleSetEngine(int32_t node, float maxTorque, float maxRpm, float minRpm);
|
||||
bool vehicleSetGears(int32_t node, const float *ratios, int32_t count, float reverse, bool automatic);
|
||||
bool vehicleSetSteering(int32_t node, float maxDegrees);
|
||||
bool vehicleSetRudder(int32_t node, float maxTorque);
|
||||
bool vehicleSetSteering(int32_t node, float maxDegrees);
|
||||
bool vehicleSetSuspension(int32_t node, float frequency, float damping);
|
||||
bool vehicleSetThrust(int32_t node, float maxForce, Vec3T point);
|
||||
bool vehicleSetWheel(int32_t node, int32_t index, bool steered, bool driven);
|
||||
int32_t physicsGetEvents(PhysicsEventT *events, int32_t maximum);
|
||||
bool physicsInit(void);
|
||||
void physicsQuit(void);
|
||||
bool physicsRaycast(Vec3T origin, Vec3T direction, float maxDistance, int32_t *node, Vec3T *point, Vec3T *normal);
|
||||
void physicsSet2D(bool planar);
|
||||
void physicsSetDebug(uint32_t mask);
|
||||
void physicsSetEnabled(bool enabled);
|
||||
void physicsSetGravity(Vec3T gravity);
|
||||
void physicsUpdate(bool advance);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -2251,8 +2251,6 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
// ===== Bodies =====
|
||||
|
||||
// A force for this step, at the centre of mass or a world point.
|
||||
bool bodyApplyForce(int32_t node, Vec3T force, const Vec3T *at) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
|
@ -2419,6 +2417,28 @@ bool bodySetBounce(int32_t node, float bounce) {
|
|||
}
|
||||
|
||||
|
||||
bool bodySetBuoyancy(int32_t node, float factor) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->buoyancy = SDL_max(0.0f, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool bodySetCurrent(int32_t node, Vec3T flow) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->current = flow;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Takes the body out of the world (it stops colliding and moving) and puts it back.
|
||||
bool bodySetEnabled(int32_t node, bool enabled) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
|
@ -2500,7 +2520,24 @@ bool bodySetVelocity(int32_t node, Vec3T velocity) {
|
|||
}
|
||||
|
||||
|
||||
// ===== Joints =====
|
||||
// Fills a static trigger with water: bodies inside float, sink and drift; players swim.
|
||||
bool bodySetWater(int32_t node, float density, float linearDrag, float angularDrag) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
||||
if ((record == nullptr) || (record->type != BODY_STATIC)) {
|
||||
utilTrace("Physics: water needs a static body on node %d.", node);
|
||||
return false;
|
||||
}
|
||||
if (!record->trigger) {
|
||||
bodySetTrigger(node, true);
|
||||
}
|
||||
record->water = true;
|
||||
record->waterDensity = SDL_max(0.0f, density);
|
||||
record->waterLinearDrag = SDL_max(0.0f, linearDrag);
|
||||
record->waterAngularDrag = SDL_max(0.0f, angularDrag);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool jointDelete(int32_t joint) {
|
||||
JointRecordT *record;
|
||||
|
|
@ -2621,8 +2658,6 @@ bool jointValid(int32_t joint) {
|
|||
}
|
||||
|
||||
|
||||
// ===== World =====
|
||||
|
||||
bool physicsAvailable(void) {
|
||||
return _world != nullptr;
|
||||
}
|
||||
|
|
@ -2914,6 +2949,13 @@ bool playerIsOnGround(int32_t node) {
|
|||
}
|
||||
|
||||
|
||||
bool playerIsSwimming(int32_t node) {
|
||||
PlayerRecordT *record = _findPlayer(node);
|
||||
|
||||
return (record != nullptr) && record->swimming;
|
||||
}
|
||||
|
||||
|
||||
// Asks for a jump at the next step; only granted with ground underfoot.
|
||||
bool playerJump(int32_t node, float speed) {
|
||||
PlayerRecordT *record = _findPlayer(node);
|
||||
|
|
@ -3121,322 +3163,6 @@ bool playerSetStep(int32_t node, float height) {
|
|||
}
|
||||
|
||||
|
||||
bool playerSetVelocity(int32_t node, Vec3T velocity) {
|
||||
PlayerRecordT *record = _findPlayer(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->character->SetLinearVelocity(JPH::Vec3(velocity.x, velocity.y, velocity.z));
|
||||
record->intent = vec3(0.0f, 0.0f, 0.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// A wheel at the wheel node's position relative to the chassis; returns its index.
|
||||
int32_t vehicleAddWheel(int32_t node, int32_t wheelNode, float radius, float width, float suspension) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
WheelRecordT *wheel;
|
||||
Vec3T chassisPosition;
|
||||
QuatT chassisRotation;
|
||||
Vec3T chassisScale;
|
||||
|
||||
if ((record == nullptr) || !nodeValid(wheelNode) || (record->wheelCount >= MAX_WHEELS)) {
|
||||
return -1;
|
||||
}
|
||||
// The attachment point is the wheel node's place in the chassis' frame right now (the body's
|
||||
// frame is unscaled: the chassis' scale is baked into its shape); the engine poses the node
|
||||
// with suspension travel from here on, so a later rebuild must not read it back.
|
||||
sceneUpdateTransforms();
|
||||
nodeGetWorldTransform(node, &chassisPosition, &chassisRotation, &chassisScale);
|
||||
wheel = &record->wheels[record->wheelCount];
|
||||
wheel->node = wheelNode;
|
||||
wheel->generation = nodeGetGeneration(wheelNode);
|
||||
wheel->rest = quatRotate(quatInverse(chassisRotation), vec3Subtract(nodeGetWorldPosition(wheelNode), chassisPosition));
|
||||
wheel->radius = SDL_max(radius, MIN_DIMENSION);
|
||||
wheel->width = SDL_max(width, MIN_DIMENSION);
|
||||
wheel->suspension = SDL_max(suspension, MIN_DIMENSION);
|
||||
wheel->steered = false;
|
||||
wheel->driven = true;
|
||||
wheel->steeredSet = false;
|
||||
record->dirty = true;
|
||||
return record->wheelCount++;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleDelete(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
_releaseVehicle(record);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleDrive(int32_t node, float forward, float right, float brake, float handBrake) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->inputForward = SDL_clamp(forward, -1.0f, 1.0f);
|
||||
record->inputRight = SDL_clamp(right, -1.0f, 1.0f);
|
||||
record->inputBrake = SDL_clamp(brake, 0.0f, 1.0f);
|
||||
record->inputHandBrake = SDL_clamp(handBrake, 0.0f, 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleExists(int32_t node) {
|
||||
return _findVehicle(node) != nullptr;
|
||||
}
|
||||
|
||||
|
||||
int32_t vehicleGetGear(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr)) {
|
||||
return 0;
|
||||
}
|
||||
if (record->kind == VEHICLE_TANK) {
|
||||
return static_cast<JPH::TrackedVehicleController *>(record->constraint->GetController())->GetTransmission().GetCurrentGear();
|
||||
}
|
||||
return static_cast<JPH::WheeledVehicleController *>(record->constraint->GetController())->GetTransmission().GetCurrentGear();
|
||||
}
|
||||
|
||||
|
||||
float vehicleGetRpm(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr)) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (record->kind == VEHICLE_TANK) {
|
||||
return static_cast<JPH::TrackedVehicleController *>(record->constraint->GetController())->GetEngine().GetCurrentRPM();
|
||||
}
|
||||
return static_cast<JPH::WheeledVehicleController *>(record->constraint->GetController())->GetEngine().GetCurrentRPM();
|
||||
}
|
||||
|
||||
|
||||
// Metres a second along the chassis' nose, negative in reverse.
|
||||
float vehicleGetSpeed(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
BodyRecordT *body;
|
||||
JPH::Vec3 velocity;
|
||||
JPH::Quat rotation;
|
||||
|
||||
if ((record == nullptr) || ((body = _find(record->node)) == nullptr)) {
|
||||
return 0.0f;
|
||||
}
|
||||
velocity = _world->system->GetBodyInterface().GetLinearVelocity(body->id);
|
||||
rotation = _world->system->GetBodyInterface().GetRotation(body->id);
|
||||
return velocity.Dot(rotation * JPH::Vec3(0.0f, 0.0f, -1.0f));
|
||||
}
|
||||
|
||||
|
||||
// Longitudinal slip of a wheel, 0 gripping to about 1 spinning or locked.
|
||||
float vehicleGetWheelSlip(int32_t node, int32_t index) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr) || (index < 0) || (index >= record->wheelCount) || (record->kind == VEHICLE_TANK)) {
|
||||
return 0.0f;
|
||||
}
|
||||
return SDL_min(fabsf(static_cast<const JPH::WheelWV *>(record->constraint->GetWheel((JPH::uint)index))->mLongitudinalSlip), 1.0f);
|
||||
}
|
||||
|
||||
|
||||
bool vehicleIsWheelOnGround(int32_t node, int32_t index) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr) || (index < 0) || (index >= record->wheelCount)) {
|
||||
return false;
|
||||
}
|
||||
return record->constraint->GetWheel((JPH::uint)index)->HasContact();
|
||||
}
|
||||
|
||||
|
||||
// A vehicle on the node, whose dynamic body is the chassis; add wheels before driving it.
|
||||
bool vehicleNew(int32_t node, VehicleKindE kind) {
|
||||
VehicleRecordT *record = nullptr;
|
||||
BodyRecordT *body = _find(node);
|
||||
int32_t x;
|
||||
|
||||
if ((_world == nullptr) || (body == nullptr) || (body->type != BODY_DYNAMIC)) {
|
||||
utilTrace("Physics: a vehicle needs a dynamic body on node %d first.", node);
|
||||
return false;
|
||||
}
|
||||
if (_world->planar && (kind != VEHICLE_BOAT)) {
|
||||
utilTrace("Physics: vehicles need a 3D world; a 2D car is a body with hinged wheels.");
|
||||
return false;
|
||||
}
|
||||
vehicleDelete(node);
|
||||
for (x = 0; x < _world->vehicleCount; x++) {
|
||||
if (!_world->vehicles[x].used) {
|
||||
record = &_world->vehicles[x];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (record == nullptr) {
|
||||
utilTrace("Physics: no room for another vehicle (%d already).", MAX_VEHICLES);
|
||||
return false;
|
||||
}
|
||||
_resetVehicle(record);
|
||||
record->node = node;
|
||||
record->generation = nodeGetGeneration(node);
|
||||
record->kind = kind;
|
||||
record->dirty = (kind != VEHICLE_BOAT);
|
||||
record->used = true;
|
||||
_indexSet(_world->vehicleOfNode, node, x);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetAntiRoll(int32_t node, float stiffness) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->antiRoll = SDL_max(0.0f, stiffness);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetBrakes(int32_t node, float brake, float handBrake) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->brakeTorque = SDL_max(0.0f, brake);
|
||||
record->handBrakeTorque = SDL_max(0.0f, handBrake);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetEngine(int32_t node, float maxTorque, float maxRpm, float minRpm) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->maxTorque = SDL_max(MIN_ENGINE_TORQUE, maxTorque);
|
||||
record->maxRpm = SDL_max(MIN_ENGINE_MAX_RPM, maxRpm);
|
||||
record->minRpm = SDL_clamp(minRpm, MIN_ENGINE_RPM, record->maxRpm);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetGears(int32_t node, const float *ratios, int32_t count, float reverse, bool automatic) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
int32_t x;
|
||||
|
||||
if ((record == nullptr) || (count < 1) || (count > VEHICLE_MAX_GEARS)) {
|
||||
return false;
|
||||
}
|
||||
for (x = 0; x < count; x++) {
|
||||
record->gears[x] = ratios[x];
|
||||
}
|
||||
record->gearCount = count;
|
||||
record->reverseGear = (reverse > 0.0f) ? -reverse : reverse;
|
||||
record->automatic = automatic;
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetSteering(int32_t node, float maxDegrees) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->maxSteer = SDL_clamp(maxDegrees, 0.0f, MAX_STEER_DEGREES);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetSuspension(int32_t node, float frequency, float damping) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->suspensionHz = SDL_max(MIN_SUSPENSION_HZ, frequency);
|
||||
record->suspensionDamping = SDL_max(0.0f, damping);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetWheel(int32_t node, int32_t index, bool steered, bool driven) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (index < 0) || (index >= record->wheelCount)) {
|
||||
return false;
|
||||
}
|
||||
record->wheels[index].steered = steered;
|
||||
record->wheels[index].driven = driven;
|
||||
record->wheels[index].steeredSet = true;
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool bodySetBuoyancy(int32_t node, float factor) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->buoyancy = SDL_max(0.0f, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool bodySetCurrent(int32_t node, Vec3T flow) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->current = flow;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Fills a static trigger with water: bodies inside float, sink and drift; players swim.
|
||||
bool bodySetWater(int32_t node, float density, float linearDrag, float angularDrag) {
|
||||
BodyRecordT *record = _find(node);
|
||||
|
||||
if ((record == nullptr) || (record->type != BODY_STATIC)) {
|
||||
utilTrace("Physics: water needs a static body on node %d.", node);
|
||||
return false;
|
||||
}
|
||||
if (!record->trigger) {
|
||||
bodySetTrigger(node, true);
|
||||
}
|
||||
record->water = true;
|
||||
record->waterDensity = SDL_max(0.0f, density);
|
||||
record->waterLinearDrag = SDL_max(0.0f, linearDrag);
|
||||
record->waterAngularDrag = SDL_max(0.0f, angularDrag);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool playerIsSwimming(int32_t node) {
|
||||
PlayerRecordT *record = _findPlayer(node);
|
||||
|
||||
return (record != nullptr) && record->swimming;
|
||||
}
|
||||
|
||||
|
||||
bool playerSetSwim(int32_t node, float sinkSpeed, float drag) {
|
||||
PlayerRecordT *record = _findPlayer(node);
|
||||
|
||||
|
|
@ -3449,25 +3175,14 @@ bool playerSetSwim(int32_t node, float sinkSpeed, float drag) {
|
|||
}
|
||||
|
||||
|
||||
bool vehicleSetRudder(int32_t node, float maxTorque) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
bool playerSetVelocity(int32_t node, Vec3T velocity) {
|
||||
PlayerRecordT *record = _findPlayer(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->rudder = SDL_max(0.0f, maxTorque);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetThrust(int32_t node, float maxForce, Vec3T point) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->thrust = SDL_max(0.0f, maxForce);
|
||||
record->thrustPoint = point;
|
||||
record->character->SetLinearVelocity(JPH::Vec3(velocity.x, velocity.y, velocity.z));
|
||||
record->intent = vec3(0.0f, 0.0f, 0.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -4132,3 +3847,281 @@ bool softUnpin(int32_t node, Vec3T point) {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
// A wheel at the wheel node's position relative to the chassis; returns its index.
|
||||
int32_t vehicleAddWheel(int32_t node, int32_t wheelNode, float radius, float width, float suspension) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
WheelRecordT *wheel;
|
||||
Vec3T chassisPosition;
|
||||
QuatT chassisRotation;
|
||||
Vec3T chassisScale;
|
||||
|
||||
if ((record == nullptr) || !nodeValid(wheelNode) || (record->wheelCount >= MAX_WHEELS)) {
|
||||
return -1;
|
||||
}
|
||||
// The attachment point is the wheel node's place in the chassis' frame right now (the body's
|
||||
// frame is unscaled: the chassis' scale is baked into its shape); the engine poses the node
|
||||
// with suspension travel from here on, so a later rebuild must not read it back.
|
||||
sceneUpdateTransforms();
|
||||
nodeGetWorldTransform(node, &chassisPosition, &chassisRotation, &chassisScale);
|
||||
wheel = &record->wheels[record->wheelCount];
|
||||
wheel->node = wheelNode;
|
||||
wheel->generation = nodeGetGeneration(wheelNode);
|
||||
wheel->rest = quatRotate(quatInverse(chassisRotation), vec3Subtract(nodeGetWorldPosition(wheelNode), chassisPosition));
|
||||
wheel->radius = SDL_max(radius, MIN_DIMENSION);
|
||||
wheel->width = SDL_max(width, MIN_DIMENSION);
|
||||
wheel->suspension = SDL_max(suspension, MIN_DIMENSION);
|
||||
wheel->steered = false;
|
||||
wheel->driven = true;
|
||||
wheel->steeredSet = false;
|
||||
record->dirty = true;
|
||||
return record->wheelCount++;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleDelete(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
_releaseVehicle(record);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleDrive(int32_t node, float forward, float right, float brake, float handBrake) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->inputForward = SDL_clamp(forward, -1.0f, 1.0f);
|
||||
record->inputRight = SDL_clamp(right, -1.0f, 1.0f);
|
||||
record->inputBrake = SDL_clamp(brake, 0.0f, 1.0f);
|
||||
record->inputHandBrake = SDL_clamp(handBrake, 0.0f, 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleExists(int32_t node) {
|
||||
return _findVehicle(node) != nullptr;
|
||||
}
|
||||
|
||||
|
||||
int32_t vehicleGetGear(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr)) {
|
||||
return 0;
|
||||
}
|
||||
if (record->kind == VEHICLE_TANK) {
|
||||
return static_cast<JPH::TrackedVehicleController *>(record->constraint->GetController())->GetTransmission().GetCurrentGear();
|
||||
}
|
||||
return static_cast<JPH::WheeledVehicleController *>(record->constraint->GetController())->GetTransmission().GetCurrentGear();
|
||||
}
|
||||
|
||||
|
||||
float vehicleGetRpm(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr)) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (record->kind == VEHICLE_TANK) {
|
||||
return static_cast<JPH::TrackedVehicleController *>(record->constraint->GetController())->GetEngine().GetCurrentRPM();
|
||||
}
|
||||
return static_cast<JPH::WheeledVehicleController *>(record->constraint->GetController())->GetEngine().GetCurrentRPM();
|
||||
}
|
||||
|
||||
|
||||
// Metres a second along the chassis' nose, negative in reverse.
|
||||
float vehicleGetSpeed(int32_t node) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
BodyRecordT *body;
|
||||
JPH::Vec3 velocity;
|
||||
JPH::Quat rotation;
|
||||
|
||||
if ((record == nullptr) || ((body = _find(record->node)) == nullptr)) {
|
||||
return 0.0f;
|
||||
}
|
||||
velocity = _world->system->GetBodyInterface().GetLinearVelocity(body->id);
|
||||
rotation = _world->system->GetBodyInterface().GetRotation(body->id);
|
||||
return velocity.Dot(rotation * JPH::Vec3(0.0f, 0.0f, -1.0f));
|
||||
}
|
||||
|
||||
|
||||
// Longitudinal slip of a wheel, 0 gripping to about 1 spinning or locked.
|
||||
float vehicleGetWheelSlip(int32_t node, int32_t index) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr) || (index < 0) || (index >= record->wheelCount) || (record->kind == VEHICLE_TANK)) {
|
||||
return 0.0f;
|
||||
}
|
||||
return SDL_min(fabsf(static_cast<const JPH::WheelWV *>(record->constraint->GetWheel((JPH::uint)index))->mLongitudinalSlip), 1.0f);
|
||||
}
|
||||
|
||||
|
||||
bool vehicleIsWheelOnGround(int32_t node, int32_t index) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (record->constraint == nullptr) || (index < 0) || (index >= record->wheelCount)) {
|
||||
return false;
|
||||
}
|
||||
return record->constraint->GetWheel((JPH::uint)index)->HasContact();
|
||||
}
|
||||
|
||||
|
||||
// A vehicle on the node, whose dynamic body is the chassis; add wheels before driving it.
|
||||
bool vehicleNew(int32_t node, VehicleKindE kind) {
|
||||
VehicleRecordT *record = nullptr;
|
||||
BodyRecordT *body = _find(node);
|
||||
int32_t x;
|
||||
|
||||
if ((_world == nullptr) || (body == nullptr) || (body->type != BODY_DYNAMIC)) {
|
||||
utilTrace("Physics: a vehicle needs a dynamic body on node %d first.", node);
|
||||
return false;
|
||||
}
|
||||
if (_world->planar && (kind != VEHICLE_BOAT)) {
|
||||
utilTrace("Physics: vehicles need a 3D world; a 2D car is a body with hinged wheels.");
|
||||
return false;
|
||||
}
|
||||
vehicleDelete(node);
|
||||
for (x = 0; x < _world->vehicleCount; x++) {
|
||||
if (!_world->vehicles[x].used) {
|
||||
record = &_world->vehicles[x];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (record == nullptr) {
|
||||
utilTrace("Physics: no room for another vehicle (%d already).", MAX_VEHICLES);
|
||||
return false;
|
||||
}
|
||||
_resetVehicle(record);
|
||||
record->node = node;
|
||||
record->generation = nodeGetGeneration(node);
|
||||
record->kind = kind;
|
||||
record->dirty = (kind != VEHICLE_BOAT);
|
||||
record->used = true;
|
||||
_indexSet(_world->vehicleOfNode, node, x);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetAntiRoll(int32_t node, float stiffness) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->antiRoll = SDL_max(0.0f, stiffness);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetBrakes(int32_t node, float brake, float handBrake) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->brakeTorque = SDL_max(0.0f, brake);
|
||||
record->handBrakeTorque = SDL_max(0.0f, handBrake);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetEngine(int32_t node, float maxTorque, float maxRpm, float minRpm) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->maxTorque = SDL_max(MIN_ENGINE_TORQUE, maxTorque);
|
||||
record->maxRpm = SDL_max(MIN_ENGINE_MAX_RPM, maxRpm);
|
||||
record->minRpm = SDL_clamp(minRpm, MIN_ENGINE_RPM, record->maxRpm);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetGears(int32_t node, const float *ratios, int32_t count, float reverse, bool automatic) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
int32_t x;
|
||||
|
||||
if ((record == nullptr) || (count < 1) || (count > VEHICLE_MAX_GEARS)) {
|
||||
return false;
|
||||
}
|
||||
for (x = 0; x < count; x++) {
|
||||
record->gears[x] = ratios[x];
|
||||
}
|
||||
record->gearCount = count;
|
||||
record->reverseGear = (reverse > 0.0f) ? -reverse : reverse;
|
||||
record->automatic = automatic;
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetRudder(int32_t node, float maxTorque) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->rudder = SDL_max(0.0f, maxTorque);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetSteering(int32_t node, float maxDegrees) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->maxSteer = SDL_clamp(maxDegrees, 0.0f, MAX_STEER_DEGREES);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetSuspension(int32_t node, float frequency, float damping) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->suspensionHz = SDL_max(MIN_SUSPENSION_HZ, frequency);
|
||||
record->suspensionDamping = SDL_max(0.0f, damping);
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetThrust(int32_t node, float maxForce, Vec3T point) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if (record == nullptr) {
|
||||
return false;
|
||||
}
|
||||
record->thrust = SDL_max(0.0f, maxForce);
|
||||
record->thrustPoint = point;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool vehicleSetWheel(int32_t node, int32_t index, bool steered, bool driven) {
|
||||
VehicleRecordT *record = _findVehicle(node);
|
||||
|
||||
if ((record == nullptr) || (index < 0) || (index >= record->wheelCount)) {
|
||||
return false;
|
||||
}
|
||||
record->wheels[index].steered = steered;
|
||||
record->wheels[index].driven = driven;
|
||||
record->wheels[index].steeredSet = true;
|
||||
record->dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
1906
src/scene.c
1906
src/scene.c
File diff suppressed because it is too large
Load diff
|
|
@ -60,11 +60,11 @@ typedef enum MaterialFilterE {
|
|||
FILTER_NEAREST = 1
|
||||
} MaterialFilterE;
|
||||
|
||||
// How a node turns to face the camera.
|
||||
// How a node turns to face the camera (the codes are the shader's, sceneShared.h).
|
||||
typedef enum BillboardE {
|
||||
BILLBOARD_NONE = 0,
|
||||
BILLBOARD_ALL = 1, // Faces the camera squarely
|
||||
BILLBOARD_Y = 2 // Turns about its own up axis only (trees, health bars)
|
||||
BILLBOARD_NONE = BILLBOARD_MODE_NONE,
|
||||
BILLBOARD_ALL = BILLBOARD_MODE_ALL, // Faces the camera squarely
|
||||
BILLBOARD_Y = BILLBOARD_MODE_Y // Turns about world up only (trees, health bars)
|
||||
} BillboardE;
|
||||
|
||||
// A material's textures, for the compressed path.
|
||||
|
|
|
|||
65
src/sceneShared.h
Normal file
65
src/sceneShared.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
*
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCENE_SHARED_H
|
||||
#define SCENE_SHARED_H
|
||||
|
||||
// The limits and codes the scene's uniform blocks are laid out with, shared by the C side
|
||||
// (scene.h, scene.c) and the shaders (shaders/scene.hlsl), so the two cannot drift apart.
|
||||
// Preprocessor definitions only: this file is read by DXC as well as the C compiler.
|
||||
|
||||
// Sizes of the uniform arrays.
|
||||
#define MAX_LIGHTS 8 // Lights the fragment shader sees per frame
|
||||
#define MAX_JOINTS 128 // Joint matrices per skinned draw
|
||||
#define MAX_SHADOWS MAX_LIGHTS // Every light may cast; the arrays are sized to the lights in use
|
||||
#define MAX_MORPHS 8 // Active morph targets per draw
|
||||
#define MAX_CASCADES 4 // Cascades of a directional light's shadow
|
||||
#define SH_COEFFICIENTS 9 // Spherical harmonics to second order
|
||||
|
||||
// A shadow slot's kind (shadowInfo[slot].x).
|
||||
#define SHADOW_NONE 0
|
||||
#define SHADOW_MAP 1 // One map: a spot light, or a directional light without cascades
|
||||
#define SHADOW_CUBE 2 // Six faces: a point light
|
||||
#define SHADOW_CASCADE 3 // A directional light's shadow split along the camera's view
|
||||
|
||||
// A light's kind (positionType.w).
|
||||
#define LIGHT_TYPE_DIRECTIONAL 0
|
||||
#define LIGHT_TYPE_POINT 1
|
||||
#define LIGHT_TYPE_SPOT 2
|
||||
|
||||
// The post pass's tone curve (postParams.y).
|
||||
#define TONEMAP_CURVE_NONE 0
|
||||
#define TONEMAP_CURVE_NEUTRAL 1
|
||||
#define TONEMAP_CURVE_ACES 2
|
||||
|
||||
// What material.w says about the base texture.
|
||||
#define TEXTURE_NONE 0
|
||||
#define TEXTURE_SRGB 1 // A texture the sampler decodes to linear
|
||||
#define TEXTURE_FEED 2 // A video frame or rendered view, sRGB, decoded in the shader
|
||||
|
||||
// How a draw turns to face the camera (the spare lane of its normal matrix, instanceMatrices).
|
||||
#define BILLBOARD_MODE_NONE 0
|
||||
#define BILLBOARD_MODE_ALL 1 // Its axes are the camera's
|
||||
#define BILLBOARD_MODE_Y 2 // Turned about world +Y toward the eye only
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
#define PI 3.14159265
|
||||
#define DEGREES_TO_RADIANS 0.017453292
|
||||
#define MIN_ROUGHNESS 0.045 // Below this the GGX lobe is narrower than a pixel and sparkles
|
||||
#define BILLBOARD_EPSILON 0.000001 // Below this a BILLBOARD_MODE_Y node is under the eye and faces +Z
|
||||
|
||||
|
||||
// ----- Vertex -----
|
||||
|
|
@ -49,6 +50,9 @@ cbuffer DrawUniforms : register(b0, space1) {
|
|||
float4 morphWeights[2]; // Up to MAX_MORPHS active targets ...
|
||||
int4 morphTargets[2]; // ... and which targets they are
|
||||
int4 morphInfo; // x = active count, y = vertices per target, z = the draw's first pair in instanceMatrices
|
||||
float4 billboardRight; // The axes and eye billboards turn to: the pass's camera (a shadow pass gives the window's)
|
||||
float4 billboardUp;
|
||||
float4 billboardEye;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -56,7 +60,8 @@ cbuffer DrawUniforms : register(b0, space1) {
|
|||
StructuredBuffer<float4> morphDeltas : register(t0, space0);
|
||||
|
||||
// Per draw, in draw order: the model matrix then its inverse transpose (for normals under
|
||||
// non-uniform scale); an instanced batch reads consecutive pairs.
|
||||
// non-uniform scale); an instanced batch reads consecutive pairs. The inverse transpose's spare
|
||||
// lane (_m33) holds the draw's BILLBOARD_MODE_*; a billboard's pair is rebuilt here per camera.
|
||||
StructuredBuffer<float4x4> instanceMatrices : register(t1, space0);
|
||||
|
||||
cbuffer SkinUniforms : register(b1, space1) {
|
||||
|
|
@ -102,6 +107,44 @@ void morph(uint vertex, inout float3 position, inout float3 normal) {
|
|||
}
|
||||
|
||||
|
||||
// Turns a billboard's model matrix (and its normal matrix) to this pass's camera: squarely, its
|
||||
// axes the camera's, or about world +Y only toward the eye. The node's own position and scale
|
||||
// stay; the normal matrix of an orthonormal frame under scale is the frame over the scale.
|
||||
void billboard(inout float4x4 model, inout float4x4 normalMatrix) {
|
||||
int mode = (int)normalMatrix._m33;
|
||||
float3 position = model._m03_m13_m23;
|
||||
float3 scale = float3(length(model._m00_m10_m20), length(model._m01_m11_m21), length(model._m02_m12_m22));
|
||||
float3 right;
|
||||
float3 up;
|
||||
float3 toward;
|
||||
|
||||
if (mode == BILLBOARD_MODE_NONE) {
|
||||
return;
|
||||
}
|
||||
if (mode == BILLBOARD_MODE_ALL) {
|
||||
right = billboardRight.xyz;
|
||||
up = billboardUp.xyz;
|
||||
toward = cross(right, up);
|
||||
} else {
|
||||
toward = billboardEye.xyz - position;
|
||||
toward.y = 0.0;
|
||||
if (length(toward) < BILLBOARD_EPSILON) {
|
||||
toward = float3(0.0, 0.0, 1.0);
|
||||
}
|
||||
toward = normalize(toward);
|
||||
up = float3(0.0, 1.0, 0.0);
|
||||
right = cross(up, toward);
|
||||
}
|
||||
scale = max(scale, BILLBOARD_EPSILON);
|
||||
model._m00_m10_m20 = right * scale.x;
|
||||
model._m01_m11_m21 = up * scale.y;
|
||||
model._m02_m12_m22 = toward * scale.z;
|
||||
normalMatrix._m00_m10_m20 = right / scale.x;
|
||||
normalMatrix._m01_m11_m21 = up / scale.y;
|
||||
normalMatrix._m02_m12_m22 = toward / scale.z;
|
||||
}
|
||||
|
||||
|
||||
VertexOutput vertexStatic(VertexInput input, uint vertex : SV_VertexID, uint instance : SV_InstanceID) {
|
||||
VertexOutput output;
|
||||
float3 position = input.position;
|
||||
|
|
@ -109,6 +152,7 @@ VertexOutput vertexStatic(VertexInput input, uint vertex : SV_VertexID, uint ins
|
|||
float4x4 model = instanceMatrices[(morphInfo.z + instance) * 2];
|
||||
float4x4 normalMatrix = instanceMatrices[(morphInfo.z + instance) * 2 + 1];
|
||||
|
||||
billboard(model, normalMatrix);
|
||||
morph(vertex, position, normal);
|
||||
output.worldPosition = mul(model, float4(position, 1.0)).xyz;
|
||||
output.position = mul(viewProjection, float4(output.worldPosition, 1.0));
|
||||
|
|
@ -130,6 +174,7 @@ VertexOutput vertexSkinned(VertexInput input, uint vertex : SV_VertexID, uint in
|
|||
float3 skinnedNormal;
|
||||
float3 skinnedTangent;
|
||||
|
||||
billboard(model, normalMatrix);
|
||||
morph(vertex, morphed, normal);
|
||||
position = float4(morphed, 1.0);
|
||||
|
||||
|
|
|
|||
1286
src/singe.c
1286
src/singe.c
File diff suppressed because it is too large
Load diff
20
src/vfs.c
20
src/vfs.c
|
|
@ -104,8 +104,8 @@ struct VfsStreamS {
|
|||
};
|
||||
|
||||
|
||||
static bool _assetExists(DatabaseT *db, const char *key);
|
||||
static bool _assetDirectory(DatabaseT *db, const char *key);
|
||||
static bool _assetExists(DatabaseT *db, const char *key);
|
||||
static bool _assetSize(DatabaseT *db, const char *key, int64_t *size);
|
||||
static char *_bindListRange(DatabaseT *db, const char *key);
|
||||
static bool _cacheCurrent(const TargetT *target);
|
||||
|
|
@ -115,12 +115,12 @@ static void _databasesClose(void);
|
|||
static bool _fileModified(const char *path, int64_t *size, int64_t *modified);
|
||||
static bool _hasDatabaseExtension(const char *path);
|
||||
static bool _hasParentComponent(const char *norm);
|
||||
static bool _isAbsolute(const char *name);
|
||||
static bool _isEngineName(const char *name);
|
||||
static void _listAdd(ListT *list, const char *name);
|
||||
static int _listCompare(const void *a, const void *b); // qsort callback. Not changing int.
|
||||
static void _listDirectory(const char *path, ListT *list);
|
||||
static char **_listFinish(ListT *list, int32_t *count);
|
||||
static bool _isAbsolute(const char *name);
|
||||
static bool _isEngineName(const char *name);
|
||||
static char *_normalise(const char *name);
|
||||
static char *_overlayFor(const char *dataDirBase, const char *dataDir, const char *databasePath, bool isContainer, const char *directory);
|
||||
static uint8_t *_readAsset(DatabaseT *db, const char *key, size_t *bytes, bool sdlMemory);
|
||||
|
|
@ -137,13 +137,6 @@ static char *_dataDir = NULL;
|
|||
static char *_dataDirKey = NULL; // _dataDirBase normalised without trailing slashes, for _isEngineName
|
||||
|
||||
|
||||
static bool _assetExists(DatabaseT *db, const char *key) {
|
||||
int64_t size = 0;
|
||||
|
||||
return _assetSize(db, key, &size);
|
||||
}
|
||||
|
||||
|
||||
// True when any asset lives below the key, which is what a directory is in a database.
|
||||
static bool _assetDirectory(DatabaseT *db, const char *key) {
|
||||
char *from = _bindListRange(db, key);
|
||||
|
|
@ -156,6 +149,13 @@ static bool _assetDirectory(DatabaseT *db, const char *key) {
|
|||
}
|
||||
|
||||
|
||||
static bool _assetExists(DatabaseT *db, const char *key) {
|
||||
int64_t size = 0;
|
||||
|
||||
return _assetSize(db, key, &size);
|
||||
}
|
||||
|
||||
|
||||
static bool _assetSize(DatabaseT *db, const char *key, int64_t *size) {
|
||||
bool found = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1484,12 +1484,6 @@ bool videoIsPlaying(int32_t playerHandle) {
|
|||
}
|
||||
|
||||
|
||||
// The mixer's audio thread runs the sound and video callbacks; hold this to read what they write.
|
||||
void videoLockAudio(void) {
|
||||
MIX_LockMixer(_mixer);
|
||||
}
|
||||
|
||||
|
||||
// audioFilename may be NULL when the audio lives in the video file. rgb players decode to BGRA so
|
||||
// scripts can read the pixels; everything else stays YUV and is converted by the GPU.
|
||||
int32_t videoLoad(const char *videoFilename, const char *audioFilename, const char *indexPath, SDL_Renderer *renderer, bool rgb) {
|
||||
|
|
@ -1572,6 +1566,12 @@ int32_t videoLoad(const char *videoFilename, const char *audioFilename, const ch
|
|||
}
|
||||
|
||||
|
||||
// The mixer's audio thread runs the sound and video callbacks; hold this to read what they write.
|
||||
void videoLockAudio(void) {
|
||||
MIX_LockMixer(_mixer);
|
||||
}
|
||||
|
||||
|
||||
void videoPause(int32_t playerHandle) {
|
||||
VideoPlayerT *v = _getPlayer(playerHandle, "videoPause");
|
||||
|
||||
|
|
@ -1664,11 +1664,6 @@ void videoSetVolume(int32_t playerHandle, int32_t leftPercent, int32_t rightPerc
|
|||
}
|
||||
|
||||
|
||||
void videoUnlockAudio(void) {
|
||||
MIX_UnlockMixer(_mixer);
|
||||
}
|
||||
|
||||
|
||||
void videoUnload(int32_t playerHandle) {
|
||||
VideoPlayerT *v = _getPlayer(playerHandle, "videoUnload");
|
||||
int32_t x = 0;
|
||||
|
|
@ -1717,6 +1712,11 @@ void videoUnload(int32_t playerHandle) {
|
|||
}
|
||||
|
||||
|
||||
void videoUnlockAudio(void) {
|
||||
MIX_UnlockMixer(_mixer);
|
||||
}
|
||||
|
||||
|
||||
// Advances playback to match the audio clock (or the wall clock for silent videos). Returns the frame now on the texture.
|
||||
int64_t videoUpdate(int32_t playerHandle, SDL_Texture **texture) {
|
||||
VideoPlayerT *v = _getPlayer(playerHandle, "videoUpdate");
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ void videoGetVolume(int32_t playerHandle, int32_t *leftPercent, int32_t *
|
|||
int32_t videoGetWidth(int32_t playerHandle);
|
||||
void videoInit(MIX_Mixer *mixer);
|
||||
bool videoIsPlaying(int32_t playerHandle);
|
||||
void videoLockAudio(void);
|
||||
int32_t videoLoad(const char *videoFilename, const char *audioFilename, const char *indexPath, SDL_Renderer *renderer, bool rgb);
|
||||
void videoLockAudio(void);
|
||||
void videoPause(int32_t playerHandle);
|
||||
void videoPlay(int32_t playerHandle);
|
||||
void videoQuit(void);
|
||||
|
|
@ -63,8 +63,8 @@ void videoSetAudioDelay(int32_t milliseconds);
|
|||
void videoSetAudioTrack(int32_t playerHandle, int32_t track);
|
||||
void videoSetHardwareDecoding(bool enabled);
|
||||
void videoSetVolume(int32_t playerHandle, int32_t leftPercent, int32_t rightPercent);
|
||||
void videoUnlockAudio(void);
|
||||
void videoUnload(int32_t playerHandle);
|
||||
void videoUnlockAudio(void);
|
||||
int64_t videoUpdate(int32_t playerHandle, SDL_Texture **texture);
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue