@genex-ai/cli-demo 0.11.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.js +203 -4
- package/package.json +7 -2
- package/templates/controllers/NOTICE.md +65 -0
- package/templates/controllers/assets/animation-library.glb +0 -0
- package/templates/controllers/assets/character.glb +0 -0
- package/templates/controllers/assets/default-avatar.vrm +0 -0
- package/templates/controllers/character/character-animations.ts +682 -0
- package/templates/controllers/character/character-controller.ts +1636 -0
- package/templates/controllers/character/follow-camera.ts +644 -0
- package/templates/controllers/character/keyboard-input.ts +277 -0
- package/templates/controllers/character/presets.ts +176 -0
- package/templates/controllers/character/touch-joystick.ts +387 -0
- package/templates/controllers/character/vrm/capsule-fit.ts +52 -0
- package/templates/controllers/character/vrm/foot-ik.ts +341 -0
- package/templates/controllers/character/vrm/vrm-loader.ts +44 -0
- package/templates/controllers/character/vrm/vrm-retarget.ts +195 -0
- package/templates/controllers/drone/drone-controller.ts +1073 -0
- package/templates/controllers/drone/presets.ts +225 -0
- package/templates/controllers/interact/enter-exit.ts +502 -0
- package/templates/controllers/shared/colliders.ts +456 -0
- package/templates/controllers/shared/math.ts +230 -0
- package/templates/controllers/shared/physics-world.ts +622 -0
- package/templates/controllers/vehicle/presets.ts +297 -0
- package/templates/controllers/vehicle/vehicle-controller.ts +615 -0
- package/templates/controllers/vehicle/wheel.ts +1200 -0
- package/templates/skills/genex-getting-started/SKILL.md +5 -0
- package/templates/skills/genex-threejs-character-controller/SKILL.md +205 -0
- package/templates/skills/genex-threejs-character-controller/references/animations.md +235 -0
- package/templates/skills/genex-threejs-character-controller/references/tuning-and-presets.md +102 -0
- package/templates/skills/genex-threejs-character-controller/references/wiring.md +198 -0
- package/templates/skills/genex-threejs-embed-auth/SKILL.md +126 -54
- package/templates/skills/genex-threejs-multiplayer/SKILL.md +17 -11
- package/templates/skills/genex-threejs-physics-rapier/SKILL.md +128 -0
- package/templates/skills/genex-threejs-physics-rapier/references/colliders-from-assets.md +202 -0
- package/templates/skills/genex-threejs-physics-rapier/references/physics-setup.md +207 -0
- package/templates/skills/genex-threejs-skill-router/SKILL.md +3 -0
- package/templates/skills/genex-threejs-skill-router/references/routing-map.md +15 -7
- package/templates/skills/genex-threejs-vehicle-controllers/SKILL.md +110 -0
- package/templates/skills/genex-threejs-vehicle-controllers/references/car.md +162 -0
- package/templates/skills/genex-threejs-vehicle-controllers/references/drone.md +150 -0
- package/templates/skills/genex-threejs-vehicle-controllers/references/enter-exit.md +199 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023-2026 Erdong Chen
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Vanilla-TypeScript port of the ecctrl vehicle controller (React/R3F removed).
|
|
4
|
+
// Pure data: named vehicle presets. `arcade-kart` and `muscle-drift` are
|
|
5
|
+
// verbatim from the upstream MIT demo's two tuned vehicles; `offroad-bouncy`
|
|
6
|
+
// and `race-grip` are Genex-authored starting points derived from them.
|
|
7
|
+
|
|
8
|
+
import type { CarConfig } from "./vehicle-controller.ts";
|
|
9
|
+
import type { WheelOptions } from "./wheel.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One chassis collider (cuboid) to attach to `vehicle.body`, e.g. via
|
|
13
|
+
* `cuboidCollider(world, vehicle.body, [x, y, z], { position, density })`.
|
|
14
|
+
*/
|
|
15
|
+
export type ChassisColliderSpec = {
|
|
16
|
+
/** Cuboid HALF extents (m). */
|
|
17
|
+
halfExtents: { x: number; y: number; z: number };
|
|
18
|
+
/** Collider offset from the body origin (m). */
|
|
19
|
+
offset: { x: number; y: number; z: number };
|
|
20
|
+
/** Collider density (kg/m^3). Chassis mass = volume x density. */
|
|
21
|
+
density: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** One wheel mount: local position (+Z = front) plus per-wheel role flags. */
|
|
25
|
+
export type WheelSlotSpec = {
|
|
26
|
+
position: { x: number; y: number; z: number };
|
|
27
|
+
steerWheel?: boolean;
|
|
28
|
+
driveWheel?: boolean;
|
|
29
|
+
brakeWheel?: boolean;
|
|
30
|
+
driveTorqueWeight?: number;
|
|
31
|
+
maxBrakeTorque?: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A complete car recipe: chassis colliders, drivetrain config, shared wheel
|
|
36
|
+
* options, and the four wheel slots (order FL, FR, RL, RR; +Z = forward,
|
|
37
|
+
* +X = left).
|
|
38
|
+
*
|
|
39
|
+
* IMPORTANT: suspension `springK`/`dampingC` in `wheelShared` scale with
|
|
40
|
+
* chassis MASS — every preset states its `assumedChassisDensity` for the
|
|
41
|
+
* listed collider volumes. If your chassis is materially heavier/lighter,
|
|
42
|
+
* rescale springK proportionally and keep dampingC below
|
|
43
|
+
* `2*sqrt(springK * massPerWheel)`.
|
|
44
|
+
*
|
|
45
|
+
* Wiring sketch:
|
|
46
|
+
* ```ts
|
|
47
|
+
* const car = new VehicleController({ world, carConfig: preset.carConfig });
|
|
48
|
+
* for (const c of preset.chassisColliders)
|
|
49
|
+
* cuboidCollider(world, car.body, [c.halfExtents.x, c.halfExtents.y, c.halfExtents.z],
|
|
50
|
+
* { position: [c.offset.x, c.offset.y, c.offset.z], density: c.density });
|
|
51
|
+
* for (const slot of preset.wheelSlots)
|
|
52
|
+
* car.addWheel({ ...preset.wheelShared, ...slot,
|
|
53
|
+
* position: new THREE.Vector3(slot.position.x, slot.position.y, slot.position.z) });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export type VehiclePreset = {
|
|
57
|
+
name: string;
|
|
58
|
+
/** Density the spring constants were tuned against (see note above). */
|
|
59
|
+
assumedChassisDensity: number;
|
|
60
|
+
carConfig: Partial<CarConfig>;
|
|
61
|
+
/** Applied to every wheel (slots override per-wheel fields). */
|
|
62
|
+
wheelShared: Omit<Partial<WheelOptions>, "position">;
|
|
63
|
+
/** FL, FR, RL, RR. */
|
|
64
|
+
wheelSlots: WheelSlotSpec[];
|
|
65
|
+
chassisColliders: ChassisColliderSpec[];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Upstream demo "Vehicle 1", verbatim: an all-wheel-drive, front-steer kart
|
|
70
|
+
* with stiff suspension and forgiving grip. The safe default.
|
|
71
|
+
*/
|
|
72
|
+
export const arcadeKart: VehiclePreset = {
|
|
73
|
+
name: "arcade-kart",
|
|
74
|
+
assumedChassisDensity: 200,
|
|
75
|
+
carConfig: {
|
|
76
|
+
engineHorsepower: 600,
|
|
77
|
+
engineMaxRPM: 6000,
|
|
78
|
+
gearRatios: [10],
|
|
79
|
+
finalDriveRatio: 1,
|
|
80
|
+
transmissionMode: "auto",
|
|
81
|
+
shiftUpRPM: 5200,
|
|
82
|
+
shiftDownRPM: 2200,
|
|
83
|
+
shiftCooldown: 0.35,
|
|
84
|
+
steerRate: Math.PI * 2,
|
|
85
|
+
maxSteerAngle: Math.PI / 6,
|
|
86
|
+
reverseTorqueScale: 1,
|
|
87
|
+
reverseRPMScale: 0.5,
|
|
88
|
+
},
|
|
89
|
+
wheelShared: {
|
|
90
|
+
springK: 38000,
|
|
91
|
+
dampingC: 4000,
|
|
92
|
+
rayShapeR: 0.5,
|
|
93
|
+
rayShapeH: 0.15,
|
|
94
|
+
rayLength: 0.5,
|
|
95
|
+
maxBrakeTorque: 3000,
|
|
96
|
+
tireGripFactor: 1.3,
|
|
97
|
+
wheelModelDensity: 100,
|
|
98
|
+
wheelModelRadius: 0.5,
|
|
99
|
+
},
|
|
100
|
+
wheelSlots: [
|
|
101
|
+
{ position: { x: 0.9, y: 0, z: 1.8 }, steerWheel: true, driveWheel: true, brakeWheel: true },
|
|
102
|
+
{ position: { x: -0.9, y: 0, z: 1.8 }, steerWheel: true, driveWheel: true, brakeWheel: true },
|
|
103
|
+
{ position: { x: 0.9, y: 0, z: -1.8 }, driveWheel: true, brakeWheel: true },
|
|
104
|
+
{ position: { x: -0.9, y: 0, z: -1.8 }, driveWheel: true, brakeWheel: true },
|
|
105
|
+
],
|
|
106
|
+
chassisColliders: [
|
|
107
|
+
{
|
|
108
|
+
halfExtents: { x: 1, y: 0.4, z: 2.4 },
|
|
109
|
+
offset: { x: 0, y: 0.1, z: 0 },
|
|
110
|
+
density: 200,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Upstream demo "Vehicle 2", verbatim: softer suspension, rear-biased drive
|
|
117
|
+
* torque (rear `driveTorqueWeight` 2) — power-oversteers into drifts under
|
|
118
|
+
* throttle.
|
|
119
|
+
*/
|
|
120
|
+
export const muscleDrift: VehiclePreset = {
|
|
121
|
+
name: "muscle-drift",
|
|
122
|
+
assumedChassisDensity: 200,
|
|
123
|
+
carConfig: {
|
|
124
|
+
engineHorsepower: 600,
|
|
125
|
+
engineMaxRPM: 6000,
|
|
126
|
+
gearRatios: [10],
|
|
127
|
+
finalDriveRatio: 1,
|
|
128
|
+
transmissionMode: "auto",
|
|
129
|
+
shiftUpRPM: 5200,
|
|
130
|
+
shiftDownRPM: 2200,
|
|
131
|
+
shiftCooldown: 0.35,
|
|
132
|
+
steerRate: Math.PI * 2,
|
|
133
|
+
maxSteerAngle: Math.PI / 6,
|
|
134
|
+
reverseTorqueScale: 1,
|
|
135
|
+
reverseRPMScale: 0.5,
|
|
136
|
+
},
|
|
137
|
+
wheelShared: {
|
|
138
|
+
springK: 25000,
|
|
139
|
+
dampingC: 3200,
|
|
140
|
+
rayShapeR: 0.5,
|
|
141
|
+
rayShapeH: 0.15,
|
|
142
|
+
rayLength: 0.5,
|
|
143
|
+
tireGripFactor: 1.3,
|
|
144
|
+
wheelModelDensity: 100,
|
|
145
|
+
wheelModelRadius: 0.5,
|
|
146
|
+
},
|
|
147
|
+
wheelSlots: [
|
|
148
|
+
{ position: { x: 0.85, y: 0, z: 1.5 }, steerWheel: true, driveWheel: true, brakeWheel: true, maxBrakeTorque: 2600 },
|
|
149
|
+
{ position: { x: -0.85, y: 0, z: 1.5 }, steerWheel: true, driveWheel: true, brakeWheel: true, maxBrakeTorque: 2600 },
|
|
150
|
+
{ position: { x: 0.85, y: 0, z: -1.5 }, driveWheel: true, brakeWheel: true, driveTorqueWeight: 2, maxBrakeTorque: 1800 },
|
|
151
|
+
{ position: { x: -0.85, y: 0, z: -1.5 }, driveWheel: true, brakeWheel: true, driveTorqueWeight: 2, maxBrakeTorque: 1800 },
|
|
152
|
+
],
|
|
153
|
+
chassisColliders: [
|
|
154
|
+
{
|
|
155
|
+
halfExtents: { x: 0.8, y: 0.17, z: 1 },
|
|
156
|
+
offset: { x: 0, y: 0.5, z: -0.6 },
|
|
157
|
+
density: 100,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
halfExtents: { x: 1, y: 0.3, z: 2.4 },
|
|
161
|
+
offset: { x: 0, y: 0, z: 0 },
|
|
162
|
+
density: 200,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Genex-authored starting point (no upstream source — TUNE IN TESTBED):
|
|
169
|
+
* softer, longer-travel suspension for a springy ride over bumps, bigger
|
|
170
|
+
* wheels, lower grip, wider steering. Derived from `arcade-kart`.
|
|
171
|
+
*/
|
|
172
|
+
export const offroadBouncy: VehiclePreset = {
|
|
173
|
+
name: "offroad-bouncy",
|
|
174
|
+
assumedChassisDensity: 200,
|
|
175
|
+
carConfig: {
|
|
176
|
+
engineHorsepower: 450,
|
|
177
|
+
engineMaxRPM: 6000,
|
|
178
|
+
gearRatios: [10],
|
|
179
|
+
finalDriveRatio: 1,
|
|
180
|
+
transmissionMode: "auto",
|
|
181
|
+
shiftUpRPM: 5200,
|
|
182
|
+
shiftDownRPM: 2200,
|
|
183
|
+
shiftCooldown: 0.35,
|
|
184
|
+
steerRate: Math.PI * 2,
|
|
185
|
+
maxSteerAngle: Math.PI / 5,
|
|
186
|
+
reverseTorqueScale: 1,
|
|
187
|
+
reverseRPMScale: 0.5,
|
|
188
|
+
},
|
|
189
|
+
wheelShared: {
|
|
190
|
+
springK: 16000,
|
|
191
|
+
dampingC: 1600,
|
|
192
|
+
rayShapeR: 0.6,
|
|
193
|
+
rayShapeH: 0.15,
|
|
194
|
+
rayLength: 0.8,
|
|
195
|
+
maxBrakeTorque: 3000,
|
|
196
|
+
tireGripFactor: 1.0,
|
|
197
|
+
lowVelThreshold: 0.5,
|
|
198
|
+
rollingResistanceCoef: 0.012,
|
|
199
|
+
wheelModelDensity: 100,
|
|
200
|
+
wheelModelRadius: 0.6,
|
|
201
|
+
},
|
|
202
|
+
wheelSlots: [
|
|
203
|
+
{ position: { x: 0.9, y: 0, z: 1.8 }, steerWheel: true, driveWheel: true, brakeWheel: true },
|
|
204
|
+
{ position: { x: -0.9, y: 0, z: 1.8 }, steerWheel: true, driveWheel: true, brakeWheel: true },
|
|
205
|
+
{ position: { x: 0.9, y: 0, z: -1.8 }, driveWheel: true, brakeWheel: true },
|
|
206
|
+
{ position: { x: -0.9, y: 0, z: -1.8 }, driveWheel: true, brakeWheel: true },
|
|
207
|
+
],
|
|
208
|
+
chassisColliders: [
|
|
209
|
+
{
|
|
210
|
+
halfExtents: { x: 1, y: 0.4, z: 2.4 },
|
|
211
|
+
offset: { x: 0, y: 0.1, z: 0 },
|
|
212
|
+
density: 200,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Genex-authored starting point (no upstream source — tuned in the testbed):
|
|
219
|
+
* stiff rear-wheel-drive racer with high grip and a 4-speed gearbox so the
|
|
220
|
+
* RPM-threshold auto shift actually exercises. Chassis from `muscle-drift`.
|
|
221
|
+
*
|
|
222
|
+
* Gearing note: the tire model's rolling resistance grows with wheel speed,
|
|
223
|
+
* so each gear has a drag-limited equilibrium RPM (measured in the testbed:
|
|
224
|
+
* ~5500 / ~4900 / ~4100 for gears 1-3). `shiftUpRPM` must sit BELOW the
|
|
225
|
+
* next-lower gear's equilibrium or the shift point is never reached; 4300
|
|
226
|
+
* gives two clean upshifts on flat ground and post-shift RPM ~2800-3000
|
|
227
|
+
* (safely above `shiftDownRPM` — no hunting).
|
|
228
|
+
*/
|
|
229
|
+
export const raceGrip: VehiclePreset = {
|
|
230
|
+
name: "race-grip",
|
|
231
|
+
assumedChassisDensity: 200,
|
|
232
|
+
carConfig: {
|
|
233
|
+
engineHorsepower: 800,
|
|
234
|
+
engineMaxRPM: 6000,
|
|
235
|
+
gearRatios: [20, 13, 9, 6.5],
|
|
236
|
+
finalDriveRatio: 1,
|
|
237
|
+
transmissionMode: "auto",
|
|
238
|
+
shiftUpRPM: 4300,
|
|
239
|
+
shiftDownRPM: 2400,
|
|
240
|
+
shiftCooldown: 0.35,
|
|
241
|
+
steerRate: Math.PI * 2,
|
|
242
|
+
maxSteerAngle: Math.PI / 7,
|
|
243
|
+
reverseTorqueScale: 1,
|
|
244
|
+
reverseRPMScale: 0.3,
|
|
245
|
+
},
|
|
246
|
+
wheelShared: {
|
|
247
|
+
springK: 42000,
|
|
248
|
+
dampingC: 5200,
|
|
249
|
+
rayShapeR: 0.5,
|
|
250
|
+
rayShapeH: 0.15,
|
|
251
|
+
rayLength: 0.5,
|
|
252
|
+
// Grip vs rollover (measured in the testbed): the lat slip curve keeps
|
|
253
|
+
// ~90% grip even in a full slide, so peak lateral acceleration is
|
|
254
|
+
// ~(groundFriction + tireGripFactor)/2 * latFrictionEllipseScale in g.
|
|
255
|
+
// Keep that at or below the static rollover threshold
|
|
256
|
+
// (halfTrack / comHeight, ~1.0 g for this chassis) or a handbrake slide
|
|
257
|
+
// trips the car over instead of drifting.
|
|
258
|
+
tireGripFactor: 1.5,
|
|
259
|
+
lngFrictionEllipseScale: 1.15,
|
|
260
|
+
latFrictionEllipseScale: 0.8,
|
|
261
|
+
// Low-resistance racing tires (library default 0.007): raises the
|
|
262
|
+
// drag-limited top speed per gear so the auto shift has headroom.
|
|
263
|
+
rollingResistanceCoef: 0.004,
|
|
264
|
+
wheelModelDensity: 100,
|
|
265
|
+
wheelModelRadius: 0.5,
|
|
266
|
+
},
|
|
267
|
+
wheelSlots: [
|
|
268
|
+
{ position: { x: 0.85, y: 0, z: 1.5 }, steerWheel: true, brakeWheel: true, maxBrakeTorque: 2600 },
|
|
269
|
+
{ position: { x: -0.85, y: 0, z: 1.5 }, steerWheel: true, brakeWheel: true, maxBrakeTorque: 2600 },
|
|
270
|
+
{ position: { x: 0.85, y: 0, z: -1.5 }, driveWheel: true, brakeWheel: true, driveTorqueWeight: 1.5, maxBrakeTorque: 1800 },
|
|
271
|
+
{ position: { x: -0.85, y: 0, z: -1.5 }, driveWheel: true, brakeWheel: true, driveTorqueWeight: 1.5, maxBrakeTorque: 1800 },
|
|
272
|
+
],
|
|
273
|
+
chassisColliders: [
|
|
274
|
+
// Light cabin + low-slung main mass: keeps the CoM near axle height so
|
|
275
|
+
// hard cornering leans instead of tripping over.
|
|
276
|
+
{
|
|
277
|
+
halfExtents: { x: 0.8, y: 0.17, z: 1 },
|
|
278
|
+
offset: { x: 0, y: 0.5, z: -0.6 },
|
|
279
|
+
density: 60,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
halfExtents: { x: 1, y: 0.3, z: 2.4 },
|
|
283
|
+
offset: { x: 0, y: -0.15, z: 0 },
|
|
284
|
+
density: 200,
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** All presets by kebab-case name. */
|
|
290
|
+
export const vehiclePresets: Readonly<
|
|
291
|
+
Record<"arcade-kart" | "muscle-drift" | "offroad-bouncy" | "race-grip", VehiclePreset>
|
|
292
|
+
> = {
|
|
293
|
+
"arcade-kart": arcadeKart,
|
|
294
|
+
"muscle-drift": muscleDrift,
|
|
295
|
+
"offroad-bouncy": offroadBouncy,
|
|
296
|
+
"race-grip": raceGrip,
|
|
297
|
+
};
|