trid3d 0.0.0-stage → 0.1.0
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 +102 -2
- package/package.json +41 -4
- package/trid/index.js +53 -0
- package/trid/loaders/obj.js +206 -0
- package/trid/loaders/texture.js +56 -0
- package/trid/math/projection.js +49 -0
- package/trid/math/transform.js +302 -0
- package/trid/render/canvasRenderer.js +634 -0
- package/trid/render/color.js +22 -0
- package/trid/render/draw.js +146 -0
- package/trid/render/effects.js +265 -0
- package/trid/render/loop.js +42 -0
- package/trid/render/normals.js +90 -0
- package/trid/render/shapes.js +230 -0
- package/trid/render/webglRenderer.js +613 -0
- package/trid/scene.js +167 -0
- package/trid/tools/dragObject.js +89 -0
- package/trid/tools/flyCamera.js +111 -0
- package/trid/tools/fpsMeter.js +51 -0
- package/trid/tools/helpers.js +85 -0
- package/trid/tools/hoverHighlight.js +62 -0
- package/trid/tools/index.js +8 -0
- package/trid/tools/manager.js +241 -0
- package/trid/tools/objUploader.js +153 -0
- package/trid/tools/orbitCamera.js +132 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { project, screen } from "./projection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Camera object. Every field is optional and falls back to its default.
|
|
5
|
+
*
|
|
6
|
+
* The camera looks down +Z when yaw and pitch are 0.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Object} Camera
|
|
9
|
+
* @property {number} [x=0] - Position on X
|
|
10
|
+
* @property {number} [y=0] - Position on Y
|
|
11
|
+
* @property {number} [z=0] - Position on Z
|
|
12
|
+
* @property {number} [yaw=0] - Turn around Y in radians (> 0 turns right)
|
|
13
|
+
* @property {number} [pitch=0] - Tilt around X in radians (> 0 looks up)
|
|
14
|
+
* @property {number} [fov=Math.PI/2] - Vertical field of view in radians
|
|
15
|
+
* @property {number} [near=0.01] - Near plane distance; closer geometry is clipped
|
|
16
|
+
* @property {number} [far=1000] - Far distance; farther geometry isn't drawn (WebGL renderer only)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Default vertical field of view (90°). */
|
|
20
|
+
export const DEFAULT_FOV = Math.PI / 2;
|
|
21
|
+
|
|
22
|
+
/** Default near plane distance. */
|
|
23
|
+
export const DEFAULT_NEAR = 0.01;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Converts a vertex from local (mesh) space to world space
|
|
27
|
+
* using an instance transform.
|
|
28
|
+
*
|
|
29
|
+
* Transformation order:
|
|
30
|
+
* 1. Scale
|
|
31
|
+
* 2. Rotate (X then Y then Z)
|
|
32
|
+
* 3. Translate to world position
|
|
33
|
+
*
|
|
34
|
+
* @param {{x:number,y:number,z:number}} v - Local-space vertex
|
|
35
|
+
* @param {{
|
|
36
|
+
* pos:{x:number,y:number,z:number},
|
|
37
|
+
* rot:{x:number,y:number,z:number},
|
|
38
|
+
* scale:{x:number,y:number,z:number}
|
|
39
|
+
* }} inst - Mesh instance transform
|
|
40
|
+
*
|
|
41
|
+
* @returns {{x:number,y:number,z:number}} World-space vertex
|
|
42
|
+
*/
|
|
43
|
+
export function localToWorld(v, inst) {
|
|
44
|
+
let p = scale(v, inst.scale.x, inst.scale.y, inst.scale.z);
|
|
45
|
+
p = rotateXYZ(p, inst.rot);
|
|
46
|
+
p = translate(p, inst.pos.x, inst.pos.y, inst.pos.z);
|
|
47
|
+
return p;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Converts a world-space point into camera space.
|
|
53
|
+
*
|
|
54
|
+
* Steps:
|
|
55
|
+
* 1. Move the point relative to the camera position
|
|
56
|
+
* 2. Undo the camera's yaw, then its pitch
|
|
57
|
+
*
|
|
58
|
+
* In camera space the camera sits at the origin looking down +Z.
|
|
59
|
+
*
|
|
60
|
+
* @param {{x:number,y:number,z:number}} pWorld - World-space point
|
|
61
|
+
* @param {Camera} camera - Camera object
|
|
62
|
+
*
|
|
63
|
+
* @returns {{x:number,y:number,z:number}} Camera-space point
|
|
64
|
+
*/
|
|
65
|
+
export function worldToCamera(pWorld, camera) {
|
|
66
|
+
let p = translate(pWorld, -(camera.x ?? 0), -(camera.y ?? 0), -(camera.z ?? 0));
|
|
67
|
+
p = rotateY(p, camera.yaw ?? 0);
|
|
68
|
+
p = rotateX(p, camera.pitch ?? 0);
|
|
69
|
+
return p;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Converts a camera-space point into screen-space coordinates.
|
|
74
|
+
*
|
|
75
|
+
* Steps:
|
|
76
|
+
* 1. Perspective projection (3D → 2D) using the camera's fov
|
|
77
|
+
* and the canvas aspect ratio
|
|
78
|
+
* 2. Convert normalized coords to screen pixels
|
|
79
|
+
*
|
|
80
|
+
* NOTE: z must be > 0 (in front of the camera). Use `clipNear`
|
|
81
|
+
* on segments first if they may cross behind the camera.
|
|
82
|
+
*
|
|
83
|
+
* @param {{x:number,y:number,z:number}} pCam - Camera-space point
|
|
84
|
+
* @param {Camera} camera - Camera object
|
|
85
|
+
* @param {number} w - Canvas width
|
|
86
|
+
* @param {number} h - Canvas height
|
|
87
|
+
*
|
|
88
|
+
* @returns {{x:number,y:number}} Screen-space point
|
|
89
|
+
*/
|
|
90
|
+
export function cameraToScreen(pCam, camera, w, h) {
|
|
91
|
+
return screen(project(pCam, camera.fov ?? DEFAULT_FOV, w / h), w, h);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Converts a world-space point into screen-space coordinates.
|
|
96
|
+
*
|
|
97
|
+
* Steps:
|
|
98
|
+
* 1. World space → camera space (`worldToCamera`)
|
|
99
|
+
* 2. Camera space → screen pixels (`cameraToScreen`)
|
|
100
|
+
*
|
|
101
|
+
* @param {{x:number,y:number,z:number}} pWorld - World-space point
|
|
102
|
+
* @param {Camera} camera - Camera object
|
|
103
|
+
* @param {number} w - Canvas width
|
|
104
|
+
* @param {number} h - Canvas height
|
|
105
|
+
*
|
|
106
|
+
* @returns {{x:number,y:number}} Screen-space point
|
|
107
|
+
*/
|
|
108
|
+
export function worldToScreen(pWorld, camera, w, h) {
|
|
109
|
+
return cameraToScreen(worldToCamera(pWorld, camera), camera, w, h);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Direction the camera looks at, in world space (unit vector).
|
|
114
|
+
*
|
|
115
|
+
* @param {Camera} camera - Camera object
|
|
116
|
+
* @returns {{x:number,y:number,z:number}} Forward vector
|
|
117
|
+
*/
|
|
118
|
+
export function cameraForward(camera) {
|
|
119
|
+
const yaw = camera.yaw ?? 0;
|
|
120
|
+
const pitch = camera.pitch ?? 0;
|
|
121
|
+
return {
|
|
122
|
+
x: Math.sin(yaw) * Math.cos(pitch),
|
|
123
|
+
y: Math.sin(pitch),
|
|
124
|
+
z: Math.cos(yaw) * Math.cos(pitch),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Direction of the screen's right side, in world space (unit vector).
|
|
130
|
+
*
|
|
131
|
+
* @param {Camera} camera - Camera object
|
|
132
|
+
* @returns {{x:number,y:number,z:number}} Right vector
|
|
133
|
+
*/
|
|
134
|
+
export function cameraRight(camera) {
|
|
135
|
+
const yaw = camera.yaw ?? 0;
|
|
136
|
+
return {
|
|
137
|
+
x: Math.cos(yaw),
|
|
138
|
+
y: 0,
|
|
139
|
+
z: -Math.sin(yaw),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Direction of the screen's top side, in world space (unit vector).
|
|
145
|
+
*
|
|
146
|
+
* @param {Camera} camera - Camera object
|
|
147
|
+
* @returns {{x:number,y:number,z:number}} Up vector
|
|
148
|
+
*/
|
|
149
|
+
export function cameraUp(camera) {
|
|
150
|
+
const yaw = camera.yaw ?? 0;
|
|
151
|
+
const pitch = camera.pitch ?? 0;
|
|
152
|
+
return {
|
|
153
|
+
x: -Math.sin(pitch) * Math.sin(yaw),
|
|
154
|
+
y: Math.cos(pitch),
|
|
155
|
+
z: -Math.sin(pitch) * Math.cos(yaw),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Clips a camera-space segment against the near plane (z = near).
|
|
161
|
+
*
|
|
162
|
+
* - Both ends in front: segment returned unchanged
|
|
163
|
+
* - Both ends behind: returns null (nothing to draw)
|
|
164
|
+
* - One end behind: that end is moved onto the near plane
|
|
165
|
+
*
|
|
166
|
+
* @param {{x:number,y:number,z:number}} a - Segment start (camera space)
|
|
167
|
+
* @param {{x:number,y:number,z:number}} b - Segment end (camera space)
|
|
168
|
+
* @param {number} near - Near plane distance (> 0)
|
|
169
|
+
*
|
|
170
|
+
* @returns {[{x:number,y:number,z:number}, {x:number,y:number,z:number}] | null}
|
|
171
|
+
*/
|
|
172
|
+
export function clipNear(a, b, near) {
|
|
173
|
+
const aIn = a.z >= near;
|
|
174
|
+
const bIn = b.z >= near;
|
|
175
|
+
|
|
176
|
+
if (aIn && bIn) return [a, b];
|
|
177
|
+
if (!aIn && !bIn) return null;
|
|
178
|
+
|
|
179
|
+
const t = (near - a.z) / (b.z - a.z);
|
|
180
|
+
const p = {
|
|
181
|
+
x: a.x + (b.x - a.x) * t,
|
|
182
|
+
y: a.y + (b.y - a.y) * t,
|
|
183
|
+
z: near,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return aIn ? [a, p] : [p, b];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Scales a 3D point along each axis.
|
|
191
|
+
*
|
|
192
|
+
* @param {{x:number,y:number,z:number}} p - Input point
|
|
193
|
+
* @param {number} sx - Scale factor on X axis
|
|
194
|
+
* @param {number} sy - Scale factor on Y axis
|
|
195
|
+
* @param {number} sz - Scale factor on Z axis
|
|
196
|
+
* @returns {{x:number,y:number,z:number}} Scaled point
|
|
197
|
+
*/
|
|
198
|
+
export function scale(p, sx, sy, sz) {
|
|
199
|
+
return {
|
|
200
|
+
x: p.x * sx,
|
|
201
|
+
y: p.y * sy,
|
|
202
|
+
z: p.z * sz,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Translates a 3D point in space.
|
|
208
|
+
*
|
|
209
|
+
* @param {{x:number,y:number,z:number}} p - Input point
|
|
210
|
+
* @param {number} dx - Translation on X axis
|
|
211
|
+
* @param {number} dy - Translation on Y axis
|
|
212
|
+
* @param {number} dz - Translation on Z axis
|
|
213
|
+
* @returns {{x:number,y:number,z:number}} Translated point
|
|
214
|
+
*/
|
|
215
|
+
export function translate(p, dx, dy, dz) {
|
|
216
|
+
return {
|
|
217
|
+
x: p.x + dx,
|
|
218
|
+
y: p.y + dy,
|
|
219
|
+
z: p.z + dz,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Rotates a 3D point around the X axis (YZ plane).
|
|
225
|
+
*
|
|
226
|
+
* This creates an up/down “pitch” rotation.
|
|
227
|
+
*
|
|
228
|
+
* @param {{x:number,y:number,z:number}} p - Input point
|
|
229
|
+
* @param {number} angle - Rotation angle in radians
|
|
230
|
+
* @returns {{x:number,y:number,z:number}} Rotated point
|
|
231
|
+
*/
|
|
232
|
+
export function rotateX(p, angle) {
|
|
233
|
+
const c = Math.cos(angle);
|
|
234
|
+
const s = Math.sin(angle);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
x: p.x,
|
|
238
|
+
y: p.y * c - p.z * s,
|
|
239
|
+
z: p.y * s + p.z * c,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Rotates a 3D point around the Y axis (XZ plane).
|
|
245
|
+
*
|
|
246
|
+
* This creates a left/right “yaw” rotation.
|
|
247
|
+
*
|
|
248
|
+
* @param {{x:number,y:number,z:number}} p - Input point
|
|
249
|
+
* @param {number} angle - Rotation angle in radians
|
|
250
|
+
* @returns {{x:number,y:number,z:number}} Rotated point
|
|
251
|
+
*/
|
|
252
|
+
export function rotateY(p, angle) {
|
|
253
|
+
const c = Math.cos(angle);
|
|
254
|
+
const s = Math.sin(angle);
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
x: p.x * c - p.z * s,
|
|
258
|
+
y: p.y,
|
|
259
|
+
z: p.x * s + p.z * c,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Rotates a 3D point around the Z axis (XY plane).
|
|
265
|
+
*
|
|
266
|
+
* This creates a “roll” rotation (spins in 2D).
|
|
267
|
+
*
|
|
268
|
+
* @param {{x:number,y:number,z:number}} p - Input point
|
|
269
|
+
* @param {number} angle - Rotation angle in radians
|
|
270
|
+
* @returns {{x:number,y:number,z:number}} Rotated point
|
|
271
|
+
*/
|
|
272
|
+
export function rotateZ(p, angle) {
|
|
273
|
+
const c = Math.cos(angle);
|
|
274
|
+
const s = Math.sin(angle);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
x: p.x * c - p.y * s,
|
|
278
|
+
y: p.x * s + p.y * c,
|
|
279
|
+
z: p.z,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Applies Euler rotations to a point.
|
|
285
|
+
*
|
|
286
|
+
* Rotation order matters. This function applies:
|
|
287
|
+
* 1) rotateX
|
|
288
|
+
* 2) rotateY
|
|
289
|
+
* 3) rotateZ
|
|
290
|
+
*
|
|
291
|
+
* If you change the order, the final orientation will change.
|
|
292
|
+
*
|
|
293
|
+
* @param {{x:number,y:number,z:number}} p - Input point
|
|
294
|
+
* @param {{x:number,y:number,z:number}} rot - Rotation angles in radians
|
|
295
|
+
* @returns {{x:number,y:number,z:number}} Rotated point
|
|
296
|
+
*/
|
|
297
|
+
export function rotateXYZ(p, rot) {
|
|
298
|
+
let r = rotateX(p, rot.x);
|
|
299
|
+
r = rotateY(r, rot.y);
|
|
300
|
+
r = rotateZ(r, rot.z);
|
|
301
|
+
return r;
|
|
302
|
+
}
|