@fonsecabarreto/genesis-gl-core 0.1.0 → 0.1.1

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.
Files changed (40) hide show
  1. package/README.md +19 -2
  2. package/dist/{Camera-DY_8gx3C.d.ts → Camera-CJVYy9fH.d.ts} +13 -2
  3. package/dist/Core/classes/Material.d.ts +1 -1
  4. package/dist/Core/classes/Material.js +1 -1
  5. package/dist/Core/classes/Model.d.ts +3 -3
  6. package/dist/Core/classes/Model.js +1 -1
  7. package/dist/Core/classes/Renderer.d.ts +11 -5
  8. package/dist/Core/classes/Renderer.js +4 -4
  9. package/dist/Core/classes/Scene.d.ts +2 -2
  10. package/dist/Core/classes/Viewport.d.ts +1 -1
  11. package/dist/Core/classes/Viewport.js +1 -1
  12. package/dist/Core/index.d.ts +4 -4
  13. package/dist/Core/index.js +4 -4
  14. package/dist/Core/utils/load-glb.d.ts +3 -3
  15. package/dist/Core/utils/load-glb.js +4 -4
  16. package/dist/Core/utils/parse-obj.d.ts +2 -2
  17. package/dist/Core/utils/parse-obj.js +4 -4
  18. package/dist/Editor/index.d.ts +126 -15
  19. package/dist/Editor/index.js +471 -74
  20. package/dist/Editor/index.js.map +1 -1
  21. package/dist/Game/controls/KeyboardInput.d.ts +4 -4
  22. package/dist/Game/index.d.ts +308 -7
  23. package/dist/Game/index.js +470 -24
  24. package/dist/Game/index.js.map +1 -1
  25. package/dist/{KeyboardInput-DTsfj3tE.d.ts → KeyboardInput-1xOAabI0.d.ts} +95 -14
  26. package/dist/{Material-BGLkldxv.d.ts → Material-DhwSRbP2.d.ts} +8 -0
  27. package/dist/{Model-CQvDXd-b.d.ts → Model-BBZHnUp1.d.ts} +24 -8
  28. package/dist/{chunk-6LS6AO5H.js → chunk-L66K4AZU.js} +36 -30
  29. package/dist/chunk-L66K4AZU.js.map +1 -0
  30. package/dist/{chunk-JK2HEZAT.js → chunk-QOAQVTAB.js} +26 -22
  31. package/dist/chunk-QOAQVTAB.js.map +1 -0
  32. package/dist/{chunk-5TAAXI6S.js → chunk-XMW2MS66.js} +39 -16
  33. package/dist/chunk-XMW2MS66.js.map +1 -0
  34. package/dist/{chunk-QCQVJCSR.js → chunk-ZCJ3MJZD.js} +103 -67
  35. package/dist/chunk-ZCJ3MJZD.js.map +1 -0
  36. package/package.json +1 -1
  37. package/dist/chunk-5TAAXI6S.js.map +0 -1
  38. package/dist/chunk-6LS6AO5H.js.map +0 -1
  39. package/dist/chunk-JK2HEZAT.js.map +0 -1
  40. package/dist/chunk-QCQVJCSR.js.map +0 -1
@@ -3,14 +3,22 @@ import {
3
3
  } from "../chunk-P7QOKDLY.js";
4
4
  import "../chunk-SUNYSY45.js";
5
5
  import {
6
- Mesh
7
- } from "../chunk-QCQVJCSR.js";
8
- import "../chunk-6LS6AO5H.js";
6
+ Mesh,
7
+ Renderer,
8
+ WebGLCore
9
+ } from "../chunk-ZCJ3MJZD.js";
10
+ import {
11
+ Material
12
+ } from "../chunk-L66K4AZU.js";
9
13
  import {
10
14
  Model
11
- } from "../chunk-JK2HEZAT.js";
12
- import "../chunk-3ULETMWF.js";
13
- import "../chunk-5TAAXI6S.js";
15
+ } from "../chunk-QOAQVTAB.js";
16
+ import {
17
+ Scene
18
+ } from "../chunk-3ULETMWF.js";
19
+ import {
20
+ Viewport
21
+ } from "../chunk-XMW2MS66.js";
14
22
 
15
23
  // src/Game/classes/AnimationController.ts
16
24
  var AnimationController = class {
@@ -76,32 +84,96 @@ var AnimationController = class {
76
84
  }
77
85
  };
78
86
 
79
- // src/Game/classes/Character.ts
80
- var Character = class extends Model {
81
- id;
82
- health = 100;
83
- /** Friction applied when grounded — updated from the surface material on landing.
84
- * 0 = no resistance (pure ice), 1 = instant stop. */
87
+ // src/Game/classes/PhysicsBody.ts
88
+ var PhysicsBody = class extends Model {
89
+ // ── Linear dynamics ───────────────────────────────────────────────────────
90
+ /** World-space velocity in units/second (XYZ). */
91
+ velocity = [0, 0, 0];
92
+ /**
93
+ * When `true` the owning physics step should add gravity to `velocity[1]`
94
+ * every tick. Set to `false` for server-driven actors whose Y position is
95
+ * authoritative from the network and must not be locally simulated.
96
+ */
97
+ useGravity = true;
98
+ // ── Surface interaction ───────────────────────────────────────────────────
99
+ /**
100
+ * Friction coefficient of the surface this body is currently standing on.
101
+ * Updated on landing from the collided model's material.
102
+ * Range: 0 (pure ice) → 1 (instant stop).
103
+ */
85
104
  surfaceFriction = 0.3;
86
- /** Horizontal damping while airborne. 0 = no air drag, 1 = instant stop. */
105
+ /**
106
+ * Horizontal damping applied while airborne.
107
+ * Kept low so air strafing feels natural and not floaty.
108
+ */
87
109
  airFriction = 0.02;
88
- speed = 0.2;
89
- stamina = 10;
90
- velocity = [0, 0, 0];
110
+ // ── Mass & restitution ────────────────────────────────────────────────────
111
+ /**
112
+ * Mass in kilograms. Used to distribute impulses proportionally when two
113
+ * dynamic bodies collide: heavier objects are displaced and accelerated
114
+ * less. Default: `1.0`. Set higher for heavy objects (e.g. boulders) or
115
+ * lower for light ones (e.g. beach balls).
116
+ */
117
+ mass = 1;
118
+ /**
119
+ * Coefficient of restitution — fraction of relative velocity preserved
120
+ * along the collision normal. Range: 0 (perfectly inelastic) → 1 (fully
121
+ * elastic). Default: `0` (rigid body, no bounce). `Ball` overrides this
122
+ * to `0.6`. When two bodies collide the effective restitution is the
123
+ * geometric mean of both values.
124
+ */
125
+ restitution = 0;
126
+ // ── Ground state ──────────────────────────────────────────────────────────
91
127
  _isGrounded = false;
92
- /** Setting to true (landing) resets the jump counter. */
93
128
  get isGrounded() {
94
129
  return this._isGrounded;
95
130
  }
131
+ /**
132
+ * Setting to `true` when the previous state was `false` triggers
133
+ * the `onLand()` hook — override in subclasses for gameplay responses
134
+ * (e.g. landing roll, jump counter reset).
135
+ */
96
136
  set isGrounded(v) {
97
- if (v && !this._isGrounded) {
98
- const hardLanding = this.velocity[1] <= this.minLandingImpact;
99
- if (this.isInputActive && hardLanding) this._landingRoll = true;
100
- this._jumpsUsed = 0;
101
- this.isDoubleJumping = false;
102
- }
137
+ const wasGrounded = this._isGrounded;
103
138
  this._isGrounded = v;
139
+ if (v && !wasGrounded) {
140
+ this.onLand();
141
+ }
142
+ }
143
+ // ── Constructor ───────────────────────────────────────────────────────────
144
+ constructor(meshes, translation = [0, 0, 0], scale = [1, 1, 1], name = "physicsBody", skeleton = null) {
145
+ super(meshes, translation, scale, name, skeleton);
146
+ }
147
+ // ── Virtual hooks ─────────────────────────────────────────────────────────
148
+ /**
149
+ * Called once when this body transitions from airborne to grounded.
150
+ * Override to add gameplay responses (landing roll, sound, VFX, etc.).
151
+ * Base implementation does nothing.
152
+ */
153
+ onLand() {
104
154
  }
155
+ /**
156
+ * Called by the collision system when a vertical (Y-axis) surface is hit.
157
+ * Receives the velocity **at the moment of impact** so subclasses can
158
+ * compute a bounce impulse before the caller updates `velocity[1]`.
159
+ *
160
+ * Default: zero out vertical velocity (rigid body, no bounce).
161
+ * Override in `Ball` (or any bouncy actor) to apply a restitution impulse.
162
+ *
163
+ * @param impactVy `velocity[1]` captured just before the collision resolve.
164
+ */
165
+ resolveYCollision(impactVy) {
166
+ void impactVy;
167
+ this.velocity[1] = 0;
168
+ }
169
+ };
170
+
171
+ // src/Game/classes/Character.ts
172
+ var Character = class extends PhysicsBody {
173
+ id;
174
+ health = 100;
175
+ speed = 0.2;
176
+ stamina = 10;
105
177
  /** Pending auto-roll on landing after a double jump. */
106
178
  _landingRoll = false;
107
179
  /** Minimum downward impact speed (negative Y velocity) required to trigger
@@ -136,6 +208,16 @@ var Character = class extends Model {
136
208
  // seconds
137
209
  _jumpsUsed = 0;
138
210
  maxJumps = 2;
211
+ /**
212
+ * Called by {@link PhysicsBody.isGrounded} setter on every landing.
213
+ * Queues a landing roll if the impact was hard enough and resets jump state.
214
+ */
215
+ onLand() {
216
+ const hardLanding = this.velocity[1] <= this.minLandingImpact;
217
+ if (this.isInputActive && hardLanding) this._landingRoll = true;
218
+ this._jumpsUsed = 0;
219
+ this.isDoubleJumping = false;
220
+ }
139
221
  constructor(id, baseModel) {
140
222
  super(
141
223
  baseModel.meshes,
@@ -342,12 +424,376 @@ var Leg = class extends Mesh {
342
424
  }
343
425
  }
344
426
  };
427
+
428
+ // src/Game/classes/Actor.ts
429
+ var Actor = class extends Model {
430
+ constructor(meshes, translation = [0, 0, 0], scale = [1, 1, 1], name = "actor", skeleton = null) {
431
+ super(meshes, translation, scale, name, skeleton);
432
+ }
433
+ };
434
+
435
+ // src/Core/utils/create-sphere.ts
436
+ function createSphereMesh(webglCore, radius = 1, latBands = 12, lonBands = 12, color = [1, 1, 1]) {
437
+ const positions = [];
438
+ const normals = [];
439
+ for (let lat = 0; lat <= latBands; lat++) {
440
+ const theta = lat * Math.PI / latBands;
441
+ const sinTheta = Math.sin(theta);
442
+ const cosTheta = Math.cos(theta);
443
+ for (let lon = 0; lon <= lonBands; lon++) {
444
+ const phi = lon * 2 * Math.PI / lonBands;
445
+ const sinPhi = Math.sin(phi);
446
+ const cosPhi = Math.cos(phi);
447
+ const x = cosPhi * sinTheta;
448
+ const y = cosTheta;
449
+ const z = sinPhi * sinTheta;
450
+ positions.push(radius * x, radius * y, radius * z);
451
+ normals.push(x, y, z);
452
+ }
453
+ }
454
+ const indices = [];
455
+ for (let lat = 0; lat < latBands; lat++) {
456
+ for (let lon = 0; lon < lonBands; lon++) {
457
+ const first = lat * (lonBands + 1) + lon;
458
+ const second = first + lonBands + 1;
459
+ indices.push(first, second, first + 1);
460
+ indices.push(second, second + 1, first + 1);
461
+ }
462
+ }
463
+ const vertices = [];
464
+ const finalNormals = [];
465
+ for (const idx of indices) {
466
+ vertices.push(
467
+ positions[idx * 3],
468
+ positions[idx * 3 + 1],
469
+ positions[idx * 3 + 2]
470
+ );
471
+ finalNormals.push(
472
+ normals[idx * 3],
473
+ normals[idx * 3 + 1],
474
+ normals[idx * 3 + 2]
475
+ );
476
+ }
477
+ const material = new Material(webglCore, {
478
+ albedoColor: [color[0], color[1], color[2], 1],
479
+ unlit: true
480
+ // lights always glow
481
+ });
482
+ return new Mesh(
483
+ "sphere",
484
+ new Float32Array(vertices),
485
+ new Float32Array(finalNormals),
486
+ material
487
+ );
488
+ }
489
+
490
+ // src/Game/classes/InteractiveActor.ts
491
+ var InteractiveActor = class _InteractiveActor extends PhysicsBody {
492
+ /**
493
+ * Minimum impact speed (|vy|) required to trigger a visible bounce.
494
+ * Below this threshold the actor comes to rest instead of micro-bouncing.
495
+ * Default: `0.05`.
496
+ */
497
+ minBounceSpeed = 0.05;
498
+ constructor(meshes, position = [0, 0, 0], name = "interactiveActor") {
499
+ super(meshes, position, [1, 1, 1], name);
500
+ }
501
+ // ── Static factories ──────────────────────────────────────────────────────
502
+ /**
503
+ * Create an {@link InteractiveActor} with a procedurally generated sphere
504
+ * mesh — convenience shorthand for the common "physics ball" use case.
505
+ *
506
+ * @param webglCore WebGL context used to build the mesh buffers.
507
+ * @param material Material applied to the sphere.
508
+ * @param radius Sphere radius in world units. Default `0.5`.
509
+ * @param position Initial world position. Default origin.
510
+ * @param latBands Latitude subdivisions. Default `16`.
511
+ * @param lonBands Longitude subdivisions. Default `16`.
512
+ */
513
+ static sphere(webglCore, material, radius = 0.5, position = [0, 0, 0], latBands = 16, lonBands = 16) {
514
+ const mesh = createSphereMesh(
515
+ webglCore,
516
+ radius,
517
+ latBands,
518
+ lonBands,
519
+ material.albedoColor?.slice(0, 3) ?? [1, 1, 1]
520
+ );
521
+ mesh.material = material;
522
+ const actor = new _InteractiveActor([mesh], position, "ball");
523
+ actor.restitution = 0.6;
524
+ actor.surfaceFriction = 0.05;
525
+ return actor;
526
+ }
527
+ // ── PhysicsBody hooks ─────────────────────────────────────────────────────
528
+ /**
529
+ * Apply a bounce impulse instead of zeroing Y velocity.
530
+ * The collision system passes the velocity captured just before impact so
531
+ * the correct outgoing speed can be computed from {@link restitution}.
532
+ */
533
+ resolveYCollision(impactVy) {
534
+ if (Math.abs(impactVy) > this.minBounceSpeed) {
535
+ this.velocity[1] = -impactVy * this.restitution;
536
+ this._isGrounded = false;
537
+ } else {
538
+ this.velocity[1] = 0;
539
+ }
540
+ }
541
+ // ── Update ────────────────────────────────────────────────────────────────
542
+ /**
543
+ * Advance one physics tick: integrate velocity, apply friction, zero
544
+ * sub-threshold components so the actor eventually comes to rest.
545
+ */
546
+ update(deltaTime) {
547
+ super.update(deltaTime);
548
+ if (this._isGrounded) {
549
+ const friction = Math.min(0.9, this.surfaceFriction + 5e-3);
550
+ this.velocity[0] += (0 - this.velocity[0]) * friction;
551
+ this.velocity[2] += (0 - this.velocity[2]) * friction;
552
+ } else {
553
+ this.velocity[0] *= 1 - this.airFriction;
554
+ this.velocity[2] *= 1 - this.airFriction;
555
+ }
556
+ this.move(this.velocity[0], this.velocity[1], this.velocity[2]);
557
+ if (Math.abs(this.velocity[0]) < 1e-3) this.velocity[0] = 0;
558
+ if (Math.abs(this.velocity[1]) < 1e-3) this.velocity[1] = 0;
559
+ if (Math.abs(this.velocity[2]) < 1e-3) this.velocity[2] = 0;
560
+ }
561
+ };
562
+
563
+ // src/Game/GameLoopRunner.ts
564
+ var GameLoopRunner = class {
565
+ /**
566
+ * @param targetFPS Number of fixed-update ticks per second. Defaults to 60.
567
+ */
568
+ constructor(targetFPS = 60) {
569
+ this.targetFPS = targetFPS;
570
+ this.stepMs = 1e3 / targetFPS;
571
+ }
572
+ targetFPS;
573
+ /** Number of milliseconds between each fixed-update tick. */
574
+ stepMs;
575
+ rafHandle = null;
576
+ lastTime = 0;
577
+ accumulated = 0;
578
+ fixedUpdateFn = null;
579
+ renderFn = null;
580
+ // ── Public API ────────────────────────────────────────────────────────────
581
+ /**
582
+ * Begin the loop.
583
+ *
584
+ * @param fixedUpdate Called at the fixed timestep rate with `dt` in seconds.
585
+ * @param render Called once per animation frame (variable rate).
586
+ */
587
+ start(fixedUpdate, render) {
588
+ if (this.rafHandle !== null) {
589
+ console.warn(
590
+ "[GameLoopRunner] start() called while already running \u2014 ignoring."
591
+ );
592
+ return;
593
+ }
594
+ this.fixedUpdateFn = fixedUpdate;
595
+ this.renderFn = render;
596
+ this.lastTime = performance.now();
597
+ this.accumulated = 0;
598
+ this.rafHandle = requestAnimationFrame(this.tick);
599
+ }
600
+ /** Pause the loop without discarding callbacks. Resume with `resume()`. */
601
+ pause() {
602
+ if (this.rafHandle !== null) {
603
+ cancelAnimationFrame(this.rafHandle);
604
+ this.rafHandle = null;
605
+ }
606
+ }
607
+ /** Resume a paused loop. */
608
+ resume() {
609
+ if (this.rafHandle !== null) return;
610
+ if (!this.fixedUpdateFn || !this.renderFn) {
611
+ console.warn(
612
+ "[GameLoopRunner] resume() called before start() \u2014 ignoring."
613
+ );
614
+ return;
615
+ }
616
+ this.lastTime = performance.now();
617
+ this.rafHandle = requestAnimationFrame(this.tick);
618
+ }
619
+ /** Stop the loop and release all callbacks. */
620
+ stop() {
621
+ this.pause();
622
+ this.fixedUpdateFn = null;
623
+ this.renderFn = null;
624
+ this.accumulated = 0;
625
+ }
626
+ get isRunning() {
627
+ return this.rafHandle !== null;
628
+ }
629
+ // ── Private ───────────────────────────────────────────────────────────────
630
+ /** Arrow function to preserve `this` across rAF callbacks. */
631
+ tick = (now) => {
632
+ const elapsed = now - this.lastTime;
633
+ this.lastTime = now;
634
+ this.accumulated += Math.min(elapsed, this.stepMs * 5);
635
+ while (this.accumulated >= this.stepMs) {
636
+ this.fixedUpdateFn?.(this.stepMs / 1e3);
637
+ this.accumulated -= this.stepMs;
638
+ }
639
+ this.renderFn?.();
640
+ if (this.rafHandle !== null) {
641
+ this.rafHandle = requestAnimationFrame(this.tick);
642
+ }
643
+ };
644
+ };
645
+
646
+ // src/Game/GameApplication.ts
647
+ var DEFAULT_CONFIG = {
648
+ viewportWidth: 1280,
649
+ viewportHeight: 720,
650
+ targetFPS: 60
651
+ };
652
+ var GameApplication = class {
653
+ // ── Core engine objects — available after construction ───────────────────
654
+ webglCore;
655
+ config;
656
+ /**
657
+ * The active scene. Populated during construction via `createScene()`
658
+ * so subclasses may reference it as early as `onLoadResources()`.
659
+ */
660
+ scene;
661
+ /**
662
+ * Set during `start()`, after `onLoadResources()` returns.
663
+ * Use in `onInit()` and lifecycle hooks.
664
+ */
665
+ viewport = null;
666
+ /**
667
+ * Set during `start()`, after `viewport` is initialised.
668
+ * `null` before `start()` resolves — safe to check from external code.
669
+ */
670
+ renderer = null;
671
+ // ── Internal ──────────────────────────────────────────────────────────────
672
+ loopRunner;
673
+ _started = false;
674
+ // ── Constructor ───────────────────────────────────────────────────────────
675
+ constructor(config) {
676
+ this.config = { ...DEFAULT_CONFIG, ...config };
677
+ this.webglCore = new WebGLCore(this.config.canvasId);
678
+ this.scene = this.createScene();
679
+ this.loopRunner = new GameLoopRunner(this.config.targetFPS);
680
+ }
681
+ // ── Public API ────────────────────────────────────────────────────────────
682
+ /**
683
+ * Execute the full startup sequence and begin the game loop.
684
+ *
685
+ * The sequence is **sealed** — extend behaviour through the protected hooks,
686
+ * not by overriding `start()`.
687
+ */
688
+ async start() {
689
+ if (this._started) {
690
+ console.warn(
691
+ "[GameApplication] start() called more than once \u2014 ignoring."
692
+ );
693
+ return;
694
+ }
695
+ this._started = true;
696
+ await this.onLoadResources();
697
+ this.viewport = this.createViewport();
698
+ this.renderer = this.createRenderer();
699
+ await this.onInit();
700
+ this.loopRunner.start(
701
+ (dt) => this.onUpdate(dt),
702
+ () => {
703
+ this.onBeforeRender();
704
+ this.renderer.render(this.scene);
705
+ this.onAfterRender();
706
+ }
707
+ );
708
+ }
709
+ /**
710
+ * Pause the game loop without discarding state.
711
+ * Call `resume()` to continue.
712
+ */
713
+ pause() {
714
+ this.loopRunner.pause();
715
+ }
716
+ /** Resume a paused game loop. */
717
+ resume() {
718
+ this.loopRunner.resume();
719
+ }
720
+ /**
721
+ * Stop the game loop permanently and invoke `onDispose()`.
722
+ * After calling `stop()` the instance should be discarded.
723
+ */
724
+ stop() {
725
+ this.loopRunner.stop();
726
+ this.onDispose();
727
+ this._started = false;
728
+ }
729
+ get isRunning() {
730
+ return this.loopRunner.isRunning;
731
+ }
732
+ // ── Virtual hooks (optional override) ────────────────────────────────────
733
+ /**
734
+ * Factory for the `Scene`. Override to customise lights or initial state.
735
+ * Default: `Scene.withDefaultLights(webglCore)`.
736
+ */
737
+ createScene() {
738
+ return Scene.withDefaultLights(this.webglCore);
739
+ }
740
+ /**
741
+ * Factory for the `Viewport`. Override to customise camera settings or
742
+ * attach extra resize behaviour.
743
+ * Default: constructed from `config.canvasId`, `viewportWidth`, `viewportHeight`.
744
+ */
745
+ createViewport() {
746
+ return new Viewport(
747
+ this.config.canvasId,
748
+ this.config.viewportWidth,
749
+ this.config.viewportHeight,
750
+ this.webglCore
751
+ );
752
+ }
753
+ /**
754
+ * Factory for the `Renderer`. Override to enable debug mode or swap
755
+ * the renderer implementation.
756
+ * Default: `new Renderer(webglCore, viewport)`.
757
+ */
758
+ createRenderer() {
759
+ return new Renderer(this.webglCore, this.viewport);
760
+ }
761
+ /**
762
+ * Called every animation frame **before** `renderer.render(scene)`.
763
+ *
764
+ * Suitable for: updating HUD overlays, syncing camera state, or any
765
+ * visual concern that must run at display rate rather than the fixed tick.
766
+ */
767
+ onBeforeRender() {
768
+ }
769
+ /**
770
+ * Called every animation frame **after** `renderer.render(scene)`.
771
+ *
772
+ * Suitable for: post-process passes, 2D canvas overlays, analytics.
773
+ */
774
+ onAfterRender() {
775
+ }
776
+ /**
777
+ * Called by `stop()` to release any custom resources held by the subclass
778
+ * (event listeners, WebSocket connections, audio contexts, etc.).
779
+ *
780
+ * The base `Scene` and engine objects are **not** auto-disposed here;
781
+ * call `scene.dispose()` explicitly if needed.
782
+ */
783
+ onDispose() {
784
+ }
785
+ };
345
786
  export {
787
+ Actor,
346
788
  AnimationController,
347
789
  Arm,
348
790
  Character,
791
+ GameApplication,
792
+ GameLoopRunner,
349
793
  Head,
794
+ InteractiveActor,
350
795
  KeyboardInput,
351
- Leg
796
+ Leg,
797
+ PhysicsBody
352
798
  };
353
799
  //# sourceMappingURL=index.js.map