@fonsecabarreto/genesis-gl-core 0.1.31 → 0.1.33
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/dist/Core/classes/Material.d.ts +1 -1
- package/dist/Core/classes/Material.js +1 -1
- package/dist/Core/classes/Model.d.ts +2 -2
- package/dist/Core/classes/Renderer.d.ts +4 -2
- package/dist/Core/classes/Renderer.js +3 -3
- package/dist/Core/classes/Scene.d.ts +3 -3
- package/dist/Core/classes/Scene.js +1 -1
- package/dist/Core/index.d.ts +25 -4
- package/dist/Core/index.js +8 -6
- package/dist/Core/utils/load-glb.d.ts +2 -2
- package/dist/Core/utils/load-glb.js +3 -3
- package/dist/Core/utils/parse-obj.d.ts +2 -2
- package/dist/Core/utils/parse-obj.js +3 -3
- package/dist/Game/controls/KeyboardInput.d.ts +3 -3
- package/dist/Game/index.d.ts +4 -4
- package/dist/Game/index.js +4 -4
- package/dist/{KeyboardInput-1xOAabI0.d.ts → KeyboardInput-RJ0mbuWM.d.ts} +1 -1
- package/dist/{Material-DhwSRbP2.d.ts → Material-A-7kKes_.d.ts} +2 -0
- package/dist/{Model-BBZHnUp1.d.ts → Model-efZcQkOK.d.ts} +2 -2
- package/dist/{chunk-ZCJ3MJZD.js → chunk-4M3XFPH3.js} +17 -3
- package/dist/chunk-4M3XFPH3.js.map +1 -0
- package/dist/chunk-IOAANJZG.js +159 -0
- package/dist/chunk-IOAANJZG.js.map +1 -0
- package/dist/{chunk-3ULETMWF.js → chunk-WKSDPPXS.js} +4 -3
- package/dist/{chunk-3ULETMWF.js.map → chunk-WKSDPPXS.js.map} +1 -1
- package/dist/{chunk-L66K4AZU.js → chunk-XCYJCLHB.js} +3 -1
- package/dist/chunk-XCYJCLHB.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-L66K4AZU.js.map +0 -1
- package/dist/chunk-LFLNQ35F.js +0 -81
- package/dist/chunk-LFLNQ35F.js.map +0 -1
- package/dist/chunk-ZCJ3MJZD.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'gl-matrix';
|
|
2
|
-
export { i as Model } from '../../Model-
|
|
2
|
+
export { i as Model } from '../../Model-efZcQkOK.js';
|
|
3
3
|
import '../domain/interfaces/Vectors.js';
|
|
4
4
|
import '../../WebGLCore-DR7ZHJB0.js';
|
|
5
|
-
import '../../Material-
|
|
5
|
+
import '../../Material-A-7kKes_.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { W as WebGLCore } from '../../WebGLCore-DR7ZHJB0.js';
|
|
2
2
|
import { Scene } from './Scene.js';
|
|
3
|
-
import { i as Model } from '../../Model-
|
|
3
|
+
import { i as Model } from '../../Model-efZcQkOK.js';
|
|
4
4
|
import { Viewport } from './Viewport.js';
|
|
5
|
-
import '../../Material-
|
|
5
|
+
import '../../Material-A-7kKes_.js';
|
|
6
6
|
import '../domain/interfaces/Vectors.js';
|
|
7
7
|
import 'gl-matrix';
|
|
8
8
|
import '../../Camera-CJVYy9fH.js';
|
|
@@ -12,6 +12,8 @@ declare class Renderer {
|
|
|
12
12
|
viewport: Viewport;
|
|
13
13
|
/** Set to `true` to render wireframe bounding boxes for debugging. */
|
|
14
14
|
debug: boolean;
|
|
15
|
+
/** RGBA clear color used at the start of each frame. */
|
|
16
|
+
clearColor: [number, number, number, number];
|
|
15
17
|
private _modelHitboxMaterial;
|
|
16
18
|
private _meshHitboxMaterial;
|
|
17
19
|
private readonly _modelWireBoxCache;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Renderer
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-
|
|
3
|
+
} from "../../chunk-4M3XFPH3.js";
|
|
4
|
+
import "../../chunk-XCYJCLHB.js";
|
|
5
5
|
import "../../chunk-QOAQVTAB.js";
|
|
6
|
-
import "../../chunk-
|
|
6
|
+
import "../../chunk-WKSDPPXS.js";
|
|
7
7
|
import "../../chunk-WHOIVV44.js";
|
|
8
8
|
export {
|
|
9
9
|
Renderer
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as Light } from '../../Material-
|
|
2
|
-
import { i as Model } from '../../Model-
|
|
1
|
+
import { L as Light } from '../../Material-A-7kKes_.js';
|
|
2
|
+
import { i as Model } from '../../Model-efZcQkOK.js';
|
|
3
3
|
import { W as WebGLCore } from '../../WebGLCore-DR7ZHJB0.js';
|
|
4
4
|
import '../domain/interfaces/Vectors.js';
|
|
5
5
|
import 'gl-matrix';
|
|
@@ -21,7 +21,7 @@ declare class Scene {
|
|
|
21
21
|
/** Register a light source in the scene. */
|
|
22
22
|
addLight(id: string, light: Light): void;
|
|
23
23
|
/** Add a model to the scene with a unique key. */
|
|
24
|
-
add(id: string, model: Model): void;
|
|
24
|
+
add(id: string, model: Model, prepend?: boolean): void;
|
|
25
25
|
/** Remove a model from the scene by key. */
|
|
26
26
|
remove(key: string): void;
|
|
27
27
|
getModels(): Model[];
|
package/dist/Core/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
1
|
+
import { M as Mesh } from '../Model-efZcQkOK.js';
|
|
2
|
+
export { A as AnimationChannel, a as AnimationClip, b as AnimationPath, c as AnimationSampler, G as GL_LINES, d as GL_TRIANGLES, I as INITIAL_BOUNDING_BOX, e as InterpolationMode, J as Joint, f as JointPose, g as MAX_JOINTS, h as MeshBuffers, i as Model, S as Skeleton } from '../Model-efZcQkOK.js';
|
|
3
|
+
import { W as WebGLCore } from '../WebGLCore-DR7ZHJB0.js';
|
|
4
|
+
export { R as RenderCallback } from '../WebGLCore-DR7ZHJB0.js';
|
|
3
5
|
export { C as Camera } from '../Camera-CJVYy9fH.js';
|
|
4
|
-
export { L as Light, a as LightType, M as MAX_LIGHTS, b as Material } from '../Material-
|
|
6
|
+
export { L as Light, a as LightType, M as MAX_LIGHTS, b as Material } from '../Material-A-7kKes_.js';
|
|
5
7
|
export { Renderer } from './classes/Renderer.js';
|
|
6
8
|
export { Scene } from './classes/Scene.js';
|
|
7
9
|
export { Viewport } from './classes/Viewport.js';
|
|
@@ -56,4 +58,23 @@ declare class MousePointerLockControl implements IMouseControl {
|
|
|
56
58
|
private onMouseMove;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
interface GroundShadowOptions {
|
|
62
|
+
/** World Y position of the shadow plane. Default 0. */
|
|
63
|
+
y?: number;
|
|
64
|
+
/** Half-size of the shadow quad in world units. Default 1.5. */
|
|
65
|
+
radius?: number;
|
|
66
|
+
/** Peak shadow opacity at center, 0–1. Default 0.55. */
|
|
67
|
+
opacity?: number;
|
|
68
|
+
/** Shadow color as [r, g, b]. Default [0, 0, 0]. */
|
|
69
|
+
color?: [number, number, number];
|
|
70
|
+
/** How tight the falloff is. 0 = linear, higher = softer edges. Default 0.5. */
|
|
71
|
+
falloff?: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates a flat quad lying on the XZ plane with a soft radial-gradient shadow
|
|
75
|
+
* texture. The gradient fades from dark at the center to transparent at the edge,
|
|
76
|
+
* approximating a contact shadow beneath a model.
|
|
77
|
+
*/
|
|
78
|
+
declare function createGroundShadow(webglCore: WebGLCore, options?: GroundShadowOptions): Mesh;
|
|
79
|
+
|
|
80
|
+
export { type GroundShadowOptions, Mesh, MouseDragControl, MousePointerLockControl, WebGLCore, createGroundShadow };
|
package/dist/Core/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
KeyboardControl
|
|
3
|
-
|
|
2
|
+
KeyboardControl,
|
|
3
|
+
createGroundShadow
|
|
4
|
+
} from "../chunk-IOAANJZG.js";
|
|
4
5
|
import {
|
|
5
6
|
AnimationClip,
|
|
6
7
|
GL_LINES,
|
|
@@ -12,18 +13,18 @@ import {
|
|
|
12
13
|
Renderer,
|
|
13
14
|
Skeleton,
|
|
14
15
|
WebGLCore
|
|
15
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-4M3XFPH3.js";
|
|
16
17
|
import {
|
|
17
18
|
MAX_LIGHTS,
|
|
18
19
|
Material
|
|
19
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-XCYJCLHB.js";
|
|
20
21
|
import {
|
|
21
22
|
Model
|
|
22
23
|
} from "../chunk-QOAQVTAB.js";
|
|
23
24
|
import {
|
|
24
25
|
Light,
|
|
25
26
|
Scene
|
|
26
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-WKSDPPXS.js";
|
|
27
28
|
import {
|
|
28
29
|
Camera,
|
|
29
30
|
MouseDragControl,
|
|
@@ -50,6 +51,7 @@ export {
|
|
|
50
51
|
Scene,
|
|
51
52
|
Skeleton,
|
|
52
53
|
Viewport,
|
|
53
|
-
WebGLCore
|
|
54
|
+
WebGLCore,
|
|
55
|
+
createGroundShadow
|
|
54
56
|
};
|
|
55
57
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { W as WebGLCore } from '../../WebGLCore-DR7ZHJB0.js';
|
|
2
|
-
import { i as Model, S as Skeleton, a as AnimationClip } from '../../Model-
|
|
2
|
+
import { i as Model, S as Skeleton, a as AnimationClip } from '../../Model-efZcQkOK.js';
|
|
3
3
|
import 'gl-matrix';
|
|
4
4
|
import '../domain/interfaces/Vectors.js';
|
|
5
|
-
import '../../Material-
|
|
5
|
+
import '../../Material-A-7kKes_.js';
|
|
6
6
|
|
|
7
7
|
/** Everything parsed from a GLB file. */
|
|
8
8
|
interface GLBLoadResult {
|
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
GL_TRIANGLES,
|
|
4
4
|
Mesh,
|
|
5
5
|
Skeleton
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-4M3XFPH3.js";
|
|
7
7
|
import {
|
|
8
8
|
Material
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-XCYJCLHB.js";
|
|
10
10
|
import {
|
|
11
11
|
Model
|
|
12
12
|
} from "../../chunk-QOAQVTAB.js";
|
|
13
|
-
import "../../chunk-
|
|
13
|
+
import "../../chunk-WKSDPPXS.js";
|
|
14
14
|
import "../../chunk-WHOIVV44.js";
|
|
15
15
|
|
|
16
16
|
// src/Core/utils/load-glb.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { W as WebGLCore } from '../../WebGLCore-DR7ZHJB0.js';
|
|
2
|
-
import { b as Material } from '../../Material-
|
|
2
|
+
import { b as Material } from '../../Material-A-7kKes_.js';
|
|
3
3
|
import { Vector3 } from '../domain/interfaces/Vectors.js';
|
|
4
|
-
import { i as Model } from '../../Model-
|
|
4
|
+
import { i as Model } from '../../Model-efZcQkOK.js';
|
|
5
5
|
import 'gl-matrix';
|
|
6
6
|
|
|
7
7
|
declare function loadMTL(url: string, webglCore: WebGLCore): Promise<Record<string, Material>>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Mesh
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-4M3XFPH3.js";
|
|
4
4
|
import {
|
|
5
5
|
Material
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-XCYJCLHB.js";
|
|
7
7
|
import {
|
|
8
8
|
Model
|
|
9
9
|
} from "../../chunk-QOAQVTAB.js";
|
|
10
|
-
import "../../chunk-
|
|
10
|
+
import "../../chunk-WKSDPPXS.js";
|
|
11
11
|
import "../../chunk-WHOIVV44.js";
|
|
12
12
|
|
|
13
13
|
// src/Core/utils/parse-obj.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import '../../Camera-CJVYy9fH.js';
|
|
2
2
|
import '../../KeyboardControl-5w7Vm0J0.js';
|
|
3
|
-
export { K as KeyboardInput } from '../../KeyboardInput-
|
|
3
|
+
export { K as KeyboardInput } from '../../KeyboardInput-RJ0mbuWM.js';
|
|
4
4
|
import 'gl-matrix';
|
|
5
5
|
import '../../Core/domain/interfaces/Vectors.js';
|
|
6
|
-
import '../../Model-
|
|
6
|
+
import '../../Model-efZcQkOK.js';
|
|
7
7
|
import '../../WebGLCore-DR7ZHJB0.js';
|
|
8
|
-
import '../../Material-
|
|
8
|
+
import '../../Material-A-7kKes_.js';
|
package/dist/Game/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { P as PhysicsBody } from '../KeyboardInput-
|
|
2
|
-
export { A as AnimationController, a as AnimationState, b as AnimationTransition, C as Character, K as KeyboardInput, T as TransitionCondition } from '../KeyboardInput-
|
|
3
|
-
import {
|
|
4
|
-
import { b as Material } from '../Material-
|
|
1
|
+
import { P as PhysicsBody } from '../KeyboardInput-RJ0mbuWM.js';
|
|
2
|
+
export { A as AnimationController, a as AnimationState, b as AnimationTransition, C as Character, K as KeyboardInput, T as TransitionCondition } from '../KeyboardInput-RJ0mbuWM.js';
|
|
3
|
+
import { M as Mesh, i as Model, S as Skeleton } from '../Model-efZcQkOK.js';
|
|
4
|
+
import { b as Material } from '../Material-A-7kKes_.js';
|
|
5
5
|
import { Vector3 } from '../Core/domain/interfaces/Vectors.js';
|
|
6
6
|
import { W as WebGLCore } from '../WebGLCore-DR7ZHJB0.js';
|
|
7
7
|
import { Scene } from '../Core/classes/Scene.js';
|
package/dist/Game/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
KeyboardInput
|
|
3
3
|
} from "../chunk-P7QOKDLY.js";
|
|
4
|
-
import "../chunk-
|
|
4
|
+
import "../chunk-IOAANJZG.js";
|
|
5
5
|
import {
|
|
6
6
|
Mesh,
|
|
7
7
|
Renderer,
|
|
8
8
|
WebGLCore
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-4M3XFPH3.js";
|
|
10
10
|
import {
|
|
11
11
|
Material
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-XCYJCLHB.js";
|
|
13
13
|
import {
|
|
14
14
|
Model
|
|
15
15
|
} from "../chunk-QOAQVTAB.js";
|
|
16
16
|
import {
|
|
17
17
|
Scene
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-WKSDPPXS.js";
|
|
19
19
|
import {
|
|
20
20
|
Viewport
|
|
21
21
|
} from "../chunk-WHOIVV44.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as Camera } from './Camera-CJVYy9fH.js';
|
|
2
2
|
import { K as KeyboardControl } from './KeyboardControl-5w7Vm0J0.js';
|
|
3
|
-
import { a as AnimationClip, i as Model,
|
|
3
|
+
import { a as AnimationClip, i as Model, M as Mesh, S as Skeleton } from './Model-efZcQkOK.js';
|
|
4
4
|
import { Vector3 } from './Core/domain/interfaces/Vectors.js';
|
|
5
5
|
|
|
6
6
|
/** A named state that maps to one animation clip. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { vec3, quat, mat4 } from 'gl-matrix';
|
|
2
2
|
import { Vector3 } from './Core/domain/interfaces/Vectors.js';
|
|
3
3
|
import { W as WebGLCore } from './WebGLCore-DR7ZHJB0.js';
|
|
4
|
-
import { b as Material } from './Material-
|
|
4
|
+
import { b as Material } from './Material-A-7kKes_.js';
|
|
5
5
|
|
|
6
6
|
interface AABB {
|
|
7
7
|
min: Vector3;
|
|
@@ -315,4 +315,4 @@ declare class Model {
|
|
|
315
315
|
update(deltaTime: number): void;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
export { type AnimationChannel as A, GL_LINES as G, INITIAL_BOUNDING_BOX as I, type Joint as J,
|
|
318
|
+
export { type AnimationChannel as A, GL_LINES as G, INITIAL_BOUNDING_BOX as I, type Joint as J, Mesh as M, Skeleton as S, AnimationClip as a, type AnimationPath as b, type AnimationSampler as c, GL_TRIANGLES as d, type InterpolationMode as e, type JointPose as f, MAX_JOINTS as g, MeshBuffers as h, Model as i };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Material
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XCYJCLHB.js";
|
|
4
4
|
|
|
5
5
|
// src/Core/classes/Renderer.ts
|
|
6
6
|
import { mat4 as mat42 } from "gl-matrix";
|
|
@@ -772,6 +772,8 @@ var Renderer = class {
|
|
|
772
772
|
viewport;
|
|
773
773
|
/** Set to `true` to render wireframe bounding boxes for debugging. */
|
|
774
774
|
debug = false;
|
|
775
|
+
/** RGBA clear color used at the start of each frame. */
|
|
776
|
+
clearColor = [0.2, 0.5, 0.95, 1];
|
|
775
777
|
// Cached debug materials to avoid per-frame GPU allocations
|
|
776
778
|
_modelHitboxMaterial = null;
|
|
777
779
|
_meshHitboxMaterial = null;
|
|
@@ -813,7 +815,9 @@ var Renderer = class {
|
|
|
813
815
|
const lights = scene.getLights();
|
|
814
816
|
const camera = this.viewport.camera;
|
|
815
817
|
const viewPosition = camera.getComputedPosition();
|
|
816
|
-
this.clearFrame();
|
|
818
|
+
this.clearFrame(this.clearColor);
|
|
819
|
+
gl.disable(gl.BLEND);
|
|
820
|
+
gl.depthMask(true);
|
|
817
821
|
const viewMatrix = camera.getViewMatrix();
|
|
818
822
|
const projMatrix = camera.getProjectionMatrix();
|
|
819
823
|
let activeProgram = null;
|
|
@@ -909,6 +913,12 @@ var Renderer = class {
|
|
|
909
913
|
gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);
|
|
910
914
|
}
|
|
911
915
|
}
|
|
916
|
+
const isTransparent = mesh.material.transparent;
|
|
917
|
+
if (isTransparent) {
|
|
918
|
+
gl.enable(gl.BLEND);
|
|
919
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
920
|
+
gl.depthMask(false);
|
|
921
|
+
}
|
|
912
922
|
mesh.material.apply(gl, lights, viewPosition);
|
|
913
923
|
const uUseSkinning = uniformLocations["uUseSkinning"];
|
|
914
924
|
const skeleton = skinnedModel?.skeleton ?? null;
|
|
@@ -930,6 +940,10 @@ var Renderer = class {
|
|
|
930
940
|
} else {
|
|
931
941
|
gl.drawArrays(mesh.mode, 0, mesh.vertices.length / 3);
|
|
932
942
|
}
|
|
943
|
+
if (isTransparent) {
|
|
944
|
+
gl.disable(gl.BLEND);
|
|
945
|
+
gl.depthMask(true);
|
|
946
|
+
}
|
|
933
947
|
if (isWebGL2 && mesh.buffers?.vao) {
|
|
934
948
|
gl.bindVertexArray(null);
|
|
935
949
|
} else {
|
|
@@ -1001,4 +1015,4 @@ export {
|
|
|
1001
1015
|
Skeleton,
|
|
1002
1016
|
AnimationClip
|
|
1003
1017
|
};
|
|
1004
|
-
//# sourceMappingURL=chunk-
|
|
1018
|
+
//# sourceMappingURL=chunk-4M3XFPH3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Core/classes/Renderer.ts","../src/Core/utils/compute-bounds.ts","../src/Core/classes/Mesh.ts","../src/Core/shaders/index.ts","../src/Core/classes/WebGLCore.ts","../src/Core/classes/Skeleton.ts","../src/Core/classes/AnimationClip.ts","../src/Core/utils/create-wire-box.ts"],"sourcesContent":["import { mat4, vec3 } from 'gl-matrix';\nimport { Material, Mesh, Scene, WebGLCore } from '.';\nimport { Viewport } from './Viewport';\nimport { Light } from './Light';\nimport { Vector3 } from '../domain/interfaces/Vectors';\nimport { Model } from './Model';\nimport { Skeleton } from './Skeleton';\nimport { createWireBox } from '../utils/create-wire-box';\n\nexport class Renderer {\n /** Set to `true` to render wireframe bounding boxes for debugging. */\n public debug = false;\n\n /** RGBA clear color used at the start of each frame. */\n public clearColor: [number, number, number, number] = [0.2, 0.5, 0.95, 1];\n\n // Cached debug materials to avoid per-frame GPU allocations\n private _modelHitboxMaterial: Material | null = null;\n private _meshHitboxMaterial: Material | null = null;\n\n // Cache wire-box debug meshes: recreated only when the bounding box changes.\n private readonly _modelWireBoxCache = new WeakMap<Model, { mesh: Mesh; key: string }>();\n private readonly _meshWireBoxCache = new WeakMap<Mesh, { mesh: Mesh; key: string }>();\n\n /** Pre-allocated identity matrix for world-space hitbox draws. */\n private readonly _identity: mat4 = mat4.create();\n\n /** Cached `gl instanceof WebGL2RenderingContext` — computed once at construction. */\n private readonly _isWebGL2: boolean;\n\n constructor(\n private webglCore: WebGLCore,\n public viewport: Viewport,\n ) {\n const gl = webglCore.getRenderingContext();\n this._isWebGL2 = gl instanceof WebGL2RenderingContext;\n if (gl) gl.enable(gl.DEPTH_TEST);\n }\n\n /** Lazily-created material for model-level hitboxes (red). */\n private get modelHitboxMaterial(): Material {\n if (!this._modelHitboxMaterial) {\n this._modelHitboxMaterial = new Material(this.webglCore);\n this._modelHitboxMaterial.setColorHex('#ff0404ff');\n this._modelHitboxMaterial.unlit = true;\n }\n return this._modelHitboxMaterial;\n }\n\n /** Lazily-created material for mesh-level hitboxes (green). */\n private get meshHitboxMaterial(): Material {\n if (!this._meshHitboxMaterial) {\n this._meshHitboxMaterial = new Material(this.webglCore);\n this._meshHitboxMaterial.setColorHex('#04ff0cff');\n this._meshHitboxMaterial.unlit = true;\n }\n return this._meshHitboxMaterial;\n }\n\n public clearFrame(\n color: [number, number, number, number] = [0.2, 0.5, 0.95, 1],\n ) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n gl.clearColor(...color);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n }\n\n public render(scene: Scene) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n const models = scene.getModels();\n const lights = scene.getLights();\n const camera = this.viewport.camera;\n\n const viewPosition: vec3 = camera.getComputedPosition();\n this.clearFrame(this.clearColor);\n\n // Ensure clean GL state at the start of every frame\n gl.disable(gl.BLEND);\n gl.depthMask(true);\n\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n let activeProgram: WebGLProgram | null = null;\n\n for (const model of models) {\n activeProgram = this.drawModel(\n gl,\n model,\n lights,\n viewPosition,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n private drawModel(\n gl: WebGLRenderingContext,\n model: Model,\n lights: Light[],\n viewPosition: vec3,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ): WebGLProgram | null {\n const modelMatrix = model.getModelMatrix();\n const hasSkeleton = model.skeleton !== null;\n\n for (const mesh of model.meshes) {\n activeProgram = this.drawMesh(\n mesh,\n lights,\n viewPosition,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n hasSkeleton && mesh.isSkinned ? model : null,\n );\n\n if (this.debug) {\n this.drawHitBoxForMesh(\n mesh,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n // Debug wireframe\n if (this.debug) {\n this.drawHitBox(model, activeProgram);\n }\n\n return activeProgram;\n }\n\n private drawMesh(\n mesh: Mesh,\n lights: Light[],\n viewPosition: vec3,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n skinnedModel: Model | null = null,\n ): WebGLProgram | null {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return activeProgram;\n if (!mesh.material) return activeProgram;\n\n mesh.initBuffer(this.webglCore);\n\n const { program, uniformLocations } = mesh.material;\n\n if (program !== activeProgram) {\n gl.useProgram(program);\n activeProgram = program;\n\n if (uniformLocations['uView'])\n gl.uniformMatrix4fv(uniformLocations['uView'], false, viewMatrix);\n if (uniformLocations['uProjection'])\n gl.uniformMatrix4fv(uniformLocations['uProjection'], false, projMatrix);\n }\n\n // Use cached WebGL2 check\n const isWebGL2 = this._isWebGL2;\n\n // --- Bind VAO if available ---\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(\n mesh.buffers.vao,\n );\n } else {\n // --- WebGL1 Fallback ---\n const aPosition = mesh.material.attribLocations['aPosition'];\n if (aPosition !== -1 && mesh.buffers?.vertexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexBuffer);\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aNormal = mesh.material.attribLocations['aNormal'];\n if (aNormal !== -1 && mesh.buffers?.normalBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.normalBuffer);\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aTexCoord = mesh.material.attribLocations['aTexCoord'];\n if (aTexCoord !== -1 && mesh.buffers?.texCoordBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.texCoordBuffer);\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n\n // Skinning attributes (joint indices + weights)\n const aJointIndices = mesh.material.attribLocations['aJointIndices'];\n if (aJointIndices !== -1 && mesh.buffers?.jointIndexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointIndexBuffer);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n const aJointWeights = mesh.material.attribLocations['aJointWeights'];\n if (aJointWeights !== -1 && mesh.buffers?.jointWeightBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointWeightBuffer);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Alpha blending for transparent materials\n const isTransparent = mesh.material.transparent;\n if (isTransparent) {\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n gl.depthMask(false);\n }\n\n // Material uniforms\n mesh.material.apply(gl, lights, viewPosition as Vector3);\n\n // ── Skinning uniforms ─────────────────────────────────────\n const uUseSkinning = uniformLocations['uUseSkinning'];\n const skeleton: Skeleton | null =\n (skinnedModel?.skeleton as Skeleton | undefined) ?? null;\n if (skeleton && mesh.isSkinned) {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 1);\n const uJointMatrices = uniformLocations['uJointMatrices[0]'];\n if (uJointMatrices) {\n gl.uniformMatrix4fv(uJointMatrices, false, skeleton.jointMatrices);\n }\n } else {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 0);\n }\n\n // Model transform\n const uModel = mesh.material.uniformLocations['uModel'];\n if (uModel) gl.uniformMatrix4fv(uModel, false, modelMatrix as Float32Array);\n\n // Draw\n if (mesh.buffers?.indexBuffer && mesh.indices) {\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.buffers.indexBuffer);\n const type =\n mesh.indices instanceof Uint32Array\n ? gl.UNSIGNED_INT\n : gl.UNSIGNED_SHORT;\n gl.drawElements(mesh.mode, mesh.indices.length, type, 0);\n } else {\n gl.drawArrays(mesh.mode, 0, mesh.vertices.length / 3);\n }\n\n // Restore blending state\n if (isTransparent) {\n gl.disable(gl.BLEND);\n gl.depthMask(true);\n }\n\n // Cleanup\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(null);\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n return activeProgram;\n }\n\n public drawHitBox(\n model: Model,\n activeProgram: WebGLProgram | null,\n ) {\n const bbox = model.boundingBox;\n const key = `${bbox.min[0]},${bbox.min[1]},${bbox.min[2]},${bbox.max[0]},${bbox.max[1]},${bbox.max[2]}`;\n const cached = this._modelWireBoxCache.get(model);\n let hitboxMesh: Mesh;\n if (!cached || cached.key !== key) {\n hitboxMesh = createWireBox(bbox.min, bbox.max, this.modelHitboxMaterial);\n hitboxMesh.isCollidable = false;\n this._modelWireBoxCache.set(model, { mesh: hitboxMesh, key });\n } else {\n hitboxMesh = cached.mesh;\n }\n\n const camera = this.viewport.camera;\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n this.drawMesh(\n hitboxMesh,\n [],\n camera.position,\n this._identity,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n\n private drawHitBoxForMesh(\n mesh: Mesh,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ) {\n if (!mesh.boundingBox) return;\n\n const { min, max } = mesh.boundingBox;\n const key = `${min[0]},${min[1]},${min[2]},${max[0]},${max[1]},${max[2]}`;\n const cached = this._meshWireBoxCache.get(mesh);\n let wireBox: Mesh;\n if (!cached || cached.key !== key) {\n wireBox = createWireBox(min, max, this.meshHitboxMaterial);\n wireBox.isCollidable = false;\n this._meshWireBoxCache.set(mesh, { mesh: wireBox, key });\n } else {\n wireBox = cached.mesh;\n }\n\n const camera = this.viewport.camera;\n this.drawMesh(\n wireBox,\n [],\n camera.position,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n}\n","import { AABB } from '../domain/value-objects/Collider';\n\n/**\n * Compute the axis-aligned bounding box (AABB) from a flat Float32Array of vertices.\n * Each vertex is expected to be 3 consecutive floats: [x, y, z, ...].\n */\nexport function computeVertexBounds(vertices: Float32Array): AABB {\n if (!vertices || vertices.length === 0) {\n return { min: [0, 0, 0], max: [0, 0, 0] };\n }\n\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n\n for (let i = 0; i < vertices.length; i += 3) {\n const x = vertices[i];\n const y = vertices[i + 1];\n const z = vertices[i + 2];\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (z < minZ) minZ = z;\n\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n if (z > maxZ) maxZ = z;\n }\n\n return {\n min: [minX, minY, minZ],\n max: [maxX, maxY, maxZ],\n };\n}\n","import WebGLCore from './WebGLCore';\nimport { Material } from './Material';\nimport { AABB } from '../domain/value-objects/Collider';\nimport { computeVertexBounds } from '../utils/compute-bounds';\n\n/** WebGL draw-mode constants. */\nexport const GL_LINES = 0x0001;\nexport const GL_TRIANGLES = 0x0004;\n\nexport const INITIAL_BOUNDING_BOX: AABB = { min: [0, 0, 0], max: [0, 0, 0] };\n\n/**\n * A single renderable mesh: vertex data, normals, indices, a material, and an\n * optional bounding box used for collision detection.\n *\n * For skinned meshes, set {@link jointIndices} and {@link jointWeights} to\n * enable GPU skeletal animation.\n */\nexport class Mesh {\n name: string;\n\n vertices: Float32Array;\n\n normals: Float32Array;\n\n texCoords: Float32Array;\n\n indices: Uint16Array | Uint32Array | null;\n\n mode: number;\n\n material: Material;\n\n // ── Skinning data (optional) ─────────────────────────────────\n /** Per-vertex joint indices (vec4 per vertex, 4 influences). */\n jointIndices: Float32Array | null = null;\n\n /** Per-vertex joint weights (vec4 per vertex, 4 influences). */\n jointWeights: Float32Array | null = null;\n\n // WebGL buffers aggregate\n buffers: MeshBuffers | null = null;\n\n boundingBox: AABB = INITIAL_BOUNDING_BOX;\n\n isCollidable = true;\n\n isInitialized = false;\n\n constructor(\n name: string,\n vertices: Float32Array,\n normals: Float32Array,\n material: Material,\n texCoords: Float32Array = new Float32Array(),\n indices: Uint16Array | Uint32Array | null = null,\n ) {\n this.mode = GL_TRIANGLES;\n this.name = name;\n this.normals = normals;\n this.indices = indices;\n this.vertices = vertices;\n this.texCoords = texCoords;\n this.material = material;\n\n this.computeBounds();\n }\n\n setMode(newMode: number) {\n this.mode = newMode;\n }\n\n /** Whether this mesh has skinning data for skeletal animation. */\n get isSkinned(): boolean {\n return this.jointIndices !== null && this.jointWeights !== null;\n }\n\n computeBounds() {\n this.boundingBox = computeVertexBounds(this.vertices);\n }\n\n clone(): Mesh {\n const clonedMesh = new Mesh(\n this.name,\n new Float32Array(this.vertices),\n new Float32Array(this.normals),\n this.material, // shallow ref — call Material.clone() if independent material is needed\n new Float32Array(this.texCoords),\n this.indices\n ? this.indices instanceof Uint16Array\n ? new Uint16Array(this.indices)\n : new Uint32Array(this.indices)\n : null,\n );\n clonedMesh.setMode(this.mode);\n clonedMesh.isCollidable = this.isCollidable;\n if (this.jointIndices)\n clonedMesh.jointIndices = new Float32Array(this.jointIndices);\n if (this.jointWeights)\n clonedMesh.jointWeights = new Float32Array(this.jointWeights);\n return clonedMesh;\n }\n\n /** Initialize GPU buffers safely */\n public initBuffer(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n\n if (!gl) {\n console.warn(\n `[Mesh:${this.name}] GL context not available, skipping buffer init.`,\n );\n return;\n }\n if (this.isInitialized) return;\n\n this.buffers = new MeshBuffers();\n this.buffers.init(gl as WebGL2RenderingContext, this);\n this.isInitialized = true;\n }\n\n /** Dispose of GPU buffers safely */\n public dispose(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n if (!gl || !this.buffers) return;\n\n this.buffers.dispose(gl as WebGL2RenderingContext);\n this.buffers = null;\n this.isInitialized = false;\n }\n}\n\nexport class MeshBuffers {\n vao: WebGLVertexArrayObject | null = null;\n vertexBuffer: WebGLBuffer | null = null;\n normalBuffer: WebGLBuffer | null = null;\n texCoordBuffer: WebGLBuffer | null = null;\n indexBuffer: WebGLBuffer | null = null;\n jointIndexBuffer: WebGLBuffer | null = null;\n jointWeightBuffer: WebGLBuffer | null = null;\n\n public init(gl: WebGLRenderingContext, mesh: Mesh) {\n if (!gl) throw new Error('Cannot init MeshBuffers: invalid GL context');\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n const gl2 = isWebGL2 ? (gl as unknown as WebGL2RenderingContext) : null;\n\n if (gl2) {\n this.vao = gl2.createVertexArray();\n gl2.bindVertexArray(this.vao);\n }\n\n // --- Attribute layout ---\n // Attribute locations must match the shader (aPosition=0, aNormal=1, aTexCoord=2).\n // Look them up from the mesh's material program so the VAO captures the bindings.\n const program = mesh.material?.program;\n const aPosition = program ? gl.getAttribLocation(program, 'aPosition') : 0;\n const aNormal = program ? gl.getAttribLocation(program, 'aNormal') : 1;\n const aTexCoord = program ? gl.getAttribLocation(program, 'aTexCoord') : 2;\n\n // Vertex buffer\n this.vertexBuffer = gl.createBuffer();\n if (!this.vertexBuffer) throw new Error('Failed to create vertex buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);\n if (aPosition !== -1) {\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n // Normal buffer\n this.normalBuffer = gl.createBuffer();\n if (!this.normalBuffer) throw new Error('Failed to create normal buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);\n if (aNormal !== -1) {\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n // TexCoord buffer\n if (mesh.texCoords.length > 0) {\n this.texCoordBuffer = gl.createBuffer();\n if (!this.texCoordBuffer)\n throw new Error('Failed to create texCoord buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.texCoords, gl.STATIC_DRAW);\n if (aTexCoord !== -1) {\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Index buffer\n if (mesh.indices && mesh.indices.length > 0) {\n this.indexBuffer = gl.createBuffer();\n if (!this.indexBuffer) throw new Error('Failed to create index buffer');\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);\n }\n\n // Joint indices buffer (skinning)\n const aJointIndices = program\n ? gl.getAttribLocation(program, 'aJointIndices')\n : -1;\n if (\n mesh.jointIndices &&\n mesh.jointIndices.length > 0 &&\n aJointIndices !== -1\n ) {\n this.jointIndexBuffer = gl.createBuffer();\n if (!this.jointIndexBuffer)\n throw new Error('Failed to create joint index buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointIndexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointIndices, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n // Joint weights buffer (skinning)\n const aJointWeights = program\n ? gl.getAttribLocation(program, 'aJointWeights')\n : -1;\n if (\n mesh.jointWeights &&\n mesh.jointWeights.length > 0 &&\n aJointWeights !== -1\n ) {\n this.jointWeightBuffer = gl.createBuffer();\n if (!this.jointWeightBuffer)\n throw new Error('Failed to create joint weight buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointWeightBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointWeights, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n\n if (gl2) {\n gl2.bindVertexArray(null);\n }\n }\n\n public dispose(gl: WebGL2RenderingContext) {\n if (!gl) {\n console.warn('Cannot dispose MeshBuffers: invalid GL context');\n return;\n }\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n\n if (this.vertexBuffer) gl.deleteBuffer(this.vertexBuffer);\n if (this.normalBuffer) gl.deleteBuffer(this.normalBuffer);\n if (this.texCoordBuffer) gl.deleteBuffer(this.texCoordBuffer);\n if (this.indexBuffer) gl.deleteBuffer(this.indexBuffer);\n if (this.jointIndexBuffer) gl.deleteBuffer(this.jointIndexBuffer);\n if (this.jointWeightBuffer) gl.deleteBuffer(this.jointWeightBuffer);\n\n if (isWebGL2 && this.vao) {\n (gl as unknown as WebGL2RenderingContext).deleteVertexArray(this.vao);\n }\n\n this.vertexBuffer =\n this.normalBuffer =\n this.texCoordBuffer =\n this.indexBuffer =\n this.jointIndexBuffer =\n this.jointWeightBuffer =\n this.vao =\n null;\n }\n}\n","// GLSL shader sources inlined as TypeScript strings so the package can\n// be built with tsup without requiring Vite's ?raw loader.\n\nexport const vsSource = /* glsl */ `\n#define MAX_JOINTS 128\n\nattribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\n// ── Skinning attributes (vec4: up to 4 joint influences per vertex) ──\nattribute vec4 aJointIndices;\nattribute vec4 aJointWeights;\n\nuniform mat4 uModel;\nuniform mat4 uView;\nuniform mat4 uProjection;\n\n// ── Skinning uniforms ──\nuniform bool uUseSkinning;\nuniform mat4 uJointMatrices[MAX_JOINTS];\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nvoid main() {\n vTexCoord = aTexCoord;\n\n vec4 pos = vec4(aPosition, 1.0);\n vec3 norm = aNormal;\n\n if (uUseSkinning) {\n mat4 skinMat =\n aJointWeights.x * uJointMatrices[int(aJointIndices.x)] +\n aJointWeights.y * uJointMatrices[int(aJointIndices.y)] +\n aJointWeights.z * uJointMatrices[int(aJointIndices.z)] +\n aJointWeights.w * uJointMatrices[int(aJointIndices.w)];\n pos = skinMat * pos;\n norm = mat3(skinMat) * norm;\n }\n\n vNormal = mat3(uModel) * norm;\n\n vec4 worldPosition = uModel * pos;\n vPosition = worldPosition.xyz;\n gl_Position = uProjection * uView * worldPosition;\n}\n`;\n\nexport const fsSource = /* glsl */ `\nprecision mediump float;\n\n#define MAX_LIGHTS 5\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nuniform vec3 uLightDirection[MAX_LIGHTS];\nuniform vec3 uLightColor[MAX_LIGHTS];\nuniform float uLightIntensity[MAX_LIGHTS];\nuniform int uLightType[MAX_LIGHTS]; // 0=directional, 1=point, 2=ambient\nuniform int uLightCount;\nuniform vec3 uLightPosition[MAX_LIGHTS];\nuniform float uLightConstant[MAX_LIGHTS];\nuniform float uLightLinear[MAX_LIGHTS];\nuniform float uLightQuadratic[MAX_LIGHTS];\n\nuniform vec4 uColor;\nuniform bool uUnlit;\nuniform bool uUseTexture;\nuniform sampler2D uTexture;\n\nuniform vec3 uViewPosition;\nuniform float uShininess;\nuniform vec3 uSpecularColor;\n\nvoid main() {\n vec4 baseColor = uColor;\n if (uUseTexture) {\n baseColor *= texture2D(uTexture, vTexCoord);\n }\n\n if (uUnlit) {\n gl_FragColor = baseColor;\n return;\n }\n\n vec3 norm = normalize(vNormal);\n vec3 totalDiffuse = vec3(0.0);\n vec3 totalAmbient = vec3(0.0);\n vec3 totalSpecular = vec3(0.0);\n\n vec3 viewDir = normalize(uViewPosition - vPosition);\n\n for (int i = 0; i < MAX_LIGHTS; i++) {\n if (i >= uLightCount) break;\n\n if (uLightType[i] == 2) {\n totalAmbient += uLightColor[i] * uLightIntensity[i];\n } else {\n vec3 lightDir;\n float attenuation = 1.0;\n\n if (uLightType[i] == 0) {\n // Directional light\n lightDir = normalize(-uLightDirection[i]);\n } else {\n // Point light\n vec3 lightVec = uLightPosition[i] - vPosition;\n float distance = length(lightVec);\n lightDir = normalize(lightVec);\n attenuation = 1.0 / (\n uLightConstant[i] +\n uLightLinear[i] * distance +\n uLightQuadratic[i] * distance * distance\n );\n }\n\n // Diffuse\n float diff = max(dot(norm, lightDir), 0.0);\n totalDiffuse += diff * uLightColor[i] * uLightIntensity[i] * attenuation;\n\n // Specular (Blinn-Phong) — softer, more natural highlights\n vec3 halfDir = normalize(lightDir + viewDir);\n float spec = pow(max(dot(norm, halfDir), 0.0), uShininess);\n totalSpecular += spec * uSpecularColor * uLightIntensity[i] * attenuation;\n }\n }\n\n vec3 lightingResult =\n totalAmbient * baseColor.rgb +\n totalDiffuse * baseColor.rgb +\n totalSpecular;\n\n gl_FragColor = vec4(lightingResult, baseColor.a);\n}\n`;\n","/* eslint-disable @typescript-eslint/no-unsafe-argument */\nimport { vsSource, fsSource } from '../shaders/index';\n\nexport type RenderCallback = (\n gl: WebGLRenderingContext,\n program: WebGLProgram,\n) => void;\n\nexport class WebGLCore {\n public gl: WebGL2RenderingContext | WebGLRenderingContext;\n public program: WebGLProgram;\n public canvas: HTMLCanvasElement;\n public isWebGL2: boolean;\n\n /**\n * Initialise the WebGL rendering core.\n *\n * @param canvasOrId - An `HTMLCanvasElement` or the DOM `id` of one.\n * Defaults to `'glcanvas'`.\n */\n constructor(canvasOrId: string | HTMLCanvasElement = 'glcanvas') {\n const canvas =\n typeof canvasOrId === 'string'\n ? (document.getElementById(canvasOrId) as HTMLCanvasElement | null)\n : canvasOrId;\n if (!canvas) throw new Error('Canvas not found');\n this.canvas = canvas;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n this.gl = (canvas.getContext('webgl2') ?? canvas.getContext('webgl')) as\n | WebGL2RenderingContext\n | WebGLRenderingContext;\n\n if (!this.gl) throw new Error('WebGL not supported in this browser!');\n\n this.isWebGL2 = this.gl instanceof WebGL2RenderingContext;\n this.gl.viewport(0, 0, canvas.width, canvas.height);\n\n const program = this.createProgram(this.gl);\n if (!program) throw new Error('Failed to create program');\n this.program = program as unknown as WebGLProgram;\n }\n\n public getProgram(): WebGLProgram {\n return this.program;\n }\n\n public getRenderingContext(): WebGLRenderingContext {\n return this.gl;\n }\n\n private createProgram(gl: WebGLRenderingContext): WebGLProgram | null {\n const vertexShader = this.compileShader(gl, gl.VERTEX_SHADER, vsSource);\n const fragmentShader = this.compileShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vertexShader || !fragmentShader)\n throw new Error('Failed to create shaders');\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n // Clean up shader objects\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error(gl.getProgramInfoLog(program));\n throw new Error(gl.getProgramInfoLog(program) || 'Program link error');\n }\n\n return program;\n }\n\n private compileShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n throw new Error(gl.getShaderInfoLog(shader) || 'Shader compile error');\n }\n return shader;\n }\n\n public resize(width: number, height: number) {\n this.canvas.width = width;\n this.canvas.height = height;\n this.gl.viewport(0, 0, width, height);\n }\n\n dispose() {\n const { gl, program } = this;\n if (program) gl.deleteProgram(program);\n }\n}\n\nexport default WebGLCore;\n","import { mat4, quat, vec3 } from 'gl-matrix';\n\n/** Maximum number of joints supported per skeleton (must match shader). */\nexport const MAX_JOINTS = 128;\n\n/**\n * Describes a single joint (bone) inside a {@link Skeleton}.\n *\n * Each joint stores its default bind-pose transform (translation, rotation,\n * scale) and the inverse bind matrix used to transform vertices from model\n * space into joint-local space.\n */\nexport interface Joint {\n /** Human-readable name (e.g. \"Hips\", \"LeftArm\"). */\n name: string;\n /** Original glTF node index this joint references. */\n nodeIndex: number;\n /** Index of the parent joint in the skeleton's joint array (-1 = root). */\n parentIndex: number;\n /** Bind-pose translation. */\n localTranslation: vec3;\n /** Bind-pose rotation (quaternion). */\n localRotation: quat;\n /** Bind-pose scale. */\n localScale: vec3;\n /** Inverse bind matrix from the glTF skin. */\n inverseBindMatrix: mat4;\n}\n\n/**\n * Per-joint local transform produced by animation sampling.\n * Any undefined component falls back to the bind-pose value.\n */\nexport interface JointPose {\n t?: vec3;\n r?: quat;\n s?: vec3;\n}\n\n/**\n * A skeleton representing a hierarchy of {@link Joint}s.\n *\n * Call {@link computeJointMatrices} each frame with the current animation pose\n * to produce a flat `Float32Array` of `mat4`s ready for upload as a shader\n * uniform.\n */\nexport class Skeleton {\n /** Ordered joint list (parent always precedes child). */\n public readonly joints: Joint[];\n\n /**\n * Flat `Float32Array` of `joints.length` column-major `mat4` values.\n * Updated by {@link computeJointMatrices} — upload directly to\n * `uJointMatrices[0]`.\n */\n public readonly jointMatrices: Float32Array;\n\n /**\n * Global transform of the non-joint ancestor nodes above the skeleton\n * root. In many exported models the armature sits under a scene-root\n * node that carries scale / rotation (e.g. cm → m conversion). This\n * transform is pre-multiplied into root-joint globals so that\n * `global * inverseBindMatrix` evaluates to identity in the bind pose.\n */\n public readonly rootTransform: mat4;\n\n // ── Pre-allocated scratch buffers ──\n /** Per-joint global transform matrices, reused each frame (allocated once in constructor). */\n private readonly _globals: mat4[];\n private readonly _local: mat4 = mat4.create();\n private readonly _final: mat4 = mat4.create();\n\n constructor(joints: Joint[], rootTransform?: mat4) {\n if (joints.length > MAX_JOINTS) {\n console.warn(\n `[Skeleton] ${joints.length} joints exceeds MAX_JOINTS (${MAX_JOINTS}). ` +\n 'Extra joints will be ignored by the shader.',\n );\n }\n this.joints = joints;\n this.jointMatrices = new Float32Array(joints.length * 16);\n this.rootTransform = rootTransform ?? mat4.create();\n // Pre-allocate per-joint globals and scratch matrices — reused every frame.\n this._globals = Array.from({ length: joints.length }, () => mat4.create());\n }\n\n /** Number of joints in the skeleton. */\n get jointCount(): number {\n return this.joints.length;\n }\n\n /**\n * Walk the joint hierarchy, composing global transforms, then multiply by\n * each joint's inverse-bind matrix. The result is stored in\n * {@link jointMatrices} ready for shader upload.\n *\n * @param animatedPoses - Optional per-joint local transforms from an\n * {@link AnimationClip}. Map keys are **joint array indices** (not glTF\n * node indices).\n */\n computeJointMatrices(animatedPoses?: Map<number, JointPose>): void {\n const local = this._local;\n const final = this._final;\n const globals = this._globals;\n\n for (let i = 0; i < this.joints.length; i++) {\n const joint = this.joints[i];\n const pose = animatedPoses?.get(i);\n\n const t = pose?.t ?? joint.localTranslation;\n const r = pose?.r ?? joint.localRotation;\n const s = pose?.s ?? joint.localScale;\n\n mat4.fromRotationTranslationScale(local, r, t, s);\n\n if (joint.parentIndex >= 0) {\n mat4.multiply(globals[i], globals[joint.parentIndex], local);\n } else {\n // Root joint — include the non-joint ancestor transform\n mat4.multiply(globals[i], this.rootTransform, local);\n }\n\n // final = global * inverseBindMatrix\n mat4.multiply(final, globals[i], joint.inverseBindMatrix);\n this.jointMatrices.set(final as unknown as Float32Array, i * 16);\n }\n }\n\n /**\n * Create a deep clone of this skeleton (useful when instancing models).\n */\n clone(): Skeleton {\n const clonedJoints: Joint[] = this.joints.map((j) => ({\n name: j.name,\n nodeIndex: j.nodeIndex,\n parentIndex: j.parentIndex,\n localTranslation: vec3.clone(j.localTranslation),\n localRotation: quat.clone(j.localRotation),\n localScale: vec3.clone(j.localScale),\n inverseBindMatrix: mat4.clone(j.inverseBindMatrix),\n }));\n return new Skeleton(clonedJoints, mat4.clone(this.rootTransform));\n }\n}\n","import { quat, vec3 } from 'gl-matrix';\nimport type { JointPose } from './Skeleton';\n\n// ── Module-level scratch buffers ──\n// Reused across all AnimationClip.sample() calls. Safe because sample() is\n// synchronous and scratch values are copied into pose storage before reuse.\nconst _sv0Vec3 = vec3.create();\nconst _sv1Vec3 = vec3.create();\nconst _sOutVec3 = vec3.create();\nconst _sv0Quat = quat.create();\nconst _sv1Quat = quat.create();\nconst _sOutQuat = quat.create();\n\n// ─── Types ──────────────────────────────────────────────────────\n\n/** Interpolation mode as defined by glTF 2.0. */\nexport type InterpolationMode = 'STEP' | 'LINEAR' | 'CUBICSPLINE';\n\n/** Which transform component the channel drives. */\nexport type AnimationPath = 'translation' | 'rotation' | 'scale';\n\n/**\n * Stores the raw keyframe data for one animated property and performs\n * interpolation between keyframes.\n */\nexport interface AnimationSampler {\n /** Keyframe timestamps (seconds). */\n times: Float32Array;\n /** Flattened keyframe output values (3 or 4 components per key). */\n values: Float32Array;\n /** Interpolation mode (default `LINEAR`). */\n interpolation: InterpolationMode;\n}\n\n/**\n * Links a {@link AnimationSampler} to a specific joint property.\n */\nexport interface AnimationChannel {\n /** Index into the owning {@link Skeleton}'s joint array. */\n jointIndex: number;\n /** Transform component this channel drives. */\n path: AnimationPath;\n /** The sampler that holds keyframe data. */\n sampler: AnimationSampler;\n}\n\n// ─── AnimationClip ──────────────────────────────────────────────\n\n/**\n * A named animation clip containing one or more {@link AnimationChannel}s.\n *\n * Call {@link update} each frame to advance the playhead, then {@link sample}\n * to obtain per-joint local transforms that feed into\n * `Skeleton.computeJointMatrices()`.\n *\n * ```ts\n * clip.play();\n * // game loop:\n * clip.update(deltaTime);\n * const poses = clip.sample();\n * skeleton.computeJointMatrices(poses);\n * ```\n */\nexport class AnimationClip {\n /** Human-readable name (e.g. \"Walk\", \"Idle\"). */\n public readonly name: string;\n\n /** All channels in this clip. */\n public readonly channels: AnimationChannel[];\n\n /** Total duration in seconds (derived from the maximum keyframe time). */\n public readonly duration: number;\n\n /** Current playback time in seconds. */\n public currentTime = 0;\n\n /** Whether the clip loops when it reaches the end. */\n public loop = true;\n\n /** Playback speed multiplier (1 = normal, 2 = double, −1 = reverse). */\n public speed = 1;\n\n /** Whether the clip is currently advancing. */\n public playing = false;\n\n /**\n * Per-joint pose objects reused across frames to avoid per-frame allocations.\n * The vec3/quat inside each entry are also pre-allocated on first use.\n */\n private readonly _posePool = new Map<number, JointPose>();\n\n constructor(name: string, channels: AnimationChannel[], duration: number) {\n this.name = name;\n this.channels = channels;\n this.duration = duration;\n }\n\n /** Start / resume playback. */\n play(): void {\n this.playing = true;\n }\n\n /** Pause playback (keeps current time). */\n pause(): void {\n this.playing = false;\n }\n\n /** Stop and rewind to the beginning. */\n stop(): void {\n this.playing = false;\n this.currentTime = 0;\n }\n\n /** Rewind to the beginning without changing play state. */\n reset(): void {\n this.currentTime = 0;\n }\n\n /**\n * Advance the playhead by `deltaTime` seconds (respects {@link speed}).\n * Automatically loops or clamps depending on {@link loop}.\n */\n update(deltaTime: number): void {\n if (!this.playing || this.duration <= 0) return;\n\n this.currentTime += deltaTime * this.speed;\n\n if (this.loop) {\n this.currentTime =\n ((this.currentTime % this.duration) + this.duration) % this.duration;\n } else {\n if (this.currentTime >= this.duration) {\n this.currentTime = this.duration;\n this.playing = false;\n } else if (this.currentTime < 0) {\n this.currentTime = 0;\n this.playing = false;\n }\n }\n }\n\n /**\n * Sample every channel at the current playhead and write per-joint local\n * transforms into `out` (or a newly created map if none is provided).\n *\n * Pass a persistent `Map` to avoid allocating a new one every frame.\n */\n sample(out?: Map<number, JointPose>): Map<number, JointPose> {\n const result = out ?? new Map<number, JointPose>();\n if (out) out.clear();\n\n for (const channel of this.channels) {\n // Reuse the JointPose object for this joint from the pool.\n let entry = this._posePool.get(channel.jointIndex);\n if (!entry) {\n entry = {};\n this._posePool.set(channel.jointIndex, entry);\n }\n result.set(channel.jointIndex, entry);\n\n // sampleChannel writes into a module-level scratch buffer.\n // Copy the result into the pre-allocated fields of the pose entry.\n const value = sampleChannel(channel, this.currentTime);\n switch (channel.path) {\n case 'translation':\n if (!entry.t) entry.t = vec3.create();\n vec3.copy(entry.t, value as vec3);\n break;\n case 'rotation':\n if (!entry.r) entry.r = quat.create();\n quat.copy(entry.r, value as quat);\n break;\n case 'scale':\n if (!entry.s) entry.s = vec3.create();\n vec3.copy(entry.s, value as vec3);\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Create an independent copy of this clip (shares the underlying sampler\n * data but has its own playback state).\n */\n clone(): AnimationClip {\n const copy = new AnimationClip(this.name, this.channels, this.duration);\n copy.loop = this.loop;\n copy.speed = this.speed;\n return copy;\n }\n}\n\n// ── Sampling helpers ───────────────────────────────────────────────\n\n/** Write `numComponents` floats from `values` at `index * numComponents` into `out`. */\nfunction readValueInto(\n values: Float32Array,\n index: number,\n numComponents: number,\n out: vec3 | quat,\n): void {\n const off = index * numComponents;\n out[0] = values[off];\n out[1] = values[off + 1];\n out[2] = values[off + 2];\n if (numComponents === 4) out[3] = values[off + 3];\n}\n\n/**\n * Sample a single channel at the given time. Writes the result into a\n * module-level scratch buffer and returns a reference to it.\n * The caller must copy the value before the next sampleChannel invocation.\n */\nfunction sampleChannel(channel: AnimationChannel, time: number): vec3 | quat {\n const { sampler, path } = channel;\n const { times, values, interpolation } = sampler;\n const numComponents = path === 'rotation' ? 4 : 3;\n const outScratch: vec3 | quat = numComponents === 4 ? _sOutQuat : _sOutVec3;\n\n // Edge cases — clamp to first / last keyframe\n if (times.length === 0) {\n if (numComponents === 4) quat.identity(outScratch as quat);\n else vec3.set(outScratch as vec3, 0, 0, 0);\n return outScratch;\n }\n if (time <= times[0]) {\n readValueInto(values, 0, numComponents, outScratch);\n return outScratch;\n }\n if (time >= times[times.length - 1]) {\n readValueInto(values, times.length - 1, numComponents, outScratch);\n return outScratch;\n }\n\n // Binary search for the surrounding keyframe pair\n let lo = 0;\n let hi = times.length - 1;\n while (lo < hi - 1) {\n const mid = (lo + hi) >>> 1;\n if (times[mid] <= time) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const t0 = times[lo];\n const t1 = times[hi];\n const factor = (time - t0) / (t1 - t0);\n\n const v0Scratch: vec3 | quat = numComponents === 4 ? _sv0Quat : _sv0Vec3;\n const v1Scratch: vec3 | quat = numComponents === 4 ? _sv1Quat : _sv1Vec3;\n readValueInto(values, lo, numComponents, v0Scratch);\n readValueInto(values, hi, numComponents, v1Scratch);\n\n if (interpolation === 'STEP') {\n // STEP: return v0 unchanged\n readValueInto(values, lo, numComponents, outScratch);\n return outScratch;\n }\n\n // LINEAR (or fallback for CUBICSPLINE — true cubic not yet implemented)\n if (path === 'rotation') {\n quat.slerp(outScratch as quat, v0Scratch as quat, v1Scratch as quat, factor);\n return outScratch;\n }\n\n vec3.lerp(outScratch as vec3, v0Scratch as vec3, v1Scratch as vec3, factor);\n return outScratch;\n}\n","import { Material, Mesh } from '../classes';\nimport { GL_LINES } from '../classes/Mesh';\nimport { Vector3 } from '../domain/interfaces/Vectors';\n\nexport function createWireBox(\n min: Vector3,\n max: Vector3,\n material: Material,\n): Mesh {\n const [minX, minY, minZ] = min;\n const [maxX, maxY, maxZ] = max;\n\n // 8 corners of the box\n const vertices: number[] = [\n minX,\n minY,\n minZ,\n maxX,\n minY,\n minZ,\n maxX,\n maxY,\n minZ,\n minX,\n maxY,\n minZ,\n minX,\n minY,\n maxZ,\n maxX,\n minY,\n maxZ,\n maxX,\n maxY,\n maxZ,\n minX,\n maxY,\n maxZ,\n ];\n\n // edges as lines (pairs of indices)\n const indices: number[] = [\n 0,\n 1,\n 1,\n 2,\n 2,\n 3,\n 3,\n 0, // bottom face\n 4,\n 5,\n 5,\n 6,\n 6,\n 7,\n 7,\n 4, // top face\n 0,\n 4,\n 1,\n 5,\n 2,\n 6,\n 3,\n 7, // vertical edges\n ];\n\n // expand indices to vertices array (since WebGL uses gl.LINES)\n const lineVertices: number[] = [];\n for (let i = 0; i < indices.length; i++) {\n const idx = indices[i];\n lineVertices.push(\n vertices[idx * 3],\n vertices[idx * 3 + 1],\n vertices[idx * 3 + 2],\n );\n }\n\n // Normals can just be zero for wireframe\n const normals = new Float32Array(lineVertices.length);\n\n const mesh = new Mesh(\n 'hitbox',\n new Float32Array(lineVertices),\n normals,\n material,\n new Float32Array(),\n null,\n );\n mesh.setMode(GL_LINES);\n return mesh;\n}\n"],"mappings":"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","names":["mat4","quat","vec3","mat4"]}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Mesh
|
|
3
|
+
} from "./chunk-4M3XFPH3.js";
|
|
4
|
+
import {
|
|
5
|
+
Material
|
|
6
|
+
} from "./chunk-XCYJCLHB.js";
|
|
7
|
+
|
|
8
|
+
// src/Core/controls/KeyboardControl.ts
|
|
9
|
+
var KeyboardControl = class {
|
|
10
|
+
pressedKeys = /* @__PURE__ */ new Set();
|
|
11
|
+
listeners = [];
|
|
12
|
+
keyDownHandler;
|
|
13
|
+
keyUpHandler;
|
|
14
|
+
enable() {
|
|
15
|
+
if (this.keyDownHandler || this.keyUpHandler) return;
|
|
16
|
+
this.keyDownHandler = (e) => {
|
|
17
|
+
const key = this.mapKey(e.key);
|
|
18
|
+
if (!key) return;
|
|
19
|
+
if (!this.pressedKeys.has(key)) {
|
|
20
|
+
this.pressedKeys.add(key);
|
|
21
|
+
this.notify(key, true);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
this.keyUpHandler = (e) => {
|
|
25
|
+
const key = this.mapKey(e.key);
|
|
26
|
+
if (!key) return;
|
|
27
|
+
if (this.pressedKeys.has(key)) {
|
|
28
|
+
this.pressedKeys.delete(key);
|
|
29
|
+
this.notify(key, false);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
window.addEventListener("keydown", this.keyDownHandler);
|
|
33
|
+
window.addEventListener("keyup", this.keyUpHandler);
|
|
34
|
+
}
|
|
35
|
+
disable() {
|
|
36
|
+
if (this.keyDownHandler) {
|
|
37
|
+
window.removeEventListener("keydown", this.keyDownHandler);
|
|
38
|
+
this.keyDownHandler = void 0;
|
|
39
|
+
}
|
|
40
|
+
if (this.keyUpHandler) {
|
|
41
|
+
window.removeEventListener("keyup", this.keyUpHandler);
|
|
42
|
+
this.keyUpHandler = void 0;
|
|
43
|
+
}
|
|
44
|
+
this.pressedKeys.clear();
|
|
45
|
+
}
|
|
46
|
+
onChange(listener) {
|
|
47
|
+
this.listeners.push(listener);
|
|
48
|
+
}
|
|
49
|
+
/** Remove a previously registered listener. */
|
|
50
|
+
removeListener(listener) {
|
|
51
|
+
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
52
|
+
}
|
|
53
|
+
notify(key, isPressed) {
|
|
54
|
+
for (const listener of this.listeners) {
|
|
55
|
+
listener(key, isPressed);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
mapKey(key) {
|
|
59
|
+
switch (key) {
|
|
60
|
+
case "w":
|
|
61
|
+
case "ArrowUp":
|
|
62
|
+
return "up";
|
|
63
|
+
case "s":
|
|
64
|
+
case "ArrowDown":
|
|
65
|
+
return "down";
|
|
66
|
+
case "a":
|
|
67
|
+
case "ArrowLeft":
|
|
68
|
+
return "left";
|
|
69
|
+
case "d":
|
|
70
|
+
case "ArrowRight":
|
|
71
|
+
return "right";
|
|
72
|
+
case " ":
|
|
73
|
+
return "jump";
|
|
74
|
+
case "Shift":
|
|
75
|
+
return "roll";
|
|
76
|
+
default:
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
isPressed(key) {
|
|
81
|
+
return this.pressedKeys.has(key);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/Core/utils/create-ground-shadow.ts
|
|
86
|
+
function createRadialGradientTexture(gl, size, color, opacity, falloff) {
|
|
87
|
+
const canvas = document.createElement("canvas");
|
|
88
|
+
canvas.width = size;
|
|
89
|
+
canvas.height = size;
|
|
90
|
+
const ctx = canvas.getContext("2d");
|
|
91
|
+
const cx = size / 2;
|
|
92
|
+
const cy = size / 2;
|
|
93
|
+
const r = size / 2;
|
|
94
|
+
const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);
|
|
95
|
+
const [cr, cg, cb] = color.map((v) => Math.round(v * 255));
|
|
96
|
+
const innerStop = Math.max(0, Math.min(1, 1 - falloff));
|
|
97
|
+
grad.addColorStop(0, `rgba(${cr},${cg},${cb},${opacity})`);
|
|
98
|
+
grad.addColorStop(innerStop, `rgba(${cr},${cg},${cb},${opacity * 0.6})`);
|
|
99
|
+
grad.addColorStop(1, `rgba(${cr},${cg},${cb},0)`);
|
|
100
|
+
ctx.fillStyle = grad;
|
|
101
|
+
ctx.fillRect(0, 0, size, size);
|
|
102
|
+
const texture = gl.createTexture();
|
|
103
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
104
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
|
|
105
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
106
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
107
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
108
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
109
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
110
|
+
return texture;
|
|
111
|
+
}
|
|
112
|
+
function createGroundShadow(webglCore, options = {}) {
|
|
113
|
+
const {
|
|
114
|
+
y = 0,
|
|
115
|
+
radius = 1.5,
|
|
116
|
+
opacity = 0.55,
|
|
117
|
+
color = [0, 0, 0],
|
|
118
|
+
falloff = 0.5
|
|
119
|
+
} = options;
|
|
120
|
+
const vertices = new Float32Array([
|
|
121
|
+
-radius,
|
|
122
|
+
y,
|
|
123
|
+
radius,
|
|
124
|
+
radius,
|
|
125
|
+
y,
|
|
126
|
+
radius,
|
|
127
|
+
radius,
|
|
128
|
+
y,
|
|
129
|
+
-radius,
|
|
130
|
+
-radius,
|
|
131
|
+
y,
|
|
132
|
+
-radius
|
|
133
|
+
]);
|
|
134
|
+
const normals = new Float32Array([0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);
|
|
135
|
+
const texCoords = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
136
|
+
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
137
|
+
const gl = webglCore.getRenderingContext();
|
|
138
|
+
const gradientTexture = createRadialGradientTexture(
|
|
139
|
+
gl,
|
|
140
|
+
256,
|
|
141
|
+
color,
|
|
142
|
+
opacity,
|
|
143
|
+
falloff
|
|
144
|
+
);
|
|
145
|
+
const mat = new Material(webglCore, {
|
|
146
|
+
albedoColor: [1, 1, 1, 1],
|
|
147
|
+
unlit: true,
|
|
148
|
+
doubleSided: true,
|
|
149
|
+
transparent: true
|
|
150
|
+
});
|
|
151
|
+
mat.texture = gradientTexture;
|
|
152
|
+
return new Mesh("ground-shadow", vertices, normals, mat, texCoords, indices);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
KeyboardControl,
|
|
157
|
+
createGroundShadow
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=chunk-IOAANJZG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Core/controls/KeyboardControl.ts","../src/Core/utils/create-ground-shadow.ts"],"sourcesContent":["type ControlKey = 'up' | 'down' | 'left' | 'right' | 'jump' | 'roll';\n\ntype ControlListener = (key: ControlKey, isPressed: boolean) => void;\n\nexport class KeyboardControl {\n private pressedKeys: Set<ControlKey> = new Set();\n private listeners: ControlListener[] = [];\n private keyDownHandler?: (e: KeyboardEvent) => void;\n private keyUpHandler?: (e: KeyboardEvent) => void;\n\n public enable() {\n if (this.keyDownHandler || this.keyUpHandler) return; // prevent duplicates\n\n this.keyDownHandler = (e: KeyboardEvent) => {\n const key = this.mapKey(e.key);\n if (!key) return;\n\n if (!this.pressedKeys.has(key)) {\n this.pressedKeys.add(key);\n this.notify(key, true);\n }\n };\n\n this.keyUpHandler = (e: KeyboardEvent) => {\n const key = this.mapKey(e.key);\n if (!key) return;\n\n if (this.pressedKeys.has(key)) {\n this.pressedKeys.delete(key);\n this.notify(key, false);\n }\n };\n\n window.addEventListener('keydown', this.keyDownHandler);\n window.addEventListener('keyup', this.keyUpHandler);\n }\n\n public disable() {\n if (this.keyDownHandler) {\n window.removeEventListener('keydown', this.keyDownHandler);\n this.keyDownHandler = undefined;\n }\n if (this.keyUpHandler) {\n window.removeEventListener('keyup', this.keyUpHandler);\n this.keyUpHandler = undefined;\n }\n this.pressedKeys.clear();\n }\n\n public onChange(listener: ControlListener) {\n this.listeners.push(listener);\n }\n\n /** Remove a previously registered listener. */\n public removeListener(listener: ControlListener) {\n this.listeners = this.listeners.filter((l) => l !== listener);\n }\n\n private notify(key: ControlKey, isPressed: boolean) {\n for (const listener of this.listeners) {\n listener(key, isPressed);\n }\n }\n\n private mapKey(key: string): ControlKey | null {\n switch (key) {\n case 'w':\n case 'ArrowUp':\n return 'up';\n case 's':\n case 'ArrowDown':\n return 'down';\n case 'a':\n case 'ArrowLeft':\n return 'left';\n case 'd':\n case 'ArrowRight':\n return 'right';\n case ' ':\n return 'jump';\n case 'Shift':\n return 'roll';\n default:\n return null;\n }\n }\n\n public isPressed(key: ControlKey): boolean {\n return this.pressedKeys.has(key);\n }\n}\n","import { Material } from '../classes/Material';\nimport { Mesh } from '../classes/Mesh';\nimport WebGLCore from '../classes/WebGLCore';\n\nexport interface GroundShadowOptions {\n /** World Y position of the shadow plane. Default 0. */\n y?: number;\n /** Half-size of the shadow quad in world units. Default 1.5. */\n radius?: number;\n /** Peak shadow opacity at center, 0–1. Default 0.55. */\n opacity?: number;\n /** Shadow color as [r, g, b]. Default [0, 0, 0]. */\n color?: [number, number, number];\n /** How tight the falloff is. 0 = linear, higher = softer edges. Default 0.5. */\n falloff?: number;\n}\n\n/** Bake a radial gradient into a WebGL texture using an offscreen canvas. */\nfunction createRadialGradientTexture(\n gl: WebGLRenderingContext,\n size: number,\n color: [number, number, number],\n opacity: number,\n falloff: number,\n): WebGLTexture {\n const canvas = document.createElement('canvas');\n canvas.width = size;\n canvas.height = size;\n const ctx = canvas.getContext('2d')!;\n\n const cx = size / 2;\n const cy = size / 2;\n const r = size / 2;\n\n const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);\n const [cr, cg, cb] = color.map((v) => Math.round(v * 255));\n\n // Center: full opacity; edge: transparent. Falloff controls the inner stop.\n const innerStop = Math.max(0, Math.min(1, 1 - falloff));\n grad.addColorStop(0, `rgba(${cr},${cg},${cb},${opacity})`);\n grad.addColorStop(innerStop, `rgba(${cr},${cg},${cb},${opacity * 0.6})`);\n grad.addColorStop(1, `rgba(${cr},${cg},${cb},0)`);\n\n ctx.fillStyle = grad;\n ctx.fillRect(0, 0, size, size);\n\n const texture = gl.createTexture()!;\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n\n return texture;\n}\n\n/**\n * Creates a flat quad lying on the XZ plane with a soft radial-gradient shadow\n * texture. The gradient fades from dark at the center to transparent at the edge,\n * approximating a contact shadow beneath a model.\n */\nexport function createGroundShadow(\n webglCore: WebGLCore,\n options: GroundShadowOptions = {},\n): Mesh {\n const {\n y = 0,\n radius = 1.5,\n opacity = 0.55,\n color = [0, 0, 0],\n falloff = 0.5,\n } = options;\n\n const vertices = new Float32Array([\n -radius,\n y,\n radius,\n radius,\n y,\n radius,\n radius,\n y,\n -radius,\n -radius,\n y,\n -radius,\n ]);\n\n const normals = new Float32Array([0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);\n\n const texCoords = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);\n\n const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);\n\n const gl = webglCore.getRenderingContext()!;\n const gradientTexture = createRadialGradientTexture(\n gl,\n 256,\n color,\n opacity,\n falloff,\n );\n\n const mat = new Material(webglCore, {\n albedoColor: [1, 1, 1, 1],\n unlit: true,\n doubleSided: true,\n transparent: true,\n });\n mat.texture = gradientTexture;\n\n return new Mesh('ground-shadow', vertices, normals, mat, texCoords, indices);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -98,8 +98,9 @@ var Scene = class _Scene {
|
|
|
98
98
|
this.lights.push(light);
|
|
99
99
|
}
|
|
100
100
|
/** Add a model to the scene with a unique key. */
|
|
101
|
-
add(id, model) {
|
|
102
|
-
this.models.
|
|
101
|
+
add(id, model, prepend = false) {
|
|
102
|
+
if (prepend) this.models.unshift(model);
|
|
103
|
+
else this.models.push(model);
|
|
103
104
|
this.modelsMap.set(id, model);
|
|
104
105
|
}
|
|
105
106
|
/** Remove a model from the scene by key. */
|
|
@@ -141,4 +142,4 @@ export {
|
|
|
141
142
|
Light,
|
|
142
143
|
Scene
|
|
143
144
|
};
|
|
144
|
-
//# sourceMappingURL=chunk-
|
|
145
|
+
//# sourceMappingURL=chunk-WKSDPPXS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Core/classes/Light.ts","../src/Core/classes/Scene.ts"],"sourcesContent":["import { Vector3 } from '../domain/interfaces/Vectors';\n\n/**\n * Supported light types.\n * - `directional` – parallel rays (sun-like).\n * - `point` – omnidirectional with distance attenuation.\n * - `ambient` – constant contribution everywhere.\n */\nexport type LightType = 'directional' | 'point' | 'ambient';\n\n/**\n * A scene light with type, position, direction, colour, intensity,\n * and attenuation factors (for point lights).\n */\nexport class Light {\n // Attenuation factors are initialized to standard values (no falloff)\n public constant: number = 1.0;\n public linear: number = 0.0;\n public quadratic: number = 0.0;\n\n constructor(\n public type: LightType = 'directional',\n public position: Vector3 = [0, 10, 0], // for point lights\n public direction: Vector3 = [0, -1, -1], // for directional lights\n public color: Vector3 = [1, 1, 1],\n public intensity: number = 1,\n constant: number = 1.0,\n linear: number = 0.0,\n quadratic: number = 0.0,\n ) {\n this.constant = constant;\n this.linear = linear;\n this.quadratic = quadratic;\n }\n\n move(dx: number, dy: number, dz: number) {\n this.position[0] += dx;\n this.position[1] += dy;\n this.position[2] += dz;\n }\n\n rotateY(delta: number) {\n if (this.type === 'directional') {\n const cos = Math.cos(delta);\n const sin = Math.sin(delta);\n const [x, y, z] = this.direction;\n this.direction[0] = x * cos - z * sin;\n this.direction[2] = x * sin + z * cos;\n }\n }\n}\n","import { Light } from './Light';\nimport { Model } from './Model';\nimport WebGLCore from './WebGLCore';\n\nexport class Scene {\n private models: Model[] = [];\n private modelsMap = new Map<string, Model>();\n public lights: Light[] = [];\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(private webglCore: WebGLCore) {}\n\n // ─────────────────────────────────────────────\n // ░░░ Factory: pre-configured 3-point lighting ░░░\n // ─────────────────────────────────────────────\n\n /**\n * Create a Scene pre-loaded with a classic **3-point lighting** rig\n * (ambient + key + fill + back).\n *\n * Use this for quick prototyping; for production, create a plain Scene and\n * add your own lights.\n */\n static withDefaultLights(webglCore: WebGLCore): Scene {\n const scene = new Scene(webglCore);\n\n // 🌈 AMBIENT LIGHT (Global Base Illumination)\n scene.addLight(\n 'ambient_base',\n new Light('ambient', [0, 0, 0], [0, 0, 0], [0.3, 0.3, 0.35], 0.5),\n );\n\n // 🔑 KEY LIGHT (Main Sun/Primary Source)\n scene.addLight(\n 'directional_key',\n new Light(\n 'directional',\n [5, 10, 5],\n [-0.5, -1.0, -0.5],\n [1.0, 0.95, 0.9],\n 1.2,\n ),\n );\n\n // ☁️ FILL LIGHT (Softens Shadows)\n scene.addLight(\n 'directional_fill',\n new Light(\n 'directional',\n [-10, 5, 10],\n [0.5, -0.2, -0.7],\n [0.8, 0.8, 1.0],\n 0.5,\n ),\n );\n\n // 💫 BACK LIGHT (Rim/Separation Light)\n scene.addLight(\n 'directional_back',\n new Light(\n 'directional',\n [0, 15, -10],\n [0.0, -0.8, 1.0],\n [1.0, 1.0, 1.0],\n 0.7,\n ),\n );\n\n return scene;\n }\n\n /** Register a light source in the scene. */\n public addLight(id: string, light: Light) {\n this.lights.push(light);\n\n /* Debug only */\n /* \n const lightSphere = createSphereMesh(\n this.webglCore,\n 0.25,\n 12,\n 12,\n [1, 1, 0.8],\n );\n\n lightSphere.isCollidable = false;\n lightSphere.translation = light.position;\n this.add(id + '_sphere', lightSphere);\n\n // Debug direction for directional lights\n if (light.type === 'point' || light.type === 'directional') {\n const dirMesh = createLightDirectionMesh(\n this.webglCore,\n light.position,\n light.direction,\n 5, // length of the arrow\n [1, 0, 0], // red arrow\n );\n this.add(id + '_dir', dirMesh);\n } \n */\n }\n\n /** Add a model to the scene with a unique key. */\n add(id: string, model: Model) {\n this.models.push(model);\n this.modelsMap.set(id, model);\n }\n\n /** Remove a model from the scene by key. */\n remove(key: string) {\n const mesh = this.modelsMap.get(key);\n if (!mesh) return;\n this.models = this.models.filter((m) => m !== mesh);\n this.modelsMap.delete(key);\n }\n\n getModels() {\n return this.models;\n }\n\n getLights() {\n return this.lights;\n }\n\n /** Retrieve a model by its key. */\n public getModel(key: string) {\n const mesh = this.modelsMap.get(key);\n return mesh;\n }\n\n public hasMesh(key: string) {\n return this.modelsMap.has(key);\n }\n\n /** Returns all [id, model] pairs — useful for debug/inspector tools. */\n public getEntries(): [string, Model][] {\n return [...this.modelsMap.entries()];\n }\n\n dispose(webglCore: WebGLCore) {\n for (const model of this.models) {\n model.dispose(webglCore);\n }\n this.models.length = 0;\n this.modelsMap.clear();\n this.lights.length = 0;\n }\n}\n"],"mappings":";AAcO,IAAM,QAAN,MAAY;AAAA,EAMjB,YACS,OAAkB,eAClB,WAAoB,CAAC,GAAG,IAAI,CAAC,GAC7B,YAAqB,CAAC,GAAG,IAAI,EAAE,GAC/B,QAAiB,CAAC,GAAG,GAAG,CAAC,GACzB,YAAoB,GAC3B,WAAmB,GACnB,SAAiB,GACjB,YAAoB,GACpB;AARO;AACA;AACA;AACA;AACA;AAKP,SAAK,WAAW;AAChB,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAZS;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EATF,WAAmB;AAAA,EACnB,SAAiB;AAAA,EACjB,YAAoB;AAAA,EAiB3B,KAAK,IAAY,IAAY,IAAY;AACvC,SAAK,SAAS,CAAC,KAAK;AACpB,SAAK,SAAS,CAAC,KAAK;AACpB,SAAK,SAAS,CAAC,KAAK;AAAA,EACtB;AAAA,EAEA,QAAQ,OAAe;AACrB,QAAI,KAAK,SAAS,eAAe;AAC/B,YAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,YAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,YAAM,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK;AACvB,WAAK,UAAU,CAAC,IAAI,IAAI,MAAM,IAAI;AAClC,WAAK,UAAU,CAAC,IAAI,IAAI,MAAM,IAAI;AAAA,IACpC;AAAA,EACF;AACF;;;AC9CO,IAAM,QAAN,MAAM,OAAM;AAAA;AAAA,EAMjB,YAAoB,WAAsB;AAAtB;AAAA,EAAuB;AAAA,EAAvB;AAAA,EALZ,SAAkB,CAAC;AAAA,EACnB,YAAY,oBAAI,IAAmB;AAAA,EACpC,SAAkB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgB1B,OAAO,kBAAkB,WAA6B;AACpD,UAAM,QAAQ,IAAI,OAAM,SAAS;AAGjC,UAAM;AAAA,MACJ;AAAA,MACA,IAAI,MAAM,WAAW,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,IAAI,GAAG,GAAG;AAAA,IAClE;AAGA,UAAM;AAAA,MACJ;AAAA,MACA,IAAI;AAAA,QACF;AAAA,QACA,CAAC,GAAG,IAAI,CAAC;AAAA,QACT,CAAC,MAAM,IAAM,IAAI;AAAA,QACjB,CAAC,GAAK,MAAM,GAAG;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAGA,UAAM;AAAA,MACJ;AAAA,MACA,IAAI;AAAA,QACF;AAAA,QACA,CAAC,KAAK,GAAG,EAAE;AAAA,QACX,CAAC,KAAK,MAAM,IAAI;AAAA,QAChB,CAAC,KAAK,KAAK,CAAG;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAGA,UAAM;AAAA,MACJ;AAAA,MACA,IAAI;AAAA,QACF;AAAA,QACA,CAAC,GAAG,IAAI,GAAG;AAAA,QACX,CAAC,GAAK,MAAM,CAAG;AAAA,QACf,CAAC,GAAK,GAAK,CAAG;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGO,SAAS,IAAY,OAAc;AACxC,SAAK,OAAO,KAAK,KAAK;AAAA,EA4BxB;AAAA;AAAA,EAGA,IAAI,IAAY,OAAc;
|
|
1
|
+
{"version":3,"sources":["../src/Core/classes/Light.ts","../src/Core/classes/Scene.ts"],"sourcesContent":["import { Vector3 } from '../domain/interfaces/Vectors';\n\n/**\n * Supported light types.\n * - `directional` – parallel rays (sun-like).\n * - `point` – omnidirectional with distance attenuation.\n * - `ambient` – constant contribution everywhere.\n */\nexport type LightType = 'directional' | 'point' | 'ambient';\n\n/**\n * A scene light with type, position, direction, colour, intensity,\n * and attenuation factors (for point lights).\n */\nexport class Light {\n // Attenuation factors are initialized to standard values (no falloff)\n public constant: number = 1.0;\n public linear: number = 0.0;\n public quadratic: number = 0.0;\n\n constructor(\n public type: LightType = 'directional',\n public position: Vector3 = [0, 10, 0], // for point lights\n public direction: Vector3 = [0, -1, -1], // for directional lights\n public color: Vector3 = [1, 1, 1],\n public intensity: number = 1,\n constant: number = 1.0,\n linear: number = 0.0,\n quadratic: number = 0.0,\n ) {\n this.constant = constant;\n this.linear = linear;\n this.quadratic = quadratic;\n }\n\n move(dx: number, dy: number, dz: number) {\n this.position[0] += dx;\n this.position[1] += dy;\n this.position[2] += dz;\n }\n\n rotateY(delta: number) {\n if (this.type === 'directional') {\n const cos = Math.cos(delta);\n const sin = Math.sin(delta);\n const [x, y, z] = this.direction;\n this.direction[0] = x * cos - z * sin;\n this.direction[2] = x * sin + z * cos;\n }\n }\n}\n","import { Light } from './Light';\nimport { Model } from './Model';\nimport WebGLCore from './WebGLCore';\n\nexport class Scene {\n private models: Model[] = [];\n private modelsMap = new Map<string, Model>();\n public lights: Light[] = [];\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(private webglCore: WebGLCore) {}\n\n // ─────────────────────────────────────────────\n // ░░░ Factory: pre-configured 3-point lighting ░░░\n // ─────────────────────────────────────────────\n\n /**\n * Create a Scene pre-loaded with a classic **3-point lighting** rig\n * (ambient + key + fill + back).\n *\n * Use this for quick prototyping; for production, create a plain Scene and\n * add your own lights.\n */\n static withDefaultLights(webglCore: WebGLCore): Scene {\n const scene = new Scene(webglCore);\n\n // 🌈 AMBIENT LIGHT (Global Base Illumination)\n scene.addLight(\n 'ambient_base',\n new Light('ambient', [0, 0, 0], [0, 0, 0], [0.3, 0.3, 0.35], 0.5),\n );\n\n // 🔑 KEY LIGHT (Main Sun/Primary Source)\n scene.addLight(\n 'directional_key',\n new Light(\n 'directional',\n [5, 10, 5],\n [-0.5, -1.0, -0.5],\n [1.0, 0.95, 0.9],\n 1.2,\n ),\n );\n\n // ☁️ FILL LIGHT (Softens Shadows)\n scene.addLight(\n 'directional_fill',\n new Light(\n 'directional',\n [-10, 5, 10],\n [0.5, -0.2, -0.7],\n [0.8, 0.8, 1.0],\n 0.5,\n ),\n );\n\n // 💫 BACK LIGHT (Rim/Separation Light)\n scene.addLight(\n 'directional_back',\n new Light(\n 'directional',\n [0, 15, -10],\n [0.0, -0.8, 1.0],\n [1.0, 1.0, 1.0],\n 0.7,\n ),\n );\n\n return scene;\n }\n\n /** Register a light source in the scene. */\n public addLight(id: string, light: Light) {\n this.lights.push(light);\n\n /* Debug only */\n /* \n const lightSphere = createSphereMesh(\n this.webglCore,\n 0.25,\n 12,\n 12,\n [1, 1, 0.8],\n );\n\n lightSphere.isCollidable = false;\n lightSphere.translation = light.position;\n this.add(id + '_sphere', lightSphere);\n\n // Debug direction for directional lights\n if (light.type === 'point' || light.type === 'directional') {\n const dirMesh = createLightDirectionMesh(\n this.webglCore,\n light.position,\n light.direction,\n 5, // length of the arrow\n [1, 0, 0], // red arrow\n );\n this.add(id + '_dir', dirMesh);\n } \n */\n }\n\n /** Add a model to the scene with a unique key. */\n add(id: string, model: Model, prepend = false) {\n if (prepend) this.models.unshift(model);\n else this.models.push(model);\n this.modelsMap.set(id, model);\n }\n\n /** Remove a model from the scene by key. */\n remove(key: string) {\n const mesh = this.modelsMap.get(key);\n if (!mesh) return;\n this.models = this.models.filter((m) => m !== mesh);\n this.modelsMap.delete(key);\n }\n\n getModels() {\n return this.models;\n }\n\n getLights() {\n return this.lights;\n }\n\n /** Retrieve a model by its key. */\n public getModel(key: string) {\n const mesh = this.modelsMap.get(key);\n return mesh;\n }\n\n public hasMesh(key: string) {\n return this.modelsMap.has(key);\n }\n\n /** Returns all [id, model] pairs — useful for debug/inspector tools. */\n public getEntries(): [string, Model][] {\n return [...this.modelsMap.entries()];\n }\n\n dispose(webglCore: WebGLCore) {\n for (const model of this.models) {\n model.dispose(webglCore);\n }\n this.models.length = 0;\n this.modelsMap.clear();\n this.lights.length = 0;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -148,6 +148,8 @@ var Material = class _Material {
|
|
|
148
148
|
specular = [0.3, 0.3, 0.3];
|
|
149
149
|
shininess = 64;
|
|
150
150
|
doubleSided = false;
|
|
151
|
+
/** When true, the renderer enables alpha blending for this mesh. */
|
|
152
|
+
transparent = false;
|
|
151
153
|
//others
|
|
152
154
|
ambientColor = [0.1, 0.1, 0.1];
|
|
153
155
|
// Ka
|
|
@@ -299,4 +301,4 @@ export {
|
|
|
299
301
|
MAX_LIGHTS,
|
|
300
302
|
Material
|
|
301
303
|
};
|
|
302
|
-
//# sourceMappingURL=chunk-
|
|
304
|
+
//# sourceMappingURL=chunk-XCYJCLHB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Core/utils/load-texture.ts","../src/Core/utils/parse-hex-to-rgb.ts","../src/Core/classes/Material.ts"],"sourcesContent":["export interface TextureOptions {\n /** When true, use gl.REPEAT wrap mode for tiling textures. */\n repeat?: boolean;\n}\n\nexport async function loadWebGlTexture(\n gl: WebGLRenderingContext,\n url: string,\n options: TextureOptions = {},\n): Promise<WebGLTexture> {\n return new Promise((resolve, reject) => {\n const texture = gl.createTexture();\n if (!texture) return reject(new Error('Failed to create texture'));\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n\n // Placeholder pixel (gray) until image loads\n const level = 0;\n const internalFormat = gl.RGBA;\n const width = 1;\n const height = 1;\n const border = 0;\n const srcFormat = gl.RGBA;\n const srcType = gl.UNSIGNED_BYTE;\n const pixel = new Uint8Array([128, 128, 128, 255]); // gray\n gl.texImage2D(\n gl.TEXTURE_2D,\n level,\n internalFormat,\n width,\n height,\n border,\n srcFormat,\n srcType,\n pixel,\n );\n\n const image = new Image();\n image.crossOrigin = 'anonymous';\n image.onload = () => {\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texImage2D(\n gl.TEXTURE_2D,\n level,\n internalFormat,\n srcFormat,\n srcType,\n image,\n );\n\n // Auto mipmaps and filtering\n if (isPowerOf2(image.width) && isPowerOf2(image.height)) {\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MIN_FILTER,\n gl.LINEAR_MIPMAP_LINEAR,\n );\n // Wrap mode: REPEAT for tiling, otherwise default (REPEAT is GL default for POT)\n if (options.repeat) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n }\n } else {\n // Non-power-of-2: REPEAT is not supported in WebGL 1\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n }\n\n resolve(texture);\n };\n image.onerror = reject;\n image.src = url;\n });\n}\n\nfunction isPowerOf2(value: number) {\n return (value & (value - 1)) === 0;\n}\n","import { Vector4 } from '../domain/interfaces/Vectors';\n\nexport function parseHexToRgbArray(hex: string): Vector4 {\n // Remove leading #\n if (hex.startsWith('#')) hex = hex.slice(1);\n\n let r = 1,\n g = 1,\n b = 1,\n a = 1;\n\n if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16) / 255;\n g = parseInt(hex.slice(2, 4), 16) / 255;\n b = parseInt(hex.slice(4, 6), 16) / 255;\n a = 1;\n } else if (hex.length === 8) {\n r = parseInt(hex.slice(0, 2), 16) / 255;\n g = parseInt(hex.slice(2, 4), 16) / 255;\n b = parseInt(hex.slice(4, 6), 16) / 255;\n a = parseInt(hex.slice(6, 8), 16) / 255;\n } else {\n throw new Error('Invalid hex color format. Use #RRGGBB or #RRGGBBAA.');\n }\n\n return [r, g, b, a];\n}\n","import { Vector3, Vector4 } from '../domain/interfaces/Vectors';\nimport { loadWebGlTexture, TextureOptions } from '../utils/load-texture';\nimport { parseHexToRgbArray } from '../utils/parse-hex-to-rgb';\nimport { Light } from './Light';\nimport WebGLCore from './WebGLCore';\n\n/** Maximum number of lights supported per draw call (must match the shader). */\nexport const MAX_LIGHTS = 5;\n\n/**\n * Encapsulates GPU material state: shader program reference, uniform/attribute\n * caches, albedo colour, textures, and lighting properties.\n */\nexport class Material {\n // cache\n public program: WebGLProgram;\n public uniformLocations: Record<string, WebGLUniformLocation | null> = {};\n public attribLocations: Record<string, number> = {};\n\n // Attributes\n public albedoColor: Vector4 = [1, 1, 1, 1];\n public unlit: boolean = false;\n public texture?: WebGLTexture;\n public specular: Vector3 = [0.3, 0.3, 0.3];\n public shininess: number = 64;\n public doubleSided: boolean = false;\n /** When true, the renderer enables alpha blending for this mesh. */\n public transparent: boolean = false;\n //others\n public ambientColor: Vector3 = [0.1, 0.1, 0.1]; // Ka\n public dissolve: number = 1.0; // d or Tr\n public diffuse: Vector3 = [1, 1, 1]; // Kd\n /** Physics friction coefficient [0–1]. 0 = no resistance (ice), 1 = instant stop. */\n public friction: number = 0.3;\n\n // ── Pre-allocated light uniform buffers (reused every frame, zero GC pressure) ──\n private readonly _lightDirs = new Float32Array(MAX_LIGHTS * 3);\n private readonly _lightColors = new Float32Array(MAX_LIGHTS * 3);\n private readonly _lightIntensities = new Float32Array(MAX_LIGHTS);\n private readonly _lightTypes = new Int32Array(MAX_LIGHTS);\n private readonly _lightPositions = new Float32Array(MAX_LIGHTS * 3);\n private readonly _lightConstants = new Float32Array(MAX_LIGHTS);\n private readonly _lightLinears = new Float32Array(MAX_LIGHTS);\n private readonly _lightQuadratics = new Float32Array(MAX_LIGHTS);\n\n constructor(\n private webglCore: WebGLCore,\n options: Partial<Material> = {},\n ) {\n this.program = webglCore.getProgram();\n const { gl } = webglCore;\n\n // Cache uniforms\n const names = [\n 'uColor',\n 'uUnlit',\n 'uModel',\n 'uView',\n 'uProjection',\n 'uLightCount',\n 'uUseTexture',\n 'uTexture',\n // --- LIGHTING UNIFORMS ---\n 'uViewPosition', // Camera position for specular light\n 'uShininess',\n 'uSpecularColor',\n 'uAmbientColor',\n 'uDissolve',\n 'uDiffuseColor',\n 'uLightDirection[0]',\n 'uLightColor[0]',\n 'uLightIntensity[0]',\n 'uLightType[0]',\n 'uLightPosition[0]', // NEW: For Point Lights\n 'uLightConstant[0]', // NEW: Attenuation\n 'uLightLinear[0]', // NEW: Attenuation\n 'uLightQuadratic[0]', // NEW: Attenuation\n // --- SKINNING UNIFORMS ---\n 'uUseSkinning',\n 'uJointMatrices[0]',\n ];\n for (const name of names) {\n this.uniformLocations[name] = gl.getUniformLocation(this.program, name);\n }\n\n // Cache attributes\n const attribs = [\n 'aPosition',\n 'aNormal',\n 'aTexCoord',\n 'aJointIndices',\n 'aJointWeights',\n ];\n for (const name of attribs) {\n this.attribLocations[name] = gl.getAttribLocation(this.program, name);\n }\n\n Object.assign(this, options);\n }\n\n setColorHex(hex: string) {\n this.albedoColor = parseHexToRgbArray(hex);\n }\n\n setColor(rgba: [number, number, number, number]) {\n this.albedoColor = rgba;\n this.dissolve = rgba[3];\n this.diffuse = [rgba[0], rgba[1], rgba[2]];\n }\n\n /**\n * Load an image from URL and create a WebGL texture.\n */\n async loadTexture(url: string, options?: TextureOptions): Promise<void> {\n const { gl } = this.webglCore;\n this.texture = await loadWebGlTexture(gl, url, options);\n }\n\n /**\n * Create an independent copy of this material.\n * Shares the same WebGL program but copies all mutable properties.\n * The texture reference is shared (immutable GPU resource).\n */\n clone(): Material {\n const copy = new Material(this.webglCore);\n copy.albedoColor = [...this.albedoColor];\n copy.specular = [...this.specular];\n copy.ambientColor = [...this.ambientColor];\n copy.diffuse = [...this.diffuse];\n copy.shininess = this.shininess;\n copy.dissolve = this.dissolve;\n copy.unlit = this.unlit;\n copy.doubleSided = this.doubleSided;\n copy.friction = this.friction;\n copy.texture = this.texture; // shared GPU resource\n return copy;\n }\n\n apply(gl: WebGLRenderingContext, lights: Light[], viewPosition?: Vector3) {\n if (this.uniformLocations['uColor'])\n gl.uniform4fv(this.uniformLocations['uColor'], this.albedoColor);\n\n if (this.uniformLocations['uUnlit'])\n gl.uniform1i(this.uniformLocations['uUnlit'], this.unlit ? 1 : 0);\n\n // --- NEW MATERIAL UNIFORMS ---\n if (this.uniformLocations['uShininess'])\n gl.uniform1f(this.uniformLocations['uShininess'], this.shininess);\n\n if (this.uniformLocations['uSpecularColor'])\n gl.uniform3fv(this.uniformLocations['uSpecularColor'], this.specular);\n\n if (this.uniformLocations['uDissolve'])\n gl.uniform1f(this.uniformLocations['uDissolve'], this.dissolve);\n\n if (this.uniformLocations['uAmbientColor'])\n gl.uniform3fv(this.uniformLocations['uAmbientColor'], this.ambientColor);\n\n if (this.uniformLocations['uDiffuseColor'])\n gl.uniform3fv(this.uniformLocations['uDiffuseColor'], this.diffuse);\n\n // --- NEW CAMERA UNIFORM FOR SPECULAR HIGHLIGHTS ---\n if (viewPosition && this.uniformLocations['uViewPosition']) {\n gl.uniform3fv(this.uniformLocations['uViewPosition'], viewPosition);\n }\n\n // Texture\n const hasTexture = !!this.texture;\n if (this.uniformLocations['uUseTexture'])\n gl.uniform1i(this.uniformLocations['uUseTexture'], hasTexture ? 1 : 0);\n\n if (hasTexture && this.texture) {\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.texture);\n if (this.uniformLocations['uTexture'])\n gl.uniform1i(this.uniformLocations['uTexture'], 0);\n }\n\n // Lights — fill pre-allocated typed arrays in-place (zero per-frame allocations)\n if (!this.unlit && lights?.length) {\n const count = Math.min(lights.length, MAX_LIGHTS);\n\n if (this.uniformLocations['uLightCount'])\n gl.uniform1i(this.uniformLocations['uLightCount'], count);\n\n for (let i = 0; i < count; i++) {\n const light = lights[i];\n const i3 = i * 3;\n\n let typeInt = 0;\n if (light.type === 'point') typeInt = 1;\n else if (light.type === 'ambient') typeInt = 2;\n this._lightTypes[i] = typeInt;\n\n const dir = light.direction ?? [0, 0, 0];\n this._lightDirs[i3] = dir[0];\n this._lightDirs[i3 + 1] = dir[1];\n this._lightDirs[i3 + 2] = dir[2];\n\n const pos = light.position ?? [0, 0, 0];\n this._lightPositions[i3] = pos[0];\n this._lightPositions[i3 + 1] = pos[1];\n this._lightPositions[i3 + 2] = pos[2];\n\n this._lightColors[i3] = light.color[0];\n this._lightColors[i3 + 1] = light.color[1];\n this._lightColors[i3 + 2] = light.color[2];\n\n this._lightIntensities[i] = light.intensity;\n this._lightConstants[i] = light.constant;\n this._lightLinears[i] = light.linear;\n this._lightQuadratics[i] = light.quadratic;\n }\n\n if (this.uniformLocations['uLightDirection[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightDirection[0]'],\n this._lightDirs,\n );\n if (this.uniformLocations['uLightColor[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightColor[0]'],\n this._lightColors,\n );\n if (this.uniformLocations['uLightIntensity[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightIntensity[0]'],\n this._lightIntensities,\n );\n if (this.uniformLocations['uLightType[0]'])\n gl.uniform1iv(this.uniformLocations['uLightType[0]'], this._lightTypes);\n if (this.uniformLocations['uLightPosition[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightPosition[0]'],\n this._lightPositions,\n );\n if (this.uniformLocations['uLightConstant[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightConstant[0]'],\n this._lightConstants,\n );\n if (this.uniformLocations['uLightLinear[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightLinear[0]'],\n this._lightLinears,\n );\n if (this.uniformLocations['uLightQuadratic[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightQuadratic[0]'],\n this._lightQuadratics,\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Core/utils/load-texture.ts","../src/Core/utils/parse-hex-to-rgb.ts","../src/Core/classes/Material.ts"],"sourcesContent":["export interface TextureOptions {\n /** When true, use gl.REPEAT wrap mode for tiling textures. */\n repeat?: boolean;\n}\n\nexport async function loadWebGlTexture(\n gl: WebGLRenderingContext,\n url: string,\n options: TextureOptions = {},\n): Promise<WebGLTexture> {\n return new Promise((resolve, reject) => {\n const texture = gl.createTexture();\n if (!texture) return reject(new Error('Failed to create texture'));\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n\n // Placeholder pixel (gray) until image loads\n const level = 0;\n const internalFormat = gl.RGBA;\n const width = 1;\n const height = 1;\n const border = 0;\n const srcFormat = gl.RGBA;\n const srcType = gl.UNSIGNED_BYTE;\n const pixel = new Uint8Array([128, 128, 128, 255]); // gray\n gl.texImage2D(\n gl.TEXTURE_2D,\n level,\n internalFormat,\n width,\n height,\n border,\n srcFormat,\n srcType,\n pixel,\n );\n\n const image = new Image();\n image.crossOrigin = 'anonymous';\n image.onload = () => {\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texImage2D(\n gl.TEXTURE_2D,\n level,\n internalFormat,\n srcFormat,\n srcType,\n image,\n );\n\n // Auto mipmaps and filtering\n if (isPowerOf2(image.width) && isPowerOf2(image.height)) {\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MIN_FILTER,\n gl.LINEAR_MIPMAP_LINEAR,\n );\n // Wrap mode: REPEAT for tiling, otherwise default (REPEAT is GL default for POT)\n if (options.repeat) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n }\n } else {\n // Non-power-of-2: REPEAT is not supported in WebGL 1\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n }\n\n resolve(texture);\n };\n image.onerror = reject;\n image.src = url;\n });\n}\n\nfunction isPowerOf2(value: number) {\n return (value & (value - 1)) === 0;\n}\n","import { Vector4 } from '../domain/interfaces/Vectors';\n\nexport function parseHexToRgbArray(hex: string): Vector4 {\n // Remove leading #\n if (hex.startsWith('#')) hex = hex.slice(1);\n\n let r = 1,\n g = 1,\n b = 1,\n a = 1;\n\n if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16) / 255;\n g = parseInt(hex.slice(2, 4), 16) / 255;\n b = parseInt(hex.slice(4, 6), 16) / 255;\n a = 1;\n } else if (hex.length === 8) {\n r = parseInt(hex.slice(0, 2), 16) / 255;\n g = parseInt(hex.slice(2, 4), 16) / 255;\n b = parseInt(hex.slice(4, 6), 16) / 255;\n a = parseInt(hex.slice(6, 8), 16) / 255;\n } else {\n throw new Error('Invalid hex color format. Use #RRGGBB or #RRGGBBAA.');\n }\n\n return [r, g, b, a];\n}\n","import { Vector3, Vector4 } from '../domain/interfaces/Vectors';\nimport { loadWebGlTexture, TextureOptions } from '../utils/load-texture';\nimport { parseHexToRgbArray } from '../utils/parse-hex-to-rgb';\nimport { Light } from './Light';\nimport WebGLCore from './WebGLCore';\n\n/** Maximum number of lights supported per draw call (must match the shader). */\nexport const MAX_LIGHTS = 5;\n\n/**\n * Encapsulates GPU material state: shader program reference, uniform/attribute\n * caches, albedo colour, textures, and lighting properties.\n */\nexport class Material {\n // cache\n public program: WebGLProgram;\n public uniformLocations: Record<string, WebGLUniformLocation | null> = {};\n public attribLocations: Record<string, number> = {};\n\n // Attributes\n public albedoColor: Vector4 = [1, 1, 1, 1];\n public unlit: boolean = false;\n public texture?: WebGLTexture;\n public specular: Vector3 = [0.3, 0.3, 0.3];\n public shininess: number = 64;\n public doubleSided: boolean = false;\n //others\n public ambientColor: Vector3 = [0.1, 0.1, 0.1]; // Ka\n public dissolve: number = 1.0; // d or Tr\n public diffuse: Vector3 = [1, 1, 1]; // Kd\n /** Physics friction coefficient [0–1]. 0 = no resistance (ice), 1 = instant stop. */\n public friction: number = 0.3;\n\n // ── Pre-allocated light uniform buffers (reused every frame, zero GC pressure) ──\n private readonly _lightDirs = new Float32Array(MAX_LIGHTS * 3);\n private readonly _lightColors = new Float32Array(MAX_LIGHTS * 3);\n private readonly _lightIntensities = new Float32Array(MAX_LIGHTS);\n private readonly _lightTypes = new Int32Array(MAX_LIGHTS);\n private readonly _lightPositions = new Float32Array(MAX_LIGHTS * 3);\n private readonly _lightConstants = new Float32Array(MAX_LIGHTS);\n private readonly _lightLinears = new Float32Array(MAX_LIGHTS);\n private readonly _lightQuadratics = new Float32Array(MAX_LIGHTS);\n\n constructor(\n private webglCore: WebGLCore,\n options: Partial<Material> = {},\n ) {\n this.program = webglCore.getProgram();\n const { gl } = webglCore;\n\n // Cache uniforms\n const names = [\n 'uColor',\n 'uUnlit',\n 'uModel',\n 'uView',\n 'uProjection',\n 'uLightCount',\n 'uUseTexture',\n 'uTexture',\n // --- LIGHTING UNIFORMS ---\n 'uViewPosition', // Camera position for specular light\n 'uShininess',\n 'uSpecularColor',\n 'uAmbientColor',\n 'uDissolve',\n 'uDiffuseColor',\n 'uLightDirection[0]',\n 'uLightColor[0]',\n 'uLightIntensity[0]',\n 'uLightType[0]',\n 'uLightPosition[0]', // NEW: For Point Lights\n 'uLightConstant[0]', // NEW: Attenuation\n 'uLightLinear[0]', // NEW: Attenuation\n 'uLightQuadratic[0]', // NEW: Attenuation\n // --- SKINNING UNIFORMS ---\n 'uUseSkinning',\n 'uJointMatrices[0]',\n ];\n for (const name of names) {\n this.uniformLocations[name] = gl.getUniformLocation(this.program, name);\n }\n\n // Cache attributes\n const attribs = [\n 'aPosition',\n 'aNormal',\n 'aTexCoord',\n 'aJointIndices',\n 'aJointWeights',\n ];\n for (const name of attribs) {\n this.attribLocations[name] = gl.getAttribLocation(this.program, name);\n }\n\n Object.assign(this, options);\n }\n\n setColorHex(hex: string) {\n this.albedoColor = parseHexToRgbArray(hex);\n }\n\n setColor(rgba: [number, number, number, number]) {\n this.albedoColor = rgba;\n this.dissolve = rgba[3];\n this.diffuse = [rgba[0], rgba[1], rgba[2]];\n }\n\n /**\n * Load an image from URL and create a WebGL texture.\n */\n async loadTexture(url: string, options?: TextureOptions): Promise<void> {\n const { gl } = this.webglCore;\n this.texture = await loadWebGlTexture(gl, url, options);\n }\n\n /**\n * Create an independent copy of this material.\n * Shares the same WebGL program but copies all mutable properties.\n * The texture reference is shared (immutable GPU resource).\n */\n clone(): Material {\n const copy = new Material(this.webglCore);\n copy.albedoColor = [...this.albedoColor];\n copy.specular = [...this.specular];\n copy.ambientColor = [...this.ambientColor];\n copy.diffuse = [...this.diffuse];\n copy.shininess = this.shininess;\n copy.dissolve = this.dissolve;\n copy.unlit = this.unlit;\n copy.doubleSided = this.doubleSided;\n copy.friction = this.friction;\n copy.texture = this.texture; // shared GPU resource\n return copy;\n }\n\n apply(gl: WebGLRenderingContext, lights: Light[], viewPosition?: Vector3) {\n if (this.uniformLocations['uColor'])\n gl.uniform4fv(this.uniformLocations['uColor'], this.albedoColor);\n\n if (this.uniformLocations['uUnlit'])\n gl.uniform1i(this.uniformLocations['uUnlit'], this.unlit ? 1 : 0);\n\n // --- NEW MATERIAL UNIFORMS ---\n if (this.uniformLocations['uShininess'])\n gl.uniform1f(this.uniformLocations['uShininess'], this.shininess);\n\n if (this.uniformLocations['uSpecularColor'])\n gl.uniform3fv(this.uniformLocations['uSpecularColor'], this.specular);\n\n if (this.uniformLocations['uDissolve'])\n gl.uniform1f(this.uniformLocations['uDissolve'], this.dissolve);\n\n if (this.uniformLocations['uAmbientColor'])\n gl.uniform3fv(this.uniformLocations['uAmbientColor'], this.ambientColor);\n\n if (this.uniformLocations['uDiffuseColor'])\n gl.uniform3fv(this.uniformLocations['uDiffuseColor'], this.diffuse);\n\n // --- NEW CAMERA UNIFORM FOR SPECULAR HIGHLIGHTS ---\n if (viewPosition && this.uniformLocations['uViewPosition']) {\n gl.uniform3fv(this.uniformLocations['uViewPosition'], viewPosition);\n }\n\n // Texture\n const hasTexture = !!this.texture;\n if (this.uniformLocations['uUseTexture'])\n gl.uniform1i(this.uniformLocations['uUseTexture'], hasTexture ? 1 : 0);\n\n if (hasTexture && this.texture) {\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.texture);\n if (this.uniformLocations['uTexture'])\n gl.uniform1i(this.uniformLocations['uTexture'], 0);\n }\n\n // Lights — fill pre-allocated typed arrays in-place (zero per-frame allocations)\n if (!this.unlit && lights?.length) {\n const count = Math.min(lights.length, MAX_LIGHTS);\n\n if (this.uniformLocations['uLightCount'])\n gl.uniform1i(this.uniformLocations['uLightCount'], count);\n\n for (let i = 0; i < count; i++) {\n const light = lights[i];\n const i3 = i * 3;\n\n let typeInt = 0;\n if (light.type === 'point') typeInt = 1;\n else if (light.type === 'ambient') typeInt = 2;\n this._lightTypes[i] = typeInt;\n\n const dir = light.direction ?? [0, 0, 0];\n this._lightDirs[i3] = dir[0];\n this._lightDirs[i3 + 1] = dir[1];\n this._lightDirs[i3 + 2] = dir[2];\n\n const pos = light.position ?? [0, 0, 0];\n this._lightPositions[i3] = pos[0];\n this._lightPositions[i3 + 1] = pos[1];\n this._lightPositions[i3 + 2] = pos[2];\n\n this._lightColors[i3] = light.color[0];\n this._lightColors[i3 + 1] = light.color[1];\n this._lightColors[i3 + 2] = light.color[2];\n\n this._lightIntensities[i] = light.intensity;\n this._lightConstants[i] = light.constant;\n this._lightLinears[i] = light.linear;\n this._lightQuadratics[i] = light.quadratic;\n }\n\n if (this.uniformLocations['uLightDirection[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightDirection[0]'],\n this._lightDirs,\n );\n if (this.uniformLocations['uLightColor[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightColor[0]'],\n this._lightColors,\n );\n if (this.uniformLocations['uLightIntensity[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightIntensity[0]'],\n this._lightIntensities,\n );\n if (this.uniformLocations['uLightType[0]'])\n gl.uniform1iv(this.uniformLocations['uLightType[0]'], this._lightTypes);\n if (this.uniformLocations['uLightPosition[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightPosition[0]'],\n this._lightPositions,\n );\n if (this.uniformLocations['uLightConstant[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightConstant[0]'],\n this._lightConstants,\n );\n if (this.uniformLocations['uLightLinear[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightLinear[0]'],\n this._lightLinears,\n );\n if (this.uniformLocations['uLightQuadratic[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightQuadratic[0]'],\n this._lightQuadratics,\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-LFLNQ35F.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// src/Core/controls/KeyboardControl.ts
|
|
2
|
-
var KeyboardControl = class {
|
|
3
|
-
pressedKeys = /* @__PURE__ */ new Set();
|
|
4
|
-
listeners = [];
|
|
5
|
-
keyDownHandler;
|
|
6
|
-
keyUpHandler;
|
|
7
|
-
enable() {
|
|
8
|
-
if (this.keyDownHandler || this.keyUpHandler) return;
|
|
9
|
-
this.keyDownHandler = (e) => {
|
|
10
|
-
const key = this.mapKey(e.key);
|
|
11
|
-
if (!key) return;
|
|
12
|
-
if (!this.pressedKeys.has(key)) {
|
|
13
|
-
this.pressedKeys.add(key);
|
|
14
|
-
this.notify(key, true);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
this.keyUpHandler = (e) => {
|
|
18
|
-
const key = this.mapKey(e.key);
|
|
19
|
-
if (!key) return;
|
|
20
|
-
if (this.pressedKeys.has(key)) {
|
|
21
|
-
this.pressedKeys.delete(key);
|
|
22
|
-
this.notify(key, false);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
window.addEventListener("keydown", this.keyDownHandler);
|
|
26
|
-
window.addEventListener("keyup", this.keyUpHandler);
|
|
27
|
-
}
|
|
28
|
-
disable() {
|
|
29
|
-
if (this.keyDownHandler) {
|
|
30
|
-
window.removeEventListener("keydown", this.keyDownHandler);
|
|
31
|
-
this.keyDownHandler = void 0;
|
|
32
|
-
}
|
|
33
|
-
if (this.keyUpHandler) {
|
|
34
|
-
window.removeEventListener("keyup", this.keyUpHandler);
|
|
35
|
-
this.keyUpHandler = void 0;
|
|
36
|
-
}
|
|
37
|
-
this.pressedKeys.clear();
|
|
38
|
-
}
|
|
39
|
-
onChange(listener) {
|
|
40
|
-
this.listeners.push(listener);
|
|
41
|
-
}
|
|
42
|
-
/** Remove a previously registered listener. */
|
|
43
|
-
removeListener(listener) {
|
|
44
|
-
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
45
|
-
}
|
|
46
|
-
notify(key, isPressed) {
|
|
47
|
-
for (const listener of this.listeners) {
|
|
48
|
-
listener(key, isPressed);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
mapKey(key) {
|
|
52
|
-
switch (key) {
|
|
53
|
-
case "w":
|
|
54
|
-
case "ArrowUp":
|
|
55
|
-
return "up";
|
|
56
|
-
case "s":
|
|
57
|
-
case "ArrowDown":
|
|
58
|
-
return "down";
|
|
59
|
-
case "a":
|
|
60
|
-
case "ArrowLeft":
|
|
61
|
-
return "left";
|
|
62
|
-
case "d":
|
|
63
|
-
case "ArrowRight":
|
|
64
|
-
return "right";
|
|
65
|
-
case " ":
|
|
66
|
-
return "jump";
|
|
67
|
-
case "Shift":
|
|
68
|
-
return "roll";
|
|
69
|
-
default:
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
isPressed(key) {
|
|
74
|
-
return this.pressedKeys.has(key);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export {
|
|
79
|
-
KeyboardControl
|
|
80
|
-
};
|
|
81
|
-
//# sourceMappingURL=chunk-LFLNQ35F.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Core/controls/KeyboardControl.ts"],"sourcesContent":["type ControlKey = 'up' | 'down' | 'left' | 'right' | 'jump' | 'roll';\n\ntype ControlListener = (key: ControlKey, isPressed: boolean) => void;\n\nexport class KeyboardControl {\n private pressedKeys: Set<ControlKey> = new Set();\n private listeners: ControlListener[] = [];\n private keyDownHandler?: (e: KeyboardEvent) => void;\n private keyUpHandler?: (e: KeyboardEvent) => void;\n\n public enable() {\n if (this.keyDownHandler || this.keyUpHandler) return; // prevent duplicates\n\n this.keyDownHandler = (e: KeyboardEvent) => {\n const key = this.mapKey(e.key);\n if (!key) return;\n\n if (!this.pressedKeys.has(key)) {\n this.pressedKeys.add(key);\n this.notify(key, true);\n }\n };\n\n this.keyUpHandler = (e: KeyboardEvent) => {\n const key = this.mapKey(e.key);\n if (!key) return;\n\n if (this.pressedKeys.has(key)) {\n this.pressedKeys.delete(key);\n this.notify(key, false);\n }\n };\n\n window.addEventListener('keydown', this.keyDownHandler);\n window.addEventListener('keyup', this.keyUpHandler);\n }\n\n public disable() {\n if (this.keyDownHandler) {\n window.removeEventListener('keydown', this.keyDownHandler);\n this.keyDownHandler = undefined;\n }\n if (this.keyUpHandler) {\n window.removeEventListener('keyup', this.keyUpHandler);\n this.keyUpHandler = undefined;\n }\n this.pressedKeys.clear();\n }\n\n public onChange(listener: ControlListener) {\n this.listeners.push(listener);\n }\n\n /** Remove a previously registered listener. */\n public removeListener(listener: ControlListener) {\n this.listeners = this.listeners.filter((l) => l !== listener);\n }\n\n private notify(key: ControlKey, isPressed: boolean) {\n for (const listener of this.listeners) {\n listener(key, isPressed);\n }\n }\n\n private mapKey(key: string): ControlKey | null {\n switch (key) {\n case 'w':\n case 'ArrowUp':\n return 'up';\n case 's':\n case 'ArrowDown':\n return 'down';\n case 'a':\n case 'ArrowLeft':\n return 'left';\n case 'd':\n case 'ArrowRight':\n return 'right';\n case ' ':\n return 'jump';\n case 'Shift':\n return 'roll';\n default:\n return null;\n }\n }\n\n public isPressed(key: ControlKey): boolean {\n return this.pressedKeys.has(key);\n }\n}\n"],"mappings":";AAIO,IAAM,kBAAN,MAAsB;AAAA,EACnB,cAA+B,oBAAI,IAAI;AAAA,EACvC,YAA+B,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,EAED,SAAS;AACd,QAAI,KAAK,kBAAkB,KAAK,aAAc;AAE9C,SAAK,iBAAiB,CAAC,MAAqB;AAC1C,YAAM,MAAM,KAAK,OAAO,EAAE,GAAG;AAC7B,UAAI,CAAC,IAAK;AAEV,UAAI,CAAC,KAAK,YAAY,IAAI,GAAG,GAAG;AAC9B,aAAK,YAAY,IAAI,GAAG;AACxB,aAAK,OAAO,KAAK,IAAI;AAAA,MACvB;AAAA,IACF;AAEA,SAAK,eAAe,CAAC,MAAqB;AACxC,YAAM,MAAM,KAAK,OAAO,EAAE,GAAG;AAC7B,UAAI,CAAC,IAAK;AAEV,UAAI,KAAK,YAAY,IAAI,GAAG,GAAG;AAC7B,aAAK,YAAY,OAAO,GAAG;AAC3B,aAAK,OAAO,KAAK,KAAK;AAAA,MACxB;AAAA,IACF;AAEA,WAAO,iBAAiB,WAAW,KAAK,cAAc;AACtD,WAAO,iBAAiB,SAAS,KAAK,YAAY;AAAA,EACpD;AAAA,EAEO,UAAU;AACf,QAAI,KAAK,gBAAgB;AACvB,aAAO,oBAAoB,WAAW,KAAK,cAAc;AACzD,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAI,KAAK,cAAc;AACrB,aAAO,oBAAoB,SAAS,KAAK,YAAY;AACrD,WAAK,eAAe;AAAA,IACtB;AACA,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA,EAEO,SAAS,UAA2B;AACzC,SAAK,UAAU,KAAK,QAAQ;AAAA,EAC9B;AAAA;AAAA,EAGO,eAAe,UAA2B;AAC/C,SAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,QAAQ;AAAA,EAC9D;AAAA,EAEQ,OAAO,KAAiB,WAAoB;AAClD,eAAW,YAAY,KAAK,WAAW;AACrC,eAAS,KAAK,SAAS;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,OAAO,KAAgC;AAC7C,YAAQ,KAAK;AAAA,MACX,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEO,UAAU,KAA0B;AACzC,WAAO,KAAK,YAAY,IAAI,GAAG;AAAA,EACjC;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Core/classes/Renderer.ts","../src/Core/utils/compute-bounds.ts","../src/Core/classes/Mesh.ts","../src/Core/shaders/index.ts","../src/Core/classes/WebGLCore.ts","../src/Core/classes/Skeleton.ts","../src/Core/classes/AnimationClip.ts","../src/Core/utils/create-wire-box.ts"],"sourcesContent":["import { mat4, vec3 } from 'gl-matrix';\nimport { Material, Mesh, Scene, WebGLCore } from '.';\nimport { Viewport } from './Viewport';\nimport { Light } from './Light';\nimport { Vector3 } from '../domain/interfaces/Vectors';\nimport { Model } from './Model';\nimport { Skeleton } from './Skeleton';\nimport { createWireBox } from '../utils/create-wire-box';\n\nexport class Renderer {\n /** Set to `true` to render wireframe bounding boxes for debugging. */\n public debug = false;\n\n // Cached debug materials to avoid per-frame GPU allocations\n private _modelHitboxMaterial: Material | null = null;\n private _meshHitboxMaterial: Material | null = null;\n\n // Cache wire-box debug meshes: recreated only when the bounding box changes.\n private readonly _modelWireBoxCache = new WeakMap<Model, { mesh: Mesh; key: string }>();\n private readonly _meshWireBoxCache = new WeakMap<Mesh, { mesh: Mesh; key: string }>();\n\n /** Pre-allocated identity matrix for world-space hitbox draws. */\n private readonly _identity: mat4 = mat4.create();\n\n /** Cached `gl instanceof WebGL2RenderingContext` — computed once at construction. */\n private readonly _isWebGL2: boolean;\n\n constructor(\n private webglCore: WebGLCore,\n public viewport: Viewport,\n ) {\n const gl = webglCore.getRenderingContext();\n this._isWebGL2 = gl instanceof WebGL2RenderingContext;\n if (gl) gl.enable(gl.DEPTH_TEST);\n }\n\n /** Lazily-created material for model-level hitboxes (red). */\n private get modelHitboxMaterial(): Material {\n if (!this._modelHitboxMaterial) {\n this._modelHitboxMaterial = new Material(this.webglCore);\n this._modelHitboxMaterial.setColorHex('#ff0404ff');\n this._modelHitboxMaterial.unlit = true;\n }\n return this._modelHitboxMaterial;\n }\n\n /** Lazily-created material for mesh-level hitboxes (green). */\n private get meshHitboxMaterial(): Material {\n if (!this._meshHitboxMaterial) {\n this._meshHitboxMaterial = new Material(this.webglCore);\n this._meshHitboxMaterial.setColorHex('#04ff0cff');\n this._meshHitboxMaterial.unlit = true;\n }\n return this._meshHitboxMaterial;\n }\n\n public clearFrame(\n color: [number, number, number, number] = [0.2, 0.5, 0.95, 1],\n ) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n gl.clearColor(...color);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n }\n\n public render(scene: Scene) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n const models = scene.getModels();\n const lights = scene.getLights();\n const camera = this.viewport.camera;\n\n const viewPosition: vec3 = camera.getComputedPosition();\n this.clearFrame();\n\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n let activeProgram: WebGLProgram | null = null;\n\n for (const model of models) {\n activeProgram = this.drawModel(\n gl,\n model,\n lights,\n viewPosition,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n private drawModel(\n gl: WebGLRenderingContext,\n model: Model,\n lights: Light[],\n viewPosition: vec3,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ): WebGLProgram | null {\n const modelMatrix = model.getModelMatrix();\n const hasSkeleton = model.skeleton !== null;\n\n for (const mesh of model.meshes) {\n activeProgram = this.drawMesh(\n mesh,\n lights,\n viewPosition,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n hasSkeleton && mesh.isSkinned ? model : null,\n );\n\n if (this.debug) {\n this.drawHitBoxForMesh(\n mesh,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n // Debug wireframe\n if (this.debug) {\n this.drawHitBox(model, activeProgram);\n }\n\n return activeProgram;\n }\n\n private drawMesh(\n mesh: Mesh,\n lights: Light[],\n viewPosition: vec3,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n skinnedModel: Model | null = null,\n ): WebGLProgram | null {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return activeProgram;\n if (!mesh.material) return activeProgram;\n\n mesh.initBuffer(this.webglCore);\n\n const { program, uniformLocations } = mesh.material;\n\n if (program !== activeProgram) {\n gl.useProgram(program);\n activeProgram = program;\n\n if (uniformLocations['uView'])\n gl.uniformMatrix4fv(uniformLocations['uView'], false, viewMatrix);\n if (uniformLocations['uProjection'])\n gl.uniformMatrix4fv(uniformLocations['uProjection'], false, projMatrix);\n }\n\n // Use cached WebGL2 check\n const isWebGL2 = this._isWebGL2;\n\n // --- Bind VAO if available ---\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(\n mesh.buffers.vao,\n );\n } else {\n // --- WebGL1 Fallback ---\n const aPosition = mesh.material.attribLocations['aPosition'];\n if (aPosition !== -1 && mesh.buffers?.vertexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexBuffer);\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aNormal = mesh.material.attribLocations['aNormal'];\n if (aNormal !== -1 && mesh.buffers?.normalBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.normalBuffer);\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aTexCoord = mesh.material.attribLocations['aTexCoord'];\n if (aTexCoord !== -1 && mesh.buffers?.texCoordBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.texCoordBuffer);\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n\n // Skinning attributes (joint indices + weights)\n const aJointIndices = mesh.material.attribLocations['aJointIndices'];\n if (aJointIndices !== -1 && mesh.buffers?.jointIndexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointIndexBuffer);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n const aJointWeights = mesh.material.attribLocations['aJointWeights'];\n if (aJointWeights !== -1 && mesh.buffers?.jointWeightBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointWeightBuffer);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Material uniforms\n mesh.material.apply(gl, lights, viewPosition as Vector3);\n\n // ── Skinning uniforms ─────────────────────────────────────\n const uUseSkinning = uniformLocations['uUseSkinning'];\n const skeleton: Skeleton | null =\n (skinnedModel?.skeleton as Skeleton | undefined) ?? null;\n if (skeleton && mesh.isSkinned) {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 1);\n const uJointMatrices = uniformLocations['uJointMatrices[0]'];\n if (uJointMatrices) {\n gl.uniformMatrix4fv(uJointMatrices, false, skeleton.jointMatrices);\n }\n } else {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 0);\n }\n\n // Model transform\n const uModel = mesh.material.uniformLocations['uModel'];\n if (uModel) gl.uniformMatrix4fv(uModel, false, modelMatrix as Float32Array);\n\n // Draw\n if (mesh.buffers?.indexBuffer && mesh.indices) {\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.buffers.indexBuffer);\n const type =\n mesh.indices instanceof Uint32Array\n ? gl.UNSIGNED_INT\n : gl.UNSIGNED_SHORT;\n gl.drawElements(mesh.mode, mesh.indices.length, type, 0);\n } else {\n gl.drawArrays(mesh.mode, 0, mesh.vertices.length / 3);\n }\n\n // Cleanup\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(null);\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n return activeProgram;\n }\n\n public drawHitBox(\n model: Model,\n activeProgram: WebGLProgram | null,\n ) {\n const bbox = model.boundingBox;\n const key = `${bbox.min[0]},${bbox.min[1]},${bbox.min[2]},${bbox.max[0]},${bbox.max[1]},${bbox.max[2]}`;\n const cached = this._modelWireBoxCache.get(model);\n let hitboxMesh: Mesh;\n if (!cached || cached.key !== key) {\n hitboxMesh = createWireBox(bbox.min, bbox.max, this.modelHitboxMaterial);\n hitboxMesh.isCollidable = false;\n this._modelWireBoxCache.set(model, { mesh: hitboxMesh, key });\n } else {\n hitboxMesh = cached.mesh;\n }\n\n const camera = this.viewport.camera;\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n this.drawMesh(\n hitboxMesh,\n [],\n camera.position,\n this._identity,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n\n private drawHitBoxForMesh(\n mesh: Mesh,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ) {\n if (!mesh.boundingBox) return;\n\n const { min, max } = mesh.boundingBox;\n const key = `${min[0]},${min[1]},${min[2]},${max[0]},${max[1]},${max[2]}`;\n const cached = this._meshWireBoxCache.get(mesh);\n let wireBox: Mesh;\n if (!cached || cached.key !== key) {\n wireBox = createWireBox(min, max, this.meshHitboxMaterial);\n wireBox.isCollidable = false;\n this._meshWireBoxCache.set(mesh, { mesh: wireBox, key });\n } else {\n wireBox = cached.mesh;\n }\n\n const camera = this.viewport.camera;\n this.drawMesh(\n wireBox,\n [],\n camera.position,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n}\n","import { AABB } from '../domain/value-objects/Collider';\n\n/**\n * Compute the axis-aligned bounding box (AABB) from a flat Float32Array of vertices.\n * Each vertex is expected to be 3 consecutive floats: [x, y, z, ...].\n */\nexport function computeVertexBounds(vertices: Float32Array): AABB {\n if (!vertices || vertices.length === 0) {\n return { min: [0, 0, 0], max: [0, 0, 0] };\n }\n\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n\n for (let i = 0; i < vertices.length; i += 3) {\n const x = vertices[i];\n const y = vertices[i + 1];\n const z = vertices[i + 2];\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (z < minZ) minZ = z;\n\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n if (z > maxZ) maxZ = z;\n }\n\n return {\n min: [minX, minY, minZ],\n max: [maxX, maxY, maxZ],\n };\n}\n","import WebGLCore from './WebGLCore';\nimport { Material } from './Material';\nimport { AABB } from '../domain/value-objects/Collider';\nimport { computeVertexBounds } from '../utils/compute-bounds';\n\n/** WebGL draw-mode constants. */\nexport const GL_LINES = 0x0001;\nexport const GL_TRIANGLES = 0x0004;\n\nexport const INITIAL_BOUNDING_BOX: AABB = { min: [0, 0, 0], max: [0, 0, 0] };\n\n/**\n * A single renderable mesh: vertex data, normals, indices, a material, and an\n * optional bounding box used for collision detection.\n *\n * For skinned meshes, set {@link jointIndices} and {@link jointWeights} to\n * enable GPU skeletal animation.\n */\nexport class Mesh {\n name: string;\n\n vertices: Float32Array;\n\n normals: Float32Array;\n\n texCoords: Float32Array;\n\n indices: Uint16Array | Uint32Array | null;\n\n mode: number;\n\n material: Material;\n\n // ── Skinning data (optional) ─────────────────────────────────\n /** Per-vertex joint indices (vec4 per vertex, 4 influences). */\n jointIndices: Float32Array | null = null;\n\n /** Per-vertex joint weights (vec4 per vertex, 4 influences). */\n jointWeights: Float32Array | null = null;\n\n // WebGL buffers aggregate\n buffers: MeshBuffers | null = null;\n\n boundingBox: AABB = INITIAL_BOUNDING_BOX;\n\n isCollidable = true;\n\n isInitialized = false;\n\n constructor(\n name: string,\n vertices: Float32Array,\n normals: Float32Array,\n material: Material,\n texCoords: Float32Array = new Float32Array(),\n indices: Uint16Array | Uint32Array | null = null,\n ) {\n this.mode = GL_TRIANGLES;\n this.name = name;\n this.normals = normals;\n this.indices = indices;\n this.vertices = vertices;\n this.texCoords = texCoords;\n this.material = material;\n\n this.computeBounds();\n }\n\n setMode(newMode: number) {\n this.mode = newMode;\n }\n\n /** Whether this mesh has skinning data for skeletal animation. */\n get isSkinned(): boolean {\n return this.jointIndices !== null && this.jointWeights !== null;\n }\n\n computeBounds() {\n this.boundingBox = computeVertexBounds(this.vertices);\n }\n\n clone(): Mesh {\n const clonedMesh = new Mesh(\n this.name,\n new Float32Array(this.vertices),\n new Float32Array(this.normals),\n this.material, // shallow ref — call Material.clone() if independent material is needed\n new Float32Array(this.texCoords),\n this.indices\n ? this.indices instanceof Uint16Array\n ? new Uint16Array(this.indices)\n : new Uint32Array(this.indices)\n : null,\n );\n clonedMesh.setMode(this.mode);\n clonedMesh.isCollidable = this.isCollidable;\n if (this.jointIndices)\n clonedMesh.jointIndices = new Float32Array(this.jointIndices);\n if (this.jointWeights)\n clonedMesh.jointWeights = new Float32Array(this.jointWeights);\n return clonedMesh;\n }\n\n /** Initialize GPU buffers safely */\n public initBuffer(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n\n if (!gl) {\n console.warn(\n `[Mesh:${this.name}] GL context not available, skipping buffer init.`,\n );\n return;\n }\n if (this.isInitialized) return;\n\n this.buffers = new MeshBuffers();\n this.buffers.init(gl as WebGL2RenderingContext, this);\n this.isInitialized = true;\n }\n\n /** Dispose of GPU buffers safely */\n public dispose(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n if (!gl || !this.buffers) return;\n\n this.buffers.dispose(gl as WebGL2RenderingContext);\n this.buffers = null;\n this.isInitialized = false;\n }\n}\n\nexport class MeshBuffers {\n vao: WebGLVertexArrayObject | null = null;\n vertexBuffer: WebGLBuffer | null = null;\n normalBuffer: WebGLBuffer | null = null;\n texCoordBuffer: WebGLBuffer | null = null;\n indexBuffer: WebGLBuffer | null = null;\n jointIndexBuffer: WebGLBuffer | null = null;\n jointWeightBuffer: WebGLBuffer | null = null;\n\n public init(gl: WebGLRenderingContext, mesh: Mesh) {\n if (!gl) throw new Error('Cannot init MeshBuffers: invalid GL context');\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n const gl2 = isWebGL2 ? (gl as unknown as WebGL2RenderingContext) : null;\n\n if (gl2) {\n this.vao = gl2.createVertexArray();\n gl2.bindVertexArray(this.vao);\n }\n\n // --- Attribute layout ---\n // Attribute locations must match the shader (aPosition=0, aNormal=1, aTexCoord=2).\n // Look them up from the mesh's material program so the VAO captures the bindings.\n const program = mesh.material?.program;\n const aPosition = program ? gl.getAttribLocation(program, 'aPosition') : 0;\n const aNormal = program ? gl.getAttribLocation(program, 'aNormal') : 1;\n const aTexCoord = program ? gl.getAttribLocation(program, 'aTexCoord') : 2;\n\n // Vertex buffer\n this.vertexBuffer = gl.createBuffer();\n if (!this.vertexBuffer) throw new Error('Failed to create vertex buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);\n if (aPosition !== -1) {\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n // Normal buffer\n this.normalBuffer = gl.createBuffer();\n if (!this.normalBuffer) throw new Error('Failed to create normal buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);\n if (aNormal !== -1) {\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n // TexCoord buffer\n if (mesh.texCoords.length > 0) {\n this.texCoordBuffer = gl.createBuffer();\n if (!this.texCoordBuffer)\n throw new Error('Failed to create texCoord buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.texCoords, gl.STATIC_DRAW);\n if (aTexCoord !== -1) {\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Index buffer\n if (mesh.indices && mesh.indices.length > 0) {\n this.indexBuffer = gl.createBuffer();\n if (!this.indexBuffer) throw new Error('Failed to create index buffer');\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);\n }\n\n // Joint indices buffer (skinning)\n const aJointIndices = program\n ? gl.getAttribLocation(program, 'aJointIndices')\n : -1;\n if (\n mesh.jointIndices &&\n mesh.jointIndices.length > 0 &&\n aJointIndices !== -1\n ) {\n this.jointIndexBuffer = gl.createBuffer();\n if (!this.jointIndexBuffer)\n throw new Error('Failed to create joint index buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointIndexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointIndices, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n // Joint weights buffer (skinning)\n const aJointWeights = program\n ? gl.getAttribLocation(program, 'aJointWeights')\n : -1;\n if (\n mesh.jointWeights &&\n mesh.jointWeights.length > 0 &&\n aJointWeights !== -1\n ) {\n this.jointWeightBuffer = gl.createBuffer();\n if (!this.jointWeightBuffer)\n throw new Error('Failed to create joint weight buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointWeightBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointWeights, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n\n if (gl2) {\n gl2.bindVertexArray(null);\n }\n }\n\n public dispose(gl: WebGL2RenderingContext) {\n if (!gl) {\n console.warn('Cannot dispose MeshBuffers: invalid GL context');\n return;\n }\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n\n if (this.vertexBuffer) gl.deleteBuffer(this.vertexBuffer);\n if (this.normalBuffer) gl.deleteBuffer(this.normalBuffer);\n if (this.texCoordBuffer) gl.deleteBuffer(this.texCoordBuffer);\n if (this.indexBuffer) gl.deleteBuffer(this.indexBuffer);\n if (this.jointIndexBuffer) gl.deleteBuffer(this.jointIndexBuffer);\n if (this.jointWeightBuffer) gl.deleteBuffer(this.jointWeightBuffer);\n\n if (isWebGL2 && this.vao) {\n (gl as unknown as WebGL2RenderingContext).deleteVertexArray(this.vao);\n }\n\n this.vertexBuffer =\n this.normalBuffer =\n this.texCoordBuffer =\n this.indexBuffer =\n this.jointIndexBuffer =\n this.jointWeightBuffer =\n this.vao =\n null;\n }\n}\n","// GLSL shader sources inlined as TypeScript strings so the package can\n// be built with tsup without requiring Vite's ?raw loader.\n\nexport const vsSource = /* glsl */ `\n#define MAX_JOINTS 128\n\nattribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\n// ── Skinning attributes (vec4: up to 4 joint influences per vertex) ──\nattribute vec4 aJointIndices;\nattribute vec4 aJointWeights;\n\nuniform mat4 uModel;\nuniform mat4 uView;\nuniform mat4 uProjection;\n\n// ── Skinning uniforms ──\nuniform bool uUseSkinning;\nuniform mat4 uJointMatrices[MAX_JOINTS];\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nvoid main() {\n vTexCoord = aTexCoord;\n\n vec4 pos = vec4(aPosition, 1.0);\n vec3 norm = aNormal;\n\n if (uUseSkinning) {\n mat4 skinMat =\n aJointWeights.x * uJointMatrices[int(aJointIndices.x)] +\n aJointWeights.y * uJointMatrices[int(aJointIndices.y)] +\n aJointWeights.z * uJointMatrices[int(aJointIndices.z)] +\n aJointWeights.w * uJointMatrices[int(aJointIndices.w)];\n pos = skinMat * pos;\n norm = mat3(skinMat) * norm;\n }\n\n vNormal = mat3(uModel) * norm;\n\n vec4 worldPosition = uModel * pos;\n vPosition = worldPosition.xyz;\n gl_Position = uProjection * uView * worldPosition;\n}\n`;\n\nexport const fsSource = /* glsl */ `\nprecision mediump float;\n\n#define MAX_LIGHTS 5\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nuniform vec3 uLightDirection[MAX_LIGHTS];\nuniform vec3 uLightColor[MAX_LIGHTS];\nuniform float uLightIntensity[MAX_LIGHTS];\nuniform int uLightType[MAX_LIGHTS]; // 0=directional, 1=point, 2=ambient\nuniform int uLightCount;\nuniform vec3 uLightPosition[MAX_LIGHTS];\nuniform float uLightConstant[MAX_LIGHTS];\nuniform float uLightLinear[MAX_LIGHTS];\nuniform float uLightQuadratic[MAX_LIGHTS];\n\nuniform vec4 uColor;\nuniform bool uUnlit;\nuniform bool uUseTexture;\nuniform sampler2D uTexture;\n\nuniform vec3 uViewPosition;\nuniform float uShininess;\nuniform vec3 uSpecularColor;\n\nvoid main() {\n vec4 baseColor = uColor;\n if (uUseTexture) {\n baseColor *= texture2D(uTexture, vTexCoord);\n }\n\n if (uUnlit) {\n gl_FragColor = baseColor;\n return;\n }\n\n vec3 norm = normalize(vNormal);\n vec3 totalDiffuse = vec3(0.0);\n vec3 totalAmbient = vec3(0.0);\n vec3 totalSpecular = vec3(0.0);\n\n vec3 viewDir = normalize(uViewPosition - vPosition);\n\n for (int i = 0; i < MAX_LIGHTS; i++) {\n if (i >= uLightCount) break;\n\n if (uLightType[i] == 2) {\n totalAmbient += uLightColor[i] * uLightIntensity[i];\n } else {\n vec3 lightDir;\n float attenuation = 1.0;\n\n if (uLightType[i] == 0) {\n // Directional light\n lightDir = normalize(-uLightDirection[i]);\n } else {\n // Point light\n vec3 lightVec = uLightPosition[i] - vPosition;\n float distance = length(lightVec);\n lightDir = normalize(lightVec);\n attenuation = 1.0 / (\n uLightConstant[i] +\n uLightLinear[i] * distance +\n uLightQuadratic[i] * distance * distance\n );\n }\n\n // Diffuse\n float diff = max(dot(norm, lightDir), 0.0);\n totalDiffuse += diff * uLightColor[i] * uLightIntensity[i] * attenuation;\n\n // Specular (Blinn-Phong) — softer, more natural highlights\n vec3 halfDir = normalize(lightDir + viewDir);\n float spec = pow(max(dot(norm, halfDir), 0.0), uShininess);\n totalSpecular += spec * uSpecularColor * uLightIntensity[i] * attenuation;\n }\n }\n\n vec3 lightingResult =\n totalAmbient * baseColor.rgb +\n totalDiffuse * baseColor.rgb +\n totalSpecular;\n\n gl_FragColor = vec4(lightingResult, baseColor.a);\n}\n`;\n","/* eslint-disable @typescript-eslint/no-unsafe-argument */\nimport { vsSource, fsSource } from '../shaders/index';\n\nexport type RenderCallback = (\n gl: WebGLRenderingContext,\n program: WebGLProgram,\n) => void;\n\nexport class WebGLCore {\n public gl: WebGL2RenderingContext | WebGLRenderingContext;\n public program: WebGLProgram;\n public canvas: HTMLCanvasElement;\n public isWebGL2: boolean;\n\n /**\n * Initialise the WebGL rendering core.\n *\n * @param canvasOrId - An `HTMLCanvasElement` or the DOM `id` of one.\n * Defaults to `'glcanvas'`.\n */\n constructor(canvasOrId: string | HTMLCanvasElement = 'glcanvas') {\n const canvas =\n typeof canvasOrId === 'string'\n ? (document.getElementById(canvasOrId) as HTMLCanvasElement | null)\n : canvasOrId;\n if (!canvas) throw new Error('Canvas not found');\n this.canvas = canvas;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n this.gl = (canvas.getContext('webgl2') ?? canvas.getContext('webgl')) as\n | WebGL2RenderingContext\n | WebGLRenderingContext;\n\n if (!this.gl) throw new Error('WebGL not supported in this browser!');\n\n this.isWebGL2 = this.gl instanceof WebGL2RenderingContext;\n this.gl.viewport(0, 0, canvas.width, canvas.height);\n\n const program = this.createProgram(this.gl);\n if (!program) throw new Error('Failed to create program');\n this.program = program as unknown as WebGLProgram;\n }\n\n public getProgram(): WebGLProgram {\n return this.program;\n }\n\n public getRenderingContext(): WebGLRenderingContext {\n return this.gl;\n }\n\n private createProgram(gl: WebGLRenderingContext): WebGLProgram | null {\n const vertexShader = this.compileShader(gl, gl.VERTEX_SHADER, vsSource);\n const fragmentShader = this.compileShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vertexShader || !fragmentShader)\n throw new Error('Failed to create shaders');\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n // Clean up shader objects\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error(gl.getProgramInfoLog(program));\n throw new Error(gl.getProgramInfoLog(program) || 'Program link error');\n }\n\n return program;\n }\n\n private compileShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n throw new Error(gl.getShaderInfoLog(shader) || 'Shader compile error');\n }\n return shader;\n }\n\n public resize(width: number, height: number) {\n this.canvas.width = width;\n this.canvas.height = height;\n this.gl.viewport(0, 0, width, height);\n }\n\n dispose() {\n const { gl, program } = this;\n if (program) gl.deleteProgram(program);\n }\n}\n\nexport default WebGLCore;\n","import { mat4, quat, vec3 } from 'gl-matrix';\n\n/** Maximum number of joints supported per skeleton (must match shader). */\nexport const MAX_JOINTS = 128;\n\n/**\n * Describes a single joint (bone) inside a {@link Skeleton}.\n *\n * Each joint stores its default bind-pose transform (translation, rotation,\n * scale) and the inverse bind matrix used to transform vertices from model\n * space into joint-local space.\n */\nexport interface Joint {\n /** Human-readable name (e.g. \"Hips\", \"LeftArm\"). */\n name: string;\n /** Original glTF node index this joint references. */\n nodeIndex: number;\n /** Index of the parent joint in the skeleton's joint array (-1 = root). */\n parentIndex: number;\n /** Bind-pose translation. */\n localTranslation: vec3;\n /** Bind-pose rotation (quaternion). */\n localRotation: quat;\n /** Bind-pose scale. */\n localScale: vec3;\n /** Inverse bind matrix from the glTF skin. */\n inverseBindMatrix: mat4;\n}\n\n/**\n * Per-joint local transform produced by animation sampling.\n * Any undefined component falls back to the bind-pose value.\n */\nexport interface JointPose {\n t?: vec3;\n r?: quat;\n s?: vec3;\n}\n\n/**\n * A skeleton representing a hierarchy of {@link Joint}s.\n *\n * Call {@link computeJointMatrices} each frame with the current animation pose\n * to produce a flat `Float32Array` of `mat4`s ready for upload as a shader\n * uniform.\n */\nexport class Skeleton {\n /** Ordered joint list (parent always precedes child). */\n public readonly joints: Joint[];\n\n /**\n * Flat `Float32Array` of `joints.length` column-major `mat4` values.\n * Updated by {@link computeJointMatrices} — upload directly to\n * `uJointMatrices[0]`.\n */\n public readonly jointMatrices: Float32Array;\n\n /**\n * Global transform of the non-joint ancestor nodes above the skeleton\n * root. In many exported models the armature sits under a scene-root\n * node that carries scale / rotation (e.g. cm → m conversion). This\n * transform is pre-multiplied into root-joint globals so that\n * `global * inverseBindMatrix` evaluates to identity in the bind pose.\n */\n public readonly rootTransform: mat4;\n\n // ── Pre-allocated scratch buffers ──\n /** Per-joint global transform matrices, reused each frame (allocated once in constructor). */\n private readonly _globals: mat4[];\n private readonly _local: mat4 = mat4.create();\n private readonly _final: mat4 = mat4.create();\n\n constructor(joints: Joint[], rootTransform?: mat4) {\n if (joints.length > MAX_JOINTS) {\n console.warn(\n `[Skeleton] ${joints.length} joints exceeds MAX_JOINTS (${MAX_JOINTS}). ` +\n 'Extra joints will be ignored by the shader.',\n );\n }\n this.joints = joints;\n this.jointMatrices = new Float32Array(joints.length * 16);\n this.rootTransform = rootTransform ?? mat4.create();\n // Pre-allocate per-joint globals and scratch matrices — reused every frame.\n this._globals = Array.from({ length: joints.length }, () => mat4.create());\n }\n\n /** Number of joints in the skeleton. */\n get jointCount(): number {\n return this.joints.length;\n }\n\n /**\n * Walk the joint hierarchy, composing global transforms, then multiply by\n * each joint's inverse-bind matrix. The result is stored in\n * {@link jointMatrices} ready for shader upload.\n *\n * @param animatedPoses - Optional per-joint local transforms from an\n * {@link AnimationClip}. Map keys are **joint array indices** (not glTF\n * node indices).\n */\n computeJointMatrices(animatedPoses?: Map<number, JointPose>): void {\n const local = this._local;\n const final = this._final;\n const globals = this._globals;\n\n for (let i = 0; i < this.joints.length; i++) {\n const joint = this.joints[i];\n const pose = animatedPoses?.get(i);\n\n const t = pose?.t ?? joint.localTranslation;\n const r = pose?.r ?? joint.localRotation;\n const s = pose?.s ?? joint.localScale;\n\n mat4.fromRotationTranslationScale(local, r, t, s);\n\n if (joint.parentIndex >= 0) {\n mat4.multiply(globals[i], globals[joint.parentIndex], local);\n } else {\n // Root joint — include the non-joint ancestor transform\n mat4.multiply(globals[i], this.rootTransform, local);\n }\n\n // final = global * inverseBindMatrix\n mat4.multiply(final, globals[i], joint.inverseBindMatrix);\n this.jointMatrices.set(final as unknown as Float32Array, i * 16);\n }\n }\n\n /**\n * Create a deep clone of this skeleton (useful when instancing models).\n */\n clone(): Skeleton {\n const clonedJoints: Joint[] = this.joints.map((j) => ({\n name: j.name,\n nodeIndex: j.nodeIndex,\n parentIndex: j.parentIndex,\n localTranslation: vec3.clone(j.localTranslation),\n localRotation: quat.clone(j.localRotation),\n localScale: vec3.clone(j.localScale),\n inverseBindMatrix: mat4.clone(j.inverseBindMatrix),\n }));\n return new Skeleton(clonedJoints, mat4.clone(this.rootTransform));\n }\n}\n","import { quat, vec3 } from 'gl-matrix';\nimport type { JointPose } from './Skeleton';\n\n// ── Module-level scratch buffers ──\n// Reused across all AnimationClip.sample() calls. Safe because sample() is\n// synchronous and scratch values are copied into pose storage before reuse.\nconst _sv0Vec3 = vec3.create();\nconst _sv1Vec3 = vec3.create();\nconst _sOutVec3 = vec3.create();\nconst _sv0Quat = quat.create();\nconst _sv1Quat = quat.create();\nconst _sOutQuat = quat.create();\n\n// ─── Types ──────────────────────────────────────────────────────\n\n/** Interpolation mode as defined by glTF 2.0. */\nexport type InterpolationMode = 'STEP' | 'LINEAR' | 'CUBICSPLINE';\n\n/** Which transform component the channel drives. */\nexport type AnimationPath = 'translation' | 'rotation' | 'scale';\n\n/**\n * Stores the raw keyframe data for one animated property and performs\n * interpolation between keyframes.\n */\nexport interface AnimationSampler {\n /** Keyframe timestamps (seconds). */\n times: Float32Array;\n /** Flattened keyframe output values (3 or 4 components per key). */\n values: Float32Array;\n /** Interpolation mode (default `LINEAR`). */\n interpolation: InterpolationMode;\n}\n\n/**\n * Links a {@link AnimationSampler} to a specific joint property.\n */\nexport interface AnimationChannel {\n /** Index into the owning {@link Skeleton}'s joint array. */\n jointIndex: number;\n /** Transform component this channel drives. */\n path: AnimationPath;\n /** The sampler that holds keyframe data. */\n sampler: AnimationSampler;\n}\n\n// ─── AnimationClip ──────────────────────────────────────────────\n\n/**\n * A named animation clip containing one or more {@link AnimationChannel}s.\n *\n * Call {@link update} each frame to advance the playhead, then {@link sample}\n * to obtain per-joint local transforms that feed into\n * `Skeleton.computeJointMatrices()`.\n *\n * ```ts\n * clip.play();\n * // game loop:\n * clip.update(deltaTime);\n * const poses = clip.sample();\n * skeleton.computeJointMatrices(poses);\n * ```\n */\nexport class AnimationClip {\n /** Human-readable name (e.g. \"Walk\", \"Idle\"). */\n public readonly name: string;\n\n /** All channels in this clip. */\n public readonly channels: AnimationChannel[];\n\n /** Total duration in seconds (derived from the maximum keyframe time). */\n public readonly duration: number;\n\n /** Current playback time in seconds. */\n public currentTime = 0;\n\n /** Whether the clip loops when it reaches the end. */\n public loop = true;\n\n /** Playback speed multiplier (1 = normal, 2 = double, −1 = reverse). */\n public speed = 1;\n\n /** Whether the clip is currently advancing. */\n public playing = false;\n\n /**\n * Per-joint pose objects reused across frames to avoid per-frame allocations.\n * The vec3/quat inside each entry are also pre-allocated on first use.\n */\n private readonly _posePool = new Map<number, JointPose>();\n\n constructor(name: string, channels: AnimationChannel[], duration: number) {\n this.name = name;\n this.channels = channels;\n this.duration = duration;\n }\n\n /** Start / resume playback. */\n play(): void {\n this.playing = true;\n }\n\n /** Pause playback (keeps current time). */\n pause(): void {\n this.playing = false;\n }\n\n /** Stop and rewind to the beginning. */\n stop(): void {\n this.playing = false;\n this.currentTime = 0;\n }\n\n /** Rewind to the beginning without changing play state. */\n reset(): void {\n this.currentTime = 0;\n }\n\n /**\n * Advance the playhead by `deltaTime` seconds (respects {@link speed}).\n * Automatically loops or clamps depending on {@link loop}.\n */\n update(deltaTime: number): void {\n if (!this.playing || this.duration <= 0) return;\n\n this.currentTime += deltaTime * this.speed;\n\n if (this.loop) {\n this.currentTime =\n ((this.currentTime % this.duration) + this.duration) % this.duration;\n } else {\n if (this.currentTime >= this.duration) {\n this.currentTime = this.duration;\n this.playing = false;\n } else if (this.currentTime < 0) {\n this.currentTime = 0;\n this.playing = false;\n }\n }\n }\n\n /**\n * Sample every channel at the current playhead and write per-joint local\n * transforms into `out` (or a newly created map if none is provided).\n *\n * Pass a persistent `Map` to avoid allocating a new one every frame.\n */\n sample(out?: Map<number, JointPose>): Map<number, JointPose> {\n const result = out ?? new Map<number, JointPose>();\n if (out) out.clear();\n\n for (const channel of this.channels) {\n // Reuse the JointPose object for this joint from the pool.\n let entry = this._posePool.get(channel.jointIndex);\n if (!entry) {\n entry = {};\n this._posePool.set(channel.jointIndex, entry);\n }\n result.set(channel.jointIndex, entry);\n\n // sampleChannel writes into a module-level scratch buffer.\n // Copy the result into the pre-allocated fields of the pose entry.\n const value = sampleChannel(channel, this.currentTime);\n switch (channel.path) {\n case 'translation':\n if (!entry.t) entry.t = vec3.create();\n vec3.copy(entry.t, value as vec3);\n break;\n case 'rotation':\n if (!entry.r) entry.r = quat.create();\n quat.copy(entry.r, value as quat);\n break;\n case 'scale':\n if (!entry.s) entry.s = vec3.create();\n vec3.copy(entry.s, value as vec3);\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Create an independent copy of this clip (shares the underlying sampler\n * data but has its own playback state).\n */\n clone(): AnimationClip {\n const copy = new AnimationClip(this.name, this.channels, this.duration);\n copy.loop = this.loop;\n copy.speed = this.speed;\n return copy;\n }\n}\n\n// ── Sampling helpers ───────────────────────────────────────────────\n\n/** Write `numComponents` floats from `values` at `index * numComponents` into `out`. */\nfunction readValueInto(\n values: Float32Array,\n index: number,\n numComponents: number,\n out: vec3 | quat,\n): void {\n const off = index * numComponents;\n out[0] = values[off];\n out[1] = values[off + 1];\n out[2] = values[off + 2];\n if (numComponents === 4) out[3] = values[off + 3];\n}\n\n/**\n * Sample a single channel at the given time. Writes the result into a\n * module-level scratch buffer and returns a reference to it.\n * The caller must copy the value before the next sampleChannel invocation.\n */\nfunction sampleChannel(channel: AnimationChannel, time: number): vec3 | quat {\n const { sampler, path } = channel;\n const { times, values, interpolation } = sampler;\n const numComponents = path === 'rotation' ? 4 : 3;\n const outScratch: vec3 | quat = numComponents === 4 ? _sOutQuat : _sOutVec3;\n\n // Edge cases — clamp to first / last keyframe\n if (times.length === 0) {\n if (numComponents === 4) quat.identity(outScratch as quat);\n else vec3.set(outScratch as vec3, 0, 0, 0);\n return outScratch;\n }\n if (time <= times[0]) {\n readValueInto(values, 0, numComponents, outScratch);\n return outScratch;\n }\n if (time >= times[times.length - 1]) {\n readValueInto(values, times.length - 1, numComponents, outScratch);\n return outScratch;\n }\n\n // Binary search for the surrounding keyframe pair\n let lo = 0;\n let hi = times.length - 1;\n while (lo < hi - 1) {\n const mid = (lo + hi) >>> 1;\n if (times[mid] <= time) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const t0 = times[lo];\n const t1 = times[hi];\n const factor = (time - t0) / (t1 - t0);\n\n const v0Scratch: vec3 | quat = numComponents === 4 ? _sv0Quat : _sv0Vec3;\n const v1Scratch: vec3 | quat = numComponents === 4 ? _sv1Quat : _sv1Vec3;\n readValueInto(values, lo, numComponents, v0Scratch);\n readValueInto(values, hi, numComponents, v1Scratch);\n\n if (interpolation === 'STEP') {\n // STEP: return v0 unchanged\n readValueInto(values, lo, numComponents, outScratch);\n return outScratch;\n }\n\n // LINEAR (or fallback for CUBICSPLINE — true cubic not yet implemented)\n if (path === 'rotation') {\n quat.slerp(outScratch as quat, v0Scratch as quat, v1Scratch as quat, factor);\n return outScratch;\n }\n\n vec3.lerp(outScratch as vec3, v0Scratch as vec3, v1Scratch as vec3, factor);\n return outScratch;\n}\n","import { Material, Mesh } from '../classes';\nimport { GL_LINES } from '../classes/Mesh';\nimport { Vector3 } from '../domain/interfaces/Vectors';\n\nexport function createWireBox(\n min: Vector3,\n max: Vector3,\n material: Material,\n): Mesh {\n const [minX, minY, minZ] = min;\n const [maxX, maxY, maxZ] = max;\n\n // 8 corners of the box\n const vertices: number[] = [\n minX,\n minY,\n minZ,\n maxX,\n minY,\n minZ,\n maxX,\n maxY,\n minZ,\n minX,\n maxY,\n minZ,\n minX,\n minY,\n maxZ,\n maxX,\n minY,\n maxZ,\n maxX,\n maxY,\n maxZ,\n minX,\n maxY,\n maxZ,\n ];\n\n // edges as lines (pairs of indices)\n const indices: number[] = [\n 0,\n 1,\n 1,\n 2,\n 2,\n 3,\n 3,\n 0, // bottom face\n 4,\n 5,\n 5,\n 6,\n 6,\n 7,\n 7,\n 4, // top face\n 0,\n 4,\n 1,\n 5,\n 2,\n 6,\n 3,\n 7, // vertical edges\n ];\n\n // expand indices to vertices array (since WebGL uses gl.LINES)\n const lineVertices: number[] = [];\n for (let i = 0; i < indices.length; i++) {\n const idx = indices[i];\n lineVertices.push(\n vertices[idx * 3],\n vertices[idx * 3 + 1],\n vertices[idx * 3 + 2],\n );\n }\n\n // Normals can just be zero for wireframe\n const normals = new Float32Array(lineVertices.length);\n\n const mesh = new Mesh(\n 'hitbox',\n new Float32Array(lineVertices),\n normals,\n material,\n new Float32Array(),\n null,\n );\n mesh.setMode(GL_LINES);\n return mesh;\n}\n"],"mappings":"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","names":["mat4","quat","vec3","mat4"]}
|