@guinetik/gcanvas 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demos/coordinates.html +698 -0
- package/demos/cube3d.html +23 -0
- package/demos/demos.css +17 -3
- package/demos/dino.html +42 -0
- package/demos/fluid-simple.html +22 -0
- package/demos/fluid.html +37 -0
- package/demos/gameobjects.html +626 -0
- package/demos/index.html +19 -7
- package/demos/js/blob.js +18 -5
- package/demos/js/coordinates.js +840 -0
- package/demos/js/cube3d.js +789 -0
- package/demos/js/dino.js +1420 -0
- package/demos/js/fluid-simple.js +253 -0
- package/demos/js/fluid.js +527 -0
- package/demos/js/gameobjects.js +176 -0
- package/demos/js/plane3d.js +256 -0
- package/demos/js/platformer.js +1579 -0
- package/demos/js/sphere3d.js +229 -0
- package/demos/js/sprite.js +473 -0
- package/demos/js/tde/accretiondisk.js +65 -12
- package/demos/js/tde/blackholescene.js +2 -2
- package/demos/js/tde/config.js +2 -2
- package/demos/js/tde/index.js +152 -27
- package/demos/js/tde/lensedstarfield.js +32 -25
- package/demos/js/tde/tdestar.js +78 -98
- package/demos/js/tde/tidalstream.js +24 -8
- package/demos/plane3d.html +24 -0
- package/demos/platformer.html +43 -0
- package/demos/sphere3d.html +24 -0
- package/demos/sprite.html +18 -0
- package/docs/README.md +230 -222
- package/docs/api/FluidSystem.md +173 -0
- package/docs/concepts/architecture-overview.md +204 -204
- package/docs/concepts/coordinate-system.md +384 -0
- package/docs/concepts/rendering-pipeline.md +279 -279
- package/docs/concepts/shapes-vs-gameobjects.md +187 -0
- package/docs/concepts/two-layer-architecture.md +229 -229
- package/docs/fluid-dynamics.md +99 -0
- package/docs/getting-started/first-game.md +354 -354
- package/docs/getting-started/installation.md +175 -157
- package/docs/modules/collision/README.md +2 -2
- package/docs/modules/fluent/README.md +6 -6
- package/docs/modules/game/README.md +303 -303
- package/docs/modules/isometric-camera.md +2 -2
- package/docs/modules/isometric.md +1 -1
- package/docs/modules/painter/README.md +328 -328
- package/docs/modules/particle/README.md +3 -3
- package/docs/modules/shapes/README.md +221 -221
- package/docs/modules/shapes/base/euclidian.md +123 -123
- package/docs/modules/shapes/base/shape.md +262 -262
- package/docs/modules/shapes/base/transformable.md +243 -243
- package/docs/modules/state/README.md +2 -2
- package/docs/modules/util/README.md +1 -1
- package/docs/modules/util/camera3d.md +3 -3
- package/docs/modules/util/scene3d.md +1 -1
- package/package.json +3 -1
- package/readme.md +19 -5
- package/src/collision/collision.js +75 -0
- package/src/game/game.js +11 -5
- package/src/game/index.js +2 -1
- package/src/game/objects/index.js +3 -0
- package/src/game/objects/platformer-scene.js +411 -0
- package/src/game/objects/scene.js +14 -0
- package/src/game/objects/sprite.js +529 -0
- package/src/game/pipeline.js +20 -16
- package/src/game/systems/FluidSystem.js +835 -0
- package/src/game/systems/index.js +11 -0
- package/src/game/ui/button.js +39 -18
- package/src/game/ui/cursor.js +14 -0
- package/src/game/ui/fps.js +12 -4
- package/src/game/ui/index.js +2 -0
- package/src/game/ui/stepper.js +549 -0
- package/src/game/ui/theme.js +123 -0
- package/src/game/ui/togglebutton.js +9 -3
- package/src/game/ui/tooltip.js +11 -4
- package/src/io/input.js +75 -45
- package/src/io/mouse.js +44 -19
- package/src/io/touch.js +35 -12
- package/src/math/fluid.js +507 -0
- package/src/math/index.js +2 -0
- package/src/mixins/anchor.js +17 -7
- package/src/motion/tweenetik.js +16 -0
- package/src/shapes/cube3d.js +599 -0
- package/src/shapes/index.js +3 -0
- package/src/shapes/plane3d.js +687 -0
- package/src/shapes/sphere3d.js +75 -6
- package/src/util/camera2d.js +315 -0
- package/src/util/camera3d.js +218 -12
- package/src/util/index.js +1 -0
- package/src/webgl/shaders/plane-shaders.js +332 -0
- package/src/webgl/shaders/sphere-shaders.js +4 -2
- package/types/fluent.d.ts +361 -0
- package/types/game.d.ts +303 -0
- package/types/index.d.ts +144 -5
- package/types/math.d.ts +361 -0
- package/types/motion.d.ts +271 -0
- package/types/particle.d.ts +373 -0
- package/types/shapes.d.ts +107 -9
- package/types/util.d.ts +353 -0
- package/types/webgl.d.ts +109 -0
- package/disk_example.png +0 -0
- package/tde.png +0 -0
package/src/util/camera3d.js
CHANGED
|
@@ -32,6 +32,9 @@ export class Camera3D {
|
|
|
32
32
|
* @param {number} [options.rotationX=0] - Initial X rotation (tilt up/down) in radians
|
|
33
33
|
* @param {number} [options.rotationY=0] - Initial Y rotation (spin left/right) in radians
|
|
34
34
|
* @param {number} [options.rotationZ=0] - Initial Z rotation (roll) in radians
|
|
35
|
+
* @param {number} [options.x=0] - Initial X position in world space
|
|
36
|
+
* @param {number} [options.y=0] - Initial Y position in world space
|
|
37
|
+
* @param {number} [options.z=0] - Initial Z position in world space
|
|
35
38
|
* @param {number} [options.perspective=800] - Perspective distance (higher = less distortion)
|
|
36
39
|
* @param {number} [options.sensitivity=0.005] - Mouse drag sensitivity
|
|
37
40
|
* @param {number} [options.minRotationX=-1.5] - Minimum X rotation limit
|
|
@@ -49,10 +52,18 @@ export class Camera3D {
|
|
|
49
52
|
this.rotationY = options.rotationY ?? 0;
|
|
50
53
|
this.rotationZ = options.rotationZ ?? 0;
|
|
51
54
|
|
|
55
|
+
// Position state (camera location in world space)
|
|
56
|
+
this.x = options.x ?? 0;
|
|
57
|
+
this.y = options.y ?? 0;
|
|
58
|
+
this.z = options.z ?? 0;
|
|
59
|
+
|
|
52
60
|
// Store initial values for reset
|
|
53
61
|
this._initialRotationX = this.rotationX;
|
|
54
62
|
this._initialRotationY = this.rotationY;
|
|
55
63
|
this._initialRotationZ = this.rotationZ;
|
|
64
|
+
this._initialX = this.x;
|
|
65
|
+
this._initialY = this.y;
|
|
66
|
+
this._initialZ = this.z;
|
|
56
67
|
|
|
57
68
|
// Perspective
|
|
58
69
|
this.perspective = options.perspective ?? 800;
|
|
@@ -86,6 +97,20 @@ export class Camera3D {
|
|
|
86
97
|
this._lastMouseY = 0;
|
|
87
98
|
this._canvas = null;
|
|
88
99
|
this._boundHandlers = null;
|
|
100
|
+
|
|
101
|
+
// Follow target state
|
|
102
|
+
this._followTarget = null;
|
|
103
|
+
this._followOffset = { x: 0, y: 0, z: 0 };
|
|
104
|
+
this._followLookAt = true; // Auto-orient to look at target's forward direction
|
|
105
|
+
this._followLerp = 0.1; // Interpolation speed (0-1, higher = snappier)
|
|
106
|
+
|
|
107
|
+
// Position animation state
|
|
108
|
+
this._targetX = null;
|
|
109
|
+
this._targetY = null;
|
|
110
|
+
this._targetZ = null;
|
|
111
|
+
this._targetRotationX = null;
|
|
112
|
+
this._targetRotationY = null;
|
|
113
|
+
this._positionLerp = 0.05; // Default smooth movement speed
|
|
89
114
|
}
|
|
90
115
|
|
|
91
116
|
/**
|
|
@@ -96,6 +121,12 @@ export class Camera3D {
|
|
|
96
121
|
* @returns {{x: number, y: number, z: number, scale: number}} Projected 2D coordinates and depth info
|
|
97
122
|
*/
|
|
98
123
|
project(x, y, z) {
|
|
124
|
+
// Translate world relative to camera position
|
|
125
|
+
// (Moving camera right = moving world left)
|
|
126
|
+
x -= this.x;
|
|
127
|
+
y -= this.y;
|
|
128
|
+
z -= this.z;
|
|
129
|
+
|
|
99
130
|
// Rotate around Z axis (roll)
|
|
100
131
|
if (this.rotationZ !== 0) {
|
|
101
132
|
const cosZ = Math.cos(this.rotationZ);
|
|
@@ -141,12 +172,78 @@ export class Camera3D {
|
|
|
141
172
|
}
|
|
142
173
|
|
|
143
174
|
/**
|
|
144
|
-
* Update camera for auto-rotation and
|
|
175
|
+
* Update camera for auto-rotation, inertia, and follow mode (call in update loop)
|
|
145
176
|
* @param {number} dt - Delta time in seconds
|
|
146
177
|
*/
|
|
147
178
|
update(dt) {
|
|
148
|
-
//
|
|
149
|
-
if (this.
|
|
179
|
+
// Follow target mode - position camera relative to target
|
|
180
|
+
if (this._followTarget) {
|
|
181
|
+
const target = this._followTarget;
|
|
182
|
+
const targetX = (target.x ?? 0) + this._followOffset.x;
|
|
183
|
+
const targetY = (target.y ?? 0) + this._followOffset.y;
|
|
184
|
+
const targetZ = (target.z ?? 0) + this._followOffset.z;
|
|
185
|
+
|
|
186
|
+
// Smooth interpolation to target position
|
|
187
|
+
this.x += (targetX - this.x) * this._followLerp;
|
|
188
|
+
this.y += (targetY - this.y) * this._followLerp;
|
|
189
|
+
this.z += (targetZ - this.z) * this._followLerp;
|
|
190
|
+
|
|
191
|
+
// Auto-orient to look toward origin (or specified look target)
|
|
192
|
+
if (this._followLookAt) {
|
|
193
|
+
const lookX = this._followLookAtTarget?.x ?? 0;
|
|
194
|
+
const lookY = this._followLookAtTarget?.y ?? 0;
|
|
195
|
+
const lookZ = this._followLookAtTarget?.z ?? 0;
|
|
196
|
+
|
|
197
|
+
// Calculate direction from camera to look target
|
|
198
|
+
const dx = lookX - this.x;
|
|
199
|
+
const dy = lookY - this.y;
|
|
200
|
+
const dz = lookZ - this.z;
|
|
201
|
+
const distXZ = Math.sqrt(dx * dx + dz * dz);
|
|
202
|
+
|
|
203
|
+
// Target rotation to face the look target
|
|
204
|
+
const targetRotY = Math.atan2(dx, dz);
|
|
205
|
+
const targetRotX = Math.atan2(-dy, distXZ);
|
|
206
|
+
|
|
207
|
+
// Smooth rotation interpolation
|
|
208
|
+
this.rotationY += this._angleDiff(this.rotationY, targetRotY) * this._followLerp;
|
|
209
|
+
this.rotationX += (targetRotX - this.rotationX) * this._followLerp;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Animated position movement (moveTo)
|
|
213
|
+
else if (this._targetX !== null) {
|
|
214
|
+
const lerp = this._positionLerp;
|
|
215
|
+
|
|
216
|
+
// Interpolate position
|
|
217
|
+
this.x += (this._targetX - this.x) * lerp;
|
|
218
|
+
this.y += (this._targetY - this.y) * lerp;
|
|
219
|
+
this.z += (this._targetZ - this.z) * lerp;
|
|
220
|
+
|
|
221
|
+
// Interpolate rotation if targets set
|
|
222
|
+
if (this._targetRotationX !== null) {
|
|
223
|
+
this.rotationX += (this._targetRotationX - this.rotationX) * lerp;
|
|
224
|
+
}
|
|
225
|
+
if (this._targetRotationY !== null) {
|
|
226
|
+
this.rotationY += this._angleDiff(this.rotationY, this._targetRotationY) * lerp;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Check if we've arrived (close enough)
|
|
230
|
+
const posDist = Math.abs(this._targetX - this.x) +
|
|
231
|
+
Math.abs(this._targetY - this.y) +
|
|
232
|
+
Math.abs(this._targetZ - this.z);
|
|
233
|
+
if (posDist < 0.1) {
|
|
234
|
+
this.x = this._targetX;
|
|
235
|
+
this.y = this._targetY;
|
|
236
|
+
this.z = this._targetZ;
|
|
237
|
+
this._targetX = null;
|
|
238
|
+
this._targetY = null;
|
|
239
|
+
this._targetZ = null;
|
|
240
|
+
this._targetRotationX = null;
|
|
241
|
+
this._targetRotationY = null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Apply inertia when not dragging and not following
|
|
246
|
+
if (this.inertia && !this._isDragging && !this._followTarget) {
|
|
150
247
|
// Apply velocity to rotation
|
|
151
248
|
if (Math.abs(this._velocityX) > 0.0001 || Math.abs(this._velocityY) > 0.0001) {
|
|
152
249
|
this.rotationY += this._velocityY;
|
|
@@ -167,8 +264,8 @@ export class Camera3D {
|
|
|
167
264
|
}
|
|
168
265
|
}
|
|
169
266
|
|
|
170
|
-
// Auto-rotate when not dragging and no significant velocity
|
|
171
|
-
if (this.autoRotate && !this._isDragging) {
|
|
267
|
+
// Auto-rotate when not dragging, not following, and no significant velocity
|
|
268
|
+
if (this.autoRotate && !this._isDragging && !this._followTarget) {
|
|
172
269
|
const hasVelocity = Math.abs(this._velocityX) > 0.001 || Math.abs(this._velocityY) > 0.001;
|
|
173
270
|
if (!hasVelocity) {
|
|
174
271
|
const delta = this.autoRotateSpeed * dt;
|
|
@@ -187,6 +284,17 @@ export class Camera3D {
|
|
|
187
284
|
}
|
|
188
285
|
}
|
|
189
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Calculate shortest angle difference (handles wraparound)
|
|
289
|
+
* @private
|
|
290
|
+
*/
|
|
291
|
+
_angleDiff(from, to) {
|
|
292
|
+
let diff = to - from;
|
|
293
|
+
while (diff > Math.PI) diff -= Math.PI * 2;
|
|
294
|
+
while (diff < -Math.PI) diff += Math.PI * 2;
|
|
295
|
+
return diff;
|
|
296
|
+
}
|
|
297
|
+
|
|
190
298
|
/**
|
|
191
299
|
* Enable mouse/touch drag rotation on a canvas
|
|
192
300
|
* @param {HTMLCanvasElement} canvas - The canvas element to attach controls to
|
|
@@ -360,15 +468,22 @@ export class Camera3D {
|
|
|
360
468
|
}
|
|
361
469
|
|
|
362
470
|
/**
|
|
363
|
-
* Reset rotation to initial values
|
|
471
|
+
* Reset rotation and position to initial values, stop inertia and following
|
|
364
472
|
* @returns {Camera3D} Returns this for chaining
|
|
365
473
|
*/
|
|
366
474
|
reset() {
|
|
367
475
|
this.rotationX = this._initialRotationX;
|
|
368
476
|
this.rotationY = this._initialRotationY;
|
|
369
477
|
this.rotationZ = this._initialRotationZ;
|
|
478
|
+
this.x = this._initialX;
|
|
479
|
+
this.y = this._initialY;
|
|
480
|
+
this.z = this._initialZ;
|
|
370
481
|
this._velocityX = 0;
|
|
371
482
|
this._velocityY = 0;
|
|
483
|
+
this._followTarget = null;
|
|
484
|
+
this._targetX = null;
|
|
485
|
+
this._targetY = null;
|
|
486
|
+
this._targetZ = null;
|
|
372
487
|
return this;
|
|
373
488
|
}
|
|
374
489
|
|
|
@@ -382,6 +497,91 @@ export class Camera3D {
|
|
|
382
497
|
return this;
|
|
383
498
|
}
|
|
384
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Set camera position in world space
|
|
502
|
+
* @param {number} x - X position
|
|
503
|
+
* @param {number} y - Y position
|
|
504
|
+
* @param {number} z - Z position
|
|
505
|
+
* @returns {Camera3D} Returns this for chaining
|
|
506
|
+
*/
|
|
507
|
+
setPosition(x, y, z) {
|
|
508
|
+
this.x = x;
|
|
509
|
+
this.y = y;
|
|
510
|
+
this.z = z;
|
|
511
|
+
return this;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Animate camera to a new position (and optionally rotation)
|
|
516
|
+
* @param {number} x - Target X position
|
|
517
|
+
* @param {number} y - Target Y position
|
|
518
|
+
* @param {number} z - Target Z position
|
|
519
|
+
* @param {object} [options] - Animation options
|
|
520
|
+
* @param {number} [options.rotationX] - Target X rotation
|
|
521
|
+
* @param {number} [options.rotationY] - Target Y rotation
|
|
522
|
+
* @param {number} [options.lerp=0.05] - Interpolation speed (0-1)
|
|
523
|
+
* @returns {Camera3D} Returns this for chaining
|
|
524
|
+
*/
|
|
525
|
+
moveTo(x, y, z, options = {}) {
|
|
526
|
+
this._targetX = x;
|
|
527
|
+
this._targetY = y;
|
|
528
|
+
this._targetZ = z;
|
|
529
|
+
this._targetRotationX = options.rotationX ?? null;
|
|
530
|
+
this._targetRotationY = options.rotationY ?? null;
|
|
531
|
+
this._positionLerp = options.lerp ?? 0.05;
|
|
532
|
+
return this;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Follow a target object (position camera relative to target)
|
|
537
|
+
* @param {object} target - Object with x, y, z properties to follow
|
|
538
|
+
* @param {object} [options] - Follow options
|
|
539
|
+
* @param {number} [options.offsetX=0] - X offset from target
|
|
540
|
+
* @param {number} [options.offsetY=0] - Y offset from target
|
|
541
|
+
* @param {number} [options.offsetZ=0] - Z offset from target
|
|
542
|
+
* @param {boolean} [options.lookAt=true] - Auto-orient to look at lookAtTarget
|
|
543
|
+
* @param {object} [options.lookAtTarget=null] - Point to look at (default: origin)
|
|
544
|
+
* @param {number} [options.lerp=0.1] - Interpolation speed (0-1, higher = snappier)
|
|
545
|
+
* @returns {Camera3D} Returns this for chaining
|
|
546
|
+
*/
|
|
547
|
+
follow(target, options = {}) {
|
|
548
|
+
this._followTarget = target;
|
|
549
|
+
this._followOffset = {
|
|
550
|
+
x: options.offsetX ?? 0,
|
|
551
|
+
y: options.offsetY ?? 0,
|
|
552
|
+
z: options.offsetZ ?? 0,
|
|
553
|
+
};
|
|
554
|
+
this._followLookAt = options.lookAt ?? true;
|
|
555
|
+
this._followLookAtTarget = options.lookAtTarget ?? null;
|
|
556
|
+
this._followLerp = options.lerp ?? 0.1;
|
|
557
|
+
return this;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Stop following target and optionally reset to initial position
|
|
562
|
+
* @param {boolean} [resetPosition=false] - Whether to animate back to initial position
|
|
563
|
+
* @returns {Camera3D} Returns this for chaining
|
|
564
|
+
*/
|
|
565
|
+
unfollow(resetPosition = false) {
|
|
566
|
+
this._followTarget = null;
|
|
567
|
+
if (resetPosition) {
|
|
568
|
+
this.moveTo(this._initialX, this._initialY, this._initialZ, {
|
|
569
|
+
rotationX: this._initialRotationX,
|
|
570
|
+
rotationY: this._initialRotationY,
|
|
571
|
+
lerp: 0.05,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
return this;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Check if camera is currently following a target
|
|
579
|
+
* @returns {boolean} True if following a target
|
|
580
|
+
*/
|
|
581
|
+
isFollowing() {
|
|
582
|
+
return this._followTarget !== null;
|
|
583
|
+
}
|
|
584
|
+
|
|
385
585
|
/**
|
|
386
586
|
* Set rotation angles
|
|
387
587
|
* @param {number} x - X rotation in radians
|
|
@@ -424,15 +624,21 @@ export class Camera3D {
|
|
|
424
624
|
}
|
|
425
625
|
|
|
426
626
|
/**
|
|
427
|
-
* Look at a specific point (sets rotation to face that direction)
|
|
428
|
-
* @param {number} x - Target X
|
|
429
|
-
* @param {number} y - Target Y
|
|
430
|
-
* @param {number} z - Target Z
|
|
627
|
+
* Look at a specific point (sets rotation to face that direction from current position)
|
|
628
|
+
* @param {number} x - Target X in world space
|
|
629
|
+
* @param {number} y - Target Y in world space
|
|
630
|
+
* @param {number} z - Target Z in world space
|
|
431
631
|
* @returns {Camera3D} Returns this for chaining
|
|
432
632
|
*/
|
|
433
633
|
lookAt(x, y, z) {
|
|
434
|
-
|
|
435
|
-
|
|
634
|
+
// Calculate direction from camera position to target
|
|
635
|
+
const dx = x - this.x;
|
|
636
|
+
const dy = y - this.y;
|
|
637
|
+
const dz = z - this.z;
|
|
638
|
+
const distXZ = Math.sqrt(dx * dx + dz * dz);
|
|
639
|
+
|
|
640
|
+
this.rotationY = Math.atan2(dx, dz);
|
|
641
|
+
this.rotationX = Math.atan2(-dy, distXZ);
|
|
436
642
|
return this;
|
|
437
643
|
}
|
|
438
644
|
}
|
package/src/util/index.js
CHANGED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plane Shaders for WebGL Rendering
|
|
3
|
+
*
|
|
4
|
+
* These shaders render 2D patterns onto a plane quad.
|
|
5
|
+
* Unlike sphere shaders which use ray-sphere intersection,
|
|
6
|
+
* these shaders work directly with UV coordinates.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// VERTEX SHADER
|
|
11
|
+
// =============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Standard vertex shader for plane rendering
|
|
15
|
+
* Simply passes through position and UV coordinates
|
|
16
|
+
*/
|
|
17
|
+
export const PLANE_VERTEX = `
|
|
18
|
+
precision highp float;
|
|
19
|
+
|
|
20
|
+
attribute vec2 aPosition;
|
|
21
|
+
attribute vec2 aUv;
|
|
22
|
+
|
|
23
|
+
varying vec2 vUv;
|
|
24
|
+
|
|
25
|
+
void main() {
|
|
26
|
+
vUv = aUv;
|
|
27
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// COMMON SHADER FUNCTIONS
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Common functions included in all plane fragment shaders
|
|
37
|
+
*/
|
|
38
|
+
export const PLANE_COMMON = `
|
|
39
|
+
precision highp float;
|
|
40
|
+
|
|
41
|
+
varying vec2 vUv;
|
|
42
|
+
|
|
43
|
+
uniform float uTime;
|
|
44
|
+
uniform vec2 uResolution;
|
|
45
|
+
|
|
46
|
+
// =============================================================================
|
|
47
|
+
// NOISE FUNCTIONS
|
|
48
|
+
// =============================================================================
|
|
49
|
+
|
|
50
|
+
float hash(float n) {
|
|
51
|
+
return fract(sin(n) * 43758.5453123);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
float hash2(vec2 p) {
|
|
55
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 2D Value noise
|
|
59
|
+
float noise2D(vec2 x) {
|
|
60
|
+
vec2 i = floor(x);
|
|
61
|
+
vec2 f = fract(x);
|
|
62
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
63
|
+
|
|
64
|
+
float a = hash2(i);
|
|
65
|
+
float b = hash2(i + vec2(1.0, 0.0));
|
|
66
|
+
float c = hash2(i + vec2(0.0, 1.0));
|
|
67
|
+
float d = hash2(i + vec2(1.0, 1.0));
|
|
68
|
+
|
|
69
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// FBM (Fractional Brownian Motion)
|
|
73
|
+
float fbm2D(vec2 p, int octaves) {
|
|
74
|
+
float value = 0.0;
|
|
75
|
+
float amplitude = 0.5;
|
|
76
|
+
float frequency = 1.0;
|
|
77
|
+
|
|
78
|
+
for (int i = 0; i < 8; i++) {
|
|
79
|
+
if (i >= octaves) break;
|
|
80
|
+
value += amplitude * noise2D(p * frequency);
|
|
81
|
+
frequency *= 2.0;
|
|
82
|
+
amplitude *= 0.5;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
// =============================================================================
|
|
90
|
+
// GRADIENT SHADER
|
|
91
|
+
// =============================================================================
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Linear gradient shader
|
|
95
|
+
* Uniforms:
|
|
96
|
+
* - uColor1: Start color [r, g, b] (0-1)
|
|
97
|
+
* - uColor2: End color [r, g, b] (0-1)
|
|
98
|
+
* - uAngle: Gradient angle in radians (0 = horizontal, PI/2 = vertical)
|
|
99
|
+
*/
|
|
100
|
+
export const GRADIENT_FRAGMENT = `
|
|
101
|
+
${PLANE_COMMON}
|
|
102
|
+
|
|
103
|
+
uniform vec3 uColor1;
|
|
104
|
+
uniform vec3 uColor2;
|
|
105
|
+
uniform float uAngle;
|
|
106
|
+
|
|
107
|
+
void main() {
|
|
108
|
+
vec2 uv = vUv;
|
|
109
|
+
|
|
110
|
+
// Calculate gradient direction from angle
|
|
111
|
+
vec2 dir = vec2(cos(uAngle), sin(uAngle));
|
|
112
|
+
|
|
113
|
+
// Project UV onto gradient direction
|
|
114
|
+
// Center at 0.5 so gradient goes through middle
|
|
115
|
+
float t = dot(uv - 0.5, dir) + 0.5;
|
|
116
|
+
t = clamp(t, 0.0, 1.0);
|
|
117
|
+
|
|
118
|
+
// Interpolate colors
|
|
119
|
+
vec3 color = mix(uColor1, uColor2, t);
|
|
120
|
+
|
|
121
|
+
gl_FragColor = vec4(color, 1.0);
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// GRID SHADER
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Grid pattern shader
|
|
131
|
+
* Uniforms:
|
|
132
|
+
* - uLineColor: Grid line color [r, g, b] (0-1)
|
|
133
|
+
* - uBackgroundColor: Background color [r, g, b] (0-1)
|
|
134
|
+
* - uGridSize: Number of grid cells (e.g., 8.0 = 8x8 grid)
|
|
135
|
+
* - uLineWidth: Line width as fraction of cell (e.g., 0.05)
|
|
136
|
+
*/
|
|
137
|
+
export const GRID_FRAGMENT = `
|
|
138
|
+
${PLANE_COMMON}
|
|
139
|
+
|
|
140
|
+
uniform vec3 uLineColor;
|
|
141
|
+
uniform vec3 uBackgroundColor;
|
|
142
|
+
uniform float uGridSize;
|
|
143
|
+
uniform float uLineWidth;
|
|
144
|
+
|
|
145
|
+
void main() {
|
|
146
|
+
vec2 uv = vUv;
|
|
147
|
+
|
|
148
|
+
// Scale UV to grid space
|
|
149
|
+
vec2 grid = fract(uv * uGridSize);
|
|
150
|
+
|
|
151
|
+
// Calculate distance to nearest grid line
|
|
152
|
+
float lineX = min(grid.x, 1.0 - grid.x);
|
|
153
|
+
float lineY = min(grid.y, 1.0 - grid.y);
|
|
154
|
+
|
|
155
|
+
// Smoothstep for anti-aliased lines
|
|
156
|
+
float halfWidth = uLineWidth * 0.5;
|
|
157
|
+
float edgeSmooth = 0.01;
|
|
158
|
+
|
|
159
|
+
float lineAlphaX = 1.0 - smoothstep(halfWidth - edgeSmooth, halfWidth + edgeSmooth, lineX);
|
|
160
|
+
float lineAlphaY = 1.0 - smoothstep(halfWidth - edgeSmooth, halfWidth + edgeSmooth, lineY);
|
|
161
|
+
float lineAlpha = max(lineAlphaX, lineAlphaY);
|
|
162
|
+
|
|
163
|
+
// Mix colors
|
|
164
|
+
vec3 color = mix(uBackgroundColor, uLineColor, lineAlpha);
|
|
165
|
+
|
|
166
|
+
gl_FragColor = vec4(color, 1.0);
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
// =============================================================================
|
|
171
|
+
// CHECKERBOARD SHADER
|
|
172
|
+
// =============================================================================
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Checkerboard pattern shader
|
|
176
|
+
* Uniforms:
|
|
177
|
+
* - uColor1: First square color [r, g, b] (0-1)
|
|
178
|
+
* - uColor2: Second square color [r, g, b] (0-1)
|
|
179
|
+
* - uSize: Number of squares per side (e.g., 8.0 = 8x8)
|
|
180
|
+
*/
|
|
181
|
+
export const CHECKERBOARD_FRAGMENT = `
|
|
182
|
+
${PLANE_COMMON}
|
|
183
|
+
|
|
184
|
+
uniform vec3 uColor1;
|
|
185
|
+
uniform vec3 uColor2;
|
|
186
|
+
uniform float uSize;
|
|
187
|
+
|
|
188
|
+
void main() {
|
|
189
|
+
vec2 uv = vUv;
|
|
190
|
+
|
|
191
|
+
// Calculate which square we're in
|
|
192
|
+
vec2 cell = floor(uv * uSize);
|
|
193
|
+
|
|
194
|
+
// Alternating pattern based on cell coordinates
|
|
195
|
+
float checker = mod(cell.x + cell.y, 2.0);
|
|
196
|
+
|
|
197
|
+
// Select color
|
|
198
|
+
vec3 color = mix(uColor1, uColor2, checker);
|
|
199
|
+
|
|
200
|
+
gl_FragColor = vec4(color, 1.0);
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// NOISE SHADER
|
|
206
|
+
// =============================================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Animated noise pattern shader
|
|
210
|
+
* Uniforms:
|
|
211
|
+
* - uColor1: Base color [r, g, b] (0-1)
|
|
212
|
+
* - uColor2: Secondary color [r, g, b] (0-1)
|
|
213
|
+
* - uNoiseScale: Scale of the noise pattern (e.g., 4.0)
|
|
214
|
+
* - uAnimSpeed: Animation speed (e.g., 0.5)
|
|
215
|
+
*/
|
|
216
|
+
export const NOISE_FRAGMENT = `
|
|
217
|
+
${PLANE_COMMON}
|
|
218
|
+
|
|
219
|
+
uniform vec3 uColor1;
|
|
220
|
+
uniform vec3 uColor2;
|
|
221
|
+
uniform float uNoiseScale;
|
|
222
|
+
uniform float uAnimSpeed;
|
|
223
|
+
|
|
224
|
+
void main() {
|
|
225
|
+
vec2 uv = vUv;
|
|
226
|
+
|
|
227
|
+
// Animated noise
|
|
228
|
+
float n = fbm2D(uv * uNoiseScale + uTime * uAnimSpeed, 4);
|
|
229
|
+
|
|
230
|
+
// Map noise to colors
|
|
231
|
+
vec3 color = mix(uColor1, uColor2, n);
|
|
232
|
+
|
|
233
|
+
gl_FragColor = vec4(color, 1.0);
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
// =============================================================================
|
|
238
|
+
// RADIAL GRADIENT SHADER
|
|
239
|
+
// =============================================================================
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Radial gradient shader
|
|
243
|
+
* Uniforms:
|
|
244
|
+
* - uColor1: Center color [r, g, b] (0-1)
|
|
245
|
+
* - uColor2: Edge color [r, g, b] (0-1)
|
|
246
|
+
* - uCenterX: Center X position (0-1, default 0.5)
|
|
247
|
+
* - uCenterY: Center Y position (0-1, default 0.5)
|
|
248
|
+
* - uRadius: Gradient radius (default 0.5)
|
|
249
|
+
*/
|
|
250
|
+
export const RADIAL_GRADIENT_FRAGMENT = `
|
|
251
|
+
${PLANE_COMMON}
|
|
252
|
+
|
|
253
|
+
uniform vec3 uColor1;
|
|
254
|
+
uniform vec3 uColor2;
|
|
255
|
+
uniform float uCenterX;
|
|
256
|
+
uniform float uCenterY;
|
|
257
|
+
uniform float uRadius;
|
|
258
|
+
|
|
259
|
+
void main() {
|
|
260
|
+
vec2 uv = vUv;
|
|
261
|
+
vec2 center = vec2(uCenterX, uCenterY);
|
|
262
|
+
|
|
263
|
+
// Calculate distance from center
|
|
264
|
+
float dist = length(uv - center);
|
|
265
|
+
|
|
266
|
+
// Normalize by radius
|
|
267
|
+
float t = clamp(dist / uRadius, 0.0, 1.0);
|
|
268
|
+
|
|
269
|
+
// Interpolate colors
|
|
270
|
+
vec3 color = mix(uColor1, uColor2, t);
|
|
271
|
+
|
|
272
|
+
gl_FragColor = vec4(color, 1.0);
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
// =============================================================================
|
|
277
|
+
// STRIPES SHADER
|
|
278
|
+
// =============================================================================
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Stripe pattern shader
|
|
282
|
+
* Uniforms:
|
|
283
|
+
* - uColor1: First stripe color [r, g, b] (0-1)
|
|
284
|
+
* - uColor2: Second stripe color [r, g, b] (0-1)
|
|
285
|
+
* - uStripeCount: Number of stripes
|
|
286
|
+
* - uAngle: Stripe angle in radians
|
|
287
|
+
*/
|
|
288
|
+
export const STRIPES_FRAGMENT = `
|
|
289
|
+
${PLANE_COMMON}
|
|
290
|
+
|
|
291
|
+
uniform vec3 uColor1;
|
|
292
|
+
uniform vec3 uColor2;
|
|
293
|
+
uniform float uStripeCount;
|
|
294
|
+
uniform float uAngle;
|
|
295
|
+
|
|
296
|
+
void main() {
|
|
297
|
+
vec2 uv = vUv;
|
|
298
|
+
|
|
299
|
+
// Rotate UV by angle
|
|
300
|
+
vec2 center = vec2(0.5);
|
|
301
|
+
vec2 rotated = uv - center;
|
|
302
|
+
float c = cos(uAngle);
|
|
303
|
+
float s = sin(uAngle);
|
|
304
|
+
rotated = vec2(rotated.x * c - rotated.y * s, rotated.x * s + rotated.y * c);
|
|
305
|
+
rotated += center;
|
|
306
|
+
|
|
307
|
+
// Create stripes along one axis
|
|
308
|
+
float stripe = floor(mod(rotated.x * uStripeCount, 2.0));
|
|
309
|
+
|
|
310
|
+
// Select color
|
|
311
|
+
vec3 color = mix(uColor1, uColor2, stripe);
|
|
312
|
+
|
|
313
|
+
gl_FragColor = vec4(color, 1.0);
|
|
314
|
+
}
|
|
315
|
+
`;
|
|
316
|
+
|
|
317
|
+
// =============================================================================
|
|
318
|
+
// SHADER EXPORT
|
|
319
|
+
// =============================================================================
|
|
320
|
+
|
|
321
|
+
export const PLANE_SHADERS = {
|
|
322
|
+
vertex: PLANE_VERTEX,
|
|
323
|
+
common: PLANE_COMMON,
|
|
324
|
+
gradient: GRADIENT_FRAGMENT,
|
|
325
|
+
grid: GRID_FRAGMENT,
|
|
326
|
+
checkerboard: CHECKERBOARD_FRAGMENT,
|
|
327
|
+
noise: NOISE_FRAGMENT,
|
|
328
|
+
radialGradient: RADIAL_GRADIENT_FRAGMENT,
|
|
329
|
+
stripes: STRIPES_FRAGMENT,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export default PLANE_SHADERS;
|
|
@@ -904,6 +904,7 @@ ${SPHERE_COMMON}
|
|
|
904
904
|
uniform vec3 uBaseColor;
|
|
905
905
|
uniform float uSeed;
|
|
906
906
|
uniform float uStormIntensity; // 0-1
|
|
907
|
+
uniform float uRotationSpeed; // rotation speed multiplier (default ~0.1)
|
|
907
908
|
|
|
908
909
|
void main() {
|
|
909
910
|
// Setup ray - camera looking at sphere from fixed position
|
|
@@ -930,8 +931,9 @@ void main() {
|
|
|
930
931
|
float latitude = asin(rotatedNormal.y); // -PI/2 to PI/2
|
|
931
932
|
float longitude = atan(rotatedNormal.z, rotatedNormal.x); // -PI to PI
|
|
932
933
|
|
|
933
|
-
// Animated rotation
|
|
934
|
-
float
|
|
934
|
+
// Animated rotation (use uRotationSpeed, default to 0.1 if not set)
|
|
935
|
+
float rotSpeed = uRotationSpeed > 0.0 ? uRotationSpeed : 0.1;
|
|
936
|
+
float time = uTime * rotSpeed;
|
|
935
937
|
|
|
936
938
|
// Create bands based on latitude
|
|
937
939
|
float bands = sin(latitude * 15.0 + time) * 0.5 + 0.5;
|