@bornengine/engine 0.4.16
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/LICENSE +21 -0
- package/README.md +231 -0
- package/native/android/Cargo.lock +1848 -0
- package/native/android/Cargo.toml +24 -0
- package/native/android/src/lib.rs +702 -0
- package/native/ios/Cargo.lock +1690 -0
- package/native/ios/Cargo.toml +32 -0
- package/native/ios/src/lib.rs +1267 -0
- package/native/linux/Cargo.lock +3279 -0
- package/native/linux/Cargo.toml +29 -0
- package/native/linux/src/lib.rs +1331 -0
- package/native/macos/Cargo.lock +3310 -0
- package/native/macos/Cargo.toml +46 -0
- package/native/macos/src/lib.rs +1302 -0
- package/native/shared/Cargo.lock +1899 -0
- package/native/shared/Cargo.toml +62 -0
- package/native/shared/assets/default_font.ttf +0 -0
- package/native/shared/build.rs +270 -0
- package/native/shared/shaders/common/clouds.wgsl +122 -0
- package/native/shared/shaders/common/fog.wgsl +16 -0
- package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
- package/native/shared/shaders/common/imposter.wgsl +112 -0
- package/native/shared/shaders/common/pbr.wgsl +186 -0
- package/native/shared/shaders/common/shadows.wgsl +186 -0
- package/native/shared/shaders/common/sky.wgsl +8 -0
- package/native/shared/shaders/common/tonemap.wgsl +25 -0
- package/native/shared/shaders/impulse_field.wgsl +57 -0
- package/native/shared/shaders/material_abi.wgsl +383 -0
- package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
- package/native/shared/src/anim_mixer.rs +61 -0
- package/native/shared/src/attach.rs +263 -0
- package/native/shared/src/audio/decode.rs +123 -0
- package/native/shared/src/audio/mod.rs +863 -0
- package/native/shared/src/audio/render.rs +892 -0
- package/native/shared/src/audio/spsc.rs +156 -0
- package/native/shared/src/audio/stream.rs +226 -0
- package/native/shared/src/custom_shaders.rs +104 -0
- package/native/shared/src/decals.rs +245 -0
- package/native/shared/src/drs.rs +211 -0
- package/native/shared/src/engine.rs +261 -0
- package/native/shared/src/ffi.rs +116 -0
- package/native/shared/src/ffi_core/assets.rs +388 -0
- package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
- package/native/shared/src/ffi_core/draw.rs +334 -0
- package/native/shared/src/ffi_core/game_loop.rs +577 -0
- package/native/shared/src/ffi_core/input.rs +234 -0
- package/native/shared/src/ffi_core/mod.rs +127 -0
- package/native/shared/src/ffi_core/models.rs +1154 -0
- package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
- package/native/shared/src/ffi_core/scene.rs +626 -0
- package/native/shared/src/ffi_core/vfx.rs +212 -0
- package/native/shared/src/ffi_core/visual.rs +691 -0
- package/native/shared/src/frame_callbacks.rs +122 -0
- package/native/shared/src/geometry.rs +236 -0
- package/native/shared/src/handles.rs +182 -0
- package/native/shared/src/input.rs +448 -0
- package/native/shared/src/jolt_sys.rs +822 -0
- package/native/shared/src/lib.rs +55 -0
- package/native/shared/src/models.rs +1093 -0
- package/native/shared/src/models_gltf.rs +1280 -0
- package/native/shared/src/particles.rs +391 -0
- package/native/shared/src/physics_jolt.rs +1908 -0
- package/native/shared/src/picking.rs +298 -0
- package/native/shared/src/postfx.rs +345 -0
- package/native/shared/src/profiler.rs +492 -0
- package/native/shared/src/ragdoll.rs +474 -0
- package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
- package/native/shared/src/renderer/brdf_lut.rs +154 -0
- package/native/shared/src/renderer/draw2d.rs +143 -0
- package/native/shared/src/renderer/formats.rs +822 -0
- package/native/shared/src/renderer/froxel.rs +421 -0
- package/native/shared/src/renderer/gi_bake.rs +653 -0
- package/native/shared/src/renderer/graph.rs +462 -0
- package/native/shared/src/renderer/hiz.rs +269 -0
- package/native/shared/src/renderer/hot_reload.rs +390 -0
- package/native/shared/src/renderer/impulse_field.rs +456 -0
- package/native/shared/src/renderer/lighting.rs +154 -0
- package/native/shared/src/renderer/material_instancing.rs +171 -0
- package/native/shared/src/renderer/material_pipeline.rs +700 -0
- package/native/shared/src/renderer/material_system.rs +1996 -0
- package/native/shared/src/renderer/material_system_tests.rs +601 -0
- package/native/shared/src/renderer/material_system_wasm.rs +41 -0
- package/native/shared/src/renderer/mod.rs +12556 -0
- package/native/shared/src/renderer/model_draw.rs +641 -0
- package/native/shared/src/renderer/occlusion.rs +429 -0
- package/native/shared/src/renderer/planar_pass.rs +593 -0
- package/native/shared/src/renderer/planar_reflection.rs +499 -0
- package/native/shared/src/renderer/post_pass.rs +249 -0
- package/native/shared/src/renderer/postfx_chain.rs +728 -0
- package/native/shared/src/renderer/pt_pass.rs +577 -0
- package/native/shared/src/renderer/scene_pass.rs +607 -0
- package/native/shared/src/renderer/shader_include.rs +205 -0
- package/native/shared/src/renderer/shader_library.rs +135 -0
- package/native/shared/src/renderer/shaders/ao.rs +570 -0
- package/native/shared/src/renderer/shaders/core.rs +1243 -0
- package/native/shared/src/renderer/shaders/env.rs +907 -0
- package/native/shared/src/renderer/shaders/gi.rs +810 -0
- package/native/shared/src/renderer/shaders/mod.rs +19 -0
- package/native/shared/src/renderer/shaders/post.rs +1558 -0
- package/native/shared/src/renderer/shaders/pt.rs +1859 -0
- package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
- package/native/shared/src/renderer/shadow_pass.rs +731 -0
- package/native/shared/src/renderer/ssgi_pass.rs +392 -0
- package/native/shared/src/renderer/ssr_pass.rs +188 -0
- package/native/shared/src/renderer/texture_store.rs +473 -0
- package/native/shared/src/renderer/transient.rs +591 -0
- package/native/shared/src/renderer/types.rs +941 -0
- package/native/shared/src/renderer/util.rs +152 -0
- package/native/shared/src/scene.rs +1362 -0
- package/native/shared/src/sdf_cache.rs +274 -0
- package/native/shared/src/shadows.rs +1036 -0
- package/native/shared/src/staging.rs +102 -0
- package/native/shared/src/string_header.rs +266 -0
- package/native/shared/src/text_renderer.rs +502 -0
- package/native/shared/src/textures.rs +197 -0
- package/native/tvos/Cargo.lock +1693 -0
- package/native/tvos/Cargo.toml +36 -0
- package/native/tvos/metal-patched/Cargo.toml +178 -0
- package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
- package/native/tvos/metal-patched/LICENSE-MIT +25 -0
- package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
- package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
- package/native/tvos/metal-patched/src/argument.rs +366 -0
- package/native/tvos/metal-patched/src/blitpass.rs +102 -0
- package/native/tvos/metal-patched/src/buffer.rs +71 -0
- package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
- package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
- package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
- package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
- package/native/tvos/metal-patched/src/computepass.rs +107 -0
- package/native/tvos/metal-patched/src/constants.rs +152 -0
- package/native/tvos/metal-patched/src/counters.rs +119 -0
- package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
- package/native/tvos/metal-patched/src/device.rs +2134 -0
- package/native/tvos/metal-patched/src/drawable.rs +39 -0
- package/native/tvos/metal-patched/src/encoder.rs +2041 -0
- package/native/tvos/metal-patched/src/heap.rs +281 -0
- package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
- package/native/tvos/metal-patched/src/lib.rs +657 -0
- package/native/tvos/metal-patched/src/library.rs +902 -0
- package/native/tvos/metal-patched/src/mps.rs +575 -0
- package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
- package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
- package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
- package/native/tvos/metal-patched/src/renderpass.rs +443 -0
- package/native/tvos/metal-patched/src/resource.rs +182 -0
- package/native/tvos/metal-patched/src/sampler.rs +165 -0
- package/native/tvos/metal-patched/src/sync.rs +178 -0
- package/native/tvos/metal-patched/src/texture.rs +352 -0
- package/native/tvos/metal-patched/src/types.rs +90 -0
- package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
- package/native/tvos/src/audio_backend.rs +197 -0
- package/native/tvos/src/lib.rs +1891 -0
- package/native/visionos/Cargo.lock +1693 -0
- package/native/visionos/Cargo.toml +40 -0
- package/native/visionos/src/audio_backend.rs +197 -0
- package/native/visionos/src/lib.rs +1887 -0
- package/native/watchos/Cargo.lock +16 -0
- package/native/watchos/Cargo.toml +19 -0
- package/native/watchos/shaders/bloom_postfx.metal +99 -0
- package/native/watchos/src/BloomWatchApp.swift +1267 -0
- package/native/watchos/src/BloomWatchAudio.swift +179 -0
- package/native/watchos/src/audio.rs +55 -0
- package/native/watchos/src/draw_list.rs +229 -0
- package/native/watchos/src/ffi_stubs.rs +915 -0
- package/native/watchos/src/ffi_stubs_manual.rs +35 -0
- package/native/watchos/src/lib.rs +1124 -0
- package/native/watchos/src/models.rs +746 -0
- package/native/watchos/src/postfx.rs +95 -0
- package/native/watchos/src/scene.rs +534 -0
- package/native/watchos/src/textures.rs +184 -0
- package/native/web/Cargo.lock +1657 -0
- package/native/web/Cargo.toml +43 -0
- package/native/web/bloom_glue.js +695 -0
- package/native/web/build.sh +131 -0
- package/native/web/index.html +35 -0
- package/native/web/jolt_bridge.js +1519 -0
- package/native/web/src/input_ffi.rs +286 -0
- package/native/web/src/lib.rs +1796 -0
- package/native/web/src/material_ffi.rs +710 -0
- package/native/web/src/parity_ffi.rs +343 -0
- package/native/web/src/physics_ffi.rs +643 -0
- package/native/web/src/ragdoll_ffi.rs +250 -0
- package/native/web/src/render_settings.rs +98 -0
- package/native/windows/Cargo.lock +1815 -0
- package/native/windows/Cargo.toml +68 -0
- package/native/windows/src/lib.rs +1486 -0
- package/package.json +4279 -0
- package/src/audio/index.ts +315 -0
- package/src/core/colors.ts +63 -0
- package/src/core/index.ts +1206 -0
- package/src/core/keys.ts +63 -0
- package/src/core/types.ts +104 -0
- package/src/index.ts +171 -0
- package/src/math/index.ts +516 -0
- package/src/mobile/index.ts +294 -0
- package/src/models/index.ts +1258 -0
- package/src/physics/index.ts +1134 -0
- package/src/scene/index.ts +698 -0
- package/src/shapes/index.ts +120 -0
- package/src/text/index.ts +48 -0
- package/src/textures/index.ts +187 -0
- package/src/vfx/index.ts +191 -0
- package/src/world/index.ts +24 -0
- package/src/world/loader.ts +423 -0
- package/src/world/prefab.ts +217 -0
- package/src/world/render.ts +172 -0
- package/src/world/saver.ts +108 -0
- package/src/world/serialize.ts +301 -0
- package/src/world/terrain.ts +355 -0
- package/src/world/types.ts +160 -0
- package/src/world/validate.ts +319 -0
- package/src/world/version.ts +114 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import { Vec2 as Vec2Type, Vec3 as Vec3Type, Vec4 as Vec4Type, Quat, Ray, BoundingBox, Mat4, FrustumPlanes, RayHit, Model } from '../core/types';
|
|
2
|
+
type Vec2 = Vec2Type;
|
|
3
|
+
type Vec3 = Vec3Type;
|
|
4
|
+
type Vec4 = Vec4Type;
|
|
5
|
+
|
|
6
|
+
// Vec2 operations
|
|
7
|
+
|
|
8
|
+
export function vec2(x: number, y: number): Vec2 {
|
|
9
|
+
return { x, y };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function vec2Add(a: Vec2, b: Vec2): Vec2 {
|
|
13
|
+
return { x: a.x + b.x, y: a.y + b.y };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function vec2Sub(a: Vec2, b: Vec2): Vec2 {
|
|
17
|
+
return { x: a.x - b.x, y: a.y - b.y };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function vec2Scale(v: Vec2, scalar: number): Vec2 {
|
|
21
|
+
return { x: v.x * scalar, y: v.y * scalar };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function vec2Length(v: Vec2): number {
|
|
25
|
+
return Math.sqrt(v.x * v.x + v.y * v.y);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function vec2LengthSq(v: Vec2): number {
|
|
29
|
+
return v.x * v.x + v.y * v.y;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function vec2Normalize(v: Vec2): Vec2 {
|
|
33
|
+
const len = vec2Length(v);
|
|
34
|
+
if (len === 0) return { x: 0, y: 0 };
|
|
35
|
+
return { x: v.x / len, y: v.y / len };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function vec2Dot(a: Vec2, b: Vec2): number {
|
|
39
|
+
return a.x * b.x + a.y * b.y;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function vec2Distance(a: Vec2, b: Vec2): number {
|
|
43
|
+
const dx = b.x - a.x;
|
|
44
|
+
const dy = b.y - a.y;
|
|
45
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function vec2Lerp(a: Vec2, b: Vec2, t: number): Vec2 {
|
|
49
|
+
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Vec3 operations
|
|
53
|
+
|
|
54
|
+
export function vec3(x: number, y: number, z: number): Vec3 {
|
|
55
|
+
return { x: x, y: y, z: z };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function vec3Add(a: Vec3, b: Vec3): Vec3 {
|
|
59
|
+
return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function vec3Sub(a: Vec3, b: Vec3): Vec3 {
|
|
63
|
+
return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function vec3Scale(v: Vec3, scalar: number): Vec3 {
|
|
67
|
+
return { x: v.x * scalar, y: v.y * scalar, z: v.z * scalar };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function vec3Length(v: Vec3): number {
|
|
71
|
+
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function vec3LengthSq(v: Vec3): number {
|
|
75
|
+
return v.x * v.x + v.y * v.y + v.z * v.z;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function vec3Normalize(v: Vec3): Vec3 {
|
|
79
|
+
const len = vec3Length(v);
|
|
80
|
+
if (len === 0) return { x: 0, y: 0, z: 0 };
|
|
81
|
+
return { x: v.x / len, y: v.y / len, z: v.z / len };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function vec3Dot(a: Vec3, b: Vec3): number {
|
|
85
|
+
return a.x * b.x + a.y * b.y + a.z * b.z;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function vec3Cross(a: Vec3, b: Vec3): Vec3 {
|
|
89
|
+
return {
|
|
90
|
+
x: a.y * b.z - a.z * b.y,
|
|
91
|
+
y: a.z * b.x - a.x * b.z,
|
|
92
|
+
z: a.x * b.y - a.y * b.x,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function vec3Distance(a: Vec3, b: Vec3): number {
|
|
97
|
+
const dx = b.x - a.x, dy = b.y - a.y, dz = b.z - a.z;
|
|
98
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function vec3Lerp(a: Vec3, b: Vec3, t: number): Vec3 {
|
|
102
|
+
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t, z: a.z + (b.z - a.z) * t };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Vec4 operations
|
|
106
|
+
|
|
107
|
+
export function vec4(x: number, y: number, z: number, w: number): Vec4 {
|
|
108
|
+
return { x, y, z, w };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function vec4Add(a: Vec4, b: Vec4): Vec4 {
|
|
112
|
+
return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z, w: a.w + b.w };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function vec4Scale(v: Vec4, s: number): Vec4 {
|
|
116
|
+
return { x: v.x * s, y: v.y * s, z: v.z * s, w: v.w * s };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function vec4Length(v: Vec4): number {
|
|
120
|
+
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function vec4Normalize(v: Vec4): Vec4 {
|
|
124
|
+
const len = vec4Length(v);
|
|
125
|
+
if (len === 0) return { x: 0, y: 0, z: 0, w: 0 };
|
|
126
|
+
return { x: v.x / len, y: v.y / len, z: v.z / len, w: v.w / len };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Scalar utilities
|
|
130
|
+
|
|
131
|
+
export function lerp(a: number, b: number, t: number): number {
|
|
132
|
+
return a + (b - a) * t;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function clamp(value: number, min: number, max: number): number {
|
|
136
|
+
if (value < min) return min;
|
|
137
|
+
if (value > max) return max;
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function remap(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number {
|
|
142
|
+
return outMin + (value - inMin) / (inMax - inMin) * (outMax - outMin);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function randomFloat(min: number, max: number): number {
|
|
146
|
+
return min + Math.random() * (max - min);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function randomInt(min: number, max: number): number {
|
|
150
|
+
return Math.floor(min + Math.random() * (max - min + 1));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Easing functions
|
|
154
|
+
|
|
155
|
+
export function easeInQuad(t: number): number { return t * t; }
|
|
156
|
+
export function easeOutQuad(t: number): number { return t * (2 - t); }
|
|
157
|
+
// BROKEN under Perry — EN-051. The parameter never arrives: `t < 0.5` is false
|
|
158
|
+
// for every input, so this returns a constant. Adding a `console.log(t)` to the
|
|
159
|
+
// body makes it correct, which is the signature of a codegen bug, not a logic
|
|
160
|
+
// one. Rewriting with `if`, reordering the expression, and binding `t` to a
|
|
161
|
+
// local were all tried and none of them fix it. `easeInOutCubic` below is the
|
|
162
|
+
// same shape and is fine, so the shape is not the trigger.
|
|
163
|
+
//
|
|
164
|
+
// Left in its honest form rather than contorted around a bug I cannot explain.
|
|
165
|
+
// Nothing in the shooter or the editor calls it. See shooter
|
|
166
|
+
// docs/perry-quirks.md #8, Case B.
|
|
167
|
+
export function easeInOutQuad(t: number): number {
|
|
168
|
+
if (t < 0.5) return 2 * t * t;
|
|
169
|
+
return (4 - 2 * t) * t - 1;
|
|
170
|
+
}
|
|
171
|
+
export function easeInCubic(t: number): number { return t * t * t; }
|
|
172
|
+
export function easeOutCubic(t: number): number { const t1 = t - 1; return t1 * t1 * t1 + 1; }
|
|
173
|
+
export function easeInOutCubic(t: number): number {
|
|
174
|
+
if (t < 0.5) return 4 * t * t * t;
|
|
175
|
+
return 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
176
|
+
}
|
|
177
|
+
export function easeInElastic(t: number): number {
|
|
178
|
+
if (t === 0 || t === 1) return t;
|
|
179
|
+
return -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * (2 * Math.PI / 3));
|
|
180
|
+
}
|
|
181
|
+
export function easeOutElastic(t: number): number {
|
|
182
|
+
if (t === 0 || t === 1) return t;
|
|
183
|
+
return Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * (2 * Math.PI / 3)) + 1;
|
|
184
|
+
}
|
|
185
|
+
export function easeBounce(t: number): number {
|
|
186
|
+
const n1 = 7.5625, d1 = 2.75;
|
|
187
|
+
if (t < 1 / d1) return n1 * t * t;
|
|
188
|
+
if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
|
|
189
|
+
if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
|
|
190
|
+
return n1 * (t -= 2.625 / d1) * t + 0.984375;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Mat4 operations (column-major, 16-element arrays)
|
|
194
|
+
|
|
195
|
+
export function mat4Identity(): Mat4 {
|
|
196
|
+
return [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function mat4Multiply(a: Mat4, b: Mat4): Mat4 {
|
|
200
|
+
const out = new Array(16).fill(0);
|
|
201
|
+
for (let col = 0; col < 4; col++) {
|
|
202
|
+
for (let row = 0; row < 4; row++) {
|
|
203
|
+
out[col * 4 + row] = a[row] * b[col * 4] + a[4 + row] * b[col * 4 + 1] +
|
|
204
|
+
a[8 + row] * b[col * 4 + 2] + a[12 + row] * b[col * 4 + 3];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function mat4Translate(m: Mat4, v: Vec3): Mat4 {
|
|
211
|
+
const out = [...m];
|
|
212
|
+
for (let i = 0; i < 4; i++) {
|
|
213
|
+
out[12 + i] += m[i] * v.x + m[4 + i] * v.y + m[8 + i] * v.z;
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function mat4Scale(m: Mat4, v: Vec3): Mat4 {
|
|
219
|
+
const out = [...m];
|
|
220
|
+
for (let i = 0; i < 4; i++) { out[i] *= v.x; out[4+i] *= v.y; out[8+i] *= v.z; }
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function mat4RotateX(m: Mat4, angle: number): Mat4 {
|
|
225
|
+
const c = Math.cos(angle), s = Math.sin(angle);
|
|
226
|
+
const rot: Mat4 = [1,0,0,0, 0,c,s,0, 0,-s,c,0, 0,0,0,1];
|
|
227
|
+
return mat4Multiply(m, rot);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function mat4RotateY(m: Mat4, angle: number): Mat4 {
|
|
231
|
+
const c = Math.cos(angle), s = Math.sin(angle);
|
|
232
|
+
const rot: Mat4 = [c,0,-s,0, 0,1,0,0, s,0,c,0, 0,0,0,1];
|
|
233
|
+
return mat4Multiply(m, rot);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function mat4RotateZ(m: Mat4, angle: number): Mat4 {
|
|
237
|
+
const c = Math.cos(angle), s = Math.sin(angle);
|
|
238
|
+
const rot: Mat4 = [c,s,0,0, -s,c,0,0, 0,0,1,0, 0,0,0,1];
|
|
239
|
+
return mat4Multiply(m, rot);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function mat4Perspective(fovy: number, aspect: number, near: number, far: number): Mat4 {
|
|
243
|
+
const f = 1.0 / Math.tan(fovy / 2);
|
|
244
|
+
const nf = 1.0 / (near - far);
|
|
245
|
+
return [f/aspect,0,0,0, 0,f,0,0, 0,0,(far+near)*nf,-1, 0,0,2*far*near*nf,0];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function mat4Ortho(left: number, right: number, bottom: number, top: number, near: number, far: number): Mat4 {
|
|
249
|
+
const lr = 1/(left-right), bt = 1/(bottom-top), nf = 1/(near-far);
|
|
250
|
+
return [-2*lr,0,0,0, 0,-2*bt,0,0, 0,0,2*nf,0, (left+right)*lr,(top+bottom)*bt,(far+near)*nf,1];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function mat4LookAt(eye: Vec3, center: Vec3, up: Vec3): Mat4 {
|
|
254
|
+
let fx = center.x-eye.x, fy = center.y-eye.y, fz = center.z-eye.z;
|
|
255
|
+
const flen = Math.sqrt(fx*fx+fy*fy+fz*fz);
|
|
256
|
+
fx /= flen; fy /= flen; fz /= flen;
|
|
257
|
+
let sx = fy*up.z-fz*up.y, sy = fz*up.x-fx*up.z, sz = fx*up.y-fy*up.x;
|
|
258
|
+
const slen = Math.sqrt(sx*sx+sy*sy+sz*sz);
|
|
259
|
+
sx /= slen; sy /= slen; sz /= slen;
|
|
260
|
+
const ux = sy*fz-sz*fy, uy = sz*fx-sx*fz, uz = sx*fy-sy*fx;
|
|
261
|
+
return [sx,ux,-fx,0, sy,uy,-fy,0, sz,uz,-fz,0,
|
|
262
|
+
-(sx*eye.x+sy*eye.y+sz*eye.z),-(ux*eye.x+uy*eye.y+uz*eye.z),fx*eye.x+fy*eye.y+fz*eye.z,1];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function mat4Invert(m: Mat4): Mat4 {
|
|
266
|
+
const a00=m[0],a01=m[1],a02=m[2],a03=m[3],a10=m[4],a11=m[5],a12=m[6],a13=m[7];
|
|
267
|
+
const a20=m[8],a21=m[9],a22=m[10],a23=m[11],a30=m[12],a31=m[13],a32=m[14],a33=m[15];
|
|
268
|
+
const b00=a00*a11-a01*a10,b01=a00*a12-a02*a10,b02=a00*a13-a03*a10;
|
|
269
|
+
const b03=a01*a12-a02*a11,b04=a01*a13-a03*a11,b05=a02*a13-a03*a12;
|
|
270
|
+
const b06=a20*a31-a21*a30,b07=a20*a32-a22*a30,b08=a20*a33-a23*a30;
|
|
271
|
+
const b09=a21*a32-a22*a31,b10=a21*a33-a23*a31,b11=a22*a33-a23*a32;
|
|
272
|
+
let det = b00*b11-b01*b10+b02*b09+b03*b08-b04*b07+b05*b06;
|
|
273
|
+
if (Math.abs(det) < 1e-10) return mat4Identity();
|
|
274
|
+
det = 1.0 / det;
|
|
275
|
+
return [
|
|
276
|
+
(a11*b11-a12*b10+a13*b09)*det,(a02*b10-a01*b11-a03*b09)*det,(a31*b05-a32*b04+a33*b03)*det,(a22*b04-a21*b05-a23*b03)*det,
|
|
277
|
+
(a12*b08-a10*b11-a13*b07)*det,(a00*b11-a02*b08+a03*b07)*det,(a32*b02-a30*b05-a33*b01)*det,(a20*b05-a22*b02+a23*b01)*det,
|
|
278
|
+
(a10*b10-a11*b08+a13*b06)*det,(a01*b08-a00*b10-a03*b06)*det,(a30*b04-a31*b02+a33*b00)*det,(a21*b02-a20*b04-a23*b00)*det,
|
|
279
|
+
(a11*b07-a10*b09-a12*b06)*det,(a00*b09-a01*b07+a02*b06)*det,(a31*b01-a30*b03-a32*b00)*det,(a20*b03-a21*b01+a22*b00)*det,
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Quaternion operations
|
|
284
|
+
|
|
285
|
+
export function quatIdentity(): Quat {
|
|
286
|
+
return { x: 0, y: 0, z: 0, w: 1 };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function quatFromEuler(pitch: number, yaw: number, roll: number): Quat {
|
|
290
|
+
const cp = Math.cos(pitch * 0.5), sp = Math.sin(pitch * 0.5);
|
|
291
|
+
const cy = Math.cos(yaw * 0.5), sy = Math.sin(yaw * 0.5);
|
|
292
|
+
const cr = Math.cos(roll * 0.5), sr = Math.sin(roll * 0.5);
|
|
293
|
+
return {
|
|
294
|
+
x: sr * cp * cy - cr * sp * sy,
|
|
295
|
+
y: cr * sp * cy + sr * cp * sy,
|
|
296
|
+
z: cr * cp * sy - sr * sp * cy,
|
|
297
|
+
w: cr * cp * cy + sr * sp * sy,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function quatToMat4(q: Quat): Mat4 {
|
|
302
|
+
const x=q.x,y=q.y,z=q.z,w=q.w;
|
|
303
|
+
const x2=x+x,y2=y+y,z2=z+z;
|
|
304
|
+
const xx=x*x2,xy=x*y2,xz=x*z2,yy=y*y2,yz=y*z2,zz=z*z2;
|
|
305
|
+
const wx=w*x2,wy=w*y2,wz=w*z2;
|
|
306
|
+
return [1-yy-zz,xy+wz,xz-wy,0, xy-wz,1-xx-zz,yz+wx,0, xz+wy,yz-wx,1-xx-yy,0, 0,0,0,1];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function quatSlerp(a: Quat, b: Quat, t: number): Quat {
|
|
310
|
+
let dot = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
|
|
311
|
+
let bx=b.x,by=b.y,bz=b.z,bw=b.w;
|
|
312
|
+
if (dot < 0) { dot = -dot; bx=-bx; by=-by; bz=-bz; bw=-bw; }
|
|
313
|
+
if (dot > 0.9995) {
|
|
314
|
+
return quatNormalize({ x:a.x+(bx-a.x)*t, y:a.y+(by-a.y)*t, z:a.z+(bz-a.z)*t, w:a.w+(bw-a.w)*t });
|
|
315
|
+
}
|
|
316
|
+
const theta = Math.acos(dot);
|
|
317
|
+
const sinT = Math.sin(theta);
|
|
318
|
+
const s0 = Math.sin((1-t)*theta)/sinT;
|
|
319
|
+
const s1 = Math.sin(t*theta)/sinT;
|
|
320
|
+
return { x:s0*a.x+s1*bx, y:s0*a.y+s1*by, z:s0*a.z+s1*bz, w:s0*a.w+s1*bw };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function quatNormalize(q: Quat): Quat {
|
|
324
|
+
const len = Math.sqrt(q.x*q.x+q.y*q.y+q.z*q.z+q.w*q.w);
|
|
325
|
+
if (len === 0) return quatIdentity();
|
|
326
|
+
return { x:q.x/len, y:q.y/len, z:q.z/len, w:q.w/len };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function quatMultiply(a: Quat, b: Quat): Quat {
|
|
330
|
+
return {
|
|
331
|
+
x: a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y,
|
|
332
|
+
y: a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x,
|
|
333
|
+
z: a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w,
|
|
334
|
+
w: a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Ray / BoundingBox
|
|
339
|
+
|
|
340
|
+
export function rayIntersectsBox(ray: Ray, box_: BoundingBox): boolean {
|
|
341
|
+
let tmin = -Infinity, tmax = Infinity;
|
|
342
|
+
for (const axis of ['x', 'y', 'z'] as const) {
|
|
343
|
+
const invD = 1.0 / ray.direction[axis];
|
|
344
|
+
let t0 = (box_.min[axis] - ray.position[axis]) * invD;
|
|
345
|
+
let t1 = (box_.max[axis] - ray.position[axis]) * invD;
|
|
346
|
+
if (invD < 0) { const tmp = t0; t0 = t1; t1 = tmp; }
|
|
347
|
+
tmin = Math.max(tmin, t0);
|
|
348
|
+
tmax = Math.min(tmax, t1);
|
|
349
|
+
if (tmax < tmin) return false;
|
|
350
|
+
}
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function rayIntersectsSphere(ray: Ray, center: Vec3, radius: number): boolean {
|
|
355
|
+
const oc: Vec3 = { x: ray.position.x - center.x, y: ray.position.y - center.y, z: ray.position.z - center.z };
|
|
356
|
+
const a = vec3Dot(ray.direction, ray.direction);
|
|
357
|
+
const b = 2.0 * vec3Dot(oc, ray.direction);
|
|
358
|
+
const c = vec3Dot(oc, oc) - radius * radius;
|
|
359
|
+
return b * b - 4 * a * c >= 0;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function checkCollisionSpheres(center1: Vec3, radius1: number, center2: Vec3, radius2: number): boolean {
|
|
363
|
+
return vec3Distance(center1, center2) <= radius1 + radius2;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function checkCollisionBoxes(a: BoundingBox, b: BoundingBox): boolean {
|
|
367
|
+
return a.min.x <= b.max.x && a.max.x >= b.min.x &&
|
|
368
|
+
a.min.y <= b.max.y && a.max.y >= b.min.y &&
|
|
369
|
+
a.min.z <= b.max.z && a.max.z >= b.min.z;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Frustum culling
|
|
373
|
+
|
|
374
|
+
export function extractFrustumPlanes(mvp: Mat4): FrustumPlanes {
|
|
375
|
+
const m = mvp;
|
|
376
|
+
const planes: Vec4[] = [
|
|
377
|
+
// Left
|
|
378
|
+
{ x: m[3] + m[0], y: m[7] + m[4], z: m[11] + m[8], w: m[15] + m[12] },
|
|
379
|
+
// Right
|
|
380
|
+
{ x: m[3] - m[0], y: m[7] - m[4], z: m[11] - m[8], w: m[15] - m[12] },
|
|
381
|
+
// Bottom
|
|
382
|
+
{ x: m[3] + m[1], y: m[7] + m[5], z: m[11] + m[9], w: m[15] + m[13] },
|
|
383
|
+
// Top
|
|
384
|
+
{ x: m[3] - m[1], y: m[7] - m[5], z: m[11] - m[9], w: m[15] - m[13] },
|
|
385
|
+
// Near
|
|
386
|
+
{ x: m[3] + m[2], y: m[7] + m[6], z: m[11] + m[10], w: m[15] + m[14] },
|
|
387
|
+
// Far
|
|
388
|
+
{ x: m[3] - m[2], y: m[7] - m[6], z: m[11] - m[10], w: m[15] - m[14] },
|
|
389
|
+
];
|
|
390
|
+
// Normalize each plane
|
|
391
|
+
for (let i = 0; i < 6; i++) {
|
|
392
|
+
const p = planes[i];
|
|
393
|
+
const len = Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
|
|
394
|
+
if (len > 0) { p.x /= len; p.y /= len; p.z /= len; p.w /= len; }
|
|
395
|
+
}
|
|
396
|
+
return { planes };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export function isBoxInFrustum(box_: BoundingBox, frustum: FrustumPlanes): boolean {
|
|
400
|
+
for (const plane of frustum.planes) {
|
|
401
|
+
// Test the positive vertex (the corner most aligned with the plane normal)
|
|
402
|
+
const px = plane.x >= 0 ? box_.max.x : box_.min.x;
|
|
403
|
+
const py = plane.y >= 0 ? box_.max.y : box_.min.y;
|
|
404
|
+
const pz = plane.z >= 0 ? box_.max.z : box_.min.z;
|
|
405
|
+
if (plane.x * px + plane.y * py + plane.z * pz + plane.w < 0) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Ray-triangle intersection (Moller-Trumbore algorithm)
|
|
413
|
+
|
|
414
|
+
export function rayIntersectsTriangle(
|
|
415
|
+
ray: Ray, v0: Vec3, v1: Vec3, v2: Vec3,
|
|
416
|
+
): RayHit {
|
|
417
|
+
const EPSILON = 1e-8;
|
|
418
|
+
const edge1: Vec3 = { x: v1.x - v0.x, y: v1.y - v0.y, z: v1.z - v0.z };
|
|
419
|
+
const edge2: Vec3 = { x: v2.x - v0.x, y: v2.y - v0.y, z: v2.z - v0.z };
|
|
420
|
+
const h = vec3Cross(ray.direction, edge2);
|
|
421
|
+
const a = vec3Dot(edge1, h);
|
|
422
|
+
const noHit: RayHit = { hit: false, distance: 0, point: { x: 0, y: 0, z: 0 }, normal: { x: 0, y: 0, z: 0 } };
|
|
423
|
+
if (a > -EPSILON && a < EPSILON) return noHit;
|
|
424
|
+
const f = 1.0 / a;
|
|
425
|
+
const s: Vec3 = { x: ray.position.x - v0.x, y: ray.position.y - v0.y, z: ray.position.z - v0.z };
|
|
426
|
+
const u = f * vec3Dot(s, h);
|
|
427
|
+
if (u < 0.0 || u > 1.0) return noHit;
|
|
428
|
+
const q = vec3Cross(s, edge1);
|
|
429
|
+
const v = f * vec3Dot(ray.direction, q);
|
|
430
|
+
if (v < 0.0 || u + v > 1.0) return noHit;
|
|
431
|
+
const t = f * vec3Dot(edge2, q);
|
|
432
|
+
if (t <= EPSILON) return noHit;
|
|
433
|
+
const point: Vec3 = {
|
|
434
|
+
x: ray.position.x + ray.direction.x * t,
|
|
435
|
+
y: ray.position.y + ray.direction.y * t,
|
|
436
|
+
z: ray.position.z + ray.direction.z * t,
|
|
437
|
+
};
|
|
438
|
+
const normal = vec3Normalize(vec3Cross(edge1, edge2));
|
|
439
|
+
return { hit: true, distance: t, point, normal };
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function getRayCollisionBox(ray: Ray, box_: BoundingBox): RayHit {
|
|
443
|
+
const noHit: RayHit = { hit: false, distance: 0, point: { x: 0, y: 0, z: 0 }, normal: { x: 0, y: 0, z: 0 } };
|
|
444
|
+
let tmin = -Infinity;
|
|
445
|
+
let tmax = Infinity;
|
|
446
|
+
const normals: Vec3[] = [{ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }];
|
|
447
|
+
for (const axis of ['x', 'y', 'z'] as const) {
|
|
448
|
+
const invD = 1.0 / ray.direction[axis];
|
|
449
|
+
let t0 = (box_.min[axis] - ray.position[axis]) * invD;
|
|
450
|
+
let t1 = (box_.max[axis] - ray.position[axis]) * invD;
|
|
451
|
+
const n0: Vec3 = { x: 0, y: 0, z: 0 };
|
|
452
|
+
const n1: Vec3 = { x: 0, y: 0, z: 0 };
|
|
453
|
+
if (invD >= 0) {
|
|
454
|
+
n0[axis] = -1;
|
|
455
|
+
n1[axis] = 1;
|
|
456
|
+
} else {
|
|
457
|
+
const tmp = t0; t0 = t1; t1 = tmp;
|
|
458
|
+
n0[axis] = 1;
|
|
459
|
+
n1[axis] = -1;
|
|
460
|
+
}
|
|
461
|
+
if (t0 > tmin) { tmin = t0; normals[0] = n0; }
|
|
462
|
+
if (t1 < tmax) { tmax = t1; normals[1] = n1; }
|
|
463
|
+
if (tmax < tmin) return noHit;
|
|
464
|
+
}
|
|
465
|
+
if (tmin < 0) return noHit;
|
|
466
|
+
const point: Vec3 = {
|
|
467
|
+
x: ray.position.x + ray.direction.x * tmin,
|
|
468
|
+
y: ray.position.y + ray.direction.y * tmin,
|
|
469
|
+
z: ray.position.z + ray.direction.z * tmin,
|
|
470
|
+
};
|
|
471
|
+
return { hit: true, distance: tmin, point, normal: normals[0] };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// PascalCase vector constructors (spec-compliant aliases)
|
|
475
|
+
export function Vec2(x: number, y: number): Vec2Type { return { x, y }; }
|
|
476
|
+
export function Vec3(x: number, y: number, z: number): Vec3Type { return { x, y, z }; }
|
|
477
|
+
export function Vec4(x: number, y: number, z: number, w: number): Vec4Type { return { x, y, z, w }; }
|
|
478
|
+
|
|
479
|
+
// Mesh-level raycasting — tests ray against all triangles in a model's stored mesh data
|
|
480
|
+
// Note: requires model meshCount > 0. For models loaded from file, this iterates
|
|
481
|
+
// through the mesh vertex/index data stored on the native side.
|
|
482
|
+
|
|
483
|
+
// Mesh vertex data registry for TS-side raycasting
|
|
484
|
+
const _meshData = new Map<number, { vertices: number[]; indices: number[] }>();
|
|
485
|
+
|
|
486
|
+
export function _registerMeshData(handle: number, vertices: number[], indices: number[]): void {
|
|
487
|
+
_meshData.set(handle, { vertices, indices });
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export function getRayCollisionMesh(ray: Ray, model: Model): RayHit {
|
|
491
|
+
const noHit: RayHit = { hit: false, distance: 0, point: { x: 0, y: 0, z: 0 }, normal: { x: 0, y: 0, z: 0 } };
|
|
492
|
+
const data = _meshData.get(model.handle);
|
|
493
|
+
if (!data) return noHit;
|
|
494
|
+
|
|
495
|
+
let closest = noHit;
|
|
496
|
+
const verts = data.vertices;
|
|
497
|
+
const idxs = data.indices;
|
|
498
|
+
|
|
499
|
+
// Each vertex is 12 floats: x,y,z, nx,ny,nz, r,g,b,a, u,v
|
|
500
|
+
for (let i = 0; i < idxs.length; i += 3) {
|
|
501
|
+
const i0 = idxs[i] * 12;
|
|
502
|
+
const i1 = idxs[i + 1] * 12;
|
|
503
|
+
const i2 = idxs[i + 2] * 12;
|
|
504
|
+
|
|
505
|
+
const v0: Vec3 = { x: verts[i0], y: verts[i0 + 1], z: verts[i0 + 2] };
|
|
506
|
+
const v1: Vec3 = { x: verts[i1], y: verts[i1 + 1], z: verts[i1 + 2] };
|
|
507
|
+
const v2: Vec3 = { x: verts[i2], y: verts[i2 + 1], z: verts[i2 + 2] };
|
|
508
|
+
|
|
509
|
+
const hit = rayIntersectsTriangle(ray, v0, v1, v2);
|
|
510
|
+
if (hit.hit && (!closest.hit || hit.distance < closest.distance)) {
|
|
511
|
+
closest = hit;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return closest;
|
|
516
|
+
}
|