aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/OrientedShape3DRetained.java
blob: 4fe4026e3c463f67ad00d08a44078b29085455cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
/*
 * Copyright 1999-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 */

package javax.media.j3d;

import javax.vecmath.AxisAngle4d;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4d;

class OrientedShape3DRetained extends Shape3DRetained {

    static final int ALIGNMENT_CHANGED          = LAST_DEFINED_BIT << 1;
    static final int AXIS_CHANGED               = LAST_DEFINED_BIT << 2;
    static final int ROTATION_CHANGED           = LAST_DEFINED_BIT << 3;
    static final int CONSTANT_SCALE_CHANGED     = LAST_DEFINED_BIT << 4;
    static final int SCALE_FACTOR_CHANGED       = LAST_DEFINED_BIT << 5;


    int mode = OrientedShape3D.ROTATE_ABOUT_AXIS;

    // Axis about which to rotate.
    Vector3f axis = new Vector3f(0.0f, 1.0f, 0.0f);
    Point3f rotationPoint = new Point3f(0.0f, 0.0f, 1.0f);
    private Vector3d nAxis = new Vector3d(0.0, 1.0, 0.0); // normalized axis

    // reused temporaries
    private Point3d viewPosition = new Point3d();
    private Point3d yUpPoint = new Point3d();

    private Vector3d eyeVec = new Vector3d();
    private Vector3d yUp = new Vector3d();
    private Vector3d zAxis  = new Vector3d();
    private Vector3d yAxis  = new Vector3d();
    private Vector3d vector = new Vector3d();

    private AxisAngle4d aa = new AxisAngle4d();

    private Transform3D xform = new Transform3D(); // used several times
    private Transform3D zRotate = new Transform3D();

    // For scale invariant mode
    boolean constantScale = false;
    double scaleFactor = 1.0;

    // Frequently used variables for scale invariant computation
    // Left and right Vworld to Clip coordinates transforms
    private Transform3D left_xform = new Transform3D();
    private Transform3D right_xform = new Transform3D();

    // Transform for scaling the OrientedShape3D to correct for
    // perspective foreshortening
    Transform3D scaleXform = new Transform3D();

    // Variables for converting between clip to local world coords
    private Vector4d im_vec[] = {new Vector4d(), new Vector4d()};
    private Vector4d lvec = new Vector4d();

    boolean orientedTransformDirty = true;

    private final Object transformLock = new Object();
    private Transform3D[] orientedTransforms = new Transform3D[1];
    static final double EPSILON = 1.0e-6;


    /**
     * Constructs a OrientedShape3D node with default parameters.
     * The default values are as follows:
     * <ul>
     * alignment mode : ROTATE_ABOUT_AXIS<br>
     * alignment axis : Y-axis (0,1,0)<br>
     * rotation point : (0,0,1)<br>
     *</ul>
     */
    public OrientedShape3DRetained() {
	super();
        this.nodeType = NodeRetained.ORIENTEDSHAPE3D;
    }

    // initializes alignment mode
    void initAlignmentMode(int mode) {
	this.mode = mode;
    }

    /**
     * Sets the alignment mode.
     * @param mode one of: ROTATE_ABOUT_AXIS or ROTATE_ABOUT_POINT
     */
    void setAlignmentMode(int mode) {
	if (this.mode != mode) {
	    initAlignmentMode(mode);
	    sendChangedMessage(ALIGNMENT_CHANGED, new Integer(mode));
	}
    }

    /**
     * Retrieves the alignment mode.
     * @return one of: ROTATE_ABOUT_AXIS or ROTATE_ABOUT_POINT
     */
    int getAlignmentMode() {
	return(mode);
    }

    // initializes alignment axis
    void initAlignmentAxis(Vector3f axis) {
	initAlignmentAxis(axis.x, axis.y, axis.z);
    }

    // initializes alignment axis
    void initAlignmentAxis(float x, float y, float z) {
        this.axis.set(x,y,z);
        double invMag;
        invMag =  1.0/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
        nAxis.x = (double)axis.x*invMag;
        nAxis.y = (double)axis.y*invMag;
        nAxis.z = (double)axis.z*invMag;
    }

    /**
     * Sets the new alignment axis.  This is the ray about which this
     * OrientedShape3D rotates when the mode is ROTATE_ABOUT_AXIS.
     * @param axis the new alignment axis
     */
    void setAlignmentAxis(Vector3f axis) {
        setAlignmentAxis(axis.x, axis.y, axis.z);
    }

    /**
     * Sets the new alignment axis.  This is the ray about which this
     * OrientedShape3D rotates when the mode is ROTATE_ABOUT_AXIS.
     * @param x the x component of the alignment axis
     * @param y the y component of the alignment axis
     * @param z the z component of the alignment axis
     */
    void setAlignmentAxis(float x, float y, float z) {
        initAlignmentAxis(x,y,z);

	if (mode == OrientedShape3D.ROTATE_ABOUT_AXIS) {
	    sendChangedMessage(AXIS_CHANGED, new Vector3f(x,y,z));
	}
    }

    /**
     * Retrieves the alignment axis of this OrientedShape3D node,
     * and copies it into the specified vector.
     * @param axis the vector that will contain the alignment axis
     */
    void getAlignmentAxis(Vector3f axis)  {
        axis.set(this.axis);
    }

    // initializes rotation point
    void initRotationPoint(Point3f point) {
	rotationPoint.set(point);
    }

    // initializes rotation point
    void initRotationPoint(float x, float y, float z) {
	rotationPoint.set(x,y,z);
    }

    /**
     * Sets the new rotation point.  This is the point about which the
     * OrientedShape3D rotates when the mode is ROTATE_ABOUT_POINT.
     * @param point the new rotation point
     */
    void setRotationPoint(Point3f point) {
	setRotationPoint(point.x, point.y, point.z);
    }

    /**
     * Sets the new rotation point.  This is the point about which the
     * OrientedShape3D rotates when the mode is ROTATE_ABOUT_POINT.
     * @param x the x component of the rotation point
     * @param y the y component of the rotation point
     * @param z the z component of the rotation point
     */
    void setRotationPoint(float x, float y, float z) {
	initRotationPoint(x,y,z);

	if (mode == OrientedShape3D.ROTATE_ABOUT_POINT) {
	    sendChangedMessage(ROTATION_CHANGED, new Point3f(x,y,z));
	}
    }

    /**
     * Retrieves the rotation point of this OrientedShape3D node,
     * and copies it into the specified vector.
     * @param axis the point that will contain the rotation point
     */
    void getRotationPoint(Point3f point)  {
        point.set(rotationPoint);
    }

    void setConstantScaleEnable(boolean enable) {
	if(constantScale != enable) {
	    initConstantScaleEnable(enable);
	    sendChangedMessage(CONSTANT_SCALE_CHANGED, new Boolean(enable));
	}
    }

    boolean getConstantScaleEnable() {
	return constantScale;
    }

    void initConstantScaleEnable(boolean cons_scale) {
	constantScale = cons_scale;
    }

    void setScale(double scale) {
	initScale(scale);
	if(constantScale)
	    sendChangedMessage(SCALE_FACTOR_CHANGED, new Double(scale));
    }

    void initScale(double scale) {
	scaleFactor = scale;
    }

    double getScale() {
	return scaleFactor;
    }

    void sendChangedMessage(int component, Object attr) {
        J3dMessage changeMessage = new J3dMessage();
        changeMessage.type = J3dMessage.ORIENTEDSHAPE3D_CHANGED;
        changeMessage.threads = targetThreads ;
        changeMessage.universe = universe;
        changeMessage.args[0] = getGeomAtomsArray(mirrorShape3D);
	changeMessage.args[1] = new Integer(component);
	changeMessage.args[2] = attr;
	OrientedShape3DRetained[] o3dArr =
	    new OrientedShape3DRetained[mirrorShape3D.size()];
	mirrorShape3D.toArray(o3dArr);
	changeMessage.args[3] = o3dArr;
	changeMessage.args[4] = this;
        VirtualUniverse.mc.processMessage(changeMessage);
    }

    @Override
    void updateImmediateMirrorObject(Object[] args) {
	int component = ((Integer)args[1]).intValue();
	if ((component & (ALIGNMENT_CHANGED      |
			  AXIS_CHANGED           |
			  ROTATION_CHANGED       |
			  CONSTANT_SCALE_CHANGED |
			  SCALE_FACTOR_CHANGED)) != 0) {
	    OrientedShape3DRetained[] msArr = (OrientedShape3DRetained[])args[3];
	    Object obj = args[2];
	    if ((component & ALIGNMENT_CHANGED) != 0) {
		int mode = ((Integer)obj).intValue();
		for (int i=0; i< msArr.length; i++) {
		    msArr[i].initAlignmentMode(mode);
		}
	    }
	    else if ((component & AXIS_CHANGED) != 0) {
		Vector3f axis =(Vector3f) obj;
		for (int i=0; i< msArr.length; i++) {
		     msArr[i].initAlignmentAxis(axis);
		}
	    }
	    else if ((component & ROTATION_CHANGED) != 0) {
		Point3f point =(Point3f) obj;
		for (int i=0; i< msArr.length; i++) {
		    msArr[i].initRotationPoint(point);
		}
	    }
	    else if((component & CONSTANT_SCALE_CHANGED) != 0) {
		boolean bool = ((Boolean)obj).booleanValue();
		for (int i=0; i< msArr.length; i++) {
		    msArr[i].initConstantScaleEnable(bool);
		}
	    }
	    else if((component & SCALE_FACTOR_CHANGED) != 0) {
		double scale = ((Double)obj).doubleValue();
		for (int i=0; i< msArr.length; i++) {
		    msArr[i].initScale(scale);
		}
	    }
	}
	else {
	    super.updateImmediateMirrorObject(args);
	}
    }


	Transform3D getOrientedTransform(int viewIndex) {
		synchronized (transformLock) {
			// check if the transforms list needs to be expanded
			if (viewIndex >= orientedTransforms.length) {
				Transform3D[] newList = new Transform3D[viewIndex + 1];
				System.arraycopy(orientedTransforms, 0, newList, 0, orientedTransforms.length);
				orientedTransforms = newList;
			}

			if (orientedTransforms[viewIndex] == null)
				orientedTransforms[viewIndex] = new Transform3D();

			return orientedTransforms[viewIndex];
		}
	}

    // called on the parent object
    // Should be synchronized so that the user thread does not modify the
    // OrientedShape3D params while computing the transform
    synchronized void updateOrientedTransform(Canvas3D canvas, int viewIndex) {
        double angle = 0.0;
        double sign;
	boolean status;

	Transform3D orientedxform = getOrientedTransform(viewIndex);
	//  get viewplatforms's location in virutal world
        if (mode == OrientedShape3D.ROTATE_ABOUT_AXIS) {   // rotate about axis
	    canvas.getCenterEyeInImagePlate(viewPosition);
	    canvas.getImagePlateToVworld(xform); // xform is imagePlateToLocal
	    xform.transform(viewPosition);

	    // get billboard's transform
	    xform.set(getCurrentLocalToVworld());
	    xform.invert(); // xform is now vWorldToLocal

	    // transform the eye position into the billboard's coordinate system
	    xform.transform(viewPosition);


	    // eyeVec is a vector from the local origin to the eye pt in local
	    eyeVec.set(viewPosition);
	    eyeVec.normalize();

	    // project the eye into the rotation plane
	    status = projectToPlane(eyeVec, nAxis);

	    if (status) {
		// project the z axis into the rotation plane
		zAxis.x = 0.0;
		zAxis.y = 0.0;
		zAxis.z = 1.0;
		status = projectToPlane(zAxis, nAxis);
	    }
	    if (status) {

		// compute the sign of the angle by checking if the cross product
		// of the two vectors is in the same direction as the normal axis
		vector.cross(eyeVec, zAxis);
		if (vector.dot(nAxis) > 0.0) {
		    sign = 1.0;
		} else {
		    sign = -1.0;
		}

		// compute the angle between the projected eye vector and the
		// projected z

		double dot = eyeVec.dot(zAxis);
		if (dot > 1.0f) {
		    dot = 1.0f;
		} else if (dot < -1.0f) {
		    dot = -1.0f;
		}

		angle = sign*Math.acos(dot);

		// use -angle because xform is to *undo* rotation by angle
		aa.x = nAxis.x;
		aa.y = nAxis.y;
		aa.z = nAxis.z;
		aa.angle = -angle;
		orientedxform.set(aa);
	    }
	    else {
		orientedxform.setIdentity();
	    }

        } else if(mode == OrientedShape3D.ROTATE_ABOUT_POINT ){ // rotate about point
	    // Need to rotate Z axis to point to eye, and Y axis to be
	    // parallel to view platform Y axis, rotating around rotation pt

	    // get the eye point
	    canvas.getCenterEyeInImagePlate(viewPosition);

	    // derive the yUp point
	    yUpPoint.set(viewPosition);
	    yUpPoint.y += 0.01; // one cm in Physical space

	    // transform the points to the Billboard's space
	    canvas.getImagePlateToVworld(xform); // xform is ImagePlateToVworld
	    xform.transform(viewPosition);
	    xform.transform(yUpPoint);

	    // get billboard's transform
	    xform.set(getCurrentLocalToVworld());
	    xform.invert(); // xform is vWorldToLocal

	    // transfom points to local coord sys
	    xform.transform(viewPosition);
	    xform.transform(yUpPoint);

	    // Make a vector from viewPostion to 0,0,0 in the BB coord sys
	    eyeVec.set(viewPosition);
	    eyeVec.normalize();

	    // create a yUp vector
	    yUp.set(yUpPoint);
	    yUp.sub(viewPosition);
	    yUp.normalize();


	    // find the plane to rotate z
	    zAxis.x = 0.0;
	    zAxis.y = 0.0;
	    zAxis.z = 1.0;

	    // rotation axis is cross product of eyeVec and zAxis
	    vector.cross(eyeVec, zAxis); // vector is cross product

	    // if cross product is non-zero, vector is rotation axis and
	    // rotation angle is acos(eyeVec.dot(zAxis)));
	    double length = vector.length();
	    if (length > 0.0001) {
		double dot = eyeVec.dot(zAxis);
		if (dot > 1.0f) {
		    dot = 1.0f;
		} else if (dot < -1.0f) {
		    dot = -1.0f;
		}
		angle = Math.acos(dot);
		aa.x = vector.x;
		aa.y = vector.y;
		aa.z = vector.z;
		aa.angle = -angle;
		zRotate.set(aa);
	    } else {
		// no rotation needed, set to identity (scale = 1.0)
		zRotate.set(1.0);
	    }

	    // Transform the yAxis by zRotate
	    yAxis.x = 0.0;
	    yAxis.y = 1.0;
	    yAxis.z = 0.0;
	    zRotate.transform(yAxis);

	    // project the yAxis onto the plane perp to the eyeVec
	    status = projectToPlane(yAxis, eyeVec);


	    if (status) {
		// project the yUp onto the plane perp to the eyeVec
		status = projectToPlane(yUp, eyeVec);
	    }

	    if (status) {
		// rotation angle is acos(yUp.dot(yAxis));
		double dot = yUp.dot(yAxis);

		// Fix numerical error, otherwise acos return NULL
		if (dot > 1.0f) {
		    dot = 1.0f;
		} else if (dot < -1.0f) {
		    dot = -1.0f;
		}

		angle = Math.acos(dot);

		// check the sign by looking a the cross product vs the eyeVec
		vector.cross(yUp, yAxis);  // vector is cross product
		if (eyeVec.dot(vector) < 0) {
		    angle *= -1;
		}
		aa.x = eyeVec.x;
		aa.y = eyeVec.y;
		aa.z = eyeVec.z;
		aa.angle = -angle;
		xform.set(aa); // xform is now yRotate

		// rotate around the rotation point
		vector.x = rotationPoint.x;
		vector.y = rotationPoint.y;
		vector.z = rotationPoint.z;     // vector to translate to RP
		orientedxform.set(vector);  // translate to RP
		orientedxform.mul(xform);   // yRotate
		orientedxform.mul(zRotate); // zRotate
		vector.scale(-1.0);   	    // vector to translate back
		xform.set(vector);    	    // xform to translate back
		orientedxform.mul(xform);   // translate back
	    }
	    else {
		orientedxform.setIdentity();
	    }

	}
	//Scale invariant computation
	if(constantScale) {
	    // Back Xform a unit vector to local world coords
	    canvas.getInverseVworldProjection(left_xform, right_xform);

	    // the two endpts of the vector have to be transformed
	    // individually because the Xform is not affine
	    im_vec[0].set(0.0, 0.0, 0.0, 1.0);
	    im_vec[1].set(1.0, 0.0, 0.0, 1.0);
	    left_xform.transform(im_vec[0]);
	    left_xform.transform(im_vec[1]);

	    left_xform.set(getCurrentLocalToVworld());
	    left_xform.invert();
	    left_xform.transform(im_vec[0]);
	    left_xform.transform(im_vec[1]);
	    lvec.set(im_vec[1]);
	    lvec.sub(im_vec[0]);

	    // We simply need the direction of this vector
	    lvec.normalize();
	    im_vec[0].set(0.0, 0.0, 0.0, 1.0);
	    im_vec[1].set(lvec);
	    im_vec[1].w = 1.0;

	    // Forward Xfrom to clip coords
	    left_xform.set(getCurrentLocalToVworld());
	    left_xform.transform(im_vec[0]);
	    left_xform.transform(im_vec[1]);

	    canvas.getVworldProjection(left_xform, right_xform);
	    left_xform.transform(im_vec[0]);
	    left_xform.transform(im_vec[1]);

	    // Perspective division
	    im_vec[0].x /= im_vec[0].w;
	    im_vec[0].y /= im_vec[0].w;
	    im_vec[0].z /= im_vec[0].w;

	    im_vec[1].x /= im_vec[1].w;
	    im_vec[1].y /= im_vec[1].w;
	    im_vec[1].z /= im_vec[1].w;

	    lvec.set(im_vec[1]);
	    lvec.sub(im_vec[0]);

	    // Use the length of this vector to determine the scaling
	    // factor
	    double scale = 1/lvec.length();

	    // Convert to meters
	    scale *= scaleFactor*canvas.getPhysicalWidth()/2;

	    // Scale object so that it appears the same size
	    scaleXform.setScale(scale);
	    orientedxform.mul(scaleXform);
	}

    }


    private boolean projectToPlane(Vector3d projVec, Vector3d planeVec)  {
        double dis = planeVec.dot(projVec);
        projVec.x = projVec.x - planeVec.x*dis;
        projVec.y = projVec.y - planeVec.y*dis;
        projVec.z = projVec.z - planeVec.z*dis;

        double length = projVec.length();
        if (length < EPSILON) { // projVec is parallel to planeVec
	    return false;
        }
        projVec.scale(1 / length);
	return true;
    }

    @Override
    void compile(CompileState compState) {

	super.compile(compState);

        mergeFlag = SceneGraphObjectRetained.DONT_MERGE;

	// don't push the static transform to orientedShape3D
        // because orientedShape3D is rendered using vertex array;
	// it's not worth pushing the transform here

        compState.keepTG = true;
    }

    @Override
    void searchGeometryAtoms(UnorderList list) {
	list.add(getGeomAtom(getMirrorShape(key)));
    }
}