-
Notifications
You must be signed in to change notification settings - Fork 31
/
scene.js
1374 lines (1196 loc) · 39.4 KB
/
scene.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright (c) 2008 Seneca College
Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
*/
/**
@class A Scene should be thought of as a scene on a movie set. A scene
would typically contain objects which are moving and a current camera,
lights, etc.
*/
c3dl.Scene = function ()
{
// Engine Variables
var glCanvas3D = null; // WebGL Context (Canvas)
var renderer = null;
var camera = null; // Reference to a Camera type
var projMat = null;
// Picking is implemented as a class, should be changed
// to a function. For now we need to make an instance of the class.
// We store this in this.pick.
this.pick;
this.pickingPrecision = c3dl.PICK_PRECISION_TRIANGLES;
// This will hold the function the user wants called everytime
// there is a mouse down event.
this.pickingHandler;
// This is off by default since users will likely only need it when
// trying to debug something.
this.boundingVolumesVisible = false;
// A reference to a model which will actually act as a
// SkyBox, except any Model can be used, not just a box.
var skyModel = null;
// list of objects in the scene and list of lights
var objList = []; // An array of objects to draw
var lightList = [c3dl.MAX_LIGHTS];
// each scene has its own point attenuation factors giving the user
// the flexibility to have different factors for each scene.
var pointAttenuation = c3dl.makeVector(1, 0, 0);
var pointSize = 5;
// default point rendering to spheres to prevent possible crashing
// when users render points which playing a DVD on OS X.
var pointRenderingMode = c3dl.POINT_MODE_SPHERE;
var pauseRender = false; //Pause the render loop
var exitRender = false; // Exits the render loop
var pauseUpdate = false; //Pause the update loop
var canvasTag = null;
var canvas2Dlist = [];
// Input Handler Variables
var kybdHandler = null;
var mouseHandler = null;
var updateHandler = null;
// Performance variables
var lastTimeTaken = Date.now();
var numFramesSinceSceneStart = 0;
// this is re-calculated every second and queried with getFPS();
var FPS = 0;
// will be reset after calculating the FPS.
var FPS_Counter = 0;
var FPS_LastTimeTaken = Date.now();
// This will be the color of the background if the user does not change it.
var backgroundColor = c3dl.makeVector(c3dl.DEFAULT_BG_RED, c3dl.DEFAULT_BG_GREEN, c3dl.DEFAULT_BG_BLUE);
var ambientLight = c3dl.makeVector(1, 1, 1);
var thisScn = null;
// If the user calls addTexture on scene, but the scene does not have
// a renderer, there's no was for the texture to be created. In that case
// place the texture path in a queue which will be passed to the renderer
// once it is set.
var textureQueue = [];
var pointPositions = null;
//type of culling
var frustumCulling = new c3dl.Frustum();
var culling = "BoundingSphere"
//Collision
var collision = false;
var collisionList = [];
var collisionDetection = new c3dl.CollisionDetection();
//can detect collision between the entire model or the geometries making up the model
//collisionType = "Collada" or "Geometry"
var collisionType = "Collada";
//cache attributes and location
this.curContextCache = { attributes: {}, locations: {} };
// -------------------------------------------------------
/**
Add a texture to this scene to be used used for assigning to a model,
particle system etc.
If the renderer was not set for the scene, the texture will be queued and
will begin to load once the renderer is set
@param {String} path
*/
/* this.addTexture = function(path)
{
// check path parameter
if(path)
{
if(renderer && renderer.getGLContext())
{
renderer.addTexture(path);
}
else
{
textureQueue.push(path);
}
}
else
{
c3dl.debug.logWarning("Invalid parameter, '" + path + "' was passed to Scene's addTexture()");
}
}*/
/**
*/
/* this.getTextureID = function(path)
{
if(renderer)
{
return renderer.getTextureID(path);
}
else
{
return -1;
}
}*/
/**
@returns {Array}
*/
this.getPointAttenuation = function ()
{
return [pointAttenuation[0], pointAttenuation[1], pointAttenuation[2]];
}
/**
@private
When the picking function runs, it needs the projection matrix
which was used to render the scene. Since one camera can be used for
many scenes, we can't get the projection from the camera because it would
change each time a canvas with a different aspect ratio is rendered.
We have to provide a accessor to the projection matrix at the scene level.
*/
this.getProjectionMatrix = function ()
{
return projMat;
}
/**
@private
Will the bounding volumes be drawn on render?
@returns {boolean} true if the bounding volumes will be drawn, false otherwise.
*/
this.getBoundingVolumeVisibility = function ()
{
return this.boundingVolumesVisible;
}
/**
Get the camera of the scene.
@returns {Camera} The camera of the scene.
*/
this.getCamera = function ()
{
return camera;
}
/**
Get the number of objects in the scene.
@returns {int} The number of objects in the object list.
*/
this.getObjListSize = function ()
{
return objList.length;
}
/**
Get the context.
@returns {Context}
*/
this.getGL = function ()
{
return glCanvas3D;
}
/**
Get the amount of frames rendered since the start of the scene.
@private
*/
this.getTotalFrameCount = function ()
{
return numFramesSinceSceneStart;
}
/**
Get the number of frames rendered in the last second.
@returns {float} the number of frames rendered in the
last second.
*/
this.getFPS = function ()
{
return FPS;
}
/**
Get the scene's Renderer
*/
this.getRenderer = function ()
{
return renderer;
}
/**
Get the Scene.
@returns {c3dl.Scene}
*/
this.getScene = function ()
{
return thisScn;
}
/**
Get the SkyModel.
@returns {c3dl.Collada} The Scene's SkyModel.
*/
this.getSkyModel = function ()
{
return skyModel;
}
/**
Get the ambient light of the scene.
@returns {Array} An Array of 3 values in the order RGB.
*/
this.getAmbientLight = function ()
{
return [ambientLight[0], ambientLight[1], ambientLight[2]];
}
/**
Get a reference of a particular object in the scene.
@param indxNum The index number of the object.
@return the reference to the object at index number indxNum or null
if indxNum was out of bounds.
*/
this.getObj = function (indxNum)
{
if (isNaN(indxNum))
{
c3dl.debug.logWarning("Scene::getObj() called with a parameter that's not a number");
return null;
}
// Check if the index that was asked for is inside the bounds of our array
if (indxNum < 0 || indxNum >= objList.length)
{
c3dl.debug.logWarning("Scene::getObj() called with " + indxNum + ", which is not betwen 0 and " + objList.length);
return null;
}
// We do this because we dont want outsiders modifying the object list,
// just the object themselves (ie. changing position, orientation, etc)
return objList[indxNum];
}
/**
Get the type of test which will run when a user clicks on the canvas.
@returns c3dl.PICK_PRECISION_BOUNDING_VOLUME or c3dl.PICK_PRECISION_TRIANGLE.
*/
this.getPickingPrecision = function ()
{
return this.pickingPrecision;
}
/**
@private
@param {boolean} visible true if the bounding volumes should be drawn, otherwise
set to false.
*/
this.setBoundingVolumeVisibility = function (visible)
{
this.boundingVolumesVisible = visible;
}
/**
Set the functions to call when a key is pressed or released. <br />
TODO: add keyPress event callback as windows and osx versions of firefox
handle keyboard events differently.
@param {function} keyUpCB The callback function for the up key.
@param {function} keyDownCD The callback function for the down key.
*/
this.setKeyboardCallback = function (keyUpCB, keyDownCB)
{
if (canvasTag)
{
// Register True keyboard listeners
if (keyUpCB != null) document.addEventListener("keyup", keyUpCB, false);
if (keyDownCB != null) document.addEventListener("keydown", keyDownCB, false);
}
}
/**
Pass in the functions to call when mouse event occur such as when
a button is pressed, released or the mousewheel is scrolled. The
scene will call these functions when the events occur.
@param {function} mouseUpCB
@param {function} mouseDownCB
@param {function} mouseMoveCB
@param {function} mouseScrollCB
*/
this.setMouseCallback = function (mouseUpCB, mouseDownCB, mouseMoveCB, mouseScrollCB)
{
if (canvasTag)
{
// Register all Mouse listeners
if (mouseMoveCB != null) canvasTag.addEventListener("mousemove", mouseMoveCB, false);
if (mouseUpCB != null) canvasTag.addEventListener("mouseup", mouseUpCB, false);
if (mouseDownCB != null) canvasTag.addEventListener("mousedown", mouseDownCB, false);
// Firefox uses DOMMouseScroll, Safari and Chrome use mousewheel
if (mouseScrollCB != null)
{
canvasTag.addEventListener("DOMMouseScroll", mouseScrollCB, false);
canvasTag.addEventListener("mousewheel", mouseScrollCB, false);
}
}
}
/**
Tell the scene what function to call when a user clicks on the canvas.
@param {function} pickingHandler The function to call when the user clicks on the canvas.
*/
this.setPickingCallback = function (pickingHandler)
{
if (pickingHandler && pickingHandler instanceof Function)
{
// for now we need to make an instance, this needs to be changed.
this.pick = new c3dl.Picking(this);
// set the picking handler
this.pickingHandler = pickingHandler;
canvasTag.addEventListener("mousedown", this.pick.onMouseDown, false);
}
else
{
c3dl.debug.logWarning("scene's setPickingCallback() was passed an invalid callback function");
}
}
/**
Get the function which will be called when the user clicks on the
canvas.
@returns {Function} the function which is called when the user clicks
on the canvas.
*/
this.getPickingCallback = function ()
{
return this.pickingHandler;
}
/**
Set how the points attenuate for this scene.
@param {Array} attn with three values.<br />
first contains constant attenuation<br />
second contains linear attenuation<br />
third contains quadratic attenuation<br />
At least one of the elements must be greater than one or the
argument is ignored.
*/
this.setPointAttenuation = function (attn)
{
if (attn.length == 3 && (attn[0] > 0 || attn[1] > 0 || attn[2] > 0))
{
pointAttenuation[0] = attn[0];
pointAttenuation[1] = attn[1];
pointAttenuation[2] = attn[2];
}
}
/**
Get the size of the spheres when they are rendered as points.
@returns {float} size the points will be when they are rendered as
spheres.
*/
this.getPointSize = function ()
{
return pointSize;
}
/**
Sets the point size when rendering points as spheres.
To change point size when rendering points as circles,
or using the built-in points, use setPointAttenuation.
@param {float} size Must be greater than zero.
*/
this.setPointSize = function (size)
{
if (size > 0)
{
pointSize = size;
}
}
/**
Set the SkyModel. A SkyModel acts like a skybox, when the camera
moves in the scene the SkyModel maintains the same distance from
the camera. This creates the illusion that there are parts to
the scene that are very far away which cannot be reached.
Applications of this would include creating clouds, or mountain
ranges, starfields, etc. Any Model can be passed in and is not
restricted to a Cube. Whatever model that is appropirate should
be used.
@param {c3dl.Collada} sky A Model which will maintain the same distance
from the Scene's camera.
*/
this.setSkyModel = function (sky)
{
if (sky instanceof c3dl.Collada)
{
skyModel = sky;
}
else
{
c3dl.debug.Warning("Scene::setSkyModel() Inavlid argument passed, was not c3dl.Collada.");
}
}
/**
Set the function to call everytime the scene is updated.
@param {function} updateCB The function to call everytime the
scene is updated.
*/
this.setUpdateCallback = function (updateCB)
{
if (canvasTag)
{
if (updateCB != null)
{
updateHandler = updateCB;
}
}
}
/**
Set the renderer used to render the scene.
@param {c3dl.WebGL} renderType
*/
this.setRenderer = function (renderType)
{
// Set the type of renderer to use
if (renderType instanceof c3dl.WebGL)
{
renderer = renderType;
}
}
/**
@param {String} canvasTagID The id of the canvas, which is
a property of the canvas tag in the html file.
*/
this.setCanvasTag = function (canvasTagID)
{
// Get the Canvas tag
canvasTag = document.getElementById(canvasTagID);
if (canvasTag == null)
{
c3dl.debug.logWarning('Scene::setCanvasTag() No canvas tag with name ' + canvasTagID + ' was found.');
}
}
/**
Return the canvas
*/
this.getCanvas = function ()
{
return canvasTag;
}
/**
Set the Scene's camera.
@param {c3dl.FreeCamera} cam The camera.
*/
this.setCamera = function (cam)
{
// Check to see if we were passed a correct Camera class
if (cam instanceof c3dl.FreeCamera || cam instanceof c3dl.OrbitCamera)
{
camera = cam;
return true;
}
c3dl.debug.logWarning('Scene::setCamera() invalid type of camera.');
return false;
}
/**
If the scene has been provided with a picking callback, when the user clicks the canvas
either one or two sets of tests will run. If the bounding volume constant is passed to
this function, a fast, approximate test is run for objects which can be picked against the
ray generated by the click. If the triangles constant is passed in, both the ray/bounding volume test
will run along with a ray/triangle test for each object which can be picked.
@param {c3dl.PICK_PRECISION_BOUNDING_VOLUME | c3dl.PICK_PRECISION_TRIANGLE} precision The precision test to use when the user clicks the canvas.
*/
this.setPickingPrecision = function (precision)
{
if (precision == c3dl.PICK_PRECISION_BOUNDING_VOLUME || precision == c3dl.PICK_PRECISION_TRIANGLE)
{
this.pickingPrecision = precision;
}
}
/**
@private
This one just calls addTextToModel()
//!! need dest as a parameter, probably in pixels, where to put the text
*/
this.addFloatingText = function (text, fontStyle, fontColour, backgroundColour)
{
var box = this.addTextToModel(null, text, fontStyle, fontColour, backgroundColour);
box.stayInFrontOfCamera = true;
this.addObjectToScene(box);
}
/**
@private
Create a 2D canvas, render the text into it, and use that as a texture for model.
If model is null, create a rectangle and stick the text onto it.
*/
this.addTextToModel = function (model, text, fontStyle, fontColour, backgroundColour)
{
// Create a SPAN element with the string and style matching what the user asked
// for the floating text.
var tempSpan = document.createElement('span');
var tempSpanStyle = document.createElement('style');
var tempSpanStyleContent = document.createTextNode('span{' + 'font: ' + fontStyle + ';' + 'color: ' + fontColour + '; ' + 'background: ' + backgroundColour + ';}');
var tempText = document.createTextNode(text);
tempSpanStyle.appendChild(tempSpanStyleContent);
tempSpan.appendChild(tempSpanStyle);
tempSpan.appendChild(tempText);
// Append it to the body so it's momentarily displayed. I couldn't find a way to measure
// the text box's size without displaying it.
document.body.appendChild(tempSpan);
var actualStringWidth = tempSpan.offsetWidth;
var actualStringHeight = tempSpan.offsetHeight;
var stringWidth = c3dl.roundUpToNextPowerOfTwo(tempSpan.offsetWidth);
var stringHeight = c3dl.roundUpToNextPowerOfTwo(tempSpan.offsetHeight);
// Now get rid of that element, we only needed it to measure it
tempSpan.removeChild(tempSpanStyle);
document.body.removeChild(tempSpan);
var box;
if (model == null)
{
var whRatio = stringWidth / stringHeight;
// Model for the plane with the text, size based on whRatio
var smallCanvasVertices = [
[-1.0 * (whRatio / 2), -1.0, 0.0], // 0 - bottom left
[-1.0 * (whRatio / 2), 1.0, 0.0], // 1 - top left
[1.0 * (whRatio / 2), 1.0, 0.0], // 2 - top right
[1.0 * (whRatio / 2), -1.0, 0.0] // 3 - bottom right
];
var smallCanvasNormals = [
[0, 0, -1]
];
var smallCanvasUVs = [
[0.0, 1.0], // 0 - bottom left
[0.0, 0.0], // 1 - top left
[1.0, 0.0], // 2 - top right
[1.0, 1.0] // 3 - bottom right
];
var smallCanvasFaces = [
[0, 0, 0],
[3, 3, 0],
[2, 2, 0],
[0, 0, 0],
[2, 2, 0],
[1, 1, 0]
];
box = new Model();
box.init(smallCanvasVertices, smallCanvasNormals, smallCanvasUVs, smallCanvasFaces);
//box.setAngularVel(new Array(0.003, 0.000, 0.000));
//box.pitch(-0.4);
//!! need something user-specified
box.setPosition([5, 0, 5]);
}
else box = model;
// Draw the text into the 2D canvas and use it for the above model's texture
var textureCanvas = this.create2Dcanvas(stringWidth, stringHeight);
if (textureCanvas.getContext)
{
var ctx = textureCanvas.getContext('2d');
if (fontStyle) ctx.mozTextStyle = fontStyle;
// Fill everything with backgroundColour if it's specified
if (backgroundColour)
{
ctx.fillStyle = backgroundColour;
ctx.fillRect(0, 0, stringWidth, stringHeight);
}
// Center the text in the 2D canvas
ctx.translate((stringWidth - actualStringWidth) / 2, stringHeight - (stringHeight - actualStringHeight));
if (fontColour) ctx.fillStyle = fontColour;
else ctx.fillStyle = 'black';
ctx.mozDrawText(text);
box.setTextureFromCanvas2D(textureCanvas.id);
//textureManager.addTextureFromCanvas2D(textureCanvas.id);
}
else c3dl.debug.logWarning("addFloatingText(): call to create2Dcanvas() failed");
return box;
}
/**
@private
Create a 2D canvas for drawing text and other stuff. Keep a
reference to it.
@return {CanvasTag}
*/
this.create2Dcanvas = function (width, height)
{
var newCanvas = document.createElement('canvas');
newCanvas.id = 'changemetorandomstring';
newCanvas.width = width;
newCanvas.height = height;
canvasTag.appendChild(newCanvas);
canvas2Dlist.push(newCanvas);
return newCanvas;
}
/**
Set the color of the background. Values are clamped to the
range [0,1].
@param {Array} bgColor Array of four values in the order [r,g,b,a].
*/
this.setBackgroundColor = function (bgColor)
{
if (bgColor.length >= 3)
{
backgroundColor = [bgColor[0], bgColor[1], bgColor[2]];
if (renderer)
{
renderer.setClearColor(backgroundColor);
}
}
}
/**
Set how c3dl.Point objects are rendered.
c3dl.POINT_MODE_POINT will render the points using WebGL's built-in 2D billboarded point primitives
c3dl.POINT_MODE_SPHERE will render points using sphere objects.
@param {c3dl.POINT_MODE_POINT | c3dl.POINT_MODE_SPHERE} mode
*/
this.setPointRenderingMode = function (mode)
{
if (mode == c3dl.POINT_MODE_POINT || mode == c3dl.POINT_MODE_SPHERE)
{
pointRenderingMode = mode;
}
else
{
c3dl.debug.logWarning("Invalid mode passed to setPointRenderingMode");
}
}
/**
Get how the points are rendered in the scene. Either they are rendered using
WebGL's built-in method, or are rendered as sphere meshes.
@returns {c3dl.POINT_MODE_POINT | c3dl.POINT_MODE_SPHERE} rendering mode.
*/
this.getPointRenderingMode = function ()
{
return pointRenderingMode;
}
/**
Get the color of the background.
@returns {Array} Array of three values in the order RGB.
*/
this.getBackgroundColor = function ()
{
return c3dl.copyVector(backgroundColor);
}
/**
Set the ambient light of the scene.
@param {Array} light An array of 3 floating point values
ranging from 0 to 1.
*/
this.setAmbientLight = function (light)
{
if (light.length >= 3)
{
ambientLight = [light[0], light[1], light[2], 1];
}
}
/**
Acquire the WebGL Context
@returns {boolean} true if the renderer was initialized, otherwise false.
*/
this.init = function ()
{
if (renderer != null && canvasTag != null)
{
// Initialize the renderer
if (!renderer.createRenderer(canvasTag))
{
c3dl.debug.logError("Your browser does not support WebGL.<br />" + "Visit the <a href='http://en.wikipedia.org/wiki/WebGL'>WebGL wiki page</a> for information on downloading a WebGL enabled browser");
return false;
}
// Get the Canvas
glCanvas3D = renderer.getGLContext();
// tell the renderer the default color the color buffer should be
// every render.
this.setBackgroundColor(backgroundColor);
// Set our global (fake static variable) to be used in rendering
thisScn = this;
// setup the lights
// we have an array of elements, but they are all undefined,
for (var i = 0, len = lightList.length; i < len; i++)
{
lightList[i] = null;
}
// Initialize the renderer
return renderer.init(canvasTag.width, canvasTag.height);
}
c3dl.debug.logError('Scene::createScene() No renderer was specified.');
return false;
}
/**
Get a reference to a light from the list in the scene. This is an O(n)
operation.
@param {String} lightName the name of the light.
@returns a reference to a light object or null if it was not found.
*/
this.getLight = function (name)
{
for (var i = 0, len = lightList.length; i < len; i++)
{
// if we found a match, since we have 'holes' in the array
// check that the value is not null before calling its method.
if (lightList[i] && lightList[i].getName() == name)
{
return lightList[i];
}
}
return null;
}
/**
Adds a light to a scene if the maximum number of lights in the scene
has not been exceeded.
@param {c3dl.PositionalLight|c3dl.DirectionalLight|c3dl.SpotLight} light the light to add.
@returns {boolean} true if the light could be added, otherwise returns false.
*/
this.addLight = function (light)
{
// start from the beginning of the list anf find the first empty spot.
for (var i = 0; i < c3dl.MAX_LIGHTS; i++)
{
// either the light was not yet set to null or we are recycling the spot.
if (lightList[i] == null)
{
lightList[i] = light;
return true;
}
}
// if the iterated over all the lights and didn't find an empty spot,
// we end up here, returning to indicate the light was not added.
return false;
}
/**
Remove a light from the scene. The first light found matching the name
light or object light will be removed.
@param {String || c3dl.Light } light the name of the light or the c3dl object light
*/
this.removeLight = function (light)
{
// There are 2 copies of the light, one in our js code and one in the WebGL
// state variable. We need to remove the light object from our list and set
// the WebGL state variable to all zeros so it will no longer affect the scene.
// first find the index of the light in our array.
var lightID = -1;
for (var i = 0; i < lightList.length && lightID == -1; i++)
{
if (lightList[i] && (lightList[i].getName() == light || lightList[i] === light))
{
lightID = i;
}
}
// now that we have the index, we have to set the corresponding WebGL state
// to zeros, which will prevent the light from affecting the scene.
//
if (lightID != -1)
{
// place a 'hole' in the array. This can later be populated with another light.
// don't delete the light, leave it up to the gc, otherwise
// the light seems to stay on and can't be removed.
lightList[lightID] = null;
// we removed the light from our list, but WebGL still has
// a light state which needs to be cleared. Otherwise the
// light will still affect the scene.
renderer.clearLight(lightID, this);
}
return (lightID == -1 ? false : true);
}
/**
@private
Update the boundingVolume light state variables with our list of lights
This happens every frame.
*/
this.updateLights = function ()
{
renderer.updateAmbientLight(this.getAmbientLight(), this);
renderer.updateLights(lightList, this);
}
/**
Add the object 'obj' to the scene.
@param {c3dl.Primitive|c3dl.ParticleSystem|c3dl.Point|c3dl.Line} obj A reference to an object.
@return {boolean} True if the object was added to the scene, false otherwise.
*/
this.addObjectToScene = function (obj)
{
var type = obj.getObjectType();
switch (type)
{
case c3dl.LINE:
case c3dl.POINT:
case c3dl.PARTICLE_SYSTEM:
case c3dl.COLLADA:
case c3dl.SHAPE:
objList.push(obj);
return true;
}
c3dl.debug.logWarning("Scene::addObjectToScene() called with an invalid argument.");
return false;
}
/**
Remove an object from the scene. This is an O(n) operation.
@param {c3dl.Primitive|c3dl.ParticleSystem|c3dl.Point|c3dl.Line} obj The object to remove from the scene.
@return {boolean} true if the object was found and removed from the scene or
false if the argument 'obj' was not found.
*/
this.removeObjectFromScene = function (obj)
{
var isFound = false;
if (obj instanceof c3dl.Primitive || obj instanceof c3dl.Point || obj instanceof c3dl.Line || obj instanceof c3dl.ParticleSystem)
{
// Check against each item in the list
for (var i = 0, len = objList.length; i < len; i++)
{
if (objList[i] == obj)
{
// Remove the item
objList.splice(i, 1);
isFound = true;
}
}
}
else
{
c3dl.debug.logWarning('Scene::removeObjectFromScene() called with an invalid argument.');
}
return isFound;
}
/**
@private
Render Loop
*/
this.render = function ()
{
// calculate FPS.
// we update the FPS after a second or more has elapsed.
var sec = (new Date().getTime() - FPS_LastTimeTaken) / 1000;
FPS_Counter++;
var fps = FPS_Counter / sec;
if (sec > 0.5)
{
// frames / seconds
FPS = fps;
FPS_Counter = 0;
FPS_LastTimeTaken = new Date().getTime();
}
// If a user wants to stop rendering, this is where it happens
if (exitRender)
{
if (c3dl.debug.SHARK === true)
{
stopShark();
disconnectShark();
}
return;
}
if (pauseUpdate) {
lastTimeTaken = Date.now();
}
if (!pauseUpdate) {
// update the camera and objects
camera.update(Date.now() - lastTimeTaken);
thisScn.updateObjects(Date.now() - lastTimeTaken);
lastTimeTaken = Date.now();
}
if (!pauseRender) {
// The user may have added a texture to the scene in
// which case, the renderer needs to create them.
if (textureQueue.length > 0)
{
for (var i = 0, len = textureQueue.length; i < len; i++)
{
renderer.addTexture(textureQueue[i]);
}