@@ -6,18 +6,22 @@ export class Tau5Shader {
66 this . startTime = Date . now ( ) ;
77 this . uniforms = { } ;
88 this . animationFrame = null ;
9-
9+
1010 // Mouse interaction state
1111 this . isDragging = false ;
1212 this . lastMouseX = 0 ;
1313 this . lastMouseY = 0 ;
14- this . velocityX = 0 ;
15- this . velocityY = 0 ;
16- this . rotationX = 0 ;
17- this . rotationY = 0 ;
18- this . rotationZ = 0 ;
19- this . baseSpeed = 0.0002 ; // Base rotation speed when not interacting (slower)
20- this . damping = 0.98 ; // Inertia damping factor (higher = longer spin)
14+
15+ // Camera rotation (view space) - controlled by mouse
16+ this . cameraVelocityX = 0 ; // Pitch velocity
17+ this . cameraVelocityY = 0 ; // Yaw velocity
18+ this . cameraPitch = 0 ; // Camera pitch (up/down)
19+ this . cameraYaw = 0 ; // Camera yaw (left/right)
20+
21+ // Auto rotation (object space) - automatic spinning
22+ this . autoRotationTime = 0 ;
23+ this . baseSpeed = 0.05 ; // Auto rotation speed
24+ this . damping = 0.985 ; // Inertia damping factor (higher = longer spin)
2125 }
2226
2327 async init ( ) {
@@ -50,7 +54,8 @@ export class Tau5Shader {
5054 // Get uniform locations
5155 this . uniforms . time = this . gl . getUniformLocation ( this . program , 'time' ) ;
5256 this . uniforms . resolution = this . gl . getUniformLocation ( this . program , 'resolution' ) ;
53- this . uniforms . rotation = this . gl . getUniformLocation ( this . program , 'rotation' ) ;
57+ this . uniforms . autoRotation = this . gl . getUniformLocation ( this . program , 'autoRotation' ) ;
58+ this . uniforms . cameraRotation = this . gl . getUniformLocation ( this . program , 'cameraRotation' ) ;
5459
5560 // Enable alpha blending
5661 this . gl . enable ( this . gl . BLEND ) ;
@@ -108,20 +113,19 @@ export class Tau5Shader {
108113 precision mediump float;
109114 uniform float time;
110115 uniform vec2 resolution;
111- uniform vec3 rotation;
112-
116+ uniform vec3 autoRotation;
117+ uniform vec2 cameraRotation;
118+
113119 #define PI 3.14159265359
114-
120+
115121 mat2 rot(float a) {
116122 float s = sin(a), c = cos(a);
117123 return mat2(c, -s, s, c);
118124 }
119-
125+
120126 vec3 cubeWireframe(vec2 p) {
121127 vec3 col = vec3(0.0);
122-
123- vec3 angles = rotation;
124-
128+
125129 // Define cube vertices
126130 vec3 verts[8];
127131 verts[0] = vec3(-1.0, -1.0, -1.0);
@@ -132,17 +136,23 @@ export class Tau5Shader {
132136 verts[5] = vec3( 1.0, -1.0, 1.0);
133137 verts[6] = vec3( 1.0, 1.0, 1.0);
134138 verts[7] = vec3(-1.0, 1.0, 1.0);
135-
139+
136140 float scale = 0.3;
137-
141+
138142 // Project vertices
139143 vec2 proj[8];
140144 for(int i = 0; i < 8; i++) {
141145 vec3 v = verts[i];
142- // Rotate
143- v.yz = rot(angles.x) * v.yz;
144- v.xz = rot(angles.y) * v.xz;
145- v.xy = rot(angles.z) * v.xy;
146+
147+ // Apply auto-rotation (object space)
148+ v.yz *= rot(autoRotation.x);
149+ v.xz *= rot(autoRotation.y);
150+ v.xy *= rot(autoRotation.z);
151+
152+ // Apply camera rotation (view space) - this gives perfect screen-space control
153+ v.xz *= rot(cameraRotation.y); // Yaw (horizontal mouse movement)
154+ v.yz *= rot(cameraRotation.x); // Pitch (vertical mouse movement)
155+
146156 // Perspective projection
147157 float z = 4.0 + v.z;
148158 proj[i] = v.xy * (2.0 / z) * scale;
@@ -201,36 +211,39 @@ export class Tau5Shader {
201211 this . isDragging = true ;
202212 this . lastMouseX = e . clientX ;
203213 this . lastMouseY = e . clientY ;
204- this . velocityX = 0 ;
205- this . velocityY = 0 ;
214+ this . cameraVelocityX = 0 ;
215+ this . cameraVelocityY = 0 ;
206216 this . canvas . style . cursor = 'grabbing' ;
207217 } ) ;
208-
218+
209219 // Mouse move - update rotation
210220 window . addEventListener ( 'mousemove' , ( e ) => {
211221 if ( ! this . isDragging ) return ;
212-
222+
213223 const deltaX = e . clientX - this . lastMouseX ;
214224 const deltaY = e . clientY - this . lastMouseY ;
215-
216- // Update velocity for inertia
217- this . velocityY = deltaX * 0.01 ; // X mouse movement -> Y rotation
218- this . velocityX = - deltaY * 0.01 ; // Y mouse movement -> X rotation (inverted)
219-
220- // Update rotation based on mouse movement
221- this . rotationY += deltaX * 0.01 ; // Horizontal drag rotates around Y
222- this . rotationX -= deltaY * 0.01 ; // Vertical drag rotates around X (inverted)
223-
225+
226+ // Camera rotation - perfect screen-space control
227+ const rotationSpeed = 0.01 ;
228+
229+ // Update camera velocities (inverted Y for intuitive control)
230+ this . cameraVelocityX = - deltaY * rotationSpeed ; // Vertical drag -> pitch (inverted)
231+ this . cameraVelocityY = deltaX * rotationSpeed ; // Horizontal drag -> yaw
232+
233+ // Apply camera rotations
234+ this . cameraPitch += this . cameraVelocityX ;
235+ this . cameraYaw += this . cameraVelocityY ;
236+
224237 this . lastMouseX = e . clientX ;
225238 this . lastMouseY = e . clientY ;
226239 } ) ;
227-
240+
228241 // Mouse up - stop dragging
229242 window . addEventListener ( 'mouseup' , ( ) => {
230243 this . isDragging = false ;
231244 this . canvas . style . cursor = 'grab' ;
232245 } ) ;
233-
246+
234247 // Add hover cursor
235248 this . canvas . style . cursor = 'grab' ;
236249 }
@@ -257,49 +270,38 @@ export class Tau5Shader {
257270
258271 this . gl . useProgram ( this . program ) ;
259272
260- // Update physics when not dragging
273+ // Auto-rotation (always active)
274+ const time = ( Date . now ( ) - this . startTime ) / 1000.0 ;
275+ this . autoRotationTime = time * this . baseSpeed ;
276+
277+ // Update camera physics when not dragging
261278 if ( ! this . isDragging ) {
262- // Apply inertia
263- this . rotationY += this . velocityY ; // Y velocity affects Y rotation
264- this . rotationX += this . velocityX ; // X velocity affects X rotation
265-
266- // Apply damping to velocity
267- this . velocityX *= this . damping ;
268- this . velocityY *= this . damping ;
269-
270- // Smoothly blend in base rotation as velocity decreases
271- const totalVelocity = Math . sqrt ( this . velocityX * this . velocityX + this . velocityY * this . velocityY ) ;
272- const blendFactor = Math . max ( 0 , 1 - totalVelocity / 0.001 ) ; // Smooth transition
273-
274- // Always apply some base rotation, scaled by how still the cube is
275- const time = ( Date . now ( ) - this . startTime ) / 1000.0 ;
276- this . rotationZ += this . baseSpeed * 30 * blendFactor ;
277-
278- // If nearly stopped, maintain minimum rotation in the last direction
279- if ( totalVelocity < 0.001 ) {
280- // Smoothly add base rotation without jerking
281- const baseX = this . baseSpeed * 0.7 * blendFactor ;
282- const baseY = this . baseSpeed * blendFactor ;
283-
284- // If we have some velocity direction, follow it
285- if ( totalVelocity > 0.0001 ) {
286- const dirX = this . velocityX / totalVelocity ;
287- const dirY = this . velocityY / totalVelocity ;
288- this . velocityX = Math . max ( Math . abs ( this . velocityX ) , baseX ) * dirX ;
289- this . velocityY = Math . max ( Math . abs ( this . velocityY ) , baseY ) * dirY ;
290- } else {
291- // Otherwise, gently start base rotation
292- this . velocityX += baseX * 0.1 ;
293- this . velocityY += baseY * 0.1 ;
294- }
295- }
279+ // Apply inertia to camera rotation
280+ this . cameraPitch += this . cameraVelocityX ;
281+ this . cameraYaw += this . cameraVelocityY ;
282+
283+ // Apply damping to camera velocity
284+ this . cameraVelocityX *= this . damping ;
285+ this . cameraVelocityY *= this . damping ;
286+
287+ // Stop when velocity is very small
288+ const minVelocity = 0.00001 ;
289+ if ( Math . abs ( this . cameraVelocityX ) < minVelocity ) this . cameraVelocityX = 0 ;
290+ if ( Math . abs ( this . cameraVelocityY ) < minVelocity ) this . cameraVelocityY = 0 ;
296291 }
297292
298293 // Set uniforms
299- const time = ( Date . now ( ) - this . startTime ) / 1000.0 ;
300294 this . gl . uniform1f ( this . uniforms . time , time ) ;
301295 this . gl . uniform2f ( this . uniforms . resolution , this . canvas . width , this . canvas . height ) ;
302- this . gl . uniform3f ( this . uniforms . rotation , this . rotationX , this . rotationY , this . rotationZ ) ;
296+
297+ // Auto-rotation angles (object space)
298+ const autoRotX = this . autoRotationTime ;
299+ const autoRotY = this . autoRotationTime * 0.7 ;
300+ const autoRotZ = this . autoRotationTime * 0.3 ;
301+ this . gl . uniform3f ( this . uniforms . autoRotation , autoRotX , autoRotY , autoRotZ ) ;
302+
303+ // Camera rotation (view space)
304+ this . gl . uniform2f ( this . uniforms . cameraRotation , this . cameraPitch , this . cameraYaw ) ;
303305
304306 // Draw
305307 this . gl . drawArrays ( this . gl . TRIANGLE_STRIP , 0 , 4 ) ;
0 commit comments