├── LICENSE
├── README.md
├── css
└── caps.css
├── index.html
├── js
├── caps.js
├── material.js
├── picking.js
├── planeGeometry.js
├── schedule.js
├── selection.js
├── selectionBoxFace.js
├── selectionBoxLine.js
├── shader.js
├── simulation.js
├── threeExtensions.js
└── uniforms.js
├── lib
├── ColladaLoader.js
├── OrbitControls.js
├── three-license.txt
└── three.js
├── models
└── house.dae
├── screenshot_01.png
└── screenshot_02.png
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Jakob Mischek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | clipping-with-caps
2 | ==================
3 |
4 | ### Clipping a mesh and covering the openings with caps in WebGL ###
5 |
6 | [View the demo](http://daign.github.io/clipping-with-caps/)
7 |
8 | 
9 |
10 | Licensed under [MIT License](./LICENSE)
11 |
12 | Dependencies:
13 | * [three.js](https://github.com/mrdoob/three.js)
14 |
15 | ---
16 |
17 | Clipping meshes inside a shader program is fast and avoids triangulation problems that can arise
18 | when actually constructing a clipped mesh with constructive solid geometry.
19 | But since a mesh is just a hollow hull of triangles the clipped object will have an opening along
20 | the clipping edge.
21 | It is however possible to visually close this gaps without actually constructing the cap faces.
22 |
23 | When there is just one clipping plane on an otherwise closed mesh and backface rendering is enabled,
24 | then there are backsides visible through the whole opening.
25 | Rendered into a stencil this area can be used to define where to render the caps.
26 | First a scene showing only the backfaces is used to increment the stencil and then another scene
27 | showing the front faces decrements the stencil.
28 | The resulting stencil is applied to a scene rendering a plane at the location of the clipping plane.
29 |
30 | But this method fails when there is more than one clipping plane.
31 | Because clipping planes facing away from the camera result in hidden backfaces, these areas are
32 | missing in the stencil.
33 | The result would look like this:
34 |
35 | 
36 |
37 | The solution is to use a different shader for rendering the stencil areas, that only clipps at
38 | clipping planes facing the camera.
39 | Since the camera position is known it can be calculated in the shader whether a clipping plane is
40 | facing towards the camera or away from it.
41 |
42 |
43 |
--------------------------------------------------------------------------------
/css/caps.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Monospace;
3 | background-color: #000000;
4 | margin: 0px;
5 | overflow: hidden;
6 | }
7 | #info {
8 | color: #000;
9 | position: absolute;
10 | top: 10px;
11 | left: 10px;
12 | z-index: 100;
13 | display: block;
14 | }
15 | #controls {
16 | color: #000;
17 | position: absolute;
18 | bottom: 10px;
19 | left: 10px;
20 | z-index: 100;
21 | display: block;
22 | }
23 | a {
24 | color: #000;
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Clipping with caps
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 |
33 |
34 | Show caps
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/js/caps.js:
--------------------------------------------------------------------------------
1 | var CAPS = {};
2 |
3 |
--------------------------------------------------------------------------------
/js/material.js:
--------------------------------------------------------------------------------
1 | CAPS.MATERIAL = {
2 |
3 | sheet: new THREE.ShaderMaterial( {
4 | uniforms: CAPS.UNIFORMS.clipping,
5 | vertexShader: CAPS.SHADER.vertexClipping,
6 | fragmentShader: CAPS.SHADER.fragmentClipping
7 | } ),
8 |
9 | cap: new THREE.ShaderMaterial( {
10 | uniforms: CAPS.UNIFORMS.caps,
11 | vertexShader: CAPS.SHADER.vertex,
12 | fragmentShader: CAPS.SHADER.fragment
13 | } ),
14 |
15 | backStencil: new THREE.ShaderMaterial( {
16 | uniforms: CAPS.UNIFORMS.clipping,
17 | vertexShader: CAPS.SHADER.vertexClipping,
18 | fragmentShader: CAPS.SHADER.fragmentClippingFront,
19 | colorWrite: false,
20 | depthWrite: false,
21 | side: THREE.BackSide
22 | } ),
23 |
24 | frontStencil: new THREE.ShaderMaterial( {
25 | uniforms: CAPS.UNIFORMS.clipping,
26 | vertexShader: CAPS.SHADER.vertexClipping,
27 | fragmentShader: CAPS.SHADER.fragmentClippingFront,
28 | colorWrite: false,
29 | depthWrite: false,
30 | } ),
31 |
32 | BoxBackFace: new THREE.MeshBasicMaterial( { color: 0xEEDDCC, transparent: true } ),
33 | BoxWireframe: new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } ),
34 | BoxWireActive: new THREE.LineBasicMaterial( { color: 0xf83610, linewidth: 4 } ),
35 |
36 | Invisible: new THREE.ShaderMaterial( {
37 | vertexShader: CAPS.SHADER.invisibleVertexShader,
38 | fragmentShader: CAPS.SHADER.invisibleFragmentShader
39 | } )
40 |
41 | };
42 |
43 |
--------------------------------------------------------------------------------
/js/picking.js:
--------------------------------------------------------------------------------
1 | CAPS.picking = function ( simulation ) {
2 |
3 | var intersected = null;
4 | var mouse = new THREE.Vector2();
5 | var ray = new THREE.Raycaster();
6 |
7 | var normals = {
8 | x1: new THREE.Vector3( -1, 0, 0 ),
9 | x2: new THREE.Vector3( 1, 0, 0 ),
10 | y1: new THREE.Vector3( 0, -1, 0 ),
11 | y2: new THREE.Vector3( 0, 1, 0 ),
12 | z1: new THREE.Vector3( 0, 0, -1 ),
13 | z2: new THREE.Vector3( 0, 0, 1 )
14 | };
15 |
16 | var plane = new THREE.Mesh( new THREE.PlaneGeometry( 100, 100, 4, 4 ), CAPS.MATERIAL.Invisible );
17 | simulation.scene.add( plane );
18 |
19 | var targeting = function ( event ) {
20 |
21 | mouse.setToNormalizedDeviceCoordinates( event, window );
22 |
23 | ray.setFromCamera( mouse, simulation.camera );
24 |
25 | var intersects = ray.intersectObjects( simulation.selection.selectables );
26 |
27 | if ( intersects.length > 0 ) {
28 |
29 | var candidate = intersects[ 0 ].object;
30 |
31 | if ( intersected !== candidate ) {
32 |
33 | if ( intersected !== null ) {
34 | intersected.guardian.rayOut();
35 | }
36 |
37 | candidate.guardian.rayOver();
38 |
39 | intersected = candidate;
40 |
41 | simulation.renderer.domElement.style.cursor = 'pointer';
42 | simulation.throttledRender();
43 |
44 | }
45 |
46 | } else if ( intersected !== null ) {
47 |
48 | intersected.guardian.rayOut();
49 | intersected = null;
50 |
51 | simulation.renderer.domElement.style.cursor = 'auto';
52 | simulation.throttledRender();
53 |
54 | }
55 |
56 | };
57 |
58 | var beginDrag = function ( event ) {
59 |
60 | mouse.setToNormalizedDeviceCoordinates( event, window );
61 |
62 | ray.setFromCamera( mouse, simulation.camera );
63 |
64 | var intersects = ray.intersectObjects( simulation.selection.selectables );
65 |
66 | if ( intersects.length > 0 ) {
67 |
68 | event.preventDefault();
69 | event.stopPropagation();
70 |
71 | simulation.controls.enabled = false;
72 |
73 | var intersectionPoint = intersects[ 0 ].point;
74 |
75 | var axis = intersects[ 0 ].object.axis;
76 |
77 | if ( axis === 'x1' || axis === 'x2' ) {
78 | intersectionPoint.setX( 0 );
79 | } else if ( axis === 'y1' || axis === 'y2' ) {
80 | intersectionPoint.setY( 0 );
81 | } else if ( axis === 'z1' || axis === 'z2' ) {
82 | intersectionPoint.setZ( 0 );
83 | }
84 | plane.position.copy( intersectionPoint );
85 |
86 | var newNormal = simulation.camera.position.clone().sub(
87 | simulation.camera.position.clone().projectOnVector( normals[ axis ] )
88 | );
89 | plane.lookAt( newNormal.add( intersectionPoint ) );
90 |
91 | simulation.renderer.domElement.style.cursor = 'move';
92 | simulation.throttledRender();
93 |
94 | var continueDrag = function ( event ) {
95 |
96 | event.preventDefault();
97 | event.stopPropagation();
98 |
99 | mouse.setToNormalizedDeviceCoordinates( event, window );
100 |
101 | ray.setFromCamera( mouse, simulation.camera );
102 |
103 | var intersects = ray.intersectObject( plane );
104 |
105 | if ( intersects.length > 0 ) {
106 |
107 | if ( axis === 'x1' || axis === 'x2' ) {
108 | value = intersects[ 0 ].point.x;
109 | } else if ( axis === 'y1' || axis === 'y2' ) {
110 | value = intersects[ 0 ].point.y;
111 | } else if ( axis === 'z1' || axis === 'z2' ) {
112 | value = intersects[ 0 ].point.z;
113 | }
114 |
115 | simulation.selection.setValue( axis, value );
116 | simulation.throttledRender();
117 |
118 | }
119 |
120 | };
121 |
122 | var endDrag = function ( event ) {
123 |
124 | simulation.controls.enabled = true;
125 |
126 | simulation.renderer.domElement.style.cursor = 'pointer';
127 |
128 | document.removeEventListener( 'mousemove', continueDrag, true );
129 | document.removeEventListener( 'touchmove', continueDrag, true );
130 |
131 | document.removeEventListener( 'mouseup', endDrag, false );
132 | document.removeEventListener( 'touchend', endDrag, false );
133 | document.removeEventListener( 'touchcancel', endDrag, false );
134 | document.removeEventListener( 'touchleave', endDrag, false );
135 |
136 | };
137 |
138 | document.addEventListener( 'mousemove', continueDrag, true );
139 | document.addEventListener( 'touchmove', continueDrag, true );
140 |
141 | document.addEventListener( 'mouseup', endDrag, false );
142 | document.addEventListener( 'touchend', endDrag, false );
143 | document.addEventListener( 'touchcancel', endDrag, false );
144 | document.addEventListener( 'touchleave', endDrag, false );
145 |
146 | }
147 |
148 | };
149 |
150 | simulation.renderer.domElement.addEventListener( 'mousemove', targeting, true );
151 | simulation.renderer.domElement.addEventListener( 'mousedown', beginDrag, false );
152 | simulation.renderer.domElement.addEventListener( 'touchstart', beginDrag, false );
153 |
154 | };
155 |
156 |
--------------------------------------------------------------------------------
/js/planeGeometry.js:
--------------------------------------------------------------------------------
1 | CAPS.PlaneGeometry = function ( v0, v1, v2, v3 ) {
2 |
3 | THREE.Geometry.call( this );
4 |
5 | this.vertices.push( v0, v1, v2, v3 );
6 | this.faces.push( new THREE.Face3( 0, 1, 2 ) );
7 | this.faces.push( new THREE.Face3( 0, 2, 3 ) );
8 |
9 | this.computeFaceNormals();
10 | this.computeVertexNormals();
11 |
12 | };
13 |
14 | CAPS.PlaneGeometry.prototype = new THREE.Geometry();
15 | CAPS.PlaneGeometry.prototype.constructor = CAPS.PlaneGeometry;
16 |
17 |
--------------------------------------------------------------------------------
/js/schedule.js:
--------------------------------------------------------------------------------
1 | CAPS.SCHEDULE = {
2 |
3 | postpone: function ( callback, context, wait ) {
4 |
5 | return function () {
6 | setTimeout( function () {
7 | callback.apply( context, arguments );
8 | }, wait );
9 | };
10 |
11 | },
12 |
13 | deferringThrottle: function ( callback, context, wait ) { // wait 60 = 16fps // wait 40 = 25fps // wait 20 = 50fps
14 |
15 | var execute = function ( arguments ) {
16 | callback.apply( context, arguments );
17 | setTimeout( function () {
18 | if ( deferredCalls ) {
19 | deferredCalls = false;
20 | execute( args );
21 | } else {
22 | blocked = false;
23 | }
24 | }, wait );
25 | };
26 |
27 | var blocked = false;
28 | var deferredCalls = false;
29 | var args = undefined;
30 |
31 | return function () {
32 | if ( blocked ) {
33 | args = arguments;
34 | deferredCalls = true;
35 | return;
36 | } else {
37 | blocked = true;
38 | deferredCalls = false;
39 | execute( arguments );
40 | }
41 | };
42 |
43 | }
44 |
45 | };
46 |
47 |
--------------------------------------------------------------------------------
/js/selection.js:
--------------------------------------------------------------------------------
1 | CAPS.Selection = function ( low, high ) {
2 |
3 | this.limitLow = low;
4 | this.limitHigh = high;
5 |
6 | this.box = new THREE.BoxGeometry( 1, 1, 1 );
7 | this.boxMesh = new THREE.Mesh( this.box, CAPS.MATERIAL.cap );
8 |
9 | this.vertices = [
10 | new THREE.Vector3(), new THREE.Vector3(),
11 | new THREE.Vector3(), new THREE.Vector3(),
12 | new THREE.Vector3(), new THREE.Vector3(),
13 | new THREE.Vector3(), new THREE.Vector3()
14 | ];
15 | this.updateVertices();
16 |
17 | var v = this.vertices;
18 |
19 | this.touchMeshes = new THREE.Object3D();
20 | this.displayMeshes = new THREE.Object3D();
21 | this.meshGeometries = [];
22 | this.lineGeometries = [];
23 | this.selectables = [];
24 |
25 | this.faces = [];
26 | var f = this.faces;
27 | this.faces.push( new CAPS.SelectionBoxFace( 'y1', v[ 0 ], v[ 1 ], v[ 5 ], v[ 4 ], this ) );
28 | this.faces.push( new CAPS.SelectionBoxFace( 'z1', v[ 0 ], v[ 2 ], v[ 3 ], v[ 1 ], this ) );
29 | this.faces.push( new CAPS.SelectionBoxFace( 'x1', v[ 0 ], v[ 4 ], v[ 6 ], v[ 2 ], this ) );
30 | this.faces.push( new CAPS.SelectionBoxFace( 'x2', v[ 7 ], v[ 5 ], v[ 1 ], v[ 3 ], this ) );
31 | this.faces.push( new CAPS.SelectionBoxFace( 'y2', v[ 7 ], v[ 3 ], v[ 2 ], v[ 6 ], this ) );
32 | this.faces.push( new CAPS.SelectionBoxFace( 'z2', v[ 7 ], v[ 6 ], v[ 4 ], v[ 5 ], this ) );
33 |
34 | var l0 = new CAPS.SelectionBoxLine( v[ 0 ], v[ 1 ], f[ 0 ], f[ 1 ], this );
35 | var l1 = new CAPS.SelectionBoxLine( v[ 0 ], v[ 2 ], f[ 1 ], f[ 2 ], this );
36 | var l2 = new CAPS.SelectionBoxLine( v[ 0 ], v[ 4 ], f[ 0 ], f[ 2 ], this );
37 | var l3 = new CAPS.SelectionBoxLine( v[ 1 ], v[ 3 ], f[ 1 ], f[ 3 ], this );
38 | var l4 = new CAPS.SelectionBoxLine( v[ 1 ], v[ 5 ], f[ 0 ], f[ 3 ], this );
39 | var l5 = new CAPS.SelectionBoxLine( v[ 2 ], v[ 3 ], f[ 1 ], f[ 4 ], this );
40 | var l6 = new CAPS.SelectionBoxLine( v[ 2 ], v[ 6 ], f[ 2 ], f[ 4 ], this );
41 | var l7 = new CAPS.SelectionBoxLine( v[ 3 ], v[ 7 ], f[ 3 ], f[ 4 ], this );
42 | var l8 = new CAPS.SelectionBoxLine( v[ 4 ], v[ 5 ], f[ 0 ], f[ 5 ], this );
43 | var l9 = new CAPS.SelectionBoxLine( v[ 4 ], v[ 6 ], f[ 2 ], f[ 5 ], this );
44 | var l10 = new CAPS.SelectionBoxLine( v[ 5 ], v[ 7 ], f[ 3 ], f[ 5 ], this );
45 | var l11 = new CAPS.SelectionBoxLine( v[ 6 ], v[ 7 ], f[ 4 ], f[ 5 ], this );
46 |
47 | this.setBox();
48 | this.setUniforms();
49 |
50 | };
51 |
52 | CAPS.Selection.prototype = {
53 |
54 | constructor: CAPS.Selection,
55 |
56 | updateVertices: function () {
57 |
58 | this.vertices[ 0 ].set( this.limitLow.x, this.limitLow.y, this.limitLow.z );
59 | this.vertices[ 1 ].set( this.limitHigh.x, this.limitLow.y, this.limitLow.z );
60 | this.vertices[ 2 ].set( this.limitLow.x, this.limitHigh.y, this.limitLow.z );
61 | this.vertices[ 3 ].set( this.limitHigh.x, this.limitHigh.y, this.limitLow.z );
62 | this.vertices[ 4 ].set( this.limitLow.x, this.limitLow.y, this.limitHigh.z );
63 | this.vertices[ 5 ].set( this.limitHigh.x, this.limitLow.y, this.limitHigh.z );
64 | this.vertices[ 6 ].set( this.limitLow.x, this.limitHigh.y, this.limitHigh.z );
65 | this.vertices[ 7 ].set( this.limitHigh.x, this.limitHigh.y, this.limitHigh.z );
66 |
67 | },
68 |
69 | updateGeometries: function () {
70 |
71 | for ( var i = 0; i < this.meshGeometries.length; i++ ) {
72 | this.meshGeometries[ i ].verticesNeedUpdate = true;
73 | this.meshGeometries[ i ].computeBoundingSphere();
74 | this.meshGeometries[ i ].computeBoundingBox();
75 | }
76 | for ( var i = 0; i < this.lineGeometries.length; i++ ) {
77 | this.lineGeometries[ i ].verticesNeedUpdate = true;
78 | }
79 |
80 | },
81 |
82 | setBox: function () {
83 |
84 | var width = new THREE.Vector3();
85 | width.subVectors( this.limitHigh, this.limitLow );
86 |
87 | this.boxMesh.scale.copy( width );
88 | width.multiplyScalar( 0.5 ).add( this.limitLow );
89 | this.boxMesh.position.copy( width );
90 |
91 | },
92 |
93 | setUniforms: function () {
94 |
95 | var uniforms = CAPS.UNIFORMS.clipping;
96 | uniforms.clippingLow.value.copy( this.limitLow );
97 | uniforms.clippingHigh.value.copy( this.limitHigh );
98 |
99 | },
100 |
101 | setValue: function ( axis, value ) {
102 |
103 | var buffer = 0.4;
104 | var limit = 14;
105 |
106 | if ( axis === 'x1' ) {
107 | this.limitLow.x = Math.max( -limit, Math.min( this.limitHigh.x-buffer, value ) );
108 | } else if ( axis === 'x2' ) {
109 | this.limitHigh.x = Math.max( this.limitLow.x+buffer, Math.min( limit, value ) );
110 | } else if ( axis === 'y1' ) {
111 | this.limitLow.y = Math.max( -limit, Math.min( this.limitHigh.y-buffer, value ) );
112 | } else if ( axis === 'y2' ) {
113 | this.limitHigh.y = Math.max( this.limitLow.y+buffer, Math.min( limit, value ) );
114 | } else if ( axis === 'z1' ) {
115 | this.limitLow.z = Math.max( -limit, Math.min( this.limitHigh.z-buffer, value ) );
116 | } else if ( axis === 'z2' ) {
117 | this.limitHigh.z = Math.max( this.limitLow.z+buffer, Math.min( limit, value ) );
118 | }
119 |
120 | this.setBox();
121 | this.setUniforms();
122 |
123 | this.updateVertices();
124 | this.updateGeometries();
125 |
126 | }
127 |
128 | };
129 |
130 |
--------------------------------------------------------------------------------
/js/selectionBoxFace.js:
--------------------------------------------------------------------------------
1 | CAPS.SelectionBoxFace = function ( axis, v0, v1, v2, v3, selection ) {
2 |
3 | var frontFaceGeometry = new CAPS.PlaneGeometry( v0, v1, v2, v3 );
4 | frontFaceGeometry.dynamic = true;
5 | selection.meshGeometries.push( frontFaceGeometry );
6 |
7 | var frontFaceMesh = new THREE.Mesh( frontFaceGeometry, CAPS.MATERIAL.Invisible );
8 | frontFaceMesh.axis = axis;
9 | frontFaceMesh.guardian = this;
10 | selection.touchMeshes.add( frontFaceMesh );
11 | selection.selectables.push( frontFaceMesh );
12 |
13 | var backFaceGeometry = new CAPS.PlaneGeometry( v3, v2, v1, v0 );
14 | backFaceGeometry.dynamic = true;
15 | selection.meshGeometries.push( backFaceGeometry );
16 |
17 | var backFaceMesh = new THREE.Mesh( backFaceGeometry, CAPS.MATERIAL.BoxBackFace );
18 | selection.displayMeshes.add( backFaceMesh );
19 |
20 | this.lines = new Array();
21 |
22 | };
23 |
24 | CAPS.SelectionBoxFace.prototype = {
25 |
26 | constructor: CAPS.SelectionBoxFace,
27 |
28 | rayOver: function () {
29 | this.highlightLines( true );
30 | },
31 |
32 | rayOut: function () {
33 | this.highlightLines( false );
34 | },
35 |
36 | highlightLines: function ( b ) {
37 | for ( var i = 0; i < this.lines.length; i++ ) {
38 | this.lines[ i ].setHighlight( b );
39 | }
40 | }
41 |
42 | };
43 |
44 |
--------------------------------------------------------------------------------
/js/selectionBoxLine.js:
--------------------------------------------------------------------------------
1 | CAPS.SelectionBoxLine = function ( v0, v1, f0, f1, selection ) {
2 |
3 | var lineGeometry = new THREE.Geometry();
4 | lineGeometry.vertices.push( v0, v1 );
5 | lineGeometry.computeLineDistances();
6 | lineGeometry.dynamic = true;
7 | selection.lineGeometries.push( lineGeometry );
8 |
9 | this.line = new THREE.LineSegments( lineGeometry, CAPS.MATERIAL.BoxWireframe );
10 | selection.displayMeshes.add( this.line );
11 |
12 | f0.lines.push( this );
13 | f1.lines.push( this );
14 |
15 | };
16 |
17 | CAPS.SelectionBoxLine.prototype = {
18 |
19 | constructor: CAPS.SelectionBoxLine,
20 |
21 | setHighlight: function ( b ) {
22 | this.line.material = b ? CAPS.MATERIAL.BoxWireActive : CAPS.MATERIAL.BoxWireframe;
23 | }
24 |
25 | };
26 |
27 |
--------------------------------------------------------------------------------
/js/shader.js:
--------------------------------------------------------------------------------
1 | CAPS.SHADER = {
2 |
3 | vertex: '\
4 | uniform vec3 color;\
5 | varying vec3 pixelNormal;\
6 | \
7 | void main() {\
8 | \
9 | pixelNormal = normal;\
10 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\
11 | \
12 | }',
13 |
14 | vertexClipping: '\
15 | uniform vec3 color;\
16 | uniform vec3 clippingLow;\
17 | uniform vec3 clippingHigh;\
18 | \
19 | varying vec3 pixelNormal;\
20 | varying vec4 worldPosition;\
21 | varying vec3 camPosition;\
22 | \
23 | void main() {\
24 | \
25 | pixelNormal = normal;\
26 | worldPosition = modelMatrix * vec4( position, 1.0 );\
27 | camPosition = cameraPosition;\
28 | \
29 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\
30 | \
31 | }',
32 |
33 | fragment: '\
34 | uniform vec3 color;\
35 | varying vec3 pixelNormal;\
36 | \
37 | void main( void ) {\
38 | \
39 | float shade = (\
40 | 3.0 * pow ( abs ( pixelNormal.y ), 2.0 )\
41 | + 2.0 * pow ( abs ( pixelNormal.z ), 2.0 )\
42 | + 1.0 * pow ( abs ( pixelNormal.x ), 2.0 )\
43 | ) / 3.0;\
44 | \
45 | gl_FragColor = vec4( color * shade, 1.0 );\
46 | \
47 | }',
48 |
49 | fragmentClipping: '\
50 | uniform vec3 color;\
51 | uniform vec3 clippingLow;\
52 | uniform vec3 clippingHigh;\
53 | \
54 | varying vec3 pixelNormal;\
55 | varying vec4 worldPosition;\
56 | \
57 | void main( void ) {\
58 | \
59 | float shade = (\
60 | 3.0 * pow ( abs ( pixelNormal.y ), 2.0 )\
61 | + 2.0 * pow ( abs ( pixelNormal.z ), 2.0 )\
62 | + 1.0 * pow ( abs ( pixelNormal.x ), 2.0 )\
63 | ) / 3.0;\
64 | \
65 | if (\
66 | worldPosition.x < clippingLow.x\
67 | || worldPosition.x > clippingHigh.x\
68 | || worldPosition.y < clippingLow.y\
69 | || worldPosition.y > clippingHigh.y\
70 | || worldPosition.z < clippingLow.z\
71 | || worldPosition.z > clippingHigh.z\
72 | ) {\
73 | \
74 | discard;\
75 | \
76 | } else {\
77 | \
78 | gl_FragColor = vec4( color * shade, 1.0 );\
79 | \
80 | }\
81 | \
82 | }',
83 |
84 | fragmentClippingFront: '\
85 | uniform vec3 color;\
86 | uniform vec3 clippingLow;\
87 | uniform vec3 clippingHigh;\
88 | \
89 | varying vec3 pixelNormal;\
90 | varying vec4 worldPosition;\
91 | varying vec3 camPosition;\
92 | \
93 | void main( void ) {\
94 | \
95 | float shade = (\
96 | 3.0 * pow ( abs ( pixelNormal.y ), 2.0 )\
97 | + 2.0 * pow ( abs ( pixelNormal.z ), 2.0 )\
98 | + 1.0 * pow ( abs ( pixelNormal.x ), 2.0 )\
99 | ) / 3.0;\
100 | \
101 | if (\
102 | worldPosition.x < clippingLow.x && camPosition.x < clippingLow.x\
103 | || worldPosition.x > clippingHigh.x && camPosition.x > clippingHigh.x\
104 | || worldPosition.y < clippingLow.y && camPosition.y < clippingLow.y\
105 | || worldPosition.y > clippingHigh.y && camPosition.y > clippingHigh.y\
106 | || worldPosition.z < clippingLow.z && camPosition.z < clippingLow.z\
107 | || worldPosition.z > clippingHigh.z && camPosition.z > clippingHigh.z\
108 | ) {\
109 | \
110 | discard;\
111 | \
112 | } else {\
113 | \
114 | gl_FragColor = vec4( color * shade, 1.0 );\
115 | \
116 | }\
117 | \
118 | }',
119 |
120 | invisibleVertexShader: '\
121 | void main() {\
122 | vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\
123 | gl_Position = projectionMatrix * mvPosition;\
124 | }',
125 |
126 | invisibleFragmentShader: '\
127 | void main( void ) {\
128 | gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\
129 | discard;\
130 | }'
131 |
132 | };
133 |
134 |
--------------------------------------------------------------------------------
/js/simulation.js:
--------------------------------------------------------------------------------
1 | CAPS.Simulation = function () {
2 |
3 | this.scene = undefined;
4 | this.capsScene = undefined;
5 | this.backStencil = undefined;
6 | this.frontStencil = undefined;
7 |
8 | this.camera = undefined;
9 | this.renderer = undefined;
10 | this.controls = undefined;
11 |
12 | this.showCaps = true;
13 |
14 | this.init();
15 |
16 | };
17 |
18 | CAPS.Simulation.prototype = {
19 |
20 | constructor: CAPS.Simulation,
21 |
22 | init: function () {
23 |
24 | var self = this;
25 |
26 | var loader = new THREE.ColladaLoader();
27 | loader.options.convertUpAxis = true;
28 | loader.load( './models/house.dae', function ( collada ) {
29 | self.initScene( collada.scene );
30 | } );
31 |
32 | var container = document.createElement( 'div' );
33 | document.body.appendChild( container );
34 |
35 | this.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
36 | this.camera.position.set( 20, 20, 30 );
37 | this.camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
38 |
39 | this.scene = new THREE.Scene();
40 | this.capsScene = new THREE.Scene();
41 | this.backStencil = new THREE.Scene();
42 | this.frontStencil = new THREE.Scene();
43 |
44 | this.selection = new CAPS.Selection(
45 | new THREE.Vector3( -7, -14, -14 ),
46 | new THREE.Vector3( 14, 9, 3 )
47 | );
48 | this.capsScene.add( this.selection.boxMesh );
49 | this.scene.add( this.selection.touchMeshes );
50 | this.scene.add( this.selection.displayMeshes );
51 |
52 | this.renderer = new THREE.WebGLRenderer( { antialias: true } );
53 | this.renderer.setPixelRatio( window.devicePixelRatio );
54 | this.renderer.setSize( window.innerWidth, window.innerHeight );
55 | this.renderer.setClearColor( 0xffffff );
56 | this.renderer.autoClear = false;
57 | container.appendChild( this.renderer.domElement );
58 |
59 | var throttledRender = CAPS.SCHEDULE.deferringThrottle( this._render, this, 40 );
60 | this.throttledRender = throttledRender;
61 |
62 | CAPS.picking( this ); // must come before OrbitControls, so it can cancel them
63 |
64 | this.controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
65 | this.controls.addEventListener( 'change', throttledRender );
66 |
67 | var onWindowResize = function () {
68 | self.camera.aspect = window.innerWidth / window.innerHeight;
69 | self.camera.updateProjectionMatrix();
70 | self.renderer.setSize( window.innerWidth, window.innerHeight );
71 | throttledRender();
72 | };
73 | window.addEventListener( 'resize', onWindowResize, false );
74 |
75 | var showCapsInput = document.getElementById( 'showCaps' );
76 | this.showCaps = showCapsInput.checked;
77 | var onShowCaps = function () {
78 | self.showCaps = showCapsInput.checked;
79 | throttledRender();
80 | };
81 | showCapsInput.addEventListener( 'change', onShowCaps, false );
82 |
83 | throttledRender();
84 |
85 | },
86 |
87 | initScene: function ( collada ) {
88 |
89 | var setMaterial = function ( node, material ) {
90 | node.material = material;
91 | if ( node.children ) {
92 | for ( var i = 0; i < node.children.length; i++ ) {
93 | setMaterial( node.children[i], material );
94 | }
95 | }
96 | };
97 |
98 | var back = collada.clone();
99 | setMaterial( back, CAPS.MATERIAL.backStencil );
100 | back.scale.set( 0.03, 0.03, 0.03 );
101 | back.updateMatrix();
102 | this.backStencil.add( back );
103 |
104 | var front = collada.clone();
105 | setMaterial( front, CAPS.MATERIAL.frontStencil );
106 | front.scale.set( 0.03, 0.03, 0.03 );
107 | front.updateMatrix();
108 | this.frontStencil.add( front );
109 |
110 | setMaterial( collada, CAPS.MATERIAL.sheet );
111 | collada.scale.set( 0.03, 0.03, 0.03 );
112 | collada.updateMatrix();
113 | this.scene.add( collada );
114 |
115 | this.throttledRender();
116 |
117 | },
118 |
119 | _render: function () {
120 |
121 | this.renderer.clear();
122 |
123 | var gl = this.renderer.context;
124 |
125 | if ( this.showCaps ) {
126 |
127 | this.renderer.state.setStencilTest( true );
128 |
129 | this.renderer.state.setStencilFunc( gl.ALWAYS, 1, 0xff );
130 | this.renderer.state.setStencilOp( gl.KEEP, gl.KEEP, gl.INCR );
131 | this.renderer.render( this.backStencil, this.camera );
132 |
133 | this.renderer.state.setStencilFunc( gl.ALWAYS, 1, 0xff );
134 | this.renderer.state.setStencilOp( gl.KEEP, gl.KEEP, gl.DECR );
135 | this.renderer.render( this.frontStencil, this.camera );
136 |
137 | this.renderer.state.setStencilFunc( gl.EQUAL, 1, 0xff );
138 | this.renderer.state.setStencilOp( gl.KEEP, gl.KEEP, gl.KEEP );
139 | this.renderer.render( this.capsScene, this.camera );
140 |
141 | this.renderer.state.setStencilTest( false );
142 |
143 | }
144 |
145 | this.renderer.render( this.scene, this.camera );
146 |
147 | }
148 |
149 | };
150 |
151 |
--------------------------------------------------------------------------------
/js/threeExtensions.js:
--------------------------------------------------------------------------------
1 | // sets this vector to the coordinates of a mouse event, uses touch event if applicable
2 | THREE.Vector2.prototype.setFromEvent = function ( event ) {
3 |
4 | this.x = ( event.clientX !== undefined ) ? event.clientX : ( event.touches && event.touches[ 0 ].clientX );
5 | this.y = ( event.clientY !== undefined ) ? event.clientY : ( event.touches && event.touches[ 0 ].clientY );
6 | return this;
7 |
8 | };
9 |
10 | // calculate mouse position in normalized device coordinates
11 | THREE.Vector2.prototype.setToNormalizedDeviceCoordinates = function ( event, window ) {
12 |
13 | this.setFromEvent( event );
14 | this.x = ( this.x / window.innerWidth ) * 2 - 1;
15 | this.y = - ( this.y / window.innerHeight ) * 2 + 1;
16 | return this;
17 |
18 | };
19 |
20 |
--------------------------------------------------------------------------------
/js/uniforms.js:
--------------------------------------------------------------------------------
1 | CAPS.UNIFORMS = {
2 |
3 | clipping: {
4 | color: { type: "c", value: new THREE.Color( 0x3d9ecb ) },
5 | clippingLow: { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) },
6 | clippingHigh: { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }
7 | },
8 |
9 | caps: {
10 | color: { type: "c", value: new THREE.Color( 0xf83610 ) }
11 | }
12 |
13 | };
14 |
15 |
--------------------------------------------------------------------------------
/lib/ColladaLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com
3 | * @author Tony Parisi / http://www.tonyparisi.com/
4 | */
5 |
6 | THREE.ColladaLoader = function () {
7 |
8 | var COLLADA = null;
9 | var scene = null;
10 | var visualScene;
11 | var kinematicsModel;
12 |
13 | var readyCallbackFunc = null;
14 |
15 | var sources = {};
16 | var images = {};
17 | var animations = {};
18 | var controllers = {};
19 | var geometries = {};
20 | var materials = {};
21 | var effects = {};
22 | var cameras = {};
23 | var lights = {};
24 |
25 | var animData;
26 | var kinematics;
27 | var visualScenes;
28 | var kinematicsModels;
29 | var baseUrl;
30 | var morphs;
31 | var skins;
32 |
33 | var flip_uv = true;
34 | var preferredShading = THREE.SmoothShading;
35 |
36 | var options = {
37 | // Force Geometry to always be centered at the local origin of the
38 | // containing Mesh.
39 | centerGeometry: false,
40 |
41 | // Axis conversion is done for geometries, animations, and controllers.
42 | // If we ever pull cameras or lights out of the COLLADA file, they'll
43 | // need extra work.
44 | convertUpAxis: false,
45 |
46 | subdivideFaces: true,
47 |
48 | upAxis: 'Y',
49 |
50 | // For reflective or refractive materials we'll use this cubemap
51 | defaultEnvMap: null
52 |
53 | };
54 |
55 | var colladaUnit = 1.0;
56 | var colladaUp = 'Y';
57 | var upConversion = null;
58 |
59 | function load ( url, readyCallback, progressCallback, failCallback ) {
60 |
61 | var length = 0;
62 |
63 | if ( document.implementation && document.implementation.createDocument ) {
64 |
65 | var request = new XMLHttpRequest();
66 |
67 | request.onreadystatechange = function() {
68 |
69 | if ( request.readyState === 4 ) {
70 |
71 | if ( request.status === 0 || request.status === 200 ) {
72 |
73 | if ( request.response ) {
74 |
75 | readyCallbackFunc = readyCallback;
76 | parse( request.response, undefined, url );
77 |
78 | } else {
79 |
80 | if ( failCallback ) {
81 |
82 | failCallback( { type: 'error', url: url } );
83 |
84 | } else {
85 |
86 | console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" );
87 |
88 | }
89 |
90 | }
91 |
92 | }else{
93 |
94 | if( failCallback ){
95 |
96 | failCallback( { type: 'error', url: url } );
97 |
98 | }else{
99 |
100 | console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' );
101 |
102 | }
103 |
104 | }
105 |
106 | } else if ( request.readyState === 3 ) {
107 |
108 | if ( progressCallback ) {
109 |
110 | if ( length === 0 ) {
111 |
112 | length = request.getResponseHeader( "Content-Length" );
113 |
114 | }
115 |
116 | progressCallback( { total: length, loaded: request.responseText.length } );
117 |
118 | }
119 |
120 | }
121 |
122 | };
123 |
124 | request.open( "GET", url, true );
125 | request.send( null );
126 |
127 | } else {
128 |
129 | alert( "Don't know how to parse XML!" );
130 |
131 | }
132 |
133 | }
134 |
135 | function parse( text, callBack, url ) {
136 |
137 | COLLADA = new DOMParser().parseFromString( text, 'text/xml' );
138 | callBack = callBack || readyCallbackFunc;
139 |
140 | if ( url !== undefined ) {
141 |
142 | var parts = url.split( '/' );
143 | parts.pop();
144 | baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
145 |
146 | }
147 |
148 | parseAsset();
149 | setUpConversion();
150 | images = parseLib( "library_images image", _Image, "image" );
151 | materials = parseLib( "library_materials material", Material, "material" );
152 | effects = parseLib( "library_effects effect", Effect, "effect" );
153 | geometries = parseLib( "library_geometries geometry", Geometry, "geometry" );
154 | cameras = parseLib( "library_cameras camera", Camera, "camera" );
155 | lights = parseLib( "library_lights light", Light, "light" );
156 | controllers = parseLib( "library_controllers controller", Controller, "controller" );
157 | animations = parseLib( "library_animations animation", Animation, "animation" );
158 | visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" );
159 | kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" );
160 |
161 | morphs = [];
162 | skins = [];
163 |
164 | visualScene = parseScene();
165 | scene = new THREE.Group();
166 |
167 | for ( var i = 0; i < visualScene.nodes.length; i ++ ) {
168 |
169 | scene.add( createSceneGraph( visualScene.nodes[ i ] ) );
170 |
171 | }
172 |
173 | // unit conversion
174 | scene.scale.multiplyScalar( colladaUnit );
175 |
176 | createAnimations();
177 |
178 | kinematicsModel = parseKinematicsModel();
179 | createKinematics();
180 |
181 | var result = {
182 |
183 | scene: scene,
184 | morphs: morphs,
185 | skins: skins,
186 | animations: animData,
187 | kinematics: kinematics,
188 | dae: {
189 | images: images,
190 | materials: materials,
191 | cameras: cameras,
192 | lights: lights,
193 | effects: effects,
194 | geometries: geometries,
195 | controllers: controllers,
196 | animations: animations,
197 | visualScenes: visualScenes,
198 | visualScene: visualScene,
199 | scene: visualScene,
200 | kinematicsModels: kinematicsModels,
201 | kinematicsModel: kinematicsModel
202 | }
203 |
204 | };
205 |
206 | if ( callBack ) {
207 |
208 | callBack( result );
209 |
210 | }
211 |
212 | return result;
213 |
214 | }
215 |
216 | function setPreferredShading ( shading ) {
217 |
218 | preferredShading = shading;
219 |
220 | }
221 |
222 | function parseAsset () {
223 |
224 | var elements = COLLADA.querySelectorAll('asset');
225 |
226 | var element = elements[0];
227 |
228 | if ( element && element.childNodes ) {
229 |
230 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
231 |
232 | var child = element.childNodes[ i ];
233 |
234 | switch ( child.nodeName ) {
235 |
236 | case 'unit':
237 |
238 | var meter = child.getAttribute( 'meter' );
239 |
240 | if ( meter ) {
241 |
242 | colladaUnit = parseFloat( meter );
243 |
244 | }
245 |
246 | break;
247 |
248 | case 'up_axis':
249 |
250 | colladaUp = child.textContent.charAt(0);
251 | break;
252 |
253 | }
254 |
255 | }
256 |
257 | }
258 |
259 | }
260 |
261 | function parseLib ( q, classSpec, prefix ) {
262 |
263 | var elements = COLLADA.querySelectorAll(q);
264 |
265 | var lib = {};
266 |
267 | var i = 0;
268 |
269 | var elementsLength = elements.length;
270 |
271 | for ( var j = 0; j < elementsLength; j ++ ) {
272 |
273 | var element = elements[j];
274 | var daeElement = ( new classSpec() ).parse( element );
275 |
276 | if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ );
277 | lib[ daeElement.id ] = daeElement;
278 |
279 | }
280 |
281 | return lib;
282 |
283 | }
284 |
285 | function parseScene() {
286 |
287 | var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0];
288 |
289 | if ( sceneElement ) {
290 |
291 | var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' );
292 | return visualScenes[ url.length > 0 ? url : 'visual_scene0' ];
293 |
294 | } else {
295 |
296 | return null;
297 |
298 | }
299 |
300 | }
301 |
302 | function parseKinematicsModel() {
303 |
304 | var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0];
305 |
306 | if ( kinematicsModelElement ) {
307 |
308 | var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, '');
309 | return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ];
310 |
311 | } else {
312 |
313 | return null;
314 |
315 | }
316 |
317 | }
318 |
319 | function createAnimations() {
320 |
321 | animData = [];
322 |
323 | // fill in the keys
324 | recurseHierarchy( scene );
325 |
326 | }
327 |
328 | function recurseHierarchy( node ) {
329 |
330 | var n = visualScene.getChildById( node.colladaId, true ),
331 | newData = null;
332 |
333 | if ( n && n.keys ) {
334 |
335 | newData = {
336 | fps: 60,
337 | hierarchy: [ {
338 | node: n,
339 | keys: n.keys,
340 | sids: n.sids
341 | } ],
342 | node: node,
343 | name: 'animation_' + node.name,
344 | length: 0
345 | };
346 |
347 | animData.push(newData);
348 |
349 | for ( var i = 0, il = n.keys.length; i < il; i ++ ) {
350 |
351 | newData.length = Math.max( newData.length, n.keys[i].time );
352 |
353 | }
354 |
355 | } else {
356 |
357 | newData = {
358 | hierarchy: [ {
359 | keys: [],
360 | sids: []
361 | } ]
362 | }
363 |
364 | }
365 |
366 | for ( var i = 0, il = node.children.length; i < il; i ++ ) {
367 |
368 | var d = recurseHierarchy( node.children[i] );
369 |
370 | for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) {
371 |
372 | newData.hierarchy.push( {
373 | keys: [],
374 | sids: []
375 | } );
376 |
377 | }
378 |
379 | }
380 |
381 | return newData;
382 |
383 | }
384 |
385 | function calcAnimationBounds () {
386 |
387 | var start = 1000000;
388 | var end = -start;
389 | var frames = 0;
390 | var ID;
391 | for ( var id in animations ) {
392 |
393 | var animation = animations[ id ];
394 | ID = ID || animation.id;
395 | for ( var i = 0; i < animation.sampler.length; i ++ ) {
396 |
397 | var sampler = animation.sampler[ i ];
398 |
399 | sampler.create();
400 |
401 | start = Math.min( start, sampler.startTime );
402 | end = Math.max( end, sampler.endTime );
403 | frames = Math.max( frames, sampler.input.length );
404 |
405 | }
406 |
407 | }
408 |
409 | return { start:start, end:end, frames:frames,ID:ID };
410 |
411 | }
412 |
413 | function createMorph ( geometry, ctrl ) {
414 |
415 | var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl;
416 |
417 | if ( !morphCtrl || !morphCtrl.morph ) {
418 |
419 | console.log("could not find morph controller!");
420 | return;
421 |
422 | }
423 |
424 | var morph = morphCtrl.morph;
425 |
426 | for ( var i = 0; i < morph.targets.length; i ++ ) {
427 |
428 | var target_id = morph.targets[ i ];
429 | var daeGeometry = geometries[ target_id ];
430 |
431 | if ( !daeGeometry.mesh ||
432 | !daeGeometry.mesh.primitives ||
433 | !daeGeometry.mesh.primitives.length ) {
434 | continue;
435 | }
436 |
437 | var target = daeGeometry.mesh.primitives[ 0 ].geometry;
438 |
439 | if ( target.vertices.length === geometry.vertices.length ) {
440 |
441 | geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } );
442 |
443 | }
444 |
445 | }
446 |
447 | geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } );
448 |
449 | }
450 |
451 | function createSkin ( geometry, ctrl, applyBindShape ) {
452 |
453 | var skinCtrl = controllers[ ctrl.url ];
454 |
455 | if ( !skinCtrl || !skinCtrl.skin ) {
456 |
457 | console.log( "could not find skin controller!" );
458 | return;
459 |
460 | }
461 |
462 | if ( !ctrl.skeleton || !ctrl.skeleton.length ) {
463 |
464 | console.log( "could not find the skeleton for the skin!" );
465 | return;
466 |
467 | }
468 |
469 | var skin = skinCtrl.skin;
470 | var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] );
471 | var hierarchy = [];
472 |
473 | applyBindShape = applyBindShape !== undefined ? applyBindShape : true;
474 |
475 | var bones = [];
476 | geometry.skinWeights = [];
477 | geometry.skinIndices = [];
478 |
479 | //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 );
480 | //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights );
481 |
482 | /*
483 | geometry.animation = {
484 | name: 'take_001',
485 | fps: 30,
486 | length: 2,
487 | JIT: true,
488 | hierarchy: hierarchy
489 | };
490 | */
491 |
492 | if ( applyBindShape ) {
493 |
494 | for ( var i = 0; i < geometry.vertices.length; i ++ ) {
495 |
496 | geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix );
497 |
498 | }
499 |
500 | }
501 |
502 | }
503 |
504 | function setupSkeleton ( node, bones, frame, parent ) {
505 |
506 | node.world = node.world || new THREE.Matrix4();
507 | node.localworld = node.localworld || new THREE.Matrix4();
508 | node.world.copy( node.matrix );
509 | node.localworld.copy( node.matrix );
510 |
511 | if ( node.channels && node.channels.length ) {
512 |
513 | var channel = node.channels[ 0 ];
514 | var m = channel.sampler.output[ frame ];
515 |
516 | if ( m instanceof THREE.Matrix4 ) {
517 |
518 | node.world.copy( m );
519 | node.localworld.copy(m);
520 | if (frame === 0)
521 | node.matrix.copy(m);
522 | }
523 |
524 | }
525 |
526 | if ( parent ) {
527 |
528 | node.world.multiplyMatrices( parent, node.world );
529 |
530 | }
531 |
532 | bones.push( node );
533 |
534 | for ( var i = 0; i < node.nodes.length; i ++ ) {
535 |
536 | setupSkeleton( node.nodes[ i ], bones, frame, node.world );
537 |
538 | }
539 |
540 | }
541 |
542 | function setupSkinningMatrices ( bones, skin ) {
543 |
544 | // FIXME: this is dumb...
545 |
546 | for ( var i = 0; i < bones.length; i ++ ) {
547 |
548 | var bone = bones[ i ];
549 | var found = -1;
550 |
551 | if ( bone.type != 'JOINT' ) continue;
552 |
553 | for ( var j = 0; j < skin.joints.length; j ++ ) {
554 |
555 | if ( bone.sid === skin.joints[ j ] ) {
556 |
557 | found = j;
558 | break;
559 |
560 | }
561 |
562 | }
563 |
564 | if ( found >= 0 ) {
565 |
566 | var inv = skin.invBindMatrices[ found ];
567 |
568 | bone.invBindMatrix = inv;
569 | bone.skinningMatrix = new THREE.Matrix4();
570 | bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi)
571 | bone.animatrix = new THREE.Matrix4();
572 |
573 | bone.animatrix.copy(bone.localworld);
574 | bone.weights = [];
575 |
576 | for ( var j = 0; j < skin.weights.length; j ++ ) {
577 |
578 | for (var k = 0; k < skin.weights[ j ].length; k ++ ) {
579 |
580 | var w = skin.weights[ j ][ k ];
581 |
582 | if ( w.joint === found ) {
583 |
584 | bone.weights.push( w );
585 |
586 | }
587 |
588 | }
589 |
590 | }
591 |
592 | } else {
593 |
594 | console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." );
595 |
596 | bone.skinningMatrix = new THREE.Matrix4();
597 | bone.weights = [];
598 |
599 | }
600 | }
601 |
602 | }
603 |
604 | //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix
605 | function flattenSkeleton(skeleton) {
606 |
607 | var list = [];
608 | var walk = function(parentid, node, list) {
609 |
610 | var bone = {};
611 | bone.name = node.sid;
612 | bone.parent = parentid;
613 | bone.matrix = node.matrix;
614 | var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ];
615 | bone.matrix.decompose(data[0], data[1], data[2]);
616 |
617 | bone.pos = [ data[0].x,data[0].y,data[0].z ];
618 |
619 | bone.scl = [ data[2].x,data[2].y,data[2].z ];
620 | bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ];
621 | list.push(bone);
622 |
623 | for (var i in node.nodes) {
624 |
625 | walk(node.sid, node.nodes[i], list);
626 |
627 | }
628 |
629 | };
630 |
631 | walk(-1, skeleton, list);
632 | return list;
633 |
634 | }
635 |
636 | //Move the vertices into the pose that is proper for the start of the animation
637 | function skinToBindPose(geometry,skeleton,skinController) {
638 |
639 | var bones = [];
640 | setupSkeleton( skeleton, bones, -1 );
641 | setupSkinningMatrices( bones, skinController.skin );
642 | var v = new THREE.Vector3();
643 | var skinned = [];
644 |
645 | for (var i = 0; i < geometry.vertices.length; i ++) {
646 |
647 | skinned.push(new THREE.Vector3());
648 |
649 | }
650 |
651 | for ( i = 0; i < bones.length; i ++ ) {
652 |
653 | if ( bones[ i ].type != 'JOINT' ) continue;
654 |
655 | for ( var j = 0; j < bones[ i ].weights.length; j ++ ) {
656 |
657 | var w = bones[ i ].weights[ j ];
658 | var vidx = w.index;
659 | var weight = w.weight;
660 |
661 | var o = geometry.vertices[vidx];
662 | var s = skinned[vidx];
663 |
664 | v.x = o.x;
665 | v.y = o.y;
666 | v.z = o.z;
667 |
668 | v.applyMatrix4( bones[i].skinningMatrix );
669 |
670 | s.x += (v.x * weight);
671 | s.y += (v.y * weight);
672 | s.z += (v.z * weight);
673 | }
674 |
675 | }
676 |
677 | for (var i = 0; i < geometry.vertices.length; i ++) {
678 |
679 | geometry.vertices[i] = skinned[i];
680 |
681 | }
682 |
683 | }
684 |
685 | function applySkin ( geometry, instanceCtrl, frame ) {
686 |
687 | var skinController = controllers[ instanceCtrl.url ];
688 |
689 | frame = frame !== undefined ? frame : 40;
690 |
691 | if ( !skinController || !skinController.skin ) {
692 |
693 | console.log( 'ColladaLoader: Could not find skin controller.' );
694 | return;
695 |
696 | }
697 |
698 | if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) {
699 |
700 | console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' );
701 | return;
702 |
703 | }
704 |
705 | var animationBounds = calcAnimationBounds();
706 | var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true );
707 |
708 | //flatten the skeleton into a list of bones
709 | var bonelist = flattenSkeleton(skeleton);
710 | var joints = skinController.skin.joints;
711 |
712 | //sort that list so that the order reflects the order in the joint list
713 | var sortedbones = [];
714 | for (var i = 0; i < joints.length; i ++) {
715 |
716 | for (var j = 0; j < bonelist.length; j ++) {
717 |
718 | if (bonelist[j].name === joints[i]) {
719 |
720 | sortedbones[i] = bonelist[j];
721 |
722 | }
723 |
724 | }
725 |
726 | }
727 |
728 | //hook up the parents by index instead of name
729 | for (var i = 0; i < sortedbones.length; i ++) {
730 |
731 | for (var j = 0; j < sortedbones.length; j ++) {
732 |
733 | if (sortedbones[i].parent === sortedbones[j].name) {
734 |
735 | sortedbones[i].parent = j;
736 |
737 | }
738 |
739 | }
740 |
741 | }
742 |
743 |
744 | var i, j, w, vidx, weight;
745 | var v = new THREE.Vector3(), o, s;
746 |
747 | // move vertices to bind shape
748 | for ( i = 0; i < geometry.vertices.length; i ++ ) {
749 | geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix );
750 | }
751 |
752 | var skinIndices = [];
753 | var skinWeights = [];
754 | var weights = skinController.skin.weights;
755 |
756 | // hook up the skin weights
757 | // TODO - this might be a good place to choose greatest 4 weights
758 | for ( var i =0; i < weights.length; i ++ ) {
759 |
760 | var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0);
761 | var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0);
762 |
763 | skinIndices.push(indicies);
764 | skinWeights.push(weight);
765 |
766 | }
767 |
768 | geometry.skinIndices = skinIndices;
769 | geometry.skinWeights = skinWeights;
770 | geometry.bones = sortedbones;
771 | // process animation, or simply pose the rig if no animation
772 |
773 | //create an animation for the animated bones
774 | //NOTE: this has no effect when using morphtargets
775 | var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] };
776 |
777 | for (var j = 0; j < sortedbones.length; j ++) {
778 |
779 | animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] });
780 |
781 | }
782 |
783 | console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' );
784 |
785 |
786 |
787 | skinToBindPose(geometry, skeleton, skinController);
788 |
789 |
790 | for ( frame = 0; frame < animationBounds.frames; frame ++ ) {
791 |
792 | var bones = [];
793 | var skinned = [];
794 | // process the frame and setup the rig with a fresh
795 | // transform, possibly from the bone's animation channel(s)
796 |
797 | setupSkeleton( skeleton, bones, frame );
798 | setupSkinningMatrices( bones, skinController.skin );
799 |
800 | for (var i = 0; i < bones.length; i ++) {
801 |
802 | for (var j = 0; j < animationdata.hierarchy.length; j ++) {
803 |
804 | if (animationdata.hierarchy[j].name === bones[i].sid) {
805 |
806 | var key = {};
807 | key.time = (frame / 30);
808 | key.matrix = bones[i].animatrix;
809 |
810 | if (frame === 0)
811 | bones[i].matrix = key.matrix;
812 |
813 | var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ];
814 | key.matrix.decompose(data[0], data[1], data[2]);
815 |
816 | key.pos = [ data[0].x,data[0].y,data[0].z ];
817 |
818 | key.scl = [ data[2].x,data[2].y,data[2].z ];
819 | key.rot = data[1];
820 |
821 | animationdata.hierarchy[j].keys.push(key);
822 |
823 | }
824 |
825 | }
826 |
827 | }
828 |
829 | geometry.animation = animationdata;
830 |
831 | }
832 |
833 | }
834 |
835 | function createKinematics() {
836 |
837 | if ( kinematicsModel && kinematicsModel.joints.length === 0 ) {
838 | kinematics = undefined;
839 | return;
840 | }
841 |
842 | var jointMap = {};
843 |
844 | var _addToMap = function( jointIndex, parentVisualElement ) {
845 |
846 | var parentVisualElementId = parentVisualElement.getAttribute( 'id' );
847 | var colladaNode = visualScene.getChildById( parentVisualElementId, true );
848 | var joint = kinematicsModel.joints[ jointIndex ];
849 |
850 | scene.traverse(function( node ) {
851 |
852 | if ( node.colladaId == parentVisualElementId ) {
853 |
854 | jointMap[ jointIndex ] = {
855 | node: node,
856 | transforms: colladaNode.transforms,
857 | joint: joint,
858 | position: joint.zeroPosition
859 | };
860 |
861 | }
862 |
863 | });
864 |
865 | };
866 |
867 | kinematics = {
868 |
869 | joints: kinematicsModel && kinematicsModel.joints,
870 |
871 | getJointValue: function( jointIndex ) {
872 |
873 | var jointData = jointMap[ jointIndex ];
874 |
875 | if ( jointData ) {
876 |
877 | return jointData.position;
878 |
879 | } else {
880 |
881 | console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' );
882 |
883 | }
884 |
885 | },
886 |
887 | setJointValue: function( jointIndex, value ) {
888 |
889 | var jointData = jointMap[ jointIndex ];
890 |
891 | if ( jointData ) {
892 |
893 | var joint = jointData.joint;
894 |
895 | if ( value > joint.limits.max || value < joint.limits.min ) {
896 |
897 | console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' );
898 |
899 | } else if ( joint.static ) {
900 |
901 | console.log( 'setJointValue: joint ' + jointIndex + ' is static' );
902 |
903 | } else {
904 |
905 | var threejsNode = jointData.node;
906 | var axis = joint.axis;
907 | var transforms = jointData.transforms;
908 |
909 | var matrix = new THREE.Matrix4();
910 |
911 | for (i = 0; i < transforms.length; i ++ ) {
912 |
913 | var transform = transforms[ i ];
914 |
915 | // kinda ghetto joint detection
916 | if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) {
917 |
918 | // apply actual joint value here
919 | switch ( joint.type ) {
920 |
921 | case 'revolute':
922 |
923 | matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) );
924 | break;
925 |
926 | case 'prismatic':
927 |
928 | matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) );
929 | break;
930 |
931 | default:
932 |
933 | console.warn( 'setJointValue: unknown joint type: ' + joint.type );
934 | break;
935 |
936 | }
937 |
938 | } else {
939 |
940 | var m1 = new THREE.Matrix4();
941 |
942 | switch ( transform.type ) {
943 |
944 | case 'matrix':
945 |
946 | matrix.multiply( transform.obj );
947 |
948 | break;
949 |
950 | case 'translate':
951 |
952 | matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) );
953 |
954 | break;
955 |
956 | case 'rotate':
957 |
958 | matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) );
959 |
960 | break;
961 |
962 | }
963 | }
964 | }
965 |
966 | // apply the matrix to the threejs node
967 | var elementsFloat32Arr = matrix.elements;
968 | var elements = Array.prototype.slice.call( elementsFloat32Arr );
969 |
970 | var elementsRowMajor = [
971 | elements[ 0 ],
972 | elements[ 4 ],
973 | elements[ 8 ],
974 | elements[ 12 ],
975 | elements[ 1 ],
976 | elements[ 5 ],
977 | elements[ 9 ],
978 | elements[ 13 ],
979 | elements[ 2 ],
980 | elements[ 6 ],
981 | elements[ 10 ],
982 | elements[ 14 ],
983 | elements[ 3 ],
984 | elements[ 7 ],
985 | elements[ 11 ],
986 | elements[ 15 ]
987 | ];
988 |
989 | threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor );
990 | threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale );
991 | }
992 |
993 | } else {
994 |
995 | console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' );
996 |
997 | }
998 |
999 | }
1000 |
1001 | };
1002 |
1003 | var element = COLLADA.querySelector('scene instance_kinematics_scene');
1004 |
1005 | if ( element ) {
1006 |
1007 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1008 |
1009 | var child = element.childNodes[ i ];
1010 |
1011 | if ( child.nodeType != 1 ) continue;
1012 |
1013 | switch ( child.nodeName ) {
1014 |
1015 | case 'bind_joint_axis':
1016 |
1017 | var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop();
1018 | var axis = child.querySelector('axis param').textContent;
1019 | var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] );
1020 | var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' );
1021 |
1022 | if ( visualTargetElement ) {
1023 | var parentVisualElement = visualTargetElement.parentElement;
1024 | _addToMap(jointIndex, parentVisualElement);
1025 | }
1026 |
1027 | break;
1028 |
1029 | default:
1030 |
1031 | break;
1032 |
1033 | }
1034 |
1035 | }
1036 | }
1037 |
1038 | }
1039 |
1040 | function createSceneGraph ( node, parent ) {
1041 |
1042 | var obj = new THREE.Object3D();
1043 | var skinned = false;
1044 | var skinController;
1045 | var morphController;
1046 | var i, j;
1047 |
1048 | // FIXME: controllers
1049 |
1050 | for ( i = 0; i < node.controllers.length; i ++ ) {
1051 |
1052 | var controller = controllers[ node.controllers[ i ].url ];
1053 |
1054 | switch ( controller.type ) {
1055 |
1056 | case 'skin':
1057 |
1058 | if ( geometries[ controller.skin.source ] ) {
1059 |
1060 | var inst_geom = new InstanceGeometry();
1061 |
1062 | inst_geom.url = controller.skin.source;
1063 | inst_geom.instance_material = node.controllers[ i ].instance_material;
1064 |
1065 | node.geometries.push( inst_geom );
1066 | skinned = true;
1067 | skinController = node.controllers[ i ];
1068 |
1069 | } else if ( controllers[ controller.skin.source ] ) {
1070 |
1071 | // urgh: controller can be chained
1072 | // handle the most basic case...
1073 |
1074 | var second = controllers[ controller.skin.source ];
1075 | morphController = second;
1076 | // skinController = node.controllers[i];
1077 |
1078 | if ( second.morph && geometries[ second.morph.source ] ) {
1079 |
1080 | var inst_geom = new InstanceGeometry();
1081 |
1082 | inst_geom.url = second.morph.source;
1083 | inst_geom.instance_material = node.controllers[ i ].instance_material;
1084 |
1085 | node.geometries.push( inst_geom );
1086 |
1087 | }
1088 |
1089 | }
1090 |
1091 | break;
1092 |
1093 | case 'morph':
1094 |
1095 | if ( geometries[ controller.morph.source ] ) {
1096 |
1097 | var inst_geom = new InstanceGeometry();
1098 |
1099 | inst_geom.url = controller.morph.source;
1100 | inst_geom.instance_material = node.controllers[ i ].instance_material;
1101 |
1102 | node.geometries.push( inst_geom );
1103 | morphController = node.controllers[ i ];
1104 |
1105 | }
1106 |
1107 | console.log( 'ColladaLoader: Morph-controller partially supported.' );
1108 |
1109 | default:
1110 | break;
1111 |
1112 | }
1113 |
1114 | }
1115 |
1116 | // geometries
1117 |
1118 | var double_sided_materials = {};
1119 |
1120 | for ( i = 0; i < node.geometries.length; i ++ ) {
1121 |
1122 | var instance_geometry = node.geometries[i];
1123 | var instance_materials = instance_geometry.instance_material;
1124 | var geometry = geometries[ instance_geometry.url ];
1125 | var used_materials = {};
1126 | var used_materials_array = [];
1127 | var num_materials = 0;
1128 | var first_material;
1129 |
1130 | if ( geometry ) {
1131 |
1132 | if ( !geometry.mesh || !geometry.mesh.primitives )
1133 | continue;
1134 |
1135 | if ( obj.name.length === 0 ) {
1136 |
1137 | obj.name = geometry.id;
1138 |
1139 | }
1140 |
1141 | // collect used fx for this geometry-instance
1142 |
1143 | if ( instance_materials ) {
1144 |
1145 | for ( j = 0; j < instance_materials.length; j ++ ) {
1146 |
1147 | var instance_material = instance_materials[ j ];
1148 | var mat = materials[ instance_material.target ];
1149 | var effect_id = mat.instance_effect.url;
1150 | var shader = effects[ effect_id ].shader;
1151 | var material3js = shader.material;
1152 |
1153 | if ( geometry.doubleSided ) {
1154 |
1155 | if ( !( instance_material.symbol in double_sided_materials ) ) {
1156 |
1157 | var _copied_material = material3js.clone();
1158 | _copied_material.side = THREE.DoubleSide;
1159 | double_sided_materials[ instance_material.symbol ] = _copied_material;
1160 |
1161 | }
1162 |
1163 | material3js = double_sided_materials[ instance_material.symbol ];
1164 |
1165 | }
1166 |
1167 | material3js.opacity = !material3js.opacity ? 1 : material3js.opacity;
1168 | used_materials[ instance_material.symbol ] = num_materials;
1169 | used_materials_array.push( material3js );
1170 | first_material = material3js;
1171 | first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name;
1172 | num_materials ++;
1173 |
1174 | }
1175 |
1176 | }
1177 |
1178 | var mesh;
1179 | var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } );
1180 | var geom = geometry.mesh.geometry3js;
1181 |
1182 | if ( num_materials > 1 ) {
1183 |
1184 | material = new THREE.MultiMaterial( used_materials_array );
1185 |
1186 | for ( j = 0; j < geom.faces.length; j ++ ) {
1187 |
1188 | var face = geom.faces[ j ];
1189 | face.materialIndex = used_materials[ face.daeMaterial ]
1190 |
1191 | }
1192 |
1193 | }
1194 |
1195 | if ( skinController !== undefined ) {
1196 |
1197 |
1198 | applySkin( geom, skinController );
1199 |
1200 | if ( geom.morphTargets.length > 0 ) {
1201 |
1202 | material.morphTargets = true;
1203 | material.skinning = false;
1204 |
1205 | } else {
1206 |
1207 | material.morphTargets = false;
1208 | material.skinning = true;
1209 |
1210 | }
1211 |
1212 |
1213 | mesh = new THREE.SkinnedMesh( geom, material, false );
1214 |
1215 |
1216 | //mesh.skeleton = skinController.skeleton;
1217 | //mesh.skinController = controllers[ skinController.url ];
1218 | //mesh.skinInstanceController = skinController;
1219 | mesh.name = 'skin_' + skins.length;
1220 |
1221 |
1222 |
1223 | //mesh.animationHandle.setKey(0);
1224 | skins.push( mesh );
1225 |
1226 | } else if ( morphController !== undefined ) {
1227 |
1228 | createMorph( geom, morphController );
1229 |
1230 | material.morphTargets = true;
1231 |
1232 | mesh = new THREE.Mesh( geom, material );
1233 | mesh.name = 'morph_' + morphs.length;
1234 |
1235 | morphs.push( mesh );
1236 |
1237 | } else {
1238 |
1239 | if ( geom.isLineStrip === true ) {
1240 |
1241 | mesh = new THREE.Line( geom );
1242 |
1243 | } else {
1244 |
1245 | mesh = new THREE.Mesh( geom, material );
1246 |
1247 | }
1248 |
1249 | }
1250 |
1251 | obj.add(mesh);
1252 |
1253 | }
1254 |
1255 | }
1256 |
1257 | for ( i = 0; i < node.cameras.length; i ++ ) {
1258 |
1259 | var instance_camera = node.cameras[i];
1260 | var cparams = cameras[instance_camera.url];
1261 |
1262 | var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio),
1263 | parseFloat(cparams.znear), parseFloat(cparams.zfar));
1264 |
1265 | obj.add(cam);
1266 | }
1267 |
1268 | for ( i = 0; i < node.lights.length; i ++ ) {
1269 |
1270 | var light = null;
1271 | var instance_light = node.lights[i];
1272 | var lparams = lights[instance_light.url];
1273 |
1274 | if ( lparams && lparams.technique ) {
1275 |
1276 | var color = lparams.color.getHex();
1277 | var intensity = lparams.intensity;
1278 | var distance = lparams.distance;
1279 | var angle = lparams.falloff_angle;
1280 |
1281 | switch ( lparams.technique ) {
1282 |
1283 | case 'directional':
1284 |
1285 | light = new THREE.DirectionalLight( color, intensity, distance );
1286 | light.position.set(0, 0, 1);
1287 | break;
1288 |
1289 | case 'point':
1290 |
1291 | light = new THREE.PointLight( color, intensity, distance );
1292 | break;
1293 |
1294 | case 'spot':
1295 |
1296 | light = new THREE.SpotLight( color, intensity, distance, angle );
1297 | light.position.set(0, 0, 1);
1298 | break;
1299 |
1300 | case 'ambient':
1301 |
1302 | light = new THREE.AmbientLight( color );
1303 | break;
1304 |
1305 | }
1306 |
1307 | }
1308 |
1309 | if (light) {
1310 | obj.add(light);
1311 | }
1312 | }
1313 |
1314 | obj.name = node.name || node.id || "";
1315 | obj.colladaId = node.id || "";
1316 | obj.layer = node.layer || "";
1317 | obj.matrix = node.matrix;
1318 | obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
1319 |
1320 | if ( options.centerGeometry && obj.geometry ) {
1321 |
1322 | var delta = obj.geometry.center();
1323 | delta.multiply( obj.scale );
1324 | delta.applyQuaternion( obj.quaternion );
1325 |
1326 | obj.position.sub( delta );
1327 |
1328 | }
1329 |
1330 | for ( i = 0; i < node.nodes.length; i ++ ) {
1331 |
1332 | obj.add( createSceneGraph( node.nodes[i], node ) );
1333 |
1334 | }
1335 |
1336 | return obj;
1337 |
1338 | }
1339 |
1340 | function getJointId( skin, id ) {
1341 |
1342 | for ( var i = 0; i < skin.joints.length; i ++ ) {
1343 |
1344 | if ( skin.joints[ i ] === id ) {
1345 |
1346 | return i;
1347 |
1348 | }
1349 |
1350 | }
1351 |
1352 | }
1353 |
1354 | function getLibraryNode( id ) {
1355 |
1356 | var nodes = COLLADA.querySelectorAll('library_nodes node');
1357 |
1358 | for ( var i = 0; i < nodes.length; i++ ) {
1359 |
1360 | var attObj = nodes[i].attributes.getNamedItem('id');
1361 |
1362 | if ( attObj && attObj.value === id ) {
1363 |
1364 | return nodes[i];
1365 |
1366 | }
1367 |
1368 | }
1369 |
1370 | return undefined;
1371 |
1372 | }
1373 |
1374 | function getChannelsForNode ( node ) {
1375 |
1376 | var channels = [];
1377 | var startTime = 1000000;
1378 | var endTime = -1000000;
1379 |
1380 | for ( var id in animations ) {
1381 |
1382 | var animation = animations[id];
1383 |
1384 | for ( var i = 0; i < animation.channel.length; i ++ ) {
1385 |
1386 | var channel = animation.channel[i];
1387 | var sampler = animation.sampler[i];
1388 | var id = channel.target.split('/')[0];
1389 |
1390 | if ( id == node.id ) {
1391 |
1392 | sampler.create();
1393 | channel.sampler = sampler;
1394 | startTime = Math.min(startTime, sampler.startTime);
1395 | endTime = Math.max(endTime, sampler.endTime);
1396 | channels.push(channel);
1397 |
1398 | }
1399 |
1400 | }
1401 |
1402 | }
1403 |
1404 | if ( channels.length ) {
1405 |
1406 | node.startTime = startTime;
1407 | node.endTime = endTime;
1408 |
1409 | }
1410 |
1411 | return channels;
1412 |
1413 | }
1414 |
1415 | function calcFrameDuration( node ) {
1416 |
1417 | var minT = 10000000;
1418 |
1419 | for ( var i = 0; i < node.channels.length; i ++ ) {
1420 |
1421 | var sampler = node.channels[i].sampler;
1422 |
1423 | for ( var j = 0; j < sampler.input.length - 1; j ++ ) {
1424 |
1425 | var t0 = sampler.input[ j ];
1426 | var t1 = sampler.input[ j + 1 ];
1427 | minT = Math.min( minT, t1 - t0 );
1428 |
1429 | }
1430 | }
1431 |
1432 | return minT;
1433 |
1434 | }
1435 |
1436 | function calcMatrixAt( node, t ) {
1437 |
1438 | var animated = {};
1439 |
1440 | var i, j;
1441 |
1442 | for ( i = 0; i < node.channels.length; i ++ ) {
1443 |
1444 | var channel = node.channels[ i ];
1445 | animated[ channel.sid ] = channel;
1446 |
1447 | }
1448 |
1449 | var matrix = new THREE.Matrix4();
1450 |
1451 | for ( i = 0; i < node.transforms.length; i ++ ) {
1452 |
1453 | var transform = node.transforms[ i ];
1454 | var channel = animated[ transform.sid ];
1455 |
1456 | if ( channel !== undefined ) {
1457 |
1458 | var sampler = channel.sampler;
1459 | var value;
1460 |
1461 | for ( j = 0; j < sampler.input.length - 1; j ++ ) {
1462 |
1463 | if ( sampler.input[ j + 1 ] > t ) {
1464 |
1465 | value = sampler.output[ j ];
1466 | //console.log(value.flatten)
1467 | break;
1468 |
1469 | }
1470 |
1471 | }
1472 |
1473 | if ( value !== undefined ) {
1474 |
1475 | if ( value instanceof THREE.Matrix4 ) {
1476 |
1477 | matrix.multiplyMatrices( matrix, value );
1478 |
1479 | } else {
1480 |
1481 | // FIXME: handle other types
1482 |
1483 | matrix.multiplyMatrices( matrix, transform.matrix );
1484 |
1485 | }
1486 |
1487 | } else {
1488 |
1489 | matrix.multiplyMatrices( matrix, transform.matrix );
1490 |
1491 | }
1492 |
1493 | } else {
1494 |
1495 | matrix.multiplyMatrices( matrix, transform.matrix );
1496 |
1497 | }
1498 |
1499 | }
1500 |
1501 | return matrix;
1502 |
1503 | }
1504 |
1505 | function bakeAnimations ( node ) {
1506 |
1507 | if ( node.channels && node.channels.length ) {
1508 |
1509 | var keys = [],
1510 | sids = [];
1511 |
1512 | for ( var i = 0, il = node.channels.length; i < il; i ++ ) {
1513 |
1514 | var channel = node.channels[i],
1515 | fullSid = channel.fullSid,
1516 | sampler = channel.sampler,
1517 | input = sampler.input,
1518 | transform = node.getTransformBySid( channel.sid ),
1519 | member;
1520 |
1521 | if ( channel.arrIndices ) {
1522 |
1523 | member = [];
1524 |
1525 | for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) {
1526 |
1527 | member[ j ] = getConvertedIndex( channel.arrIndices[ j ] );
1528 |
1529 | }
1530 |
1531 | } else {
1532 |
1533 | member = getConvertedMember( channel.member );
1534 |
1535 | }
1536 |
1537 | if ( transform ) {
1538 |
1539 | if ( sids.indexOf( fullSid ) === -1 ) {
1540 |
1541 | sids.push( fullSid );
1542 |
1543 | }
1544 |
1545 | for ( var j = 0, jl = input.length; j < jl; j ++ ) {
1546 |
1547 | var time = input[j],
1548 | data = sampler.getData( transform.type, j, member ),
1549 | key = findKey( keys, time );
1550 |
1551 | if ( !key ) {
1552 |
1553 | key = new Key( time );
1554 | var timeNdx = findTimeNdx( keys, time );
1555 | keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key );
1556 |
1557 | }
1558 |
1559 | key.addTarget( fullSid, transform, member, data );
1560 |
1561 | }
1562 |
1563 | } else {
1564 |
1565 | console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id );
1566 |
1567 | }
1568 |
1569 | }
1570 |
1571 | // post process
1572 | for ( var i = 0; i < sids.length; i ++ ) {
1573 |
1574 | var sid = sids[ i ];
1575 |
1576 | for ( var j = 0; j < keys.length; j ++ ) {
1577 |
1578 | var key = keys[ j ];
1579 |
1580 | if ( !key.hasTarget( sid ) ) {
1581 |
1582 | interpolateKeys( keys, key, j, sid );
1583 |
1584 | }
1585 |
1586 | }
1587 |
1588 | }
1589 |
1590 | node.keys = keys;
1591 | node.sids = sids;
1592 |
1593 | }
1594 |
1595 | }
1596 |
1597 | function findKey ( keys, time) {
1598 |
1599 | var retVal = null;
1600 |
1601 | for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) {
1602 |
1603 | var key = keys[i];
1604 |
1605 | if ( key.time === time ) {
1606 |
1607 | retVal = key;
1608 |
1609 | } else if ( key.time > time ) {
1610 |
1611 | break;
1612 |
1613 | }
1614 |
1615 | }
1616 |
1617 | return retVal;
1618 |
1619 | }
1620 |
1621 | function findTimeNdx ( keys, time) {
1622 |
1623 | var ndx = -1;
1624 |
1625 | for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) {
1626 |
1627 | var key = keys[i];
1628 |
1629 | if ( key.time >= time ) {
1630 |
1631 | ndx = i;
1632 |
1633 | }
1634 |
1635 | }
1636 |
1637 | return ndx;
1638 |
1639 | }
1640 |
1641 | function interpolateKeys ( keys, key, ndx, fullSid ) {
1642 |
1643 | var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ),
1644 | nextKey = getNextKeyWith( keys, fullSid, ndx + 1 );
1645 |
1646 | if ( prevKey && nextKey ) {
1647 |
1648 | var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time),
1649 | prevTarget = prevKey.getTarget( fullSid ),
1650 | nextData = nextKey.getTarget( fullSid ).data,
1651 | prevData = prevTarget.data,
1652 | data;
1653 |
1654 | if ( prevTarget.type === 'matrix' ) {
1655 |
1656 | data = prevData;
1657 |
1658 | } else if ( prevData.length ) {
1659 |
1660 | data = [];
1661 |
1662 | for ( var i = 0; i < prevData.length; ++ i ) {
1663 |
1664 | data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale;
1665 |
1666 | }
1667 |
1668 | } else {
1669 |
1670 | data = prevData + ( nextData - prevData ) * scale;
1671 |
1672 | }
1673 |
1674 | key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data );
1675 |
1676 | }
1677 |
1678 | }
1679 |
1680 | // Get next key with given sid
1681 |
1682 | function getNextKeyWith( keys, fullSid, ndx ) {
1683 |
1684 | for ( ; ndx < keys.length; ndx ++ ) {
1685 |
1686 | var key = keys[ ndx ];
1687 |
1688 | if ( key.hasTarget( fullSid ) ) {
1689 |
1690 | return key;
1691 |
1692 | }
1693 |
1694 | }
1695 |
1696 | return null;
1697 |
1698 | }
1699 |
1700 | // Get previous key with given sid
1701 |
1702 | function getPrevKeyWith( keys, fullSid, ndx ) {
1703 |
1704 | ndx = ndx >= 0 ? ndx : ndx + keys.length;
1705 |
1706 | for ( ; ndx >= 0; ndx -- ) {
1707 |
1708 | var key = keys[ ndx ];
1709 |
1710 | if ( key.hasTarget( fullSid ) ) {
1711 |
1712 | return key;
1713 |
1714 | }
1715 |
1716 | }
1717 |
1718 | return null;
1719 |
1720 | }
1721 |
1722 | function _Image() {
1723 |
1724 | this.id = "";
1725 | this.init_from = "";
1726 |
1727 | }
1728 |
1729 | _Image.prototype.parse = function(element) {
1730 |
1731 | this.id = element.getAttribute('id');
1732 |
1733 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1734 |
1735 | var child = element.childNodes[ i ];
1736 |
1737 | if ( child.nodeName === 'init_from' ) {
1738 |
1739 | this.init_from = child.textContent;
1740 |
1741 | }
1742 |
1743 | }
1744 |
1745 | return this;
1746 |
1747 | };
1748 |
1749 | function Controller() {
1750 |
1751 | this.id = "";
1752 | this.name = "";
1753 | this.type = "";
1754 | this.skin = null;
1755 | this.morph = null;
1756 |
1757 | }
1758 |
1759 | Controller.prototype.parse = function( element ) {
1760 |
1761 | this.id = element.getAttribute('id');
1762 | this.name = element.getAttribute('name');
1763 | this.type = "none";
1764 |
1765 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1766 |
1767 | var child = element.childNodes[ i ];
1768 |
1769 | switch ( child.nodeName ) {
1770 |
1771 | case 'skin':
1772 |
1773 | this.skin = (new Skin()).parse(child);
1774 | this.type = child.nodeName;
1775 | break;
1776 |
1777 | case 'morph':
1778 |
1779 | this.morph = (new Morph()).parse(child);
1780 | this.type = child.nodeName;
1781 | break;
1782 |
1783 | default:
1784 | break;
1785 |
1786 | }
1787 | }
1788 |
1789 | return this;
1790 |
1791 | };
1792 |
1793 | function Morph() {
1794 |
1795 | this.method = null;
1796 | this.source = null;
1797 | this.targets = null;
1798 | this.weights = null;
1799 |
1800 | }
1801 |
1802 | Morph.prototype.parse = function( element ) {
1803 |
1804 | var sources = {};
1805 | var inputs = [];
1806 | var i;
1807 |
1808 | this.method = element.getAttribute( 'method' );
1809 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' );
1810 |
1811 | for ( i = 0; i < element.childNodes.length; i ++ ) {
1812 |
1813 | var child = element.childNodes[ i ];
1814 | if ( child.nodeType != 1 ) continue;
1815 |
1816 | switch ( child.nodeName ) {
1817 |
1818 | case 'source':
1819 |
1820 | var source = ( new Source() ).parse( child );
1821 | sources[ source.id ] = source;
1822 | break;
1823 |
1824 | case 'targets':
1825 |
1826 | inputs = this.parseInputs( child );
1827 | break;
1828 |
1829 | default:
1830 |
1831 | console.log( child.nodeName );
1832 | break;
1833 |
1834 | }
1835 |
1836 | }
1837 |
1838 | for ( i = 0; i < inputs.length; i ++ ) {
1839 |
1840 | var input = inputs[ i ];
1841 | var source = sources[ input.source ];
1842 |
1843 | switch ( input.semantic ) {
1844 |
1845 | case 'MORPH_TARGET':
1846 |
1847 | this.targets = source.read();
1848 | break;
1849 |
1850 | case 'MORPH_WEIGHT':
1851 |
1852 | this.weights = source.read();
1853 | break;
1854 |
1855 | default:
1856 | break;
1857 |
1858 | }
1859 | }
1860 |
1861 | return this;
1862 |
1863 | };
1864 |
1865 | Morph.prototype.parseInputs = function(element) {
1866 |
1867 | var inputs = [];
1868 |
1869 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1870 |
1871 | var child = element.childNodes[i];
1872 | if ( child.nodeType != 1) continue;
1873 |
1874 | switch ( child.nodeName ) {
1875 |
1876 | case 'input':
1877 |
1878 | inputs.push( (new Input()).parse(child) );
1879 | break;
1880 |
1881 | default:
1882 | break;
1883 | }
1884 | }
1885 |
1886 | return inputs;
1887 |
1888 | };
1889 |
1890 | function Skin() {
1891 |
1892 | this.source = "";
1893 | this.bindShapeMatrix = null;
1894 | this.invBindMatrices = [];
1895 | this.joints = [];
1896 | this.weights = [];
1897 |
1898 | }
1899 |
1900 | Skin.prototype.parse = function( element ) {
1901 |
1902 | var sources = {};
1903 | var joints, weights;
1904 |
1905 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' );
1906 | this.invBindMatrices = [];
1907 | this.joints = [];
1908 | this.weights = [];
1909 |
1910 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1911 |
1912 | var child = element.childNodes[i];
1913 | if ( child.nodeType != 1 ) continue;
1914 |
1915 | switch ( child.nodeName ) {
1916 |
1917 | case 'bind_shape_matrix':
1918 |
1919 | var f = _floats(child.textContent);
1920 | this.bindShapeMatrix = getConvertedMat4( f );
1921 | break;
1922 |
1923 | case 'source':
1924 |
1925 | var src = new Source().parse(child);
1926 | sources[ src.id ] = src;
1927 | break;
1928 |
1929 | case 'joints':
1930 |
1931 | joints = child;
1932 | break;
1933 |
1934 | case 'vertex_weights':
1935 |
1936 | weights = child;
1937 | break;
1938 |
1939 | default:
1940 |
1941 | console.log( child.nodeName );
1942 | break;
1943 |
1944 | }
1945 | }
1946 |
1947 | this.parseJoints( joints, sources );
1948 | this.parseWeights( weights, sources );
1949 |
1950 | return this;
1951 |
1952 | };
1953 |
1954 | Skin.prototype.parseJoints = function ( element, sources ) {
1955 |
1956 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1957 |
1958 | var child = element.childNodes[ i ];
1959 | if ( child.nodeType != 1 ) continue;
1960 |
1961 | switch ( child.nodeName ) {
1962 |
1963 | case 'input':
1964 |
1965 | var input = ( new Input() ).parse( child );
1966 | var source = sources[ input.source ];
1967 |
1968 | if ( input.semantic === 'JOINT' ) {
1969 |
1970 | this.joints = source.read();
1971 |
1972 | } else if ( input.semantic === 'INV_BIND_MATRIX' ) {
1973 |
1974 | this.invBindMatrices = source.read();
1975 |
1976 | }
1977 |
1978 | break;
1979 |
1980 | default:
1981 | break;
1982 | }
1983 |
1984 | }
1985 |
1986 | };
1987 |
1988 | Skin.prototype.parseWeights = function ( element, sources ) {
1989 |
1990 | var v, vcount, inputs = [];
1991 |
1992 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
1993 |
1994 | var child = element.childNodes[ i ];
1995 | if ( child.nodeType != 1 ) continue;
1996 |
1997 | switch ( child.nodeName ) {
1998 |
1999 | case 'input':
2000 |
2001 | inputs.push( ( new Input() ).parse( child ) );
2002 | break;
2003 |
2004 | case 'v':
2005 |
2006 | v = _ints( child.textContent );
2007 | break;
2008 |
2009 | case 'vcount':
2010 |
2011 | vcount = _ints( child.textContent );
2012 | break;
2013 |
2014 | default:
2015 | break;
2016 |
2017 | }
2018 |
2019 | }
2020 |
2021 | var index = 0;
2022 |
2023 | for ( var i = 0; i < vcount.length; i ++ ) {
2024 |
2025 | var numBones = vcount[i];
2026 | var vertex_weights = [];
2027 |
2028 | for ( var j = 0; j < numBones; j ++ ) {
2029 |
2030 | var influence = {};
2031 |
2032 | for ( var k = 0; k < inputs.length; k ++ ) {
2033 |
2034 | var input = inputs[ k ];
2035 | var value = v[ index + input.offset ];
2036 |
2037 | switch ( input.semantic ) {
2038 |
2039 | case 'JOINT':
2040 |
2041 | influence.joint = value;//this.joints[value];
2042 | break;
2043 |
2044 | case 'WEIGHT':
2045 |
2046 | influence.weight = sources[ input.source ].data[ value ];
2047 | break;
2048 |
2049 | default:
2050 | break;
2051 |
2052 | }
2053 |
2054 | }
2055 |
2056 | vertex_weights.push( influence );
2057 | index += inputs.length;
2058 | }
2059 |
2060 | for ( var j = 0; j < vertex_weights.length; j ++ ) {
2061 |
2062 | vertex_weights[ j ].index = i;
2063 |
2064 | }
2065 |
2066 | this.weights.push( vertex_weights );
2067 |
2068 | }
2069 |
2070 | };
2071 |
2072 | function VisualScene () {
2073 |
2074 | this.id = "";
2075 | this.name = "";
2076 | this.nodes = [];
2077 | this.scene = new THREE.Group();
2078 |
2079 | }
2080 |
2081 | VisualScene.prototype.getChildById = function( id, recursive ) {
2082 |
2083 | for ( var i = 0; i < this.nodes.length; i ++ ) {
2084 |
2085 | var node = this.nodes[ i ].getChildById( id, recursive );
2086 |
2087 | if ( node ) {
2088 |
2089 | return node;
2090 |
2091 | }
2092 |
2093 | }
2094 |
2095 | return null;
2096 |
2097 | };
2098 |
2099 | VisualScene.prototype.getChildBySid = function( sid, recursive ) {
2100 |
2101 | for ( var i = 0; i < this.nodes.length; i ++ ) {
2102 |
2103 | var node = this.nodes[ i ].getChildBySid( sid, recursive );
2104 |
2105 | if ( node ) {
2106 |
2107 | return node;
2108 |
2109 | }
2110 |
2111 | }
2112 |
2113 | return null;
2114 |
2115 | };
2116 |
2117 | VisualScene.prototype.parse = function( element ) {
2118 |
2119 | this.id = element.getAttribute( 'id' );
2120 | this.name = element.getAttribute( 'name' );
2121 | this.nodes = [];
2122 |
2123 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
2124 |
2125 | var child = element.childNodes[ i ];
2126 | if ( child.nodeType != 1 ) continue;
2127 |
2128 | switch ( child.nodeName ) {
2129 |
2130 | case 'node':
2131 |
2132 | this.nodes.push( ( new Node() ).parse( child ) );
2133 | break;
2134 |
2135 | default:
2136 | break;
2137 |
2138 | }
2139 |
2140 | }
2141 |
2142 | return this;
2143 |
2144 | };
2145 |
2146 | function Node() {
2147 |
2148 | this.id = "";
2149 | this.name = "";
2150 | this.sid = "";
2151 | this.nodes = [];
2152 | this.controllers = [];
2153 | this.transforms = [];
2154 | this.geometries = [];
2155 | this.channels = [];
2156 | this.matrix = new THREE.Matrix4();
2157 |
2158 | }
2159 |
2160 | Node.prototype.getChannelForTransform = function( transformSid ) {
2161 |
2162 | for ( var i = 0; i < this.channels.length; i ++ ) {
2163 |
2164 | var channel = this.channels[i];
2165 | var parts = channel.target.split('/');
2166 | var id = parts.shift();
2167 | var sid = parts.shift();
2168 | var dotSyntax = (sid.indexOf(".") >= 0);
2169 | var arrSyntax = (sid.indexOf("(") >= 0);
2170 | var arrIndices;
2171 | var member;
2172 |
2173 | if ( dotSyntax ) {
2174 |
2175 | parts = sid.split(".");
2176 | sid = parts.shift();
2177 | member = parts.shift();
2178 |
2179 | } else if ( arrSyntax ) {
2180 |
2181 | arrIndices = sid.split("(");
2182 | sid = arrIndices.shift();
2183 |
2184 | for ( var j = 0; j < arrIndices.length; j ++ ) {
2185 |
2186 | arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) );
2187 |
2188 | }
2189 |
2190 | }
2191 |
2192 | if ( sid === transformSid ) {
2193 |
2194 | channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices };
2195 | return channel;
2196 |
2197 | }
2198 |
2199 | }
2200 |
2201 | return null;
2202 |
2203 | };
2204 |
2205 | Node.prototype.getChildById = function ( id, recursive ) {
2206 |
2207 | if ( this.id === id ) {
2208 |
2209 | return this;
2210 |
2211 | }
2212 |
2213 | if ( recursive ) {
2214 |
2215 | for ( var i = 0; i < this.nodes.length; i ++ ) {
2216 |
2217 | var n = this.nodes[ i ].getChildById( id, recursive );
2218 |
2219 | if ( n ) {
2220 |
2221 | return n;
2222 |
2223 | }
2224 |
2225 | }
2226 |
2227 | }
2228 |
2229 | return null;
2230 |
2231 | };
2232 |
2233 | Node.prototype.getChildBySid = function ( sid, recursive ) {
2234 |
2235 | if ( this.sid === sid ) {
2236 |
2237 | return this;
2238 |
2239 | }
2240 |
2241 | if ( recursive ) {
2242 |
2243 | for ( var i = 0; i < this.nodes.length; i ++ ) {
2244 |
2245 | var n = this.nodes[ i ].getChildBySid( sid, recursive );
2246 |
2247 | if ( n ) {
2248 |
2249 | return n;
2250 |
2251 | }
2252 |
2253 | }
2254 | }
2255 |
2256 | return null;
2257 |
2258 | };
2259 |
2260 | Node.prototype.getTransformBySid = function ( sid ) {
2261 |
2262 | for ( var i = 0; i < this.transforms.length; i ++ ) {
2263 |
2264 | if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ];
2265 |
2266 | }
2267 |
2268 | return null;
2269 |
2270 | };
2271 |
2272 | Node.prototype.parse = function( element ) {
2273 |
2274 | var url;
2275 |
2276 | this.id = element.getAttribute('id');
2277 | this.sid = element.getAttribute('sid');
2278 | this.name = element.getAttribute('name');
2279 | this.type = element.getAttribute('type');
2280 | this.layer = element.getAttribute('layer');
2281 |
2282 | this.type = this.type === 'JOINT' ? this.type : 'NODE';
2283 |
2284 | this.nodes = [];
2285 | this.transforms = [];
2286 | this.geometries = [];
2287 | this.cameras = [];
2288 | this.lights = [];
2289 | this.controllers = [];
2290 | this.matrix = new THREE.Matrix4();
2291 |
2292 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
2293 |
2294 | var child = element.childNodes[ i ];
2295 | if ( child.nodeType != 1 ) continue;
2296 |
2297 | switch ( child.nodeName ) {
2298 |
2299 | case 'node':
2300 |
2301 | this.nodes.push( ( new Node() ).parse( child ) );
2302 | break;
2303 |
2304 | case 'instance_camera':
2305 |
2306 | this.cameras.push( ( new InstanceCamera() ).parse( child ) );
2307 | break;
2308 |
2309 | case 'instance_controller':
2310 |
2311 | this.controllers.push( ( new InstanceController() ).parse( child ) );
2312 | break;
2313 |
2314 | case 'instance_geometry':
2315 |
2316 | this.geometries.push( ( new InstanceGeometry() ).parse( child ) );
2317 | break;
2318 |
2319 | case 'instance_light':
2320 |
2321 | this.lights.push( ( new InstanceLight() ).parse( child ) );
2322 | break;
2323 |
2324 | case 'instance_node':
2325 |
2326 | url = child.getAttribute( 'url' ).replace( /^#/, '' );
2327 | var iNode = getLibraryNode( url );
2328 |
2329 | if ( iNode ) {
2330 |
2331 | this.nodes.push( ( new Node() ).parse( iNode )) ;
2332 |
2333 | }
2334 |
2335 | break;
2336 |
2337 | case 'rotate':
2338 | case 'translate':
2339 | case 'scale':
2340 | case 'matrix':
2341 | case 'lookat':
2342 | case 'skew':
2343 |
2344 | this.transforms.push( ( new Transform() ).parse( child ) );
2345 | break;
2346 |
2347 | case 'extra':
2348 | break;
2349 |
2350 | default:
2351 |
2352 | console.log( child.nodeName );
2353 | break;
2354 |
2355 | }
2356 |
2357 | }
2358 |
2359 | this.channels = getChannelsForNode( this );
2360 | bakeAnimations( this );
2361 |
2362 | this.updateMatrix();
2363 |
2364 | return this;
2365 |
2366 | };
2367 |
2368 | Node.prototype.updateMatrix = function () {
2369 |
2370 | this.matrix.identity();
2371 |
2372 | for ( var i = 0; i < this.transforms.length; i ++ ) {
2373 |
2374 | this.transforms[ i ].apply( this.matrix );
2375 |
2376 | }
2377 |
2378 | };
2379 |
2380 | function Transform () {
2381 |
2382 | this.sid = "";
2383 | this.type = "";
2384 | this.data = [];
2385 | this.obj = null;
2386 |
2387 | }
2388 |
2389 | Transform.prototype.parse = function ( element ) {
2390 |
2391 | this.sid = element.getAttribute( 'sid' );
2392 | this.type = element.nodeName;
2393 | this.data = _floats( element.textContent );
2394 | this.convert();
2395 |
2396 | return this;
2397 |
2398 | };
2399 |
2400 | Transform.prototype.convert = function () {
2401 |
2402 | switch ( this.type ) {
2403 |
2404 | case 'matrix':
2405 |
2406 | this.obj = getConvertedMat4( this.data );
2407 | break;
2408 |
2409 | case 'rotate':
2410 |
2411 | this.angle = THREE.Math.degToRad( this.data[3] );
2412 |
2413 | case 'translate':
2414 |
2415 | fixCoords( this.data, -1 );
2416 | this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] );
2417 | break;
2418 |
2419 | case 'scale':
2420 |
2421 | fixCoords( this.data, 1 );
2422 | this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] );
2423 | break;
2424 |
2425 | default:
2426 | console.log( 'Can not convert Transform of type ' + this.type );
2427 | break;
2428 |
2429 | }
2430 |
2431 | };
2432 |
2433 | Transform.prototype.apply = function () {
2434 |
2435 | var m1 = new THREE.Matrix4();
2436 |
2437 | return function ( matrix ) {
2438 |
2439 | switch ( this.type ) {
2440 |
2441 | case 'matrix':
2442 |
2443 | matrix.multiply( this.obj );
2444 |
2445 | break;
2446 |
2447 | case 'translate':
2448 |
2449 | matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) );
2450 |
2451 | break;
2452 |
2453 | case 'rotate':
2454 |
2455 | matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) );
2456 |
2457 | break;
2458 |
2459 | case 'scale':
2460 |
2461 | matrix.scale( this.obj );
2462 |
2463 | break;
2464 |
2465 | }
2466 |
2467 | };
2468 |
2469 | }();
2470 |
2471 | Transform.prototype.update = function ( data, member ) {
2472 |
2473 | var members = [ 'X', 'Y', 'Z', 'ANGLE' ];
2474 |
2475 | switch ( this.type ) {
2476 |
2477 | case 'matrix':
2478 |
2479 | if ( ! member ) {
2480 |
2481 | this.obj.copy( data );
2482 |
2483 | } else if ( member.length === 1 ) {
2484 |
2485 | switch ( member[ 0 ] ) {
2486 |
2487 | case 0:
2488 |
2489 | this.obj.n11 = data[ 0 ];
2490 | this.obj.n21 = data[ 1 ];
2491 | this.obj.n31 = data[ 2 ];
2492 | this.obj.n41 = data[ 3 ];
2493 |
2494 | break;
2495 |
2496 | case 1:
2497 |
2498 | this.obj.n12 = data[ 0 ];
2499 | this.obj.n22 = data[ 1 ];
2500 | this.obj.n32 = data[ 2 ];
2501 | this.obj.n42 = data[ 3 ];
2502 |
2503 | break;
2504 |
2505 | case 2:
2506 |
2507 | this.obj.n13 = data[ 0 ];
2508 | this.obj.n23 = data[ 1 ];
2509 | this.obj.n33 = data[ 2 ];
2510 | this.obj.n43 = data[ 3 ];
2511 |
2512 | break;
2513 |
2514 | case 3:
2515 |
2516 | this.obj.n14 = data[ 0 ];
2517 | this.obj.n24 = data[ 1 ];
2518 | this.obj.n34 = data[ 2 ];
2519 | this.obj.n44 = data[ 3 ];
2520 |
2521 | break;
2522 |
2523 | }
2524 |
2525 | } else if ( member.length === 2 ) {
2526 |
2527 | var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 );
2528 | this.obj[ propName ] = data;
2529 |
2530 | } else {
2531 |
2532 | console.log('Incorrect addressing of matrix in transform.');
2533 |
2534 | }
2535 |
2536 | break;
2537 |
2538 | case 'translate':
2539 | case 'scale':
2540 |
2541 | if ( Object.prototype.toString.call( member ) === '[object Array]' ) {
2542 |
2543 | member = members[ member[ 0 ] ];
2544 |
2545 | }
2546 |
2547 | switch ( member ) {
2548 |
2549 | case 'X':
2550 |
2551 | this.obj.x = data;
2552 | break;
2553 |
2554 | case 'Y':
2555 |
2556 | this.obj.y = data;
2557 | break;
2558 |
2559 | case 'Z':
2560 |
2561 | this.obj.z = data;
2562 | break;
2563 |
2564 | default:
2565 |
2566 | this.obj.x = data[ 0 ];
2567 | this.obj.y = data[ 1 ];
2568 | this.obj.z = data[ 2 ];
2569 | break;
2570 |
2571 | }
2572 |
2573 | break;
2574 |
2575 | case 'rotate':
2576 |
2577 | if ( Object.prototype.toString.call( member ) === '[object Array]' ) {
2578 |
2579 | member = members[ member[ 0 ] ];
2580 |
2581 | }
2582 |
2583 | switch ( member ) {
2584 |
2585 | case 'X':
2586 |
2587 | this.obj.x = data;
2588 | break;
2589 |
2590 | case 'Y':
2591 |
2592 | this.obj.y = data;
2593 | break;
2594 |
2595 | case 'Z':
2596 |
2597 | this.obj.z = data;
2598 | break;
2599 |
2600 | case 'ANGLE':
2601 |
2602 | this.angle = THREE.Math.degToRad( data );
2603 | break;
2604 |
2605 | default:
2606 |
2607 | this.obj.x = data[ 0 ];
2608 | this.obj.y = data[ 1 ];
2609 | this.obj.z = data[ 2 ];
2610 | this.angle = THREE.Math.degToRad( data[ 3 ] );
2611 | break;
2612 |
2613 | }
2614 | break;
2615 |
2616 | }
2617 |
2618 | };
2619 |
2620 | function InstanceController() {
2621 |
2622 | this.url = "";
2623 | this.skeleton = [];
2624 | this.instance_material = [];
2625 |
2626 | }
2627 |
2628 | InstanceController.prototype.parse = function ( element ) {
2629 |
2630 | this.url = element.getAttribute('url').replace(/^#/, '');
2631 | this.skeleton = [];
2632 | this.instance_material = [];
2633 |
2634 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
2635 |
2636 | var child = element.childNodes[ i ];
2637 | if ( child.nodeType !== 1 ) continue;
2638 |
2639 | switch ( child.nodeName ) {
2640 |
2641 | case 'skeleton':
2642 |
2643 | this.skeleton.push( child.textContent.replace(/^#/, '') );
2644 | break;
2645 |
2646 | case 'bind_material':
2647 |
2648 | var instances = child.querySelectorAll('instance_material');
2649 |
2650 | for ( var j = 0; j < instances.length; j ++ ) {
2651 |
2652 | var instance = instances[j];
2653 | this.instance_material.push( (new InstanceMaterial()).parse(instance) );
2654 |
2655 | }
2656 |
2657 |
2658 | break;
2659 |
2660 | case 'extra':
2661 | break;
2662 |
2663 | default:
2664 | break;
2665 |
2666 | }
2667 | }
2668 |
2669 | return this;
2670 |
2671 | };
2672 |
2673 | function InstanceMaterial () {
2674 |
2675 | this.symbol = "";
2676 | this.target = "";
2677 |
2678 | }
2679 |
2680 | InstanceMaterial.prototype.parse = function ( element ) {
2681 |
2682 | this.symbol = element.getAttribute('symbol');
2683 | this.target = element.getAttribute('target').replace(/^#/, '');
2684 | return this;
2685 |
2686 | };
2687 |
2688 | function InstanceGeometry() {
2689 |
2690 | this.url = "";
2691 | this.instance_material = [];
2692 |
2693 | }
2694 |
2695 | InstanceGeometry.prototype.parse = function ( element ) {
2696 |
2697 | this.url = element.getAttribute('url').replace(/^#/, '');
2698 | this.instance_material = [];
2699 |
2700 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
2701 |
2702 | var child = element.childNodes[i];
2703 | if ( child.nodeType != 1 ) continue;
2704 |
2705 | if ( child.nodeName === 'bind_material' ) {
2706 |
2707 | var instances = child.querySelectorAll('instance_material');
2708 |
2709 | for ( var j = 0; j < instances.length; j ++ ) {
2710 |
2711 | var instance = instances[j];
2712 | this.instance_material.push( (new InstanceMaterial()).parse(instance) );
2713 |
2714 | }
2715 |
2716 | break;
2717 |
2718 | }
2719 |
2720 | }
2721 |
2722 | return this;
2723 |
2724 | };
2725 |
2726 | function Geometry() {
2727 |
2728 | this.id = "";
2729 | this.mesh = null;
2730 |
2731 | }
2732 |
2733 | Geometry.prototype.parse = function ( element ) {
2734 |
2735 | this.id = element.getAttribute('id');
2736 |
2737 | extractDoubleSided( this, element );
2738 |
2739 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
2740 |
2741 | var child = element.childNodes[i];
2742 |
2743 | switch ( child.nodeName ) {
2744 |
2745 | case 'mesh':
2746 |
2747 | this.mesh = (new Mesh(this)).parse(child);
2748 | break;
2749 |
2750 | case 'extra':
2751 |
2752 | // console.log( child );
2753 | break;
2754 |
2755 | default:
2756 | break;
2757 | }
2758 | }
2759 |
2760 | return this;
2761 |
2762 | };
2763 |
2764 | function Mesh( geometry ) {
2765 |
2766 | this.geometry = geometry.id;
2767 | this.primitives = [];
2768 | this.vertices = null;
2769 | this.geometry3js = null;
2770 |
2771 | }
2772 |
2773 | Mesh.prototype.parse = function ( element ) {
2774 |
2775 | this.primitives = [];
2776 |
2777 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
2778 |
2779 | var child = element.childNodes[ i ];
2780 |
2781 | switch ( child.nodeName ) {
2782 |
2783 | case 'source':
2784 |
2785 | _source( child );
2786 | break;
2787 |
2788 | case 'vertices':
2789 |
2790 | this.vertices = ( new Vertices() ).parse( child );
2791 | break;
2792 |
2793 | case 'linestrips':
2794 |
2795 | this.primitives.push( ( new LineStrips().parse( child ) ) );
2796 | break;
2797 |
2798 | case 'triangles':
2799 |
2800 | this.primitives.push( ( new Triangles().parse( child ) ) );
2801 | break;
2802 |
2803 | case 'polygons':
2804 |
2805 | this.primitives.push( ( new Polygons().parse( child ) ) );
2806 | break;
2807 |
2808 | case 'polylist':
2809 |
2810 | this.primitives.push( ( new Polylist().parse( child ) ) );
2811 | break;
2812 |
2813 | default:
2814 | break;
2815 |
2816 | }
2817 |
2818 | }
2819 |
2820 | this.geometry3js = new THREE.Geometry();
2821 |
2822 | if ( this.vertices === null ) {
2823 |
2824 | // TODO (mrdoob): Study case when this is null (carrier.dae)
2825 |
2826 | return this;
2827 |
2828 | }
2829 |
2830 | var vertexData = sources[ this.vertices.input['POSITION'].source ].data;
2831 |
2832 | for ( var i = 0; i < vertexData.length; i += 3 ) {
2833 |
2834 | this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() );
2835 |
2836 | }
2837 |
2838 | for ( var i = 0; i < this.primitives.length; i ++ ) {
2839 |
2840 | var primitive = this.primitives[ i ];
2841 | primitive.setVertices( this.vertices );
2842 | this.handlePrimitive( primitive, this.geometry3js );
2843 |
2844 | }
2845 |
2846 | if ( this.geometry3js.calcNormals ) {
2847 |
2848 | this.geometry3js.computeVertexNormals();
2849 | delete this.geometry3js.calcNormals;
2850 |
2851 | }
2852 |
2853 | return this;
2854 |
2855 | };
2856 |
2857 | Mesh.prototype.handlePrimitive = function ( primitive, geom ) {
2858 |
2859 | if ( primitive instanceof LineStrips ) {
2860 |
2861 | // TODO: Handle indices. Maybe easier with BufferGeometry?
2862 |
2863 | geom.isLineStrip = true;
2864 | return;
2865 |
2866 | }
2867 |
2868 | var j, k, pList = primitive.p, inputs = primitive.inputs;
2869 | var input, index, idx32;
2870 | var source, numParams;
2871 | var vcIndex = 0, vcount = 3, maxOffset = 0;
2872 | var texture_sets = [];
2873 |
2874 | for ( j = 0; j < inputs.length; j ++ ) {
2875 |
2876 | input = inputs[ j ];
2877 |
2878 | var offset = input.offset + 1;
2879 | maxOffset = (maxOffset < offset) ? offset : maxOffset;
2880 |
2881 | switch ( input.semantic ) {
2882 |
2883 | case 'TEXCOORD':
2884 | texture_sets.push( input.set );
2885 | break;
2886 |
2887 | }
2888 |
2889 | }
2890 |
2891 | for ( var pCount = 0; pCount < pList.length; ++ pCount ) {
2892 |
2893 | var p = pList[ pCount ], i = 0;
2894 |
2895 | while ( i < p.length ) {
2896 |
2897 | var vs = [];
2898 | var ns = [];
2899 | var ts = null;
2900 | var cs = [];
2901 |
2902 | if ( primitive.vcount ) {
2903 |
2904 | vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount;
2905 |
2906 | } else {
2907 |
2908 | vcount = p.length / maxOffset;
2909 |
2910 | }
2911 |
2912 |
2913 | for ( j = 0; j < vcount; j ++ ) {
2914 |
2915 | for ( k = 0; k < inputs.length; k ++ ) {
2916 |
2917 | input = inputs[ k ];
2918 | source = sources[ input.source ];
2919 |
2920 | index = p[ i + ( j * maxOffset ) + input.offset ];
2921 | numParams = source.accessor.params.length;
2922 | idx32 = index * numParams;
2923 |
2924 | switch ( input.semantic ) {
2925 |
2926 | case 'VERTEX':
2927 |
2928 | vs.push( index );
2929 |
2930 | break;
2931 |
2932 | case 'NORMAL':
2933 |
2934 | ns.push( getConvertedVec3( source.data, idx32 ) );
2935 |
2936 | break;
2937 |
2938 | case 'TEXCOORD':
2939 |
2940 | ts = ts || { };
2941 | if ( ts[ input.set ] === undefined ) ts[ input.set ] = [];
2942 | // invert the V
2943 | ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) );
2944 |
2945 | break;
2946 |
2947 | case 'COLOR':
2948 |
2949 | cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) );
2950 |
2951 | break;
2952 |
2953 | default:
2954 |
2955 | break;
2956 |
2957 | }
2958 |
2959 | }
2960 |
2961 | }
2962 |
2963 | if ( ns.length === 0 ) {
2964 |
2965 | // check the vertices inputs
2966 | input = this.vertices.input.NORMAL;
2967 |
2968 | if ( input ) {
2969 |
2970 | source = sources[ input.source ];
2971 | numParams = source.accessor.params.length;
2972 |
2973 | for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) {
2974 |
2975 | ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) );
2976 |
2977 | }
2978 |
2979 | } else {
2980 |
2981 | geom.calcNormals = true;
2982 |
2983 | }
2984 |
2985 | }
2986 |
2987 | if ( !ts ) {
2988 |
2989 | ts = { };
2990 | // check the vertices inputs
2991 | input = this.vertices.input.TEXCOORD;
2992 |
2993 | if ( input ) {
2994 |
2995 | texture_sets.push( input.set );
2996 | source = sources[ input.source ];
2997 | numParams = source.accessor.params.length;
2998 |
2999 | for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) {
3000 |
3001 | idx32 = vs[ ndx ] * numParams;
3002 | if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ];
3003 | // invert the V
3004 | ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) );
3005 |
3006 | }
3007 |
3008 | }
3009 |
3010 | }
3011 |
3012 | if ( cs.length === 0 ) {
3013 |
3014 | // check the vertices inputs
3015 | input = this.vertices.input.COLOR;
3016 |
3017 | if ( input ) {
3018 |
3019 | source = sources[ input.source ];
3020 | numParams = source.accessor.params.length;
3021 |
3022 | for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) {
3023 |
3024 | idx32 = vs[ ndx ] * numParams;
3025 | cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) );
3026 |
3027 | }
3028 |
3029 | }
3030 |
3031 | }
3032 |
3033 | var face = null, faces = [], uv, uvArr;
3034 |
3035 | if ( vcount === 3 ) {
3036 |
3037 | faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) );
3038 |
3039 | } else if ( vcount === 4 ) {
3040 |
3041 | faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) );
3042 |
3043 | faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) );
3044 |
3045 | } else if ( vcount > 4 && options.subdivideFaces ) {
3046 |
3047 | var clr = cs.length ? cs : new THREE.Color(),
3048 | vec1, vec2, vec3, v1, v2, norm;
3049 |
3050 | // subdivide into multiple Face3s
3051 |
3052 | for ( k = 1; k < vcount - 1; ) {
3053 |
3054 | faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) );
3055 |
3056 | }
3057 |
3058 | }
3059 |
3060 | if ( faces.length ) {
3061 |
3062 | for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) {
3063 |
3064 | face = faces[ndx];
3065 | face.daeMaterial = primitive.material;
3066 | geom.faces.push( face );
3067 |
3068 | for ( k = 0; k < texture_sets.length; k ++ ) {
3069 |
3070 | uv = ts[ texture_sets[k] ];
3071 |
3072 | if ( vcount > 4 ) {
3073 |
3074 | // Grab the right UVs for the vertices in this face
3075 | uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ];
3076 |
3077 | } else if ( vcount === 4 ) {
3078 |
3079 | if ( ndx === 0 ) {
3080 |
3081 | uvArr = [ uv[0], uv[1], uv[3] ];
3082 |
3083 | } else {
3084 |
3085 | uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ];
3086 |
3087 | }
3088 |
3089 | } else {
3090 |
3091 | uvArr = [ uv[0], uv[1], uv[2] ];
3092 |
3093 | }
3094 |
3095 | if ( geom.faceVertexUvs[k] === undefined ) {
3096 |
3097 | geom.faceVertexUvs[k] = [];
3098 |
3099 | }
3100 |
3101 | geom.faceVertexUvs[k].push( uvArr );
3102 |
3103 | }
3104 |
3105 | }
3106 |
3107 | } else {
3108 |
3109 | console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id );
3110 |
3111 | }
3112 |
3113 | i += maxOffset * vcount;
3114 |
3115 | }
3116 |
3117 | }
3118 |
3119 | };
3120 |
3121 | function Polygons () {
3122 |
3123 | this.material = "";
3124 | this.count = 0;
3125 | this.inputs = [];
3126 | this.vcount = null;
3127 | this.p = [];
3128 | this.geometry = new THREE.Geometry();
3129 |
3130 | }
3131 |
3132 | Polygons.prototype.setVertices = function ( vertices ) {
3133 |
3134 | for ( var i = 0; i < this.inputs.length; i ++ ) {
3135 |
3136 | if ( this.inputs[ i ].source === vertices.id ) {
3137 |
3138 | this.inputs[ i ].source = vertices.input[ 'POSITION' ].source;
3139 |
3140 | }
3141 |
3142 | }
3143 |
3144 | };
3145 |
3146 | Polygons.prototype.parse = function ( element ) {
3147 |
3148 | this.material = element.getAttribute( 'material' );
3149 | this.count = _attr_as_int( element, 'count', 0 );
3150 |
3151 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3152 |
3153 | var child = element.childNodes[ i ];
3154 |
3155 | switch ( child.nodeName ) {
3156 |
3157 | case 'input':
3158 |
3159 | this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) );
3160 | break;
3161 |
3162 | case 'vcount':
3163 |
3164 | this.vcount = _ints( child.textContent );
3165 | break;
3166 |
3167 | case 'p':
3168 |
3169 | this.p.push( _ints( child.textContent ) );
3170 | break;
3171 |
3172 | case 'ph':
3173 |
3174 | console.warn( 'polygon holes not yet supported!' );
3175 | break;
3176 |
3177 | default:
3178 | break;
3179 |
3180 | }
3181 |
3182 | }
3183 |
3184 | return this;
3185 |
3186 | };
3187 |
3188 | function Polylist () {
3189 |
3190 | Polygons.call( this );
3191 |
3192 | this.vcount = [];
3193 |
3194 | }
3195 |
3196 | Polylist.prototype = Object.create( Polygons.prototype );
3197 | Polylist.prototype.constructor = Polylist;
3198 |
3199 | function LineStrips() {
3200 |
3201 | Polygons.call( this );
3202 |
3203 | this.vcount = 1;
3204 |
3205 | }
3206 |
3207 | LineStrips.prototype = Object.create( Polygons.prototype );
3208 | LineStrips.prototype.constructor = LineStrips;
3209 |
3210 | function Triangles () {
3211 |
3212 | Polygons.call( this );
3213 |
3214 | this.vcount = 3;
3215 |
3216 | }
3217 |
3218 | Triangles.prototype = Object.create( Polygons.prototype );
3219 | Triangles.prototype.constructor = Triangles;
3220 |
3221 | function Accessor() {
3222 |
3223 | this.source = "";
3224 | this.count = 0;
3225 | this.stride = 0;
3226 | this.params = [];
3227 |
3228 | }
3229 |
3230 | Accessor.prototype.parse = function ( element ) {
3231 |
3232 | this.params = [];
3233 | this.source = element.getAttribute( 'source' );
3234 | this.count = _attr_as_int( element, 'count', 0 );
3235 | this.stride = _attr_as_int( element, 'stride', 0 );
3236 |
3237 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3238 |
3239 | var child = element.childNodes[ i ];
3240 |
3241 | if ( child.nodeName === 'param' ) {
3242 |
3243 | var param = {};
3244 | param[ 'name' ] = child.getAttribute( 'name' );
3245 | param[ 'type' ] = child.getAttribute( 'type' );
3246 | this.params.push( param );
3247 |
3248 | }
3249 |
3250 | }
3251 |
3252 | return this;
3253 |
3254 | };
3255 |
3256 | function Vertices() {
3257 |
3258 | this.input = {};
3259 |
3260 | }
3261 |
3262 | Vertices.prototype.parse = function ( element ) {
3263 |
3264 | this.id = element.getAttribute('id');
3265 |
3266 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3267 |
3268 | if ( element.childNodes[i].nodeName === 'input' ) {
3269 |
3270 | var input = ( new Input() ).parse( element.childNodes[ i ] );
3271 | this.input[ input.semantic ] = input;
3272 |
3273 | }
3274 |
3275 | }
3276 |
3277 | return this;
3278 |
3279 | };
3280 |
3281 | function Input () {
3282 |
3283 | this.semantic = "";
3284 | this.offset = 0;
3285 | this.source = "";
3286 | this.set = 0;
3287 |
3288 | }
3289 |
3290 | Input.prototype.parse = function ( element ) {
3291 |
3292 | this.semantic = element.getAttribute('semantic');
3293 | this.source = element.getAttribute('source').replace(/^#/, '');
3294 | this.set = _attr_as_int(element, 'set', -1);
3295 | this.offset = _attr_as_int(element, 'offset', 0);
3296 |
3297 | if ( this.semantic === 'TEXCOORD' && this.set < 0 ) {
3298 |
3299 | this.set = 0;
3300 |
3301 | }
3302 |
3303 | return this;
3304 |
3305 | };
3306 |
3307 | function Source ( id ) {
3308 |
3309 | this.id = id;
3310 | this.type = null;
3311 |
3312 | }
3313 |
3314 | Source.prototype.parse = function ( element ) {
3315 |
3316 | this.id = element.getAttribute( 'id' );
3317 |
3318 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3319 |
3320 | var child = element.childNodes[i];
3321 |
3322 | switch ( child.nodeName ) {
3323 |
3324 | case 'bool_array':
3325 |
3326 | this.data = _bools( child.textContent );
3327 | this.type = child.nodeName;
3328 | break;
3329 |
3330 | case 'float_array':
3331 |
3332 | this.data = _floats( child.textContent );
3333 | this.type = child.nodeName;
3334 | break;
3335 |
3336 | case 'int_array':
3337 |
3338 | this.data = _ints( child.textContent );
3339 | this.type = child.nodeName;
3340 | break;
3341 |
3342 | case 'IDREF_array':
3343 | case 'Name_array':
3344 |
3345 | this.data = _strings( child.textContent );
3346 | this.type = child.nodeName;
3347 | break;
3348 |
3349 | case 'technique_common':
3350 |
3351 | for ( var j = 0; j < child.childNodes.length; j ++ ) {
3352 |
3353 | if ( child.childNodes[ j ].nodeName === 'accessor' ) {
3354 |
3355 | this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] );
3356 | break;
3357 |
3358 | }
3359 | }
3360 | break;
3361 |
3362 | default:
3363 | // console.log(child.nodeName);
3364 | break;
3365 |
3366 | }
3367 |
3368 | }
3369 |
3370 | return this;
3371 |
3372 | };
3373 |
3374 | Source.prototype.read = function () {
3375 |
3376 | var result = [];
3377 |
3378 | //for (var i = 0; i < this.accessor.params.length; i++) {
3379 |
3380 | var param = this.accessor.params[ 0 ];
3381 |
3382 | //console.log(param.name + " " + param.type);
3383 |
3384 | switch ( param.type ) {
3385 |
3386 | case 'IDREF':
3387 | case 'Name': case 'name':
3388 | case 'float':
3389 |
3390 | return this.data;
3391 |
3392 | case 'float4x4':
3393 |
3394 | for ( var j = 0; j < this.data.length; j += 16 ) {
3395 |
3396 | var s = this.data.slice( j, j + 16 );
3397 | var m = getConvertedMat4( s );
3398 | result.push( m );
3399 | }
3400 |
3401 | break;
3402 |
3403 | default:
3404 |
3405 | console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' );
3406 | break;
3407 |
3408 | }
3409 |
3410 | //}
3411 |
3412 | return result;
3413 |
3414 | };
3415 |
3416 | function Material () {
3417 |
3418 | this.id = "";
3419 | this.name = "";
3420 | this.instance_effect = null;
3421 |
3422 | }
3423 |
3424 | Material.prototype.parse = function ( element ) {
3425 |
3426 | this.id = element.getAttribute( 'id' );
3427 | this.name = element.getAttribute( 'name' );
3428 |
3429 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3430 |
3431 | if ( element.childNodes[ i ].nodeName === 'instance_effect' ) {
3432 |
3433 | this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] );
3434 | break;
3435 |
3436 | }
3437 |
3438 | }
3439 |
3440 | return this;
3441 |
3442 | };
3443 |
3444 | function ColorOrTexture () {
3445 |
3446 | this.color = new THREE.Color();
3447 | this.color.setRGB( Math.random(), Math.random(), Math.random() );
3448 | this.color.a = 1.0;
3449 |
3450 | this.texture = null;
3451 | this.texcoord = null;
3452 | this.texOpts = null;
3453 |
3454 | }
3455 |
3456 | ColorOrTexture.prototype.isColor = function () {
3457 |
3458 | return ( this.texture === null );
3459 |
3460 | };
3461 |
3462 | ColorOrTexture.prototype.isTexture = function () {
3463 |
3464 | return ( this.texture != null );
3465 |
3466 | };
3467 |
3468 | ColorOrTexture.prototype.parse = function ( element ) {
3469 |
3470 | if (element.nodeName === 'transparent') {
3471 |
3472 | this.opaque = element.getAttribute('opaque');
3473 |
3474 | }
3475 |
3476 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3477 |
3478 | var child = element.childNodes[ i ];
3479 | if ( child.nodeType != 1 ) continue;
3480 |
3481 | switch ( child.nodeName ) {
3482 |
3483 | case 'color':
3484 |
3485 | var rgba = _floats( child.textContent );
3486 | this.color = new THREE.Color();
3487 | this.color.setRGB( rgba[0], rgba[1], rgba[2] );
3488 | this.color.a = rgba[3];
3489 | break;
3490 |
3491 | case 'texture':
3492 |
3493 | this.texture = child.getAttribute('texture');
3494 | this.texcoord = child.getAttribute('texcoord');
3495 | // Defaults from:
3496 | // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension
3497 | this.texOpts = {
3498 | offsetU: 0,
3499 | offsetV: 0,
3500 | repeatU: 1,
3501 | repeatV: 1,
3502 | wrapU: 1,
3503 | wrapV: 1
3504 | };
3505 | this.parseTexture( child );
3506 | break;
3507 |
3508 | default:
3509 | break;
3510 |
3511 | }
3512 |
3513 | }
3514 |
3515 | return this;
3516 |
3517 | };
3518 |
3519 | ColorOrTexture.prototype.parseTexture = function ( element ) {
3520 |
3521 | if ( ! element.childNodes ) return this;
3522 |
3523 | // This should be supported by Maya, 3dsMax, and MotionBuilder
3524 |
3525 | if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) {
3526 |
3527 | element = element.childNodes[1];
3528 |
3529 | if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) {
3530 |
3531 | element = element.childNodes[1];
3532 |
3533 | }
3534 |
3535 | }
3536 |
3537 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3538 |
3539 | var child = element.childNodes[ i ];
3540 |
3541 | switch ( child.nodeName ) {
3542 |
3543 | case 'offsetU':
3544 | case 'offsetV':
3545 | case 'repeatU':
3546 | case 'repeatV':
3547 |
3548 | this.texOpts[ child.nodeName ] = parseFloat( child.textContent );
3549 |
3550 | break;
3551 |
3552 | case 'wrapU':
3553 | case 'wrapV':
3554 |
3555 | // some dae have a value of true which becomes NaN via parseInt
3556 |
3557 | if ( child.textContent.toUpperCase() === 'TRUE' ) {
3558 |
3559 | this.texOpts[ child.nodeName ] = 1;
3560 |
3561 | } else {
3562 |
3563 | this.texOpts[ child.nodeName ] = parseInt( child.textContent );
3564 |
3565 | }
3566 | break;
3567 |
3568 | default:
3569 |
3570 | this.texOpts[ child.nodeName ] = child.textContent;
3571 |
3572 | break;
3573 |
3574 | }
3575 |
3576 | }
3577 |
3578 | return this;
3579 |
3580 | };
3581 |
3582 | function Shader ( type, effect ) {
3583 |
3584 | this.type = type;
3585 | this.effect = effect;
3586 | this.material = null;
3587 |
3588 | }
3589 |
3590 | Shader.prototype.parse = function ( element ) {
3591 |
3592 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3593 |
3594 | var child = element.childNodes[ i ];
3595 | if ( child.nodeType != 1 ) continue;
3596 |
3597 | switch ( child.nodeName ) {
3598 |
3599 | case 'emission':
3600 | case 'diffuse':
3601 | case 'specular':
3602 | case 'transparent':
3603 |
3604 | this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child );
3605 | break;
3606 |
3607 | case 'bump':
3608 |
3609 | // If 'bumptype' is 'heightfield', create a 'bump' property
3610 | // Else if 'bumptype' is 'normalmap', create a 'normal' property
3611 | // (Default to 'bump')
3612 | var bumpType = child.getAttribute( 'bumptype' );
3613 | if ( bumpType ) {
3614 | if ( bumpType.toLowerCase() === "heightfield" ) {
3615 | this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
3616 | } else if ( bumpType.toLowerCase() === "normalmap" ) {
3617 | this[ 'normal' ] = ( new ColorOrTexture() ).parse( child );
3618 | } else {
3619 | console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" );
3620 | this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
3621 | }
3622 | } else {
3623 | console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" );
3624 | this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
3625 | }
3626 |
3627 | break;
3628 |
3629 | case 'shininess':
3630 | case 'reflectivity':
3631 | case 'index_of_refraction':
3632 | case 'transparency':
3633 |
3634 | var f = child.querySelectorAll('float');
3635 |
3636 | if ( f.length > 0 )
3637 | this[ child.nodeName ] = parseFloat( f[ 0 ].textContent );
3638 |
3639 | break;
3640 |
3641 | default:
3642 | break;
3643 |
3644 | }
3645 |
3646 | }
3647 |
3648 | this.create();
3649 | return this;
3650 |
3651 | };
3652 |
3653 | Shader.prototype.create = function() {
3654 |
3655 | var props = {};
3656 |
3657 | var transparent = false;
3658 |
3659 | if (this['transparency'] !== undefined && this['transparent'] !== undefined) {
3660 | // convert transparent color RBG to average value
3661 | var transparentColor = this['transparent'];
3662 | var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency;
3663 |
3664 | if (transparencyLevel > 0) {
3665 | transparent = true;
3666 | props[ 'transparent' ] = true;
3667 | props[ 'opacity' ] = 1 - transparencyLevel;
3668 |
3669 | }
3670 |
3671 | }
3672 |
3673 | var keys = {
3674 | 'diffuse':'map',
3675 | 'ambient':'lightMap',
3676 | 'specular':'specularMap',
3677 | 'emission':'emissionMap',
3678 | 'bump':'bumpMap',
3679 | 'normal':'normalMap'
3680 | };
3681 |
3682 | for ( var prop in this ) {
3683 |
3684 | switch ( prop ) {
3685 |
3686 | case 'ambient':
3687 | case 'emission':
3688 | case 'diffuse':
3689 | case 'specular':
3690 | case 'bump':
3691 | case 'normal':
3692 |
3693 | var cot = this[ prop ];
3694 |
3695 | if ( cot instanceof ColorOrTexture ) {
3696 |
3697 | if ( cot.isTexture() ) {
3698 |
3699 | var samplerId = cot.texture;
3700 | var surfaceId = this.effect.sampler[samplerId];
3701 |
3702 | if ( surfaceId !== undefined && surfaceId.source !== undefined ) {
3703 |
3704 | var surface = this.effect.surface[surfaceId.source];
3705 |
3706 | if ( surface !== undefined ) {
3707 |
3708 | var image = images[ surface.init_from ];
3709 |
3710 | if ( image ) {
3711 |
3712 | var url = baseUrl + image.init_from;
3713 |
3714 | var texture;
3715 | var loader = THREE.Loader.Handlers.get( url );
3716 |
3717 | if ( loader !== null ) {
3718 |
3719 | texture = loader.load( url );
3720 |
3721 | } else {
3722 |
3723 | texture = new THREE.Texture();
3724 |
3725 | loadTextureImage( texture, url );
3726 |
3727 | }
3728 |
3729 | texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
3730 | texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
3731 | texture.offset.x = cot.texOpts.offsetU;
3732 | texture.offset.y = cot.texOpts.offsetV;
3733 | texture.repeat.x = cot.texOpts.repeatU;
3734 | texture.repeat.y = cot.texOpts.repeatV;
3735 | props[keys[prop]] = texture;
3736 |
3737 | // Texture with baked lighting?
3738 | if (prop === 'emission') props['emissive'] = 0xffffff;
3739 |
3740 | }
3741 |
3742 | }
3743 |
3744 | }
3745 |
3746 | } else if ( prop === 'diffuse' || !transparent ) {
3747 |
3748 | if ( prop === 'emission' ) {
3749 |
3750 | props[ 'emissive' ] = cot.color.getHex();
3751 |
3752 | } else {
3753 |
3754 | props[ prop ] = cot.color.getHex();
3755 |
3756 | }
3757 |
3758 | }
3759 |
3760 | }
3761 |
3762 | break;
3763 |
3764 | case 'shininess':
3765 |
3766 | props[ prop ] = this[ prop ];
3767 | break;
3768 |
3769 | case 'reflectivity':
3770 |
3771 | props[ prop ] = this[ prop ];
3772 | if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap;
3773 | props['combine'] = THREE.MixOperation; //mix regular shading with reflective component
3774 | break;
3775 |
3776 | case 'index_of_refraction':
3777 |
3778 | props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable
3779 | if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap;
3780 | break;
3781 |
3782 | case 'transparency':
3783 | // gets figured out up top
3784 | break;
3785 |
3786 | default:
3787 | break;
3788 |
3789 | }
3790 |
3791 | }
3792 |
3793 | //props[ 'shading' ] = preferredShading;
3794 | props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide;
3795 |
3796 | if ( props.diffuse !== undefined ) {
3797 |
3798 | props.color = props.diffuse;
3799 | delete props.diffuse;
3800 |
3801 | }
3802 |
3803 | switch ( this.type ) {
3804 |
3805 | case 'constant':
3806 |
3807 | if (props.emissive != undefined) props.color = props.emissive;
3808 | this.material = new THREE.MeshBasicMaterial( props );
3809 | break;
3810 |
3811 | case 'phong':
3812 | case 'blinn':
3813 |
3814 | this.material = new THREE.MeshPhongMaterial( props );
3815 | break;
3816 |
3817 | case 'lambert':
3818 | default:
3819 |
3820 | this.material = new THREE.MeshLambertMaterial( props );
3821 | break;
3822 |
3823 | }
3824 |
3825 | return this.material;
3826 |
3827 | };
3828 |
3829 | function Surface ( effect ) {
3830 |
3831 | this.effect = effect;
3832 | this.init_from = null;
3833 | this.format = null;
3834 |
3835 | }
3836 |
3837 | Surface.prototype.parse = function ( element ) {
3838 |
3839 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3840 |
3841 | var child = element.childNodes[ i ];
3842 | if ( child.nodeType != 1 ) continue;
3843 |
3844 | switch ( child.nodeName ) {
3845 |
3846 | case 'init_from':
3847 |
3848 | this.init_from = child.textContent;
3849 | break;
3850 |
3851 | case 'format':
3852 |
3853 | this.format = child.textContent;
3854 | break;
3855 |
3856 | default:
3857 |
3858 | console.log( "unhandled Surface prop: " + child.nodeName );
3859 | break;
3860 |
3861 | }
3862 |
3863 | }
3864 |
3865 | return this;
3866 |
3867 | };
3868 |
3869 | function Sampler2D ( effect ) {
3870 |
3871 | this.effect = effect;
3872 | this.source = null;
3873 | this.wrap_s = null;
3874 | this.wrap_t = null;
3875 | this.minfilter = null;
3876 | this.magfilter = null;
3877 | this.mipfilter = null;
3878 |
3879 | }
3880 |
3881 | Sampler2D.prototype.parse = function ( element ) {
3882 |
3883 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3884 |
3885 | var child = element.childNodes[ i ];
3886 | if ( child.nodeType != 1 ) continue;
3887 |
3888 | switch ( child.nodeName ) {
3889 |
3890 | case 'source':
3891 |
3892 | this.source = child.textContent;
3893 | break;
3894 |
3895 | case 'minfilter':
3896 |
3897 | this.minfilter = child.textContent;
3898 | break;
3899 |
3900 | case 'magfilter':
3901 |
3902 | this.magfilter = child.textContent;
3903 | break;
3904 |
3905 | case 'mipfilter':
3906 |
3907 | this.mipfilter = child.textContent;
3908 | break;
3909 |
3910 | case 'wrap_s':
3911 |
3912 | this.wrap_s = child.textContent;
3913 | break;
3914 |
3915 | case 'wrap_t':
3916 |
3917 | this.wrap_t = child.textContent;
3918 | break;
3919 |
3920 | default:
3921 |
3922 | console.log( "unhandled Sampler2D prop: " + child.nodeName );
3923 | break;
3924 |
3925 | }
3926 |
3927 | }
3928 |
3929 | return this;
3930 |
3931 | };
3932 |
3933 | function Effect () {
3934 |
3935 | this.id = "";
3936 | this.name = "";
3937 | this.shader = null;
3938 | this.surface = {};
3939 | this.sampler = {};
3940 |
3941 | }
3942 |
3943 | Effect.prototype.create = function () {
3944 |
3945 | if ( this.shader === null ) {
3946 |
3947 | return null;
3948 |
3949 | }
3950 |
3951 | };
3952 |
3953 | Effect.prototype.parse = function ( element ) {
3954 |
3955 | this.id = element.getAttribute( 'id' );
3956 | this.name = element.getAttribute( 'name' );
3957 |
3958 | extractDoubleSided( this, element );
3959 |
3960 | this.shader = null;
3961 |
3962 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3963 |
3964 | var child = element.childNodes[ i ];
3965 | if ( child.nodeType != 1 ) continue;
3966 |
3967 | switch ( child.nodeName ) {
3968 |
3969 | case 'profile_COMMON':
3970 |
3971 | this.parseTechnique( this.parseProfileCOMMON( child ) );
3972 | break;
3973 |
3974 | default:
3975 | break;
3976 |
3977 | }
3978 |
3979 | }
3980 |
3981 | return this;
3982 |
3983 | };
3984 |
3985 | Effect.prototype.parseNewparam = function ( element ) {
3986 |
3987 | var sid = element.getAttribute( 'sid' );
3988 |
3989 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
3990 |
3991 | var child = element.childNodes[ i ];
3992 | if ( child.nodeType != 1 ) continue;
3993 |
3994 | switch ( child.nodeName ) {
3995 |
3996 | case 'surface':
3997 |
3998 | this.surface[sid] = ( new Surface( this ) ).parse( child );
3999 | break;
4000 |
4001 | case 'sampler2D':
4002 |
4003 | this.sampler[sid] = ( new Sampler2D( this ) ).parse( child );
4004 | break;
4005 |
4006 | case 'extra':
4007 |
4008 | break;
4009 |
4010 | default:
4011 |
4012 | console.log( child.nodeName );
4013 | break;
4014 |
4015 | }
4016 |
4017 | }
4018 |
4019 | };
4020 |
4021 | Effect.prototype.parseProfileCOMMON = function ( element ) {
4022 |
4023 | var technique;
4024 |
4025 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4026 |
4027 | var child = element.childNodes[ i ];
4028 |
4029 | if ( child.nodeType != 1 ) continue;
4030 |
4031 | switch ( child.nodeName ) {
4032 |
4033 | case 'profile_COMMON':
4034 |
4035 | this.parseProfileCOMMON( child );
4036 | break;
4037 |
4038 | case 'technique':
4039 |
4040 | technique = child;
4041 | break;
4042 |
4043 | case 'newparam':
4044 |
4045 | this.parseNewparam( child );
4046 | break;
4047 |
4048 | case 'image':
4049 |
4050 | var _image = ( new _Image() ).parse( child );
4051 | images[ _image.id ] = _image;
4052 | break;
4053 |
4054 | case 'extra':
4055 | break;
4056 |
4057 | default:
4058 |
4059 | console.log( child.nodeName );
4060 | break;
4061 |
4062 | }
4063 |
4064 | }
4065 |
4066 | return technique;
4067 |
4068 | };
4069 |
4070 | Effect.prototype.parseTechnique = function ( element ) {
4071 |
4072 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4073 |
4074 | var child = element.childNodes[i];
4075 | if ( child.nodeType != 1 ) continue;
4076 |
4077 | switch ( child.nodeName ) {
4078 |
4079 | case 'constant':
4080 | case 'lambert':
4081 | case 'blinn':
4082 | case 'phong':
4083 |
4084 | this.shader = ( new Shader( child.nodeName, this ) ).parse( child );
4085 | break;
4086 | case 'extra':
4087 | this.parseExtra(child);
4088 | break;
4089 | default:
4090 | break;
4091 |
4092 | }
4093 |
4094 | }
4095 |
4096 | };
4097 |
4098 | Effect.prototype.parseExtra = function ( element ) {
4099 |
4100 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4101 |
4102 | var child = element.childNodes[i];
4103 | if ( child.nodeType != 1 ) continue;
4104 |
4105 | switch ( child.nodeName ) {
4106 |
4107 | case 'technique':
4108 | this.parseExtraTechnique( child );
4109 | break;
4110 | default:
4111 | break;
4112 |
4113 | }
4114 |
4115 | }
4116 |
4117 | };
4118 |
4119 | Effect.prototype.parseExtraTechnique = function ( element ) {
4120 |
4121 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4122 |
4123 | var child = element.childNodes[i];
4124 | if ( child.nodeType != 1 ) continue;
4125 |
4126 | switch ( child.nodeName ) {
4127 |
4128 | case 'bump':
4129 | this.shader.parse( element );
4130 | break;
4131 | default:
4132 | break;
4133 |
4134 | }
4135 |
4136 | }
4137 |
4138 | };
4139 |
4140 | function InstanceEffect () {
4141 |
4142 | this.url = "";
4143 |
4144 | }
4145 |
4146 | InstanceEffect.prototype.parse = function ( element ) {
4147 |
4148 | this.url = element.getAttribute( 'url' ).replace( /^#/, '' );
4149 | return this;
4150 |
4151 | };
4152 |
4153 | function Animation() {
4154 |
4155 | this.id = "";
4156 | this.name = "";
4157 | this.source = {};
4158 | this.sampler = [];
4159 | this.channel = [];
4160 |
4161 | }
4162 |
4163 | Animation.prototype.parse = function ( element ) {
4164 |
4165 | this.id = element.getAttribute( 'id' );
4166 | this.name = element.getAttribute( 'name' );
4167 | this.source = {};
4168 |
4169 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4170 |
4171 | var child = element.childNodes[ i ];
4172 |
4173 | if ( child.nodeType != 1 ) continue;
4174 |
4175 | switch ( child.nodeName ) {
4176 |
4177 | case 'animation':
4178 |
4179 | var anim = ( new Animation() ).parse( child );
4180 |
4181 | for ( var src in anim.source ) {
4182 |
4183 | this.source[ src ] = anim.source[ src ];
4184 |
4185 | }
4186 |
4187 | for ( var j = 0; j < anim.channel.length; j ++ ) {
4188 |
4189 | this.channel.push( anim.channel[ j ] );
4190 | this.sampler.push( anim.sampler[ j ] );
4191 |
4192 | }
4193 |
4194 | break;
4195 |
4196 | case 'source':
4197 |
4198 | var src = ( new Source() ).parse( child );
4199 | this.source[ src.id ] = src;
4200 | break;
4201 |
4202 | case 'sampler':
4203 |
4204 | this.sampler.push( ( new Sampler( this ) ).parse( child ) );
4205 | break;
4206 |
4207 | case 'channel':
4208 |
4209 | this.channel.push( ( new Channel( this ) ).parse( child ) );
4210 | break;
4211 |
4212 | default:
4213 | break;
4214 |
4215 | }
4216 |
4217 | }
4218 |
4219 | return this;
4220 |
4221 | };
4222 |
4223 | function Channel( animation ) {
4224 |
4225 | this.animation = animation;
4226 | this.source = "";
4227 | this.target = "";
4228 | this.fullSid = null;
4229 | this.sid = null;
4230 | this.dotSyntax = null;
4231 | this.arrSyntax = null;
4232 | this.arrIndices = null;
4233 | this.member = null;
4234 |
4235 | }
4236 |
4237 | Channel.prototype.parse = function ( element ) {
4238 |
4239 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' );
4240 | this.target = element.getAttribute( 'target' );
4241 |
4242 | var parts = this.target.split( '/' );
4243 |
4244 | var id = parts.shift();
4245 | var sid = parts.shift();
4246 |
4247 | var dotSyntax = ( sid.indexOf(".") >= 0 );
4248 | var arrSyntax = ( sid.indexOf("(") >= 0 );
4249 |
4250 | if ( dotSyntax ) {
4251 |
4252 | parts = sid.split(".");
4253 | this.sid = parts.shift();
4254 | this.member = parts.shift();
4255 |
4256 | } else if ( arrSyntax ) {
4257 |
4258 | var arrIndices = sid.split("(");
4259 | this.sid = arrIndices.shift();
4260 |
4261 | for (var j = 0; j < arrIndices.length; j ++ ) {
4262 |
4263 | arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') );
4264 |
4265 | }
4266 |
4267 | this.arrIndices = arrIndices;
4268 |
4269 | } else {
4270 |
4271 | this.sid = sid;
4272 |
4273 | }
4274 |
4275 | this.fullSid = sid;
4276 | this.dotSyntax = dotSyntax;
4277 | this.arrSyntax = arrSyntax;
4278 |
4279 | return this;
4280 |
4281 | };
4282 |
4283 | function Sampler ( animation ) {
4284 |
4285 | this.id = "";
4286 | this.animation = animation;
4287 | this.inputs = [];
4288 | this.input = null;
4289 | this.output = null;
4290 | this.strideOut = null;
4291 | this.interpolation = null;
4292 | this.startTime = null;
4293 | this.endTime = null;
4294 | this.duration = 0;
4295 |
4296 | }
4297 |
4298 | Sampler.prototype.parse = function ( element ) {
4299 |
4300 | this.id = element.getAttribute( 'id' );
4301 | this.inputs = [];
4302 |
4303 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4304 |
4305 | var child = element.childNodes[ i ];
4306 | if ( child.nodeType != 1 ) continue;
4307 |
4308 | switch ( child.nodeName ) {
4309 |
4310 | case 'input':
4311 |
4312 | this.inputs.push( (new Input()).parse( child ) );
4313 | break;
4314 |
4315 | default:
4316 | break;
4317 |
4318 | }
4319 |
4320 | }
4321 |
4322 | return this;
4323 |
4324 | };
4325 |
4326 | Sampler.prototype.create = function () {
4327 |
4328 | for ( var i = 0; i < this.inputs.length; i ++ ) {
4329 |
4330 | var input = this.inputs[ i ];
4331 | var source = this.animation.source[ input.source ];
4332 |
4333 | switch ( input.semantic ) {
4334 |
4335 | case 'INPUT':
4336 |
4337 | this.input = source.read();
4338 | break;
4339 |
4340 | case 'OUTPUT':
4341 |
4342 | this.output = source.read();
4343 | this.strideOut = source.accessor.stride;
4344 | break;
4345 |
4346 | case 'INTERPOLATION':
4347 |
4348 | this.interpolation = source.read();
4349 | break;
4350 |
4351 | case 'IN_TANGENT':
4352 |
4353 | break;
4354 |
4355 | case 'OUT_TANGENT':
4356 |
4357 | break;
4358 |
4359 | default:
4360 |
4361 | console.log(input.semantic);
4362 | break;
4363 |
4364 | }
4365 |
4366 | }
4367 |
4368 | this.startTime = 0;
4369 | this.endTime = 0;
4370 | this.duration = 0;
4371 |
4372 | if ( this.input.length ) {
4373 |
4374 | this.startTime = 100000000;
4375 | this.endTime = -100000000;
4376 |
4377 | for ( var i = 0; i < this.input.length; i ++ ) {
4378 |
4379 | this.startTime = Math.min( this.startTime, this.input[ i ] );
4380 | this.endTime = Math.max( this.endTime, this.input[ i ] );
4381 |
4382 | }
4383 |
4384 | this.duration = this.endTime - this.startTime;
4385 |
4386 | }
4387 |
4388 | };
4389 |
4390 | Sampler.prototype.getData = function ( type, ndx, member ) {
4391 |
4392 | var data;
4393 |
4394 | if ( type === 'matrix' && this.strideOut === 16 ) {
4395 |
4396 | data = this.output[ ndx ];
4397 |
4398 | } else if ( this.strideOut > 1 ) {
4399 |
4400 | data = [];
4401 | ndx *= this.strideOut;
4402 |
4403 | for ( var i = 0; i < this.strideOut; ++ i ) {
4404 |
4405 | data[ i ] = this.output[ ndx + i ];
4406 |
4407 | }
4408 |
4409 | if ( this.strideOut === 3 ) {
4410 |
4411 | switch ( type ) {
4412 |
4413 | case 'rotate':
4414 | case 'translate':
4415 |
4416 | fixCoords( data, -1 );
4417 | break;
4418 |
4419 | case 'scale':
4420 |
4421 | fixCoords( data, 1 );
4422 | break;
4423 |
4424 | }
4425 |
4426 | } else if ( this.strideOut === 4 && type === 'matrix' ) {
4427 |
4428 | fixCoords( data, -1 );
4429 |
4430 | }
4431 |
4432 | } else {
4433 |
4434 | data = this.output[ ndx ];
4435 |
4436 | if ( member && type === 'translate' ) {
4437 | data = getConvertedTranslation( member, data );
4438 | }
4439 |
4440 | }
4441 |
4442 | return data;
4443 |
4444 | };
4445 |
4446 | function Key ( time ) {
4447 |
4448 | this.targets = [];
4449 | this.time = time;
4450 |
4451 | }
4452 |
4453 | Key.prototype.addTarget = function ( fullSid, transform, member, data ) {
4454 |
4455 | this.targets.push( {
4456 | sid: fullSid,
4457 | member: member,
4458 | transform: transform,
4459 | data: data
4460 | } );
4461 |
4462 | };
4463 |
4464 | Key.prototype.apply = function ( opt_sid ) {
4465 |
4466 | for ( var i = 0; i < this.targets.length; ++ i ) {
4467 |
4468 | var target = this.targets[ i ];
4469 |
4470 | if ( !opt_sid || target.sid === opt_sid ) {
4471 |
4472 | target.transform.update( target.data, target.member );
4473 |
4474 | }
4475 |
4476 | }
4477 |
4478 | };
4479 |
4480 | Key.prototype.getTarget = function ( fullSid ) {
4481 |
4482 | for ( var i = 0; i < this.targets.length; ++ i ) {
4483 |
4484 | if ( this.targets[ i ].sid === fullSid ) {
4485 |
4486 | return this.targets[ i ];
4487 |
4488 | }
4489 |
4490 | }
4491 |
4492 | return null;
4493 |
4494 | };
4495 |
4496 | Key.prototype.hasTarget = function ( fullSid ) {
4497 |
4498 | for ( var i = 0; i < this.targets.length; ++ i ) {
4499 |
4500 | if ( this.targets[ i ].sid === fullSid ) {
4501 |
4502 | return true;
4503 |
4504 | }
4505 |
4506 | }
4507 |
4508 | return false;
4509 |
4510 | };
4511 |
4512 | // TODO: Currently only doing linear interpolation. Should support full COLLADA spec.
4513 | Key.prototype.interpolate = function ( nextKey, time ) {
4514 |
4515 | for ( var i = 0, l = this.targets.length; i < l; i ++ ) {
4516 |
4517 | var target = this.targets[ i ],
4518 | nextTarget = nextKey.getTarget( target.sid ),
4519 | data;
4520 |
4521 | if ( target.transform.type !== 'matrix' && nextTarget ) {
4522 |
4523 | var scale = ( time - this.time ) / ( nextKey.time - this.time ),
4524 | nextData = nextTarget.data,
4525 | prevData = target.data;
4526 |
4527 | if ( scale < 0 ) scale = 0;
4528 | if ( scale > 1 ) scale = 1;
4529 |
4530 | if ( prevData.length ) {
4531 |
4532 | data = [];
4533 |
4534 | for ( var j = 0; j < prevData.length; ++ j ) {
4535 |
4536 | data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale;
4537 |
4538 | }
4539 |
4540 | } else {
4541 |
4542 | data = prevData + ( nextData - prevData ) * scale;
4543 |
4544 | }
4545 |
4546 | } else {
4547 |
4548 | data = target.data;
4549 |
4550 | }
4551 |
4552 | target.transform.update( data, target.member );
4553 |
4554 | }
4555 |
4556 | };
4557 |
4558 | // Camera
4559 | function Camera() {
4560 |
4561 | this.id = "";
4562 | this.name = "";
4563 | this.technique = "";
4564 |
4565 | }
4566 |
4567 | Camera.prototype.parse = function ( element ) {
4568 |
4569 | this.id = element.getAttribute( 'id' );
4570 | this.name = element.getAttribute( 'name' );
4571 |
4572 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4573 |
4574 | var child = element.childNodes[ i ];
4575 | if ( child.nodeType != 1 ) continue;
4576 |
4577 | switch ( child.nodeName ) {
4578 |
4579 | case 'optics':
4580 |
4581 | this.parseOptics( child );
4582 | break;
4583 |
4584 | default:
4585 | break;
4586 |
4587 | }
4588 |
4589 | }
4590 |
4591 | return this;
4592 |
4593 | };
4594 |
4595 | Camera.prototype.parseOptics = function ( element ) {
4596 |
4597 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4598 |
4599 | if ( element.childNodes[ i ].nodeName === 'technique_common' ) {
4600 |
4601 | var technique = element.childNodes[ i ];
4602 |
4603 | for ( var j = 0; j < technique.childNodes.length; j ++ ) {
4604 |
4605 | this.technique = technique.childNodes[ j ].nodeName;
4606 |
4607 | if ( this.technique === 'perspective' ) {
4608 |
4609 | var perspective = technique.childNodes[ j ];
4610 |
4611 | for ( var k = 0; k < perspective.childNodes.length; k ++ ) {
4612 |
4613 | var param = perspective.childNodes[ k ];
4614 |
4615 | switch ( param.nodeName ) {
4616 |
4617 | case 'yfov':
4618 | this.yfov = param.textContent;
4619 | break;
4620 | case 'xfov':
4621 | this.xfov = param.textContent;
4622 | break;
4623 | case 'znear':
4624 | this.znear = param.textContent;
4625 | break;
4626 | case 'zfar':
4627 | this.zfar = param.textContent;
4628 | break;
4629 | case 'aspect_ratio':
4630 | this.aspect_ratio = param.textContent;
4631 | break;
4632 |
4633 | }
4634 |
4635 | }
4636 |
4637 | } else if ( this.technique === 'orthographic' ) {
4638 |
4639 | var orthographic = technique.childNodes[ j ];
4640 |
4641 | for ( var k = 0; k < orthographic.childNodes.length; k ++ ) {
4642 |
4643 | var param = orthographic.childNodes[ k ];
4644 |
4645 | switch ( param.nodeName ) {
4646 |
4647 | case 'xmag':
4648 | this.xmag = param.textContent;
4649 | break;
4650 | case 'ymag':
4651 | this.ymag = param.textContent;
4652 | break;
4653 | case 'znear':
4654 | this.znear = param.textContent;
4655 | break;
4656 | case 'zfar':
4657 | this.zfar = param.textContent;
4658 | break;
4659 | case 'aspect_ratio':
4660 | this.aspect_ratio = param.textContent;
4661 | break;
4662 |
4663 | }
4664 |
4665 | }
4666 |
4667 | }
4668 |
4669 | }
4670 |
4671 | }
4672 |
4673 | }
4674 |
4675 | return this;
4676 |
4677 | };
4678 |
4679 | function InstanceCamera() {
4680 |
4681 | this.url = "";
4682 |
4683 | }
4684 |
4685 | InstanceCamera.prototype.parse = function ( element ) {
4686 |
4687 | this.url = element.getAttribute('url').replace(/^#/, '');
4688 |
4689 | return this;
4690 |
4691 | };
4692 |
4693 | // Light
4694 |
4695 | function Light() {
4696 |
4697 | this.id = "";
4698 | this.name = "";
4699 | this.technique = "";
4700 |
4701 | }
4702 |
4703 | Light.prototype.parse = function ( element ) {
4704 |
4705 | this.id = element.getAttribute( 'id' );
4706 | this.name = element.getAttribute( 'name' );
4707 |
4708 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4709 |
4710 | var child = element.childNodes[ i ];
4711 | if ( child.nodeType != 1 ) continue;
4712 |
4713 | switch ( child.nodeName ) {
4714 |
4715 | case 'technique_common':
4716 |
4717 | this.parseCommon( child );
4718 | break;
4719 |
4720 | case 'technique':
4721 |
4722 | this.parseTechnique( child );
4723 | break;
4724 |
4725 | default:
4726 | break;
4727 |
4728 | }
4729 |
4730 | }
4731 |
4732 | return this;
4733 |
4734 | };
4735 |
4736 | Light.prototype.parseCommon = function ( element ) {
4737 |
4738 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4739 |
4740 | switch ( element.childNodes[ i ].nodeName ) {
4741 |
4742 | case 'directional':
4743 | case 'point':
4744 | case 'spot':
4745 | case 'ambient':
4746 |
4747 | this.technique = element.childNodes[ i ].nodeName;
4748 |
4749 | var light = element.childNodes[ i ];
4750 |
4751 | for ( var j = 0; j < light.childNodes.length; j ++ ) {
4752 |
4753 | var child = light.childNodes[j];
4754 |
4755 | switch ( child.nodeName ) {
4756 |
4757 | case 'color':
4758 |
4759 | var rgba = _floats( child.textContent );
4760 | this.color = new THREE.Color(0);
4761 | this.color.setRGB( rgba[0], rgba[1], rgba[2] );
4762 | this.color.a = rgba[3];
4763 | break;
4764 |
4765 | case 'falloff_angle':
4766 |
4767 | this.falloff_angle = parseFloat( child.textContent );
4768 | break;
4769 |
4770 | case 'quadratic_attenuation':
4771 | var f = parseFloat( child.textContent );
4772 | this.distance = f ? Math.sqrt( 1 / f ) : 0;
4773 | }
4774 |
4775 | }
4776 |
4777 | }
4778 |
4779 | }
4780 |
4781 | return this;
4782 |
4783 | };
4784 |
4785 | Light.prototype.parseTechnique = function ( element ) {
4786 |
4787 | this.profile = element.getAttribute( 'profile' );
4788 |
4789 | for ( var i = 0; i < element.childNodes.length; i ++ ) {
4790 |
4791 | var child = element.childNodes[ i ];
4792 |
4793 | switch ( child.nodeName ) {
4794 |
4795 | case 'intensity':
4796 |
4797 | this.intensity = parseFloat(child.textContent);
4798 | break;
4799 |
4800 | }
4801 |
4802 | }
4803 |
4804 | return this;
4805 |
4806 | };
4807 |
4808 | function InstanceLight() {
4809 |
4810 | this.url = "";
4811 |
4812 | }
4813 |
4814 | InstanceLight.prototype.parse = function ( element ) {
4815 |
4816 | this.url = element.getAttribute('url').replace(/^#/, '');
4817 |
4818 | return this;
4819 |
4820 | };
4821 |
4822 | function KinematicsModel( ) {
4823 |
4824 | this.id = '';
4825 | this.name = '';
4826 | this.joints = [];
4827 | this.links = [];
4828 |
4829 | }
4830 |
4831 | KinematicsModel.prototype.parse = function( element ) {
4832 |
4833 | this.id = element.getAttribute('id');
4834 | this.name = element.getAttribute('name');
4835 | this.joints = [];
4836 | this.links = [];
4837 |
4838 | for (var i = 0; i < element.childNodes.length; i ++ ) {
4839 |
4840 | var child = element.childNodes[ i ];
4841 | if ( child.nodeType != 1 ) continue;
4842 |
4843 | switch ( child.nodeName ) {
4844 |
4845 | case 'technique_common':
4846 |
4847 | this.parseCommon(child);
4848 | break;
4849 |
4850 | default:
4851 | break;
4852 |
4853 | }
4854 |
4855 | }
4856 |
4857 | return this;
4858 |
4859 | };
4860 |
4861 | KinematicsModel.prototype.parseCommon = function( element ) {
4862 |
4863 | for (var i = 0; i < element.childNodes.length; i ++ ) {
4864 |
4865 | var child = element.childNodes[ i ];
4866 | if ( child.nodeType != 1 ) continue;
4867 |
4868 | switch ( element.childNodes[ i ].nodeName ) {
4869 |
4870 | case 'joint':
4871 | this.joints.push( (new Joint()).parse(child) );
4872 | break;
4873 |
4874 | case 'link':
4875 | this.links.push( (new Link()).parse(child) );
4876 | break;
4877 |
4878 | default:
4879 | break;
4880 |
4881 | }
4882 |
4883 | }
4884 |
4885 | return this;
4886 |
4887 | };
4888 |
4889 | function Joint( ) {
4890 |
4891 | this.sid = '';
4892 | this.name = '';
4893 | this.axis = new THREE.Vector3();
4894 | this.limits = {
4895 | min: 0,
4896 | max: 0
4897 | };
4898 | this.type = '';
4899 | this.static = false;
4900 | this.zeroPosition = 0.0;
4901 | this.middlePosition = 0.0;
4902 |
4903 | }
4904 |
4905 | Joint.prototype.parse = function( element ) {
4906 |
4907 | this.sid = element.getAttribute('sid');
4908 | this.name = element.getAttribute('name');
4909 | this.axis = new THREE.Vector3();
4910 | this.limits = {
4911 | min: 0,
4912 | max: 0
4913 | };
4914 | this.type = '';
4915 | this.static = false;
4916 | this.zeroPosition = 0.0;
4917 | this.middlePosition = 0.0;
4918 |
4919 | var axisElement = element.querySelector('axis');
4920 | var _axis = _floats(axisElement.textContent);
4921 | this.axis = getConvertedVec3(_axis, 0);
4922 |
4923 | var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360;
4924 | var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360;
4925 |
4926 | this.limits = {
4927 | min: min,
4928 | max: max
4929 | };
4930 |
4931 | var jointTypes = [ 'prismatic', 'revolute' ];
4932 | for (var i = 0; i < jointTypes.length; i ++ ) {
4933 |
4934 | var type = jointTypes[ i ];
4935 |
4936 | var jointElement = element.querySelector(type);
4937 |
4938 | if ( jointElement ) {
4939 |
4940 | this.type = type;
4941 |
4942 | }
4943 |
4944 | }
4945 |
4946 | // if the min is equal to or somehow greater than the max, consider the joint static
4947 | if ( this.limits.min >= this.limits.max ) {
4948 |
4949 | this.static = true;
4950 |
4951 | }
4952 |
4953 | this.middlePosition = (this.limits.min + this.limits.max) / 2.0;
4954 | return this;
4955 |
4956 | };
4957 |
4958 | function Link( ) {
4959 |
4960 | this.sid = '';
4961 | this.name = '';
4962 | this.transforms = [];
4963 | this.attachments = [];
4964 |
4965 | }
4966 |
4967 | Link.prototype.parse = function( element ) {
4968 |
4969 | this.sid = element.getAttribute('sid');
4970 | this.name = element.getAttribute('name');
4971 | this.transforms = [];
4972 | this.attachments = [];
4973 |
4974 | for (var i = 0; i < element.childNodes.length; i ++ ) {
4975 |
4976 | var child = element.childNodes[ i ];
4977 | if ( child.nodeType != 1 ) continue;
4978 |
4979 | switch ( child.nodeName ) {
4980 |
4981 | case 'attachment_full':
4982 | this.attachments.push( (new Attachment()).parse(child) );
4983 | break;
4984 |
4985 | case 'rotate':
4986 | case 'translate':
4987 | case 'matrix':
4988 |
4989 | this.transforms.push( (new Transform()).parse(child) );
4990 | break;
4991 |
4992 | default:
4993 |
4994 | break;
4995 |
4996 | }
4997 |
4998 | }
4999 |
5000 | return this;
5001 |
5002 | };
5003 |
5004 | function Attachment( ) {
5005 |
5006 | this.joint = '';
5007 | this.transforms = [];
5008 | this.links = [];
5009 |
5010 | }
5011 |
5012 | Attachment.prototype.parse = function( element ) {
5013 |
5014 | this.joint = element.getAttribute('joint').split('/').pop();
5015 | this.links = [];
5016 |
5017 | for (var i = 0; i < element.childNodes.length; i ++ ) {
5018 |
5019 | var child = element.childNodes[ i ];
5020 | if ( child.nodeType != 1 ) continue;
5021 |
5022 | switch ( child.nodeName ) {
5023 |
5024 | case 'link':
5025 | this.links.push( (new Link()).parse(child) );
5026 | break;
5027 |
5028 | case 'rotate':
5029 | case 'translate':
5030 | case 'matrix':
5031 |
5032 | this.transforms.push( (new Transform()).parse(child) );
5033 | break;
5034 |
5035 | default:
5036 |
5037 | break;
5038 |
5039 | }
5040 |
5041 | }
5042 |
5043 | return this;
5044 |
5045 | };
5046 |
5047 | function _source( element ) {
5048 |
5049 | var id = element.getAttribute( 'id' );
5050 |
5051 | if ( sources[ id ] != undefined ) {
5052 |
5053 | return sources[ id ];
5054 |
5055 | }
5056 |
5057 | sources[ id ] = ( new Source(id )).parse( element );
5058 | return sources[ id ];
5059 |
5060 | }
5061 |
5062 | function _nsResolver( nsPrefix ) {
5063 |
5064 | if ( nsPrefix === "dae" ) {
5065 |
5066 | return "http://www.collada.org/2005/11/COLLADASchema";
5067 |
5068 | }
5069 |
5070 | return null;
5071 |
5072 | }
5073 |
5074 | function _bools( str ) {
5075 |
5076 | var raw = _strings( str );
5077 | var data = [];
5078 |
5079 | for ( var i = 0, l = raw.length; i < l; i ++ ) {
5080 |
5081 | data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false );
5082 |
5083 | }
5084 |
5085 | return data;
5086 |
5087 | }
5088 |
5089 | function _floats( str ) {
5090 |
5091 | var raw = _strings(str);
5092 | var data = [];
5093 |
5094 | for ( var i = 0, l = raw.length; i < l; i ++ ) {
5095 |
5096 | data.push( parseFloat( raw[ i ] ) );
5097 |
5098 | }
5099 |
5100 | return data;
5101 |
5102 | }
5103 |
5104 | function _ints( str ) {
5105 |
5106 | var raw = _strings( str );
5107 | var data = [];
5108 |
5109 | for ( var i = 0, l = raw.length; i < l; i ++ ) {
5110 |
5111 | data.push( parseInt( raw[ i ], 10 ) );
5112 |
5113 | }
5114 |
5115 | return data;
5116 |
5117 | }
5118 |
5119 | function _strings( str ) {
5120 |
5121 | return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : [];
5122 |
5123 | }
5124 |
5125 | function _trimString( str ) {
5126 |
5127 | return str.replace( /^\s+/, "" ).replace( /\s+$/, "" );
5128 |
5129 | }
5130 |
5131 | function _attr_as_float( element, name, defaultValue ) {
5132 |
5133 | if ( element.hasAttribute( name ) ) {
5134 |
5135 | return parseFloat( element.getAttribute( name ) );
5136 |
5137 | } else {
5138 |
5139 | return defaultValue;
5140 |
5141 | }
5142 |
5143 | }
5144 |
5145 | function _attr_as_int( element, name, defaultValue ) {
5146 |
5147 | if ( element.hasAttribute( name ) ) {
5148 |
5149 | return parseInt( element.getAttribute( name ), 10) ;
5150 |
5151 | } else {
5152 |
5153 | return defaultValue;
5154 |
5155 | }
5156 |
5157 | }
5158 |
5159 | function _attr_as_string( element, name, defaultValue ) {
5160 |
5161 | if ( element.hasAttribute( name ) ) {
5162 |
5163 | return element.getAttribute( name );
5164 |
5165 | } else {
5166 |
5167 | return defaultValue;
5168 |
5169 | }
5170 |
5171 | }
5172 |
5173 | function _format_float( f, num ) {
5174 |
5175 | if ( f === undefined ) {
5176 |
5177 | var s = '0.';
5178 |
5179 | while ( s.length < num + 2 ) {
5180 |
5181 | s += '0';
5182 |
5183 | }
5184 |
5185 | return s;
5186 |
5187 | }
5188 |
5189 | num = num || 2;
5190 |
5191 | var parts = f.toString().split( '.' );
5192 | parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0";
5193 |
5194 | while ( parts[ 1 ].length < num ) {
5195 |
5196 | parts[ 1 ] += '0';
5197 |
5198 | }
5199 |
5200 | return parts.join( '.' );
5201 |
5202 | }
5203 |
5204 | function loadTextureImage ( texture, url ) {
5205 |
5206 | var loader = new THREE.ImageLoader();
5207 |
5208 | loader.load( url, function ( image ) {
5209 |
5210 | texture.image = image;
5211 | texture.needsUpdate = true;
5212 |
5213 | } );
5214 |
5215 | }
5216 |
5217 | function extractDoubleSided( obj, element ) {
5218 |
5219 | obj.doubleSided = false;
5220 |
5221 | var node = element.querySelectorAll('extra double_sided')[0];
5222 |
5223 | if ( node ) {
5224 |
5225 | if ( node && parseInt( node.textContent, 10 ) === 1 ) {
5226 |
5227 | obj.doubleSided = true;
5228 |
5229 | }
5230 |
5231 | }
5232 |
5233 | }
5234 |
5235 | // Up axis conversion
5236 |
5237 | function setUpConversion() {
5238 |
5239 | if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) {
5240 |
5241 | upConversion = null;
5242 |
5243 | } else {
5244 |
5245 | switch ( colladaUp ) {
5246 |
5247 | case 'X':
5248 |
5249 | upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ';
5250 | break;
5251 |
5252 | case 'Y':
5253 |
5254 | upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ';
5255 | break;
5256 |
5257 | case 'Z':
5258 |
5259 | upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY';
5260 | break;
5261 |
5262 | }
5263 |
5264 | }
5265 |
5266 | }
5267 |
5268 | function fixCoords( data, sign ) {
5269 |
5270 | if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) {
5271 |
5272 | return;
5273 |
5274 | }
5275 |
5276 | switch ( upConversion ) {
5277 |
5278 | case 'XtoY':
5279 |
5280 | var tmp = data[ 0 ];
5281 | data[ 0 ] = sign * data[ 1 ];
5282 | data[ 1 ] = tmp;
5283 | break;
5284 |
5285 | case 'XtoZ':
5286 |
5287 | var tmp = data[ 2 ];
5288 | data[ 2 ] = data[ 1 ];
5289 | data[ 1 ] = data[ 0 ];
5290 | data[ 0 ] = tmp;
5291 | break;
5292 |
5293 | case 'YtoX':
5294 |
5295 | var tmp = data[ 0 ];
5296 | data[ 0 ] = data[ 1 ];
5297 | data[ 1 ] = sign * tmp;
5298 | break;
5299 |
5300 | case 'YtoZ':
5301 |
5302 | var tmp = data[ 1 ];
5303 | data[ 1 ] = sign * data[ 2 ];
5304 | data[ 2 ] = tmp;
5305 | break;
5306 |
5307 | case 'ZtoX':
5308 |
5309 | var tmp = data[ 0 ];
5310 | data[ 0 ] = data[ 1 ];
5311 | data[ 1 ] = data[ 2 ];
5312 | data[ 2 ] = tmp;
5313 | break;
5314 |
5315 | case 'ZtoY':
5316 |
5317 | var tmp = data[ 1 ];
5318 | data[ 1 ] = data[ 2 ];
5319 | data[ 2 ] = sign * tmp;
5320 | break;
5321 |
5322 | }
5323 |
5324 | }
5325 |
5326 | function getConvertedTranslation( axis, data ) {
5327 |
5328 | if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) {
5329 |
5330 | return data;
5331 |
5332 | }
5333 |
5334 | switch ( axis ) {
5335 | case 'X':
5336 | data = upConversion === 'XtoY' ? data * -1 : data;
5337 | break;
5338 | case 'Y':
5339 | data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data;
5340 | break;
5341 | case 'Z':
5342 | data = upConversion === 'ZtoY' ? data * -1 : data ;
5343 | break;
5344 | default:
5345 | break;
5346 | }
5347 |
5348 | return data;
5349 | }
5350 |
5351 | function getConvertedVec3( data, offset ) {
5352 |
5353 | var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ];
5354 | fixCoords( arr, -1 );
5355 | return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] );
5356 |
5357 | }
5358 |
5359 | function getConvertedMat4( data ) {
5360 |
5361 | if ( options.convertUpAxis ) {
5362 |
5363 | // First fix rotation and scale
5364 |
5365 | // Columns first
5366 | var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ];
5367 | fixCoords( arr, -1 );
5368 | data[ 0 ] = arr[ 0 ];
5369 | data[ 4 ] = arr[ 1 ];
5370 | data[ 8 ] = arr[ 2 ];
5371 | arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ];
5372 | fixCoords( arr, -1 );
5373 | data[ 1 ] = arr[ 0 ];
5374 | data[ 5 ] = arr[ 1 ];
5375 | data[ 9 ] = arr[ 2 ];
5376 | arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ];
5377 | fixCoords( arr, -1 );
5378 | data[ 2 ] = arr[ 0 ];
5379 | data[ 6 ] = arr[ 1 ];
5380 | data[ 10 ] = arr[ 2 ];
5381 | // Rows second
5382 | arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ];
5383 | fixCoords( arr, -1 );
5384 | data[ 0 ] = arr[ 0 ];
5385 | data[ 1 ] = arr[ 1 ];
5386 | data[ 2 ] = arr[ 2 ];
5387 | arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ];
5388 | fixCoords( arr, -1 );
5389 | data[ 4 ] = arr[ 0 ];
5390 | data[ 5 ] = arr[ 1 ];
5391 | data[ 6 ] = arr[ 2 ];
5392 | arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ];
5393 | fixCoords( arr, -1 );
5394 | data[ 8 ] = arr[ 0 ];
5395 | data[ 9 ] = arr[ 1 ];
5396 | data[ 10 ] = arr[ 2 ];
5397 |
5398 | // Now fix translation
5399 | arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ];
5400 | fixCoords( arr, -1 );
5401 | data[ 3 ] = arr[ 0 ];
5402 | data[ 7 ] = arr[ 1 ];
5403 | data[ 11 ] = arr[ 2 ];
5404 |
5405 | }
5406 |
5407 | return new THREE.Matrix4().set(
5408 | data[0], data[1], data[2], data[3],
5409 | data[4], data[5], data[6], data[7],
5410 | data[8], data[9], data[10], data[11],
5411 | data[12], data[13], data[14], data[15]
5412 | );
5413 |
5414 | }
5415 |
5416 | function getConvertedIndex( index ) {
5417 |
5418 | if ( index > -1 && index < 3 ) {
5419 |
5420 | var members = [ 'X', 'Y', 'Z' ],
5421 | indices = { X: 0, Y: 1, Z: 2 };
5422 |
5423 | index = getConvertedMember( members[ index ] );
5424 | index = indices[ index ];
5425 |
5426 | }
5427 |
5428 | return index;
5429 |
5430 | }
5431 |
5432 | function getConvertedMember( member ) {
5433 |
5434 | if ( options.convertUpAxis ) {
5435 |
5436 | switch ( member ) {
5437 |
5438 | case 'X':
5439 |
5440 | switch ( upConversion ) {
5441 |
5442 | case 'XtoY':
5443 | case 'XtoZ':
5444 | case 'YtoX':
5445 |
5446 | member = 'Y';
5447 | break;
5448 |
5449 | case 'ZtoX':
5450 |
5451 | member = 'Z';
5452 | break;
5453 |
5454 | }
5455 |
5456 | break;
5457 |
5458 | case 'Y':
5459 |
5460 | switch ( upConversion ) {
5461 |
5462 | case 'XtoY':
5463 | case 'YtoX':
5464 | case 'ZtoX':
5465 |
5466 | member = 'X';
5467 | break;
5468 |
5469 | case 'XtoZ':
5470 | case 'YtoZ':
5471 | case 'ZtoY':
5472 |
5473 | member = 'Z';
5474 | break;
5475 |
5476 | }
5477 |
5478 | break;
5479 |
5480 | case 'Z':
5481 |
5482 | switch ( upConversion ) {
5483 |
5484 | case 'XtoZ':
5485 |
5486 | member = 'X';
5487 | break;
5488 |
5489 | case 'YtoZ':
5490 | case 'ZtoX':
5491 | case 'ZtoY':
5492 |
5493 | member = 'Y';
5494 | break;
5495 |
5496 | }
5497 |
5498 | break;
5499 |
5500 | }
5501 |
5502 | }
5503 |
5504 | return member;
5505 |
5506 | }
5507 |
5508 | return {
5509 |
5510 | load: load,
5511 | parse: parse,
5512 | setPreferredShading: setPreferredShading,
5513 | applySkin: applySkin,
5514 | geometries : geometries,
5515 | options: options
5516 |
5517 | };
5518 |
5519 | };
5520 |
--------------------------------------------------------------------------------
/lib/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 |
9 | // This set of controls performs orbiting, dollying (zooming), and panning.
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
11 | //
12 | // Orbit - left mouse / touch: one finger move
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
14 | // Pan - right mouse, or arrow keys / touch: three finter swipe
15 |
16 | THREE.OrbitControls = function ( object, domElement ) {
17 |
18 | this.object = object;
19 |
20 | this.domElement = ( domElement !== undefined ) ? domElement : document;
21 |
22 | // Set to false to disable this control
23 | this.enabled = true;
24 |
25 | // "target" sets the location of focus, where the object orbits around
26 | this.target = new THREE.Vector3();
27 |
28 | // How far you can dolly in and out ( PerspectiveCamera only )
29 | this.minDistance = 0;
30 | this.maxDistance = Infinity;
31 |
32 | // How far you can zoom in and out ( OrthographicCamera only )
33 | this.minZoom = 0;
34 | this.maxZoom = Infinity;
35 |
36 | // How far you can orbit vertically, upper and lower limits.
37 | // Range is 0 to Math.PI radians.
38 | this.minPolarAngle = 0; // radians
39 | this.maxPolarAngle = Math.PI; // radians
40 |
41 | // How far you can orbit horizontally, upper and lower limits.
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
43 | this.minAzimuthAngle = - Infinity; // radians
44 | this.maxAzimuthAngle = Infinity; // radians
45 |
46 | // Set to true to enable damping (inertia)
47 | // If damping is enabled, you must call controls.update() in your animation loop
48 | this.enableDamping = false;
49 | this.dampingFactor = 0.25;
50 |
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
52 | // Set to false to disable zooming
53 | this.enableZoom = true;
54 | this.zoomSpeed = 1.0;
55 |
56 | // Set to false to disable rotating
57 | this.enableRotate = true;
58 | this.rotateSpeed = 1.0;
59 |
60 | // Set to false to disable panning
61 | this.enablePan = true;
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
63 |
64 | // Set to true to automatically rotate around the target
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
66 | this.autoRotate = false;
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
68 |
69 | // Set to false to disable use of the keys
70 | this.enableKeys = true;
71 |
72 | // The four arrow keys
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
74 |
75 | // Mouse buttons
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
77 |
78 | // for reset
79 | this.target0 = this.target.clone();
80 | this.position0 = this.object.position.clone();
81 | this.zoom0 = this.object.zoom;
82 |
83 | //
84 | // public methods
85 | //
86 |
87 | this.getPolarAngle = function () {
88 |
89 | return phi;
90 |
91 | };
92 |
93 | this.getAzimuthalAngle = function () {
94 |
95 | return theta;
96 |
97 | };
98 |
99 | this.reset = function () {
100 |
101 | scope.target.copy( scope.target0 );
102 | scope.object.position.copy( scope.position0 );
103 | scope.object.zoom = scope.zoom0;
104 |
105 | scope.object.updateProjectionMatrix();
106 | scope.dispatchEvent( changeEvent );
107 |
108 | scope.update();
109 |
110 | state = STATE.NONE;
111 |
112 | };
113 |
114 | // this method is exposed, but perhaps it would be better if we can make it private...
115 | this.update = function() {
116 |
117 | var offset = new THREE.Vector3();
118 |
119 | // so camera.up is the orbit axis
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
121 | var quatInverse = quat.clone().inverse();
122 |
123 | var lastPosition = new THREE.Vector3();
124 | var lastQuaternion = new THREE.Quaternion();
125 |
126 | return function () {
127 |
128 | var position = scope.object.position;
129 |
130 | offset.copy( position ).sub( scope.target );
131 |
132 | // rotate offset to "y-axis-is-up" space
133 | offset.applyQuaternion( quat );
134 |
135 | // angle from z-axis around y-axis
136 | spherical.setFromVector3( offset );
137 |
138 | if ( scope.autoRotate && state === STATE.NONE ) {
139 |
140 | rotateLeft( getAutoRotationAngle() );
141 |
142 | }
143 |
144 | spherical.theta += sphericalDelta.theta;
145 | spherical.phi += sphericalDelta.phi;
146 |
147 | // restrict theta to be between desired limits
148 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
149 |
150 | // restrict phi to be between desired limits
151 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
152 |
153 | spherical.makeSafe();
154 |
155 |
156 | spherical.radius *= scale;
157 |
158 | // restrict radius to be between desired limits
159 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
160 |
161 | // move target to panned location
162 | scope.target.add( panOffset );
163 |
164 | offset.setFromSpherical( spherical );
165 |
166 | // rotate offset back to "camera-up-vector-is-up" space
167 | offset.applyQuaternion( quatInverse );
168 |
169 | position.copy( scope.target ).add( offset );
170 |
171 | scope.object.lookAt( scope.target );
172 |
173 | if ( scope.enableDamping === true ) {
174 |
175 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
176 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
177 |
178 | } else {
179 |
180 | sphericalDelta.set( 0, 0, 0 );
181 |
182 | }
183 |
184 | scale = 1;
185 | panOffset.set( 0, 0, 0 );
186 |
187 | // update condition is:
188 | // min(camera displacement, camera rotation in radians)^2 > EPS
189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
190 |
191 | if ( zoomChanged ||
192 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
194 |
195 | scope.dispatchEvent( changeEvent );
196 |
197 | lastPosition.copy( scope.object.position );
198 | lastQuaternion.copy( scope.object.quaternion );
199 | zoomChanged = false;
200 |
201 | return true;
202 |
203 | }
204 |
205 | return false;
206 |
207 | };
208 |
209 | }();
210 |
211 | this.dispose = function() {
212 |
213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
215 | scope.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
216 | scope.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
217 |
218 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
219 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
220 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
221 |
222 | document.removeEventListener( 'mousemove', onMouseMove, false );
223 | document.removeEventListener( 'mouseup', onMouseUp, false );
224 | document.removeEventListener( 'mouseout', onMouseUp, false );
225 |
226 | window.removeEventListener( 'keydown', onKeyDown, false );
227 |
228 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
229 |
230 | };
231 |
232 | //
233 | // internals
234 | //
235 |
236 | var scope = this;
237 |
238 | var changeEvent = { type: 'change' };
239 | var startEvent = { type: 'start' };
240 | var endEvent = { type: 'end' };
241 |
242 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
243 |
244 | var state = STATE.NONE;
245 |
246 | var EPS = 0.000001;
247 |
248 | // current position in spherical coordinates
249 | var spherical = new THREE.Spherical();
250 | var sphericalDelta = new THREE.Spherical();
251 |
252 | var scale = 1;
253 | var panOffset = new THREE.Vector3();
254 | var zoomChanged = false;
255 |
256 | var rotateStart = new THREE.Vector2();
257 | var rotateEnd = new THREE.Vector2();
258 | var rotateDelta = new THREE.Vector2();
259 |
260 | var panStart = new THREE.Vector2();
261 | var panEnd = new THREE.Vector2();
262 | var panDelta = new THREE.Vector2();
263 |
264 | var dollyStart = new THREE.Vector2();
265 | var dollyEnd = new THREE.Vector2();
266 | var dollyDelta = new THREE.Vector2();
267 |
268 | function getAutoRotationAngle() {
269 |
270 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
271 |
272 | }
273 |
274 | function getZoomScale() {
275 |
276 | return Math.pow( 0.95, scope.zoomSpeed );
277 |
278 | }
279 |
280 | function rotateLeft( angle ) {
281 |
282 | sphericalDelta.theta -= angle;
283 |
284 | }
285 |
286 | function rotateUp( angle ) {
287 |
288 | sphericalDelta.phi -= angle;
289 |
290 | }
291 |
292 | var panLeft = function() {
293 |
294 | var v = new THREE.Vector3();
295 |
296 | return function panLeft( distance, objectMatrix ) {
297 |
298 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
299 | v.multiplyScalar( - distance );
300 |
301 | panOffset.add( v );
302 |
303 | };
304 |
305 | }();
306 |
307 | var panUp = function() {
308 |
309 | var v = new THREE.Vector3();
310 |
311 | return function panUp( distance, objectMatrix ) {
312 |
313 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix
314 | v.multiplyScalar( distance );
315 |
316 | panOffset.add( v );
317 |
318 | };
319 |
320 | }();
321 |
322 | // deltaX and deltaY are in pixels; right and down are positive
323 | var pan = function() {
324 |
325 | var offset = new THREE.Vector3();
326 |
327 | return function( deltaX, deltaY ) {
328 |
329 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
330 |
331 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
332 |
333 | // perspective
334 | var position = scope.object.position;
335 | offset.copy( position ).sub( scope.target );
336 | var targetDistance = offset.length();
337 |
338 | // half of the fov is center to top of screen
339 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
340 |
341 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
342 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
343 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
344 |
345 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
346 |
347 | // orthographic
348 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
349 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
350 |
351 | } else {
352 |
353 | // camera neither orthographic nor perspective
354 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
355 | scope.enablePan = false;
356 |
357 | }
358 |
359 | };
360 |
361 | }();
362 |
363 | function dollyIn( dollyScale ) {
364 |
365 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
366 |
367 | scale /= dollyScale;
368 |
369 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
370 |
371 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
372 | scope.object.updateProjectionMatrix();
373 | zoomChanged = true;
374 |
375 | } else {
376 |
377 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
378 | scope.enableZoom = false;
379 |
380 | }
381 |
382 | }
383 |
384 | function dollyOut( dollyScale ) {
385 |
386 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
387 |
388 | scale *= dollyScale;
389 |
390 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
391 |
392 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
393 | scope.object.updateProjectionMatrix();
394 | zoomChanged = true;
395 |
396 | } else {
397 |
398 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
399 | scope.enableZoom = false;
400 |
401 | }
402 |
403 | }
404 |
405 | //
406 | // event callbacks - update the object state
407 | //
408 |
409 | function handleMouseDownRotate( event ) {
410 |
411 | //console.log( 'handleMouseDownRotate' );
412 |
413 | rotateStart.set( event.clientX, event.clientY );
414 |
415 | }
416 |
417 | function handleMouseDownDolly( event ) {
418 |
419 | //console.log( 'handleMouseDownDolly' );
420 |
421 | dollyStart.set( event.clientX, event.clientY );
422 |
423 | }
424 |
425 | function handleMouseDownPan( event ) {
426 |
427 | //console.log( 'handleMouseDownPan' );
428 |
429 | panStart.set( event.clientX, event.clientY );
430 |
431 | }
432 |
433 | function handleMouseMoveRotate( event ) {
434 |
435 | //console.log( 'handleMouseMoveRotate' );
436 |
437 | rotateEnd.set( event.clientX, event.clientY );
438 | rotateDelta.subVectors( rotateEnd, rotateStart );
439 |
440 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
441 |
442 | // rotating across whole screen goes 360 degrees around
443 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
444 |
445 | // rotating up and down along whole screen attempts to go 360, but limited to 180
446 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
447 |
448 | rotateStart.copy( rotateEnd );
449 |
450 | scope.update();
451 |
452 | }
453 |
454 | function handleMouseMoveDolly( event ) {
455 |
456 | //console.log( 'handleMouseMoveDolly' );
457 |
458 | dollyEnd.set( event.clientX, event.clientY );
459 |
460 | dollyDelta.subVectors( dollyEnd, dollyStart );
461 |
462 | if ( dollyDelta.y > 0 ) {
463 |
464 | dollyIn( getZoomScale() );
465 |
466 | } else if ( dollyDelta.y < 0 ) {
467 |
468 | dollyOut( getZoomScale() );
469 |
470 | }
471 |
472 | dollyStart.copy( dollyEnd );
473 |
474 | scope.update();
475 |
476 | }
477 |
478 | function handleMouseMovePan( event ) {
479 |
480 | //console.log( 'handleMouseMovePan' );
481 |
482 | panEnd.set( event.clientX, event.clientY );
483 |
484 | panDelta.subVectors( panEnd, panStart );
485 |
486 | pan( panDelta.x, panDelta.y );
487 |
488 | panStart.copy( panEnd );
489 |
490 | scope.update();
491 |
492 | }
493 |
494 | function handleMouseUp( event ) {
495 |
496 | //console.log( 'handleMouseUp' );
497 |
498 | }
499 |
500 | function handleMouseWheel( event ) {
501 |
502 | //console.log( 'handleMouseWheel' );
503 |
504 | var delta = 0;
505 |
506 | if ( event.wheelDelta !== undefined ) {
507 |
508 | // WebKit / Opera / Explorer 9
509 |
510 | delta = event.wheelDelta;
511 |
512 | } else if ( event.detail !== undefined ) {
513 |
514 | // Firefox
515 |
516 | delta = - event.detail;
517 |
518 | }
519 |
520 | if ( delta > 0 ) {
521 |
522 | dollyOut( getZoomScale() );
523 |
524 | } else if ( delta < 0 ) {
525 |
526 | dollyIn( getZoomScale() );
527 |
528 | }
529 |
530 | scope.update();
531 |
532 | }
533 |
534 | function handleKeyDown( event ) {
535 |
536 | //console.log( 'handleKeyDown' );
537 |
538 | switch ( event.keyCode ) {
539 |
540 | case scope.keys.UP:
541 | pan( 0, scope.keyPanSpeed );
542 | scope.update();
543 | break;
544 |
545 | case scope.keys.BOTTOM:
546 | pan( 0, - scope.keyPanSpeed );
547 | scope.update();
548 | break;
549 |
550 | case scope.keys.LEFT:
551 | pan( scope.keyPanSpeed, 0 );
552 | scope.update();
553 | break;
554 |
555 | case scope.keys.RIGHT:
556 | pan( - scope.keyPanSpeed, 0 );
557 | scope.update();
558 | break;
559 |
560 | }
561 |
562 | }
563 |
564 | function handleTouchStartRotate( event ) {
565 |
566 | //console.log( 'handleTouchStartRotate' );
567 |
568 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
569 |
570 | }
571 |
572 | function handleTouchStartDolly( event ) {
573 |
574 | //console.log( 'handleTouchStartDolly' );
575 |
576 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
577 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
578 |
579 | var distance = Math.sqrt( dx * dx + dy * dy );
580 |
581 | dollyStart.set( 0, distance );
582 |
583 | }
584 |
585 | function handleTouchStartPan( event ) {
586 |
587 | //console.log( 'handleTouchStartPan' );
588 |
589 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
590 |
591 | }
592 |
593 | function handleTouchMoveRotate( event ) {
594 |
595 | //console.log( 'handleTouchMoveRotate' );
596 |
597 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
598 | rotateDelta.subVectors( rotateEnd, rotateStart );
599 |
600 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
601 |
602 | // rotating across whole screen goes 360 degrees around
603 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
604 |
605 | // rotating up and down along whole screen attempts to go 360, but limited to 180
606 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
607 |
608 | rotateStart.copy( rotateEnd );
609 |
610 | scope.update();
611 |
612 | }
613 |
614 | function handleTouchMoveDolly( event ) {
615 |
616 | //console.log( 'handleTouchMoveDolly' );
617 |
618 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
619 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
620 |
621 | var distance = Math.sqrt( dx * dx + dy * dy );
622 |
623 | dollyEnd.set( 0, distance );
624 |
625 | dollyDelta.subVectors( dollyEnd, dollyStart );
626 |
627 | if ( dollyDelta.y > 0 ) {
628 |
629 | dollyOut( getZoomScale() );
630 |
631 | } else if ( dollyDelta.y < 0 ) {
632 |
633 | dollyIn( getZoomScale() );
634 |
635 | }
636 |
637 | dollyStart.copy( dollyEnd );
638 |
639 | scope.update();
640 |
641 | }
642 |
643 | function handleTouchMovePan( event ) {
644 |
645 | //console.log( 'handleTouchMovePan' );
646 |
647 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
648 |
649 | panDelta.subVectors( panEnd, panStart );
650 |
651 | pan( panDelta.x, panDelta.y );
652 |
653 | panStart.copy( panEnd );
654 |
655 | scope.update();
656 |
657 | }
658 |
659 | function handleTouchEnd( event ) {
660 |
661 | //console.log( 'handleTouchEnd' );
662 |
663 | }
664 |
665 | //
666 | // event handlers - FSM: listen for events and reset state
667 | //
668 |
669 | function onMouseDown( event ) {
670 |
671 | if ( scope.enabled === false ) return;
672 |
673 | event.preventDefault();
674 |
675 | if ( event.button === scope.mouseButtons.ORBIT ) {
676 |
677 | if ( scope.enableRotate === false ) return;
678 |
679 | handleMouseDownRotate( event );
680 |
681 | state = STATE.ROTATE;
682 |
683 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
684 |
685 | if ( scope.enableZoom === false ) return;
686 |
687 | handleMouseDownDolly( event );
688 |
689 | state = STATE.DOLLY;
690 |
691 | } else if ( event.button === scope.mouseButtons.PAN ) {
692 |
693 | if ( scope.enablePan === false ) return;
694 |
695 | handleMouseDownPan( event );
696 |
697 | state = STATE.PAN;
698 |
699 | }
700 |
701 | if ( state !== STATE.NONE ) {
702 |
703 | document.addEventListener( 'mousemove', onMouseMove, false );
704 | document.addEventListener( 'mouseup', onMouseUp, false );
705 | document.addEventListener( 'mouseout', onMouseUp, false );
706 |
707 | scope.dispatchEvent( startEvent );
708 |
709 | }
710 |
711 | }
712 |
713 | function onMouseMove( event ) {
714 |
715 | if ( scope.enabled === false ) return;
716 |
717 | event.preventDefault();
718 |
719 | if ( state === STATE.ROTATE ) {
720 |
721 | if ( scope.enableRotate === false ) return;
722 |
723 | handleMouseMoveRotate( event );
724 |
725 | } else if ( state === STATE.DOLLY ) {
726 |
727 | if ( scope.enableZoom === false ) return;
728 |
729 | handleMouseMoveDolly( event );
730 |
731 | } else if ( state === STATE.PAN ) {
732 |
733 | if ( scope.enablePan === false ) return;
734 |
735 | handleMouseMovePan( event );
736 |
737 | }
738 |
739 | }
740 |
741 | function onMouseUp( event ) {
742 |
743 | if ( scope.enabled === false ) return;
744 |
745 | handleMouseUp( event );
746 |
747 | document.removeEventListener( 'mousemove', onMouseMove, false );
748 | document.removeEventListener( 'mouseup', onMouseUp, false );
749 | document.removeEventListener( 'mouseout', onMouseUp, false );
750 |
751 | scope.dispatchEvent( endEvent );
752 |
753 | state = STATE.NONE;
754 |
755 | }
756 |
757 | function onMouseWheel( event ) {
758 |
759 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
760 |
761 | event.preventDefault();
762 | event.stopPropagation();
763 |
764 | handleMouseWheel( event );
765 |
766 | scope.dispatchEvent( startEvent ); // not sure why these are here...
767 | scope.dispatchEvent( endEvent );
768 |
769 | }
770 |
771 | function onKeyDown( event ) {
772 |
773 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
774 |
775 | handleKeyDown( event );
776 |
777 | }
778 |
779 | function onTouchStart( event ) {
780 |
781 | if ( scope.enabled === false ) return;
782 |
783 | switch ( event.touches.length ) {
784 |
785 | case 1: // one-fingered touch: rotate
786 |
787 | if ( scope.enableRotate === false ) return;
788 |
789 | handleTouchStartRotate( event );
790 |
791 | state = STATE.TOUCH_ROTATE;
792 |
793 | break;
794 |
795 | case 2: // two-fingered touch: dolly
796 |
797 | if ( scope.enableZoom === false ) return;
798 |
799 | handleTouchStartDolly( event );
800 |
801 | state = STATE.TOUCH_DOLLY;
802 |
803 | break;
804 |
805 | case 3: // three-fingered touch: pan
806 |
807 | if ( scope.enablePan === false ) return;
808 |
809 | handleTouchStartPan( event );
810 |
811 | state = STATE.TOUCH_PAN;
812 |
813 | break;
814 |
815 | default:
816 |
817 | state = STATE.NONE;
818 |
819 | }
820 |
821 | if ( state !== STATE.NONE ) {
822 |
823 | scope.dispatchEvent( startEvent );
824 |
825 | }
826 |
827 | }
828 |
829 | function onTouchMove( event ) {
830 |
831 | if ( scope.enabled === false ) return;
832 |
833 | event.preventDefault();
834 | event.stopPropagation();
835 |
836 | switch ( event.touches.length ) {
837 |
838 | case 1: // one-fingered touch: rotate
839 |
840 | if ( scope.enableRotate === false ) return;
841 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
842 |
843 | handleTouchMoveRotate( event );
844 |
845 | break;
846 |
847 | case 2: // two-fingered touch: dolly
848 |
849 | if ( scope.enableZoom === false ) return;
850 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
851 |
852 | handleTouchMoveDolly( event );
853 |
854 | break;
855 |
856 | case 3: // three-fingered touch: pan
857 |
858 | if ( scope.enablePan === false ) return;
859 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
860 |
861 | handleTouchMovePan( event );
862 |
863 | break;
864 |
865 | default:
866 |
867 | state = STATE.NONE;
868 |
869 | }
870 |
871 | }
872 |
873 | function onTouchEnd( event ) {
874 |
875 | if ( scope.enabled === false ) return;
876 |
877 | handleTouchEnd( event );
878 |
879 | scope.dispatchEvent( endEvent );
880 |
881 | state = STATE.NONE;
882 |
883 | }
884 |
885 | function onContextMenu( event ) {
886 |
887 | event.preventDefault();
888 |
889 | }
890 |
891 | //
892 |
893 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
894 |
895 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
896 | scope.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
897 | scope.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
898 |
899 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
900 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
901 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
902 |
903 | window.addEventListener( 'keydown', onKeyDown, false );
904 |
905 | // force an update at start
906 |
907 | this.update();
908 |
909 | };
910 |
911 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
912 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
913 |
914 | Object.defineProperties( THREE.OrbitControls.prototype, {
915 |
916 | center: {
917 |
918 | get: function () {
919 |
920 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
921 | return this.target;
922 |
923 | }
924 |
925 | },
926 |
927 | // backward compatibility
928 |
929 | noZoom: {
930 |
931 | get: function () {
932 |
933 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
934 | return ! this.enableZoom;
935 |
936 | },
937 |
938 | set: function ( value ) {
939 |
940 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
941 | this.enableZoom = ! value;
942 |
943 | }
944 |
945 | },
946 |
947 | noRotate: {
948 |
949 | get: function () {
950 |
951 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
952 | return ! this.enableRotate;
953 |
954 | },
955 |
956 | set: function ( value ) {
957 |
958 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
959 | this.enableRotate = ! value;
960 |
961 | }
962 |
963 | },
964 |
965 | noPan: {
966 |
967 | get: function () {
968 |
969 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
970 | return ! this.enablePan;
971 |
972 | },
973 |
974 | set: function ( value ) {
975 |
976 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
977 | this.enablePan = ! value;
978 |
979 | }
980 |
981 | },
982 |
983 | noKeys: {
984 |
985 | get: function () {
986 |
987 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
988 | return ! this.enableKeys;
989 |
990 | },
991 |
992 | set: function ( value ) {
993 |
994 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
995 | this.enableKeys = ! value;
996 |
997 | }
998 |
999 | },
1000 |
1001 | staticMoving : {
1002 |
1003 | get: function () {
1004 |
1005 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1006 | return ! this.constraint.enableDamping;
1007 |
1008 | },
1009 |
1010 | set: function ( value ) {
1011 |
1012 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1013 | this.constraint.enableDamping = ! value;
1014 |
1015 | }
1016 |
1017 | },
1018 |
1019 | dynamicDampingFactor : {
1020 |
1021 | get: function () {
1022 |
1023 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1024 | return this.constraint.dampingFactor;
1025 |
1026 | },
1027 |
1028 | set: function ( value ) {
1029 |
1030 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1031 | this.constraint.dampingFactor = value;
1032 |
1033 | }
1034 |
1035 | }
1036 |
1037 | } );
1038 |
--------------------------------------------------------------------------------
/lib/three-license.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright © 2010-2016 three.js authors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/screenshot_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daign/clipping-with-caps/4909816e0bcd931d5b0fd2e1dc9714ec9e10de93/screenshot_01.png
--------------------------------------------------------------------------------
/screenshot_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daign/clipping-with-caps/4909816e0bcd931d5b0fd2e1dc9714ec9e10de93/screenshot_02.png
--------------------------------------------------------------------------------