@@ -12,11 +12,86 @@ var RenderableModel = Renderable.RenderableModel;
12
12
var BLACK = new THREE . Color ( 'black' ) ;
13
13
14
14
15
+ // Set camera near and far planes close to sphere with given center
16
+ // and radius, assuming the camera is already oriented to look at this
17
+ // sphere.
18
+ function shrinkFrustumPlanes ( camera , center , radius , distOffset = 0.1 ) {
19
+ // distOffset = 0.1 --> 10% of radius
20
+
21
+ // Find distance from camera to edges of sphere
22
+ const dist = camera . position . distanceTo ( center ) ;
23
+ const nearEdge = dist - radius ;
24
+ const farEdge = dist + radius ;
25
+
26
+ // Set near/far sufficiently close to edges of sphere
27
+ camera . near = ( 1 - distOffset ) * nearEdge ,
28
+ camera . far = ( 1 + distOffset ) * farEdge ;
29
+
30
+ // Bound near plane away from zero
31
+ camera . near = Math . max ( camera . near , 0.01 * radius ) ;
32
+ }
33
+
34
+ // Set camera near and far planes with some headroom around sphere
35
+ // with given center and radius, assuming the camera is already
36
+ // oriented to look at this sphere.
37
+ function safeFrustumPlanes ( camera , center , radius , allowZoom = 20 ) {
38
+ // Find distance from camera to edges of sphere
39
+ const dist = camera . position . distanceTo ( center ) ;
40
+ const nearEdge = dist - radius ;
41
+ const farEdge = dist + radius ;
42
+
43
+ // Set near/far sufficiently far from edge of sphere to allow some zooming
44
+ camera . near = ( 1 / allowZoom ) * nearEdge ;
45
+ camera . far = allowZoom * farEdge ;
46
+
47
+ // Bound near plane away from zero
48
+ camera . near = Math . max ( camera . near , 0.001 * radius ) ;
49
+ }
50
+
51
+ function lookAtSphere ( camera , center , radius ) {
52
+ if ( ! camera . isPerspectiveCamera ) {
53
+ console . error ( "Expecting a perspective camera." ) ;
54
+ }
55
+
56
+ // Compute distance based on FOV
57
+ const radScale = 1.5 ; // Include this much more than the sphere
58
+ const distance = ( radScale * radius ) / Math . tan ( 0.5 * camera . fov * Math . PI / 180 ) ;
59
+
60
+ // Place camera such that the model is in the -z direction from the camera
61
+ camera . position . setX ( center . x ) ;
62
+ camera . position . setY ( center . y ) ;
63
+ camera . position . setZ ( center . z + distance ) ;
64
+
65
+ // Look at scene center
66
+ camera . lookAt ( center . clone ( ) ) ;
67
+
68
+ // Set near and far planes to include sphere with a narrow margin
69
+ //shrinkFrustumPlanes(camera, center, radius);
70
+
71
+ // Set near and far planes to include sphere with a wide margin for zooming
72
+ safeFrustumPlanes ( camera , center , radius ) ;
73
+
74
+ // Update matrix
75
+ camera . updateProjectionMatrix ( ) ;
76
+ }
77
+
78
+ // TODO: Make this available as a general utility somewhere?
79
+ function computeSceneBoundingSphere ( scene ) {
80
+ // FIXME: The Box3.setFromObject implementation is not great,
81
+ // replace with something that reuses bounding box computations
82
+ // of the underlying objects
83
+ const box = new THREE . Box3 ( ) ;
84
+ box . setFromObject ( scene ) ;
85
+ return box . getBoundingSphere ( ) ;
86
+ }
87
+
88
+
15
89
var PreviewView = RenderableView . extend ( {
16
90
17
91
initialize : function ( ) {
18
92
RenderableView . prototype . initialize . apply ( this , arguments ) ;
19
93
94
+ this . _resetCameraNeeded = true ;
20
95
this . _rebuildNeeded = true ;
21
96
22
97
} ,
@@ -39,6 +114,10 @@ var PreviewView = RenderableView.extend({
39
114
} ,
40
115
41
116
onChildChange : function ( ) {
117
+ // Enabling this line will reset the camera
118
+ // when any changes are made to the child
119
+ //this._resetCameraNeeded = true;
120
+
42
121
this . _rebuildNeeded = true ;
43
122
} ,
44
123
@@ -47,8 +126,6 @@ var PreviewView = RenderableView.extend({
47
126
var obj = this . model . get ( 'child' ) . obj ;
48
127
49
128
this . clearScene ( ) ;
50
- // cameras need to be added to scene
51
- this . scene . add ( this . camera ) ;
52
129
53
130
if ( obj instanceof THREE . Object3D ) {
54
131
@@ -71,7 +148,7 @@ var PreviewView = RenderableView.extend({
71
148
shading : THREE . FlatShading ,
72
149
} ) ;
73
150
} else {
74
- material = new THREE . MeshStandardMaterial ( {
151
+ material = new THREE . MeshLambertMaterial ( {
75
152
color : '#ffffff' ,
76
153
} ) ;
77
154
}
@@ -107,13 +184,44 @@ var PreviewView = RenderableView.extend({
107
184
var mesh = new THREE . Mesh ( geometry , mat ) ;
108
185
this . scene . add ( mesh ) ;
109
186
187
+ } else {
188
+
189
+ console . log ( "Unexpected object in preview, scene will be empty:" , obj ) ;
190
+
191
+ }
192
+
193
+ // Reset camera initially and on later requests
194
+ if ( this . _resetCameraNeeded ) {
195
+ this . resetCamera ( ) ; // Depends on this.scene to be correctly set up
196
+ this . _resetCameraNeeded = false ;
110
197
}
111
198
199
+ // Cameras need to be added to scene
200
+ this . scene . add ( this . camera ) ;
201
+
112
202
// Clear at end to ensure that any changes to obj does not
113
203
// cause infinite rebuild chain.
114
204
this . _rebuildNeeded = false ;
115
205
} ,
116
206
207
+ resetCamera : function ( ) {
208
+ // Compute bounding sphere for entire scene
209
+ const sphere = computeSceneBoundingSphere ( this . scene ) ;
210
+
211
+ // Update camera to include bounding sphere
212
+ lookAtSphere ( this . camera , sphere . center , sphere . radius ) ;
213
+
214
+ // Update controls with new target
215
+ const control = this . controls [ 0 ] ;
216
+ control . target . copy ( sphere . center ) ;
217
+ control . target0 . copy ( sphere . center ) ;
218
+ control . update ( ) ;
219
+
220
+ // Position light up to the left and behind camera
221
+ const dist = 2.5 * ( this . camera . position . z - sphere . center . z ) ;
222
+ this . pointLight . position . set ( - dist , dist , dist ) ;
223
+ } ,
224
+
117
225
clearScene : function ( ) {
118
226
// this.controls.reset();
119
227
this . scene . remove . apply ( this . scene , this . scene . children . slice ( ) ) ;
@@ -129,9 +237,10 @@ var PreviewView = RenderableView.extend({
129
237
} ,
130
238
131
239
lazyRendererSetup : function ( ) {
132
- this . camera = new THREE . PerspectiveCamera ( 60 , 1.0 ) ; // aspect is updated by this.updateSize()
133
- this . camera . position . set ( - 40 , 40 , 40 ) ;
134
- this . camera . lookAt ( new THREE . Vector3 ( 0 , 0 , 0 ) ) ;
240
+ this . camera = new THREE . PerspectiveCamera ( 60 , 1.0 ) ;
241
+ // aspect is updated by this.updateSize()
242
+ // position and lookat target is updated to fit scene
243
+ // by this.resetCamera() via constructScene()
135
244
136
245
// Update aspect ratio of camera:
137
246
this . updateSize ( ) ;
@@ -142,9 +251,7 @@ var PreviewView = RenderableView.extend({
142
251
this . scene . background = BLACK ;
143
252
144
253
// Lights
145
- this . pointLight = new THREE . PointLight ( '#ffffff' , 1 , 0 ) ;
146
- this . pointLight . position . set ( - 100 , 100 , 100 ) ;
147
- this . pointLight . lookAt ( new THREE . Vector3 ( 0 , 0 , 0 ) ) ;
254
+ this . pointLight = new THREE . PointLight ( '#ffffff' , 1 , 0 , 0 ) ;
148
255
this . ambLight = new THREE . AmbientLight ( '#ffffff' , 0.5 ) ;
149
256
this . camera . add ( this . ambLight ) ;
150
257
this . camera . add ( this . pointLight ) ;
0 commit comments