Skip to content

Commit d1571ba

Browse files
committed
Add STL loader demo and camera script
1 parent 1235e14 commit d1571ba

File tree

4 files changed

+3573
-0
lines changed

4 files changed

+3573
-0
lines changed

camera.js

Lines changed: 395 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
2+
/**
3+
* A class for controlling the projection and
4+
* view of a 3D scene, in the nature of an abstract "camera".
5+
* @class
6+
* @alias Camera
7+
* @param {glutil.Scene} A 3D scene to associate with this
8+
* camera object.
9+
* @param {number} fov Vertical field of view, in degrees. Should be less
10+
* than 180 degrees. (The smaller
11+
* this number, the bigger close objects appear to be.)
12+
* @param {number} near The distance from the camera to
13+
* the near clipping plane. Objects closer than this distance won't be
14+
* seen. This should be slightly greater than 0.
15+
* @param {number} far The distance from the camera to
16+
* the far clipping plane. Objects beyond this distance will be too far
17+
* to be seen.
18+
*/
19+
function Camera(scene, fov, nearZ, farZ){
20+
this.target=[0,0,0]; // target position
21+
this.distance=Math.max(nearZ,10); // distance from the target in units
22+
this.position=[0,0,this.distance];
23+
this.angleQuat=GLMath.quatIdentity();
24+
this.angleX=0;
25+
this.angleY=0;
26+
this.scene=scene;
27+
this.fov=fov;
28+
this.near=nearZ;
29+
this.far=farZ;
30+
if(typeof InputTracker!="undefined"){
31+
this.input=new InputTracker(scene.getContext().canvas);
32+
}
33+
this.currentAspect=this.scene.getAspect();
34+
this.scene.setPerspective(this.fov,this.currentAspect,this.near,this.far);
35+
this._updateLookAt();
36+
}
37+
Camera._vec3diff=function(a,b){
38+
return [a[0]-b[0],a[1]-b[1],a[2]-b[2]];
39+
}
40+
/**
41+
* Moves the camera to the left or right, adjusting
42+
* its angle, while maintaining its distance to the target
43+
* position.
44+
* @param {number} angleDegrees Angle, in degrees,
45+
* to move the camera. Positive angles mean left,
46+
* negative angles mean right.
47+
* @return {Camera} This object.
48+
*/
49+
Camera.prototype.moveAngleHorizontal=function(angleDegrees){
50+
if(angleDegrees!=0){
51+
this._angleHorizontal(-angleDegrees);
52+
this._resetPosition();
53+
}
54+
return this;
55+
}
56+
/**
57+
* Not documented yet.
58+
* @param {*} angleDegrees
59+
*//** @private */
60+
Camera.prototype._angleHorizontal=function(angleDegrees){
61+
this.angleY+=(angleDegrees*GLMath.PiDividedBy180)%(Math.PI*2);
62+
if(this.angleY<0)this.angleY=(Math.PI*2+this.angleY);
63+
this.angleQuat=GLMath.quatFromEuler(
64+
this.angleX*GLMath.Num180DividedByPi,
65+
this.angleY*GLMath.Num180DividedByPi,
66+
0,
67+
GLMath.PitchYawRoll);
68+
}
69+
/**
70+
* Not documented yet.
71+
* @param {*} angleDegrees
72+
*//** @private */
73+
Camera.prototype._angleVertical=function(angleDegrees){
74+
var oldangle=this.angleX;
75+
this.angleX+=(angleDegrees*GLMath.PiDividedBy180)%(Math.PI*2);
76+
if(this.angleX<0)this.angleX=(Math.PI*2+this.angleX);
77+
this.angleQuat=GLMath.quatFromEuler(
78+
this.angleX*GLMath.Num180DividedByPi,
79+
this.angleY*GLMath.Num180DividedByPi,
80+
0,
81+
GLMath.PitchYawRoll);
82+
}
83+
84+
/**
85+
* Moves the camera up or down, adjusting its angle, while
86+
* maintaining its distance to the target position.
87+
* The camera won't turn to 90 degrees above or beyond,
88+
* or to 90 degrees below or beyond.
89+
* @param {number} angleDegrees Angle, in degrees,
90+
* to move the camera. Positive angles mean down,
91+
* negative angles mean up.
92+
* @return {Camera} This object.
93+
*/
94+
Camera.prototype.moveAngleVertical=function(angleDegrees){
95+
if(angleDegrees!=0){
96+
this._angleVertical(-angleDegrees);
97+
this._resetPosition();
98+
}
99+
return this;
100+
}
101+
102+
/**
103+
* Turns the camera up or down, maintaining
104+
* its current position.
105+
* @param {number} angleDegrees Angle, in degrees,
106+
* to move the camera. Positive angles mean down,
107+
* negative angles mean up.
108+
* @return {Camera} This object.
109+
*/
110+
Camera.prototype.turnVertical=function(angleDegrees){
111+
if(angleDegrees!=0){
112+
var pos=this.getPosition();
113+
this._angleVertical(angleDegrees);
114+
this._resetTarget();
115+
}
116+
return this;
117+
}
118+
/** @private */
119+
Camera.prototype._resetPosition=function(){
120+
var diff=Camera._vec3diff(this.target,this.position);
121+
var dist=GLMath.vec3length(diff);
122+
var newVector=GLMath.quatTransform(this.angleQuat,[0,0,-dist]);
123+
this.position[0]=this.target[0]-newVector[0];
124+
this.position[1]=this.target[1]-newVector[1];
125+
this.position[2]=this.target[2]-newVector[2];
126+
this._updateLookAt();
127+
this.setDistance(this.distance);
128+
}
129+
/** @private */
130+
Camera.prototype._resetTarget=function(){
131+
var diff=Camera._vec3diff(this.target,this.position);
132+
var dist=GLMath.vec3length(diff);
133+
var newVector=GLMath.quatTransform(this.angleQuat,[0,0,-dist]);
134+
this.target[0]=this.position[0]+newVector[0];
135+
this.target[1]=this.position[1]+newVector[1];
136+
this.target[2]=this.position[2]+newVector[2];
137+
this._updateLookAt();
138+
this.setDistance(this.distance);
139+
}
140+
141+
/**
142+
* Turns the camera to the left or right, maintaining
143+
* its current position.
144+
* @param {number} angleDegrees Angle, in degrees,
145+
* to move the camera. Positive angles mean right,
146+
* negative angles mean left.
147+
* @return {Camera} This object.
148+
*/
149+
Camera.prototype.turnHorizontal=function(angleDegrees){
150+
if(angleDegrees!=0){
151+
var pos=this.getPosition();
152+
this._angleHorizontal(-angleDegrees);
153+
this._resetTarget();
154+
}
155+
return this;
156+
}
157+
158+
/**
159+
* Gets the current position of the camera.
160+
* @return {Camera} A 3-element array giving
161+
* the X, Y, and Z coordinates, respectively, of
162+
* the camera's position.
163+
*/
164+
Camera.prototype.getPosition=function(){
165+
return this.position.slice(0,3);
166+
}
167+
168+
Camera.prototype.setDistance=function(dist){
169+
if(dist<0)throw new Error("invalid distance")
170+
if(dist<this.near)dist=this.near;
171+
var diff=Camera._vec3diff(this.target,this.position);
172+
var curdist=GLMath.vec3length(diff);
173+
var distdiff=curdist-diff;
174+
this.distance=dist;
175+
if(distdiff!=0){
176+
var factor=dist/curdist;
177+
this.position[0]=this.target[0]-diff[0]*factor;
178+
this.position[1]=this.target[1]-diff[1]*factor;
179+
this.position[2]=this.target[2]-diff[2]*factor;
180+
this._updateLookAt();
181+
}
182+
return this;
183+
}
184+
185+
/**
186+
* Moves the camera's position to the given
187+
* X, Y, and Z coordinates, and adjusts the target's
188+
* position to maintain the current distance and
189+
* orientation.
190+
* @param {number} X coordinate.
191+
* @param {number} Y coordinate.
192+
* @param {number} Z coordinate.
193+
* @return {Camera} This object.
194+
*/
195+
Camera.prototype.movePosition=function(cx, cy, cz){
196+
if(cx!=0 && cy!=0 && cz!=0){
197+
var pos=this.getPosition();
198+
this.target[0]+=(cx-pos[0]);
199+
this.target[1]+=(cy-pos[1]);
200+
this.target[2]+=(cz-pos[2]);
201+
this.position=[cx,cy,cz];
202+
}
203+
return this;
204+
}
205+
206+
/**
207+
* Moves the camera closer or farther to the target
208+
* position. The camera won't move closer than the distance
209+
* to the near clipping plane.
210+
* @param {number} distance Distance, in world
211+
* space units, to move the camera. Positive values
212+
* mean closer, negative angles mean farther.
213+
* @return {Camera} This object.
214+
*/
215+
Camera.prototype.moveClose=function(dist){
216+
if(dist!=0){
217+
var diff=Camera._vec3diff(this.target,this.position);
218+
var realDist=GLMath.vec3length(diff);
219+
var newDist=realDist-dist;
220+
if(newDist<this.near){
221+
newDist=this.near; // too close
222+
}
223+
this.distance=newDist;
224+
var factor=newDist/realDist;
225+
this.position[0]=this.target[0]-diff[0]*factor;
226+
this.position[1]=this.target[1]-diff[1]*factor;
227+
this.position[2]=this.target[2]-diff[2]*factor;
228+
this._updateLookAt();
229+
}
230+
return this;
231+
}
232+
233+
/**
234+
* Moves the target and the camera either forward or back,
235+
* maintaining the distance between the camera and the target.
236+
* @param {number} distance Distance, in world
237+
* space units, to move the target and camera, either forward
238+
* or back. Positive values
239+
* cause the camera to move forward, negative angles
240+
* cause it to move back.
241+
* @return {Camera} This object.
242+
*/
243+
Camera.prototype.moveForward=function(dist){
244+
if(dist!=0){
245+
var diff=Camera._vec3diff(this.target,this.position);
246+
var realDist=GLMath.vec3length(diff);
247+
var factor=dist/realDist;
248+
this.position[0]+=diff[0]*factor;
249+
this.position[1]+=diff[1]*factor;
250+
this.position[2]+=diff[2]*factor;
251+
this.target[0]+=diff[0]*factor;
252+
this.target[1]+=diff[1]*factor;
253+
this.target[2]+=diff[2]*factor;
254+
this._updateLookAt();
255+
}
256+
return this;
257+
}
258+
/** @private */
259+
Camera.prototype._updateLookAt=function(){
260+
this.scene.setLookAt(this.getPosition(),this.target,
261+
GLMath.quatTransform(this.angleQuat,[0,1,0]));
262+
}
263+
/**
264+
* Not documented yet.
265+
* @return {Camera} This object.
266+
*/
267+
Camera.prototype.update=function(){
268+
var delta=this.input.deltaXY();
269+
if(this.input.leftButton){
270+
this.moveAngleHorizontal(0.5*delta.x)
271+
this.moveAngleVertical(0.5*delta.y)
272+
}
273+
if(this.input.keys[InputTracker.DOWN]){
274+
this.moveForward(-0.2)
275+
}
276+
if(this.input.keys[InputTracker.UP]){
277+
this.moveForward(0.2)
278+
}
279+
if(this.input.keys[InputTracker.LEFT]){
280+
this.turnHorizontal(-0.5)
281+
}
282+
if(this.input.keys[InputTracker.RIGHT]){
283+
this.turnHorizontal(0.5)
284+
}
285+
var aspect=this.scene.getAspect();
286+
if(aspect!=this.currentAspect){
287+
this.currentAspect=aspect;
288+
this.scene.setPerspective(this.fov,this.currentAspect,this.near,this.far);
289+
}
290+
this._updateLookAt();
291+
return this;
292+
}
293+
function InputTracker(element){
294+
this.leftButton=false;
295+
this.rightButton=false;
296+
this.keys={};
297+
this.lastClient=[];
298+
this.clientX=null;
299+
this.clientY=null;
300+
var thisObj=this;
301+
window.addEventListener("blur",function(e){
302+
thisObj.leftButton=false;
303+
thisObj.rightButton=false;
304+
thisObj.keys={};
305+
})
306+
document.addEventListener("keydown",function(e){
307+
thisObj.keys[e.keyCode]=true;
308+
})
309+
document.addEventListener("keyup",function(e){
310+
delete thisObj.keys[e.keyCode];
311+
})
312+
element.addEventListener("mousedown",function(e){
313+
if(e.button==0){
314+
thisObj.leftButton=true;
315+
}
316+
if(e.button==2){
317+
thisObj.rightButton=true;
318+
}
319+
thisObj.clientX=e.clientX-InputTracker._getPageX(e.target);
320+
thisObj.clientY=e.clientY-InputTracker._getPageY(e.target);
321+
})
322+
element.addEventListener("mouseup",function(e){
323+
if(e.button==0){
324+
thisObj.leftButton=false;
325+
}
326+
if(e.button==2){
327+
thisObj.rightButton=false;
328+
}
329+
thisObj.clientX=e.clientX-InputTracker._getPageX(e.target);
330+
thisObj.clientY=e.clientY-InputTracker._getPageY(e.target);
331+
})
332+
element.addEventListener("mousemove",function(e){
333+
thisObj.clientX=e.clientX-InputTracker._getPageX(e.target);
334+
thisObj.clientY=e.clientY-InputTracker._getPageY(e.target);
335+
})
336+
};
337+
/** @private */
338+
InputTracker._getPageX=function(o) {
339+
"use strict";
340+
var x=0;
341+
while(o!==null && typeof o!=="undefined") {
342+
if(typeof o.offsetLeft!=="undefined")
343+
x+=o.offsetLeft;
344+
o=o.offsetParent;
345+
}
346+
return x;
347+
}
348+
/** @private */
349+
InputTracker._getPageY=function(o) {
350+
"use strict";
351+
var x=0;
352+
while(o!==null && typeof o!=="undefined") {
353+
if(typeof o.offsetTop!=="undefined")
354+
x+=o.offsetTop;
355+
o=o.offsetParent;
356+
}
357+
return x;
358+
}
359+
InputTracker.A=65;
360+
InputTracker.ZERO=48;
361+
InputTracker.RETURN=10;
362+
InputTracker.ENTER=13;
363+
InputTracker.TAB=9;
364+
InputTracker.SHIFT=16;
365+
InputTracker.CTRL=17;
366+
InputTracker.ESC=27;
367+
InputTracker.SPACE=32;
368+
InputTracker.PAGEUP=33;
369+
InputTracker.PAGEDOWN=34;
370+
InputTracker.END=35;
371+
InputTracker.HOME=36;
372+
InputTracker.LEFT=37;
373+
InputTracker.UP=38;
374+
InputTracker.RIGHT=39;
375+
InputTracker.DOWN=40;
376+
InputTracker.DELETE=46;
377+
InputTracker.ADD=107;
378+
InputTracker.SUBTRACT=109;
379+
/**
380+
* Not documented yet.
381+
*/
382+
InputTracker.prototype.deltaXY=function(){
383+
var deltaX=0;
384+
var deltaY=0;
385+
if(this.clientX==null || this.clientY==null){
386+
return {"x":0,"y":0};
387+
}
388+
var deltaX=(this.lastClient.length==0) ? 0 :
389+
this.clientX-this.lastClient[0];
390+
var deltaY=(this.lastClient.length==0) ? 0 :
391+
this.clientY-this.lastClient[1];
392+
this.lastClient[0]=this.clientX;
393+
this.lastClient[1]=this.clientY;
394+
return {"x":deltaX,"y":deltaY};
395+
}

0 commit comments

Comments
 (0)