001: /*
002: * $RCSfile: RasterRetained.java,v $
003: *
004: * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.10 $
028: * $Date: 2008/02/28 20:17:28 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import javax.vecmath.*;
035: import java.awt.Point;
036: import java.awt.Dimension;
037: import java.util.ArrayList;
038:
039: import java.awt.image.DataBuffer;
040: import java.awt.image.DataBufferByte;
041:
042: /**
043: * A Retained Raster.
044: */
045:
046: class RasterRetained extends GeometryRetained {
047:
048: /**
049: * Raster type
050: */
051: int type = Raster.RASTER_COLOR;
052:
053: private int clipMode = Raster.CLIP_POSITION;
054: private Point3f position = new Point3f();
055: private int xSrcOffset = 0;
056: private int ySrcOffset = 0;
057: private int width = 0;
058: private int height = 0;
059: private int xDstOffset = 0;
060: private int yDstOffset = 0;
061: ImageComponent2DRetained image = null;
062: Texture2DRetained texture = null;
063: DepthComponentRetained depthComponent = null;
064:
065: RasterRetained() {
066: this .geoType = GEO_TYPE_RASTER;
067: }
068:
069: /**
070: * Set the Raster position
071: * @param position new raster position
072: */
073: final void setPosition(Point3f pos) {
074: geomLock.getLock();
075: position.x = pos.x;
076: position.y = pos.y;
077: position.z = pos.z;
078: geomLock.unLock();
079: sendChangedMessage(J3dThread.UPDATE_GEOMETRY, null, null);
080: }
081:
082: /**
083: * Retrieves the Raster's position
084: * @param position the variable to receive the position vector
085: */
086: final void getPosition(Point3f pos) {
087: pos.x = position.x;
088: pos.y = position.y;
089: pos.z = position.z;
090: }
091:
092: /**
093: * Sets the type of this raster object to one of: RASTER_COLOR,
094: * RASTER_DEPTH, or RASTER_COLOR_DEPTH.
095: * @param type the new type of this raster
096: */
097: final void setType(int type) {
098: geomLock.getLock();
099: this .type = type;
100: geomLock.unLock();
101: }
102:
103: /**
104: * Retrieves the current type of this raster object, one of: RASTER_COLOR,
105: * RASTER_DEPTH, or RASTER_COLOR_DEPTH.
106: * @return type the type of this raster
107: */
108: final int getType() {
109: return type;
110: }
111:
112: /**
113: * Sets the clipping mode of this raster object.
114: * @param clipMode the new clipping mode of this raster,
115: * one of: CLIP_POSITION or CLIP_IMAGE. The default mode
116: * is CLIP_POSITION.
117: */
118: final void setClipMode(int clipMode) {
119:
120: geomLock.getLock();
121: this .clipMode = clipMode;
122: geomLock.unLock();
123: computeBoundingBox();
124: if (source.isLive()) {
125: //update the Shape3Ds that refer to this Raster
126: int un = userLists.size();
127: ArrayList shapeList;
128: Shape3DRetained ms, shape;
129: int sn;
130: for (int i = 0; i < un; i++) {
131: shapeList = (ArrayList) userLists.get(i);
132: sn = shapeList.size();
133: for (int j = 0; j < sn; j++) {
134: ms = (Shape3DRetained) shapeList.get(j);
135: shape = (Shape3DRetained) ms.sourceNode;
136: shape.setBoundsAutoCompute(false);
137: shape.setBounds(geoBounds);
138: }
139: }
140: }
141:
142: }
143:
144: /**
145: * Retrieves the current clipping mode of this raster object.
146: * @return clipMode the clipping mode of this raster,
147: * one of: CLIP_POSITION or CLIP_IMAGE.
148: */
149: final int getClipMode() {
150: return clipMode;
151: }
152:
153: /**
154: * Sets the offset within the source array of pixels at which
155: * to start copying.
156: * @param xSrcOffset the x offset within the source array of pixels
157: * at which to start copying
158: * @param ySrcOffset the y offset within the source array of pixels
159: * at which to start copying
160: */
161: final void setSrcOffset(int xSrcOffset, int ySrcOffset) {
162: geomLock.getLock();
163: this .xSrcOffset = xSrcOffset;
164: this .ySrcOffset = ySrcOffset;
165: geomLock.unLock();
166: }
167:
168: /**
169: * Retrieves the current source pixel offset.
170: * @param srcOffset the object that will receive the source offset
171: */
172: final void getSrcOffset(Point srcOffset) {
173: srcOffset.setLocation(xSrcOffset, ySrcOffset);
174: }
175:
176: /**
177: * Sets the number of pixels to be copied from the pixel array.
178: * @param width the number of columns in the array of pixels to copy
179: * @param height the number of rows in the array of pixels to copy
180: */
181: final void setSize(int width, int height) {
182: geomLock.getLock();
183: this .width = width;
184: this .height = height;
185: geomLock.unLock();
186: }
187:
188: /**
189: * Gets the size of the array of pixels to be copied.
190: * @param size the new size
191: */
192: final void getSize(Dimension size) {
193: size.setSize(width, height);
194: }
195:
196: /**
197: * Sets the destination pixel offset of the upper-left
198: * corner of the rendered image relative to the transformed position.
199: * @param xDstOffset the x coordinate of the new offset
200: * @param yDstOffset the y coordinate of the new offset
201: */
202: final void setDstOffset(int xDstOffset, int yDstOffset) {
203: geomLock.getLock();
204: this .xDstOffset = xDstOffset;
205: this .yDstOffset = yDstOffset;
206: geomLock.unLock();
207: }
208:
209: /**
210: * Retrieves the current destination pixel offset.
211: * @param dstOffset the object that will receive the destination offset
212: */
213: final void getDstOffset(Point dstOffset) {
214: dstOffset.setLocation(xDstOffset, yDstOffset);
215: }
216:
217: /**
218: * Initializes the raster image to the specified image.
219: * @param image new ImageCompoent2D object used as the raster image
220: */
221: final void initImage(ImageComponent2D img) {
222:
223: int texFormat;
224:
225: if (img == null) {
226: image = null;
227: texture = null;
228: return;
229: }
230:
231: image = (ImageComponent2DRetained) img.retained;
232: image.setEnforceNonPowerOfTwoSupport(true);
233: switch (image.getNumberOfComponents()) {
234: case 1:
235: texFormat = Texture.INTENSITY;
236: break;
237: case 2:
238: texFormat = Texture.LUMINANCE_ALPHA;
239: break;
240: case 3:
241: texFormat = Texture.RGB;
242: break;
243: case 4:
244: texFormat = Texture.RGBA;
245: break;
246: default:
247: assert false;
248: return;
249: }
250:
251: Texture2D tex2D = new Texture2D(Texture.BASE_LEVEL, texFormat,
252: img.getWidth(), img.getHeight());
253: texture = (Texture2DRetained) tex2D.retained;
254: texture.setUseAsRaster(true);
255: // Fix to issue 372 : ImageComponent.set(BufferedImage) ignored when used by Raster
256: image.addUser(texture);
257: texture.initImage(0, img);
258:
259: }
260:
261: /**
262: * Sets the pixel array used to copy pixels to/from a Canvas3D.
263: * This is used when the type is RASTER_COLOR or RASTER_COLOR_DEPTH.
264: * @param image the ImageComponent2D object containing the
265: * color data
266: */
267: final void setImage(ImageComponent2D img) {
268:
269: if ((img != null)
270: && (img.getImageClass() == ImageComponent.ImageClass.NIO_IMAGE_BUFFER)) {
271: throw new IllegalArgumentException(J3dI18N
272: .getString("Background14"));
273: }
274:
275: TextureRetained oldTex = this .texture;
276: if (source.isLive()) {
277: if (this .texture != null) {
278: this .texture.clearLive(refCount);
279: }
280: }
281:
282: // Issue 370: only hold the geomLock while calling initImage
283: // (cannot hold it while sending a message).
284: geomLock.getLock();
285: initImage(img);
286: geomLock.unLock();
287:
288: if (source.isLive()) {
289: if (texture != null) {
290: texture.setLive(inBackgroundGroup, refCount);
291: }
292:
293: sendChangedMessage(
294: (J3dThread.UPDATE_RENDER | J3dThread.UPDATE_RENDERING_ATTRIBUTES),
295: oldTex, this .texture);
296: }
297: }
298:
299: /**
300: * Retrieves the current pixel array object.
301: * @return image the ImageComponent2D object containing the
302: * color data
303: */
304: final ImageComponent2D getImage() {
305: return (image == null ? null : (ImageComponent2D) image.source);
306: }
307:
308: /**
309: * Sets the depth image used to copy pixels to/from a Canvas3D.
310: * This is used when the type is RASTER_DEPTH or RASTER_COLOR_DEPTH.
311: * @param depthImage the DepthComponent object containing the
312: * depth (z-buffer) data
313: */
314: final void setDepthComponent(DepthComponent depthComponent) {
315: geomLock.getLock();
316: if (this .source.isLive()) {
317: if (this .depthComponent != null) {
318: this .depthComponent.clearLive(refCount);
319: }
320: if (depthComponent != null) {
321: ((DepthComponentRetained) depthComponent.retained)
322: .setLive(inBackgroundGroup, refCount);
323: }
324: }
325:
326: if (depthComponent == null) {
327: this .depthComponent = null;
328: } else {
329: this .depthComponent = (DepthComponentRetained) depthComponent.retained;
330: }
331: geomLock.unLock();
332: }
333:
334: /**
335: * Retrieves the current depth image object.
336: * @return depthImage DepthComponent containing the
337: * depth (z-buffer) data
338: */
339: final DepthComponent getDepthComponent() {
340: return (depthComponent == null ? null
341: : (DepthComponent) depthComponent.source);
342: }
343:
344: void setLive(boolean inBackgroundGroup, int refCount) {
345: super .doSetLive(inBackgroundGroup, refCount);
346: if (texture != null) {
347: texture.setLive(inBackgroundGroup, refCount);
348: }
349: if (depthComponent != null) {
350: depthComponent.setLive(inBackgroundGroup, refCount);
351: }
352: isEditable = source.getCapability(Raster.ALLOW_OFFSET_WRITE)
353: || source.getCapability(Raster.ALLOW_POSITION_WRITE)
354: || ((type & Raster.RASTER_COLOR) != 0 && source
355: .getCapability(Raster.ALLOW_IMAGE_WRITE))
356: || ((type & Raster.RASTER_DEPTH) != 0 && source
357: .getCapability(Raster.ALLOW_DEPTH_COMPONENT_WRITE))
358: || source.getCapability(Raster.ALLOW_SIZE_WRITE);
359:
360: super .markAsLive();
361: }
362:
363: void clearLive(int refCount) {
364: super .clearLive(refCount);
365: if (texture != null)
366: texture.clearLive(refCount);
367: if (depthComponent != null)
368: depthComponent.clearLive(refCount);
369: }
370:
371: /*
372: // Simply pass along to the NodeComponents
373: void compile(CompileState compState) {
374: setCompiled();
375:
376: if (image != null)
377: image.compile(compState);
378:
379: if (depthComponent != null)
380: depthComponent.compile(compState);
381: }
382: */
383:
384: void computeBoundingBox() {
385: if (clipMode == Raster.CLIP_IMAGE) {
386: // Disable view frustum culling by setting the raster's bounds to
387: // infinity.
388: Point3d minBounds = new Point3d(Double.NEGATIVE_INFINITY,
389: Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
390: Point3d maxBounds = new Point3d(Double.POSITIVE_INFINITY,
391: Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
392: geoBounds.setUpper(maxBounds);
393: geoBounds.setLower(minBounds);
394: } else {
395: Point3d center = new Point3d();
396: center.x = position.x;
397: center.y = position.y;
398: center.z = position.z;
399: geoBounds.setUpper(center);
400: geoBounds.setLower(center);
401: }
402: }
403:
404: void update() {
405: computeBoundingBox();
406: }
407:
408: private void sendChangedMessage(int threads, Object arg1,
409: Object arg2) {
410:
411: synchronized (liveStateLock) {
412: if (source.isLive()) {
413: synchronized (universeList) {
414: int numMessages = universeList.size();
415: J3dMessage[] m = new J3dMessage[numMessages];
416: for (int i = 0; i < numMessages; i++) {
417: m[i] = new J3dMessage();
418: m[i].type = J3dMessage.GEOMETRY_CHANGED;
419: m[i].threads = threads;
420: m[i].args[0] = Shape3DRetained
421: .getGeomAtomsArray((ArrayList) userLists
422: .get(i));
423: m[i].args[1] = this ;
424: Object[] obj = new Object[2];
425: obj[0] = arg1;
426: obj[1] = arg2;
427: m[i].args[2] = obj;
428: m[i].args[3] = new Integer(changedFrequent);
429: m[i].universe = (VirtualUniverse) universeList
430: .get(i);
431: }
432: VirtualUniverse.mc.processMessage(m);
433: }
434: }
435: }
436: }
437:
438: void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
439: boolean updateAlpha, float alpha, int screen,
440: boolean ignoreVertexColors) {
441:
442: // Compute the offset position of the raster
443: // This has to be done at render time because we need access
444: // to the Canvas3D info
445:
446: // Check if adjusted position needs to be computed
447: Point3d adjPos = new Point3d(); // Position of the Raster after adjusting for dstOffset
448: adjPos.set(position);
449:
450: Point2d winCoord = new Point2d(); // Position of Raster in window coordinates
451: Transform3D localToImagePlate = new Transform3D(); // Local to Image plate transform
452:
453: Point3d clipCoord = computeWinCoord(cv, ra, winCoord, adjPos,
454: localToImagePlate);
455:
456: // Test raster for out of bounds in Z.
457: if (clipCoord == null) {
458: return;
459: }
460:
461: if (clipMode == Raster.CLIP_POSITION) {
462: // Do trivial reject test on Raster position.
463: if (!isRasterClipPositionInside(clipCoord)) {
464: return;
465: }
466: }
467:
468: // Add the destination offset to the Raster position in window coordinates
469: winCoord.x += xDstOffset;
470: winCoord.y += yDstOffset;
471:
472: // System.err.println("Step 2 : adjPos " + adjPos + " winCoord " + winCoord);
473:
474: if ((type == Raster.RASTER_COLOR)
475: || (type == Raster.RASTER_COLOR_DEPTH)) {
476: float devCoordZ = (float) (clipCoord.z * 0.5 - 0.5);
477: // Do textfill stuffs
478: if (texture != null) {
479: // setup Texture pipe.
480: cv.updateTextureForRaster(texture);
481:
482: cv
483: .textureFill(this , winCoord, (float) devCoordZ,
484: alpha);
485:
486: // Restore texture pipe.
487: cv.restoreTextureBin();
488: }
489:
490: }
491:
492: if ((type == Raster.RASTER_DEPTH)
493: || (type == Raster.RASTER_COLOR_DEPTH)) {
494:
495: Point2i srcOffset = new Point2i(xSrcOffset, ySrcOffset);
496:
497: if (clipMode == Raster.CLIP_IMAGE) {
498: clipImage(cv, ra, winCoord, srcOffset);
499: }
500:
501: computeObjCoord(cv, winCoord, adjPos, localToImagePlate);
502:
503: cv
504: .executeRasterDepth(
505: cv.ctx,
506: (float) adjPos.x,
507: (float) adjPos.y,
508: (float) adjPos.z,
509: srcOffset.x,
510: srcOffset.y,
511: width,
512: height,
513: depthComponent.width,
514: depthComponent.height,
515: depthComponent.type,
516: ((DepthComponentIntRetained) depthComponent).depthData);
517:
518: }
519: }
520:
521: /**
522: * Clips the image against the window. This method simulates
523: * clipping the image by determining the subimage that will be
524: * drawn and adjusting the xOffset and yOffset accordingly. Only
525: * clipping against the left and top edges needs to be handled,
526: * clipping against the right and bottom edges will be handled by
527: * the underlying graphics library automatically.
528: */
529: private void clipImage(Canvas3D canvas, RenderAtom ra,
530: Point2d winCoord, Point2i srcOffset) {
531:
532: if ((winCoord.x > 0) && (winCoord.y > 0)) {
533: return;
534: }
535:
536: // Check if the Raster point will be culled
537: // Note that w use 1 instead of 0, because when hardware
538: // tranform the coordinate back to winCoord it may get
539: // a small negative value due to numerically inaccurancy.
540: // This clip the Raster away and cause flickering
541: // (see bug 4732965)
542: if (winCoord.x < 1) {
543: // Negate the window position and use this as the offset
544: srcOffset.x = (int) -winCoord.x + 1;
545: winCoord.x = 1;
546: }
547:
548: if (winCoord.y < 1) {
549: // Negate the window position and use this as the offset
550: srcOffset.y = (int) -winCoord.y + 1;
551: winCoord.y = 1;
552: }
553:
554: //check if user-specified subimage is smaller than the clipped image
555: if (srcOffset.x < xSrcOffset)
556: srcOffset.x = xSrcOffset;
557: if (srcOffset.y < ySrcOffset)
558: srcOffset.y = ySrcOffset;
559:
560: }
561:
562: private boolean isRasterClipPositionInside(Point3d clipCoord) {
563: return (clipCoord.x >= -1.0) && (clipCoord.x <= 1.0)
564: && (clipCoord.y >= -1.0) && (clipCoord.y <= 1.0);
565: }
566:
567: private void computeObjCoord(Canvas3D canvas, Point2d winCoord,
568: Point3d objCoord, Transform3D localToImagePlate) {
569: // Back transform this pt. from window to object coordinates
570: // Assumes this method is ALWAYS called after computeWinCoord has been
571: // called. computeWinCoord calculates the Vworld to Image Plate Xform.
572: // This method simply uses it without recomputing it.
573:
574: canvas.getPixelLocationInImagePlate(winCoord.x, winCoord.y,
575: objCoord.z, objCoord);
576: // Get image plate to object coord transform
577: // inv(P x M)
578: localToImagePlate.invert();
579: localToImagePlate.transform(objCoord);
580: }
581:
582: private Point3d computeWinCoord(Canvas3D canvas, RenderAtom ra,
583: Point2d winCoord, Point3d objCoord,
584: Transform3D localToImagePlate) {
585: // Get local to Vworld transform
586: RenderMolecule rm = ra.renderMolecule;
587:
588: if (rm == null) {
589: // removeRenderAtom() may set ra.renderMolecule to null
590: // in RenderBin before this renderer thread run.
591: return null;
592: }
593:
594: // MT safe issue: We can't reference ra.renderMolecule below since
595: // RenderBin thread may set it to null anytime. Use rm instead.
596:
597: Transform3D lvw = rm.localToVworld[rm.localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD]];
598:
599: Point3d clipCoord3 = new Point3d();
600: clipCoord3.set(objCoord);
601: Point4d clipCoord4 = new Point4d();
602:
603: // Transform point from local coord. to clipping coord.
604: lvw.transform(clipCoord3);
605: canvas.vworldToEc.transform(clipCoord3);
606: canvas.projTrans.transform(clipCoord3, clipCoord4);
607:
608: // clip check in Z
609: if ((clipCoord4.w <= 0.0) || (clipCoord4.z > clipCoord4.w)
610: || (-clipCoord4.z > clipCoord4.w)) {
611:
612: return null;
613: }
614: double invW = 1.0 / clipCoord4.w;
615:
616: clipCoord3.x = clipCoord4.x * invW;
617: clipCoord3.y = clipCoord4.y * invW;
618: clipCoord3.z = clipCoord4.z * invW;
619:
620: // Get Vworld to image plate Xform
621: canvas.getLastVworldToImagePlate(localToImagePlate);
622:
623: // v' = vwip x lvw x v
624: // where v' = transformed vertex,
625: // lvw = local to Vworld Xform
626: // vwip = Vworld to Image plate Xform
627: // v = vertex
628:
629: // Compute composite local to image plate Xform
630: localToImagePlate.mul(lvw);
631:
632: // Transform the Raster's position from object to world coordinates
633: localToImagePlate.transform(objCoord);
634:
635: // Get the window coordinates of this point
636: canvas.getPixelLocationFromImagePlate(objCoord, winCoord);
637:
638: return clipCoord3;
639: }
640:
641: int getClassType() {
642: return RASTER_TYPE;
643: }
644:
645: // notifies the Raster mirror object that the image data in a referenced
646: // ImageComponent object is changed.
647: // Currently we are not making use of this information.
648:
649: void notifyImageComponentImageChanged(ImageComponentRetained image,
650: ImageComponentUpdateInfo value) {
651: }
652:
653: boolean intersect(PickShape pickShape, PickInfo pickInfo,
654: int flags, Point3d iPnt, GeometryRetained geom,
655: int geomIndex) {
656: return false;
657: }
658:
659: boolean intersect(Bounds targetBound) {
660: return false;
661: }
662:
663: boolean intersect(Point3d[] pnts) {
664: return false;
665: }
666:
667: boolean intersect(Transform3D this ToOtherVworld,
668: GeometryRetained geom) {
669: return false;
670: }
671:
672: boolean intersect(Transform3D this LocalToVworld,
673: Transform3D otherLocalToVworld, GeometryRetained geom) {
674: return false;
675: }
676:
677: boolean intersect(Transform3D this LocalToVworld, Bounds targetBound) {
678: return false;
679: }
680:
681: void handleFrequencyChange(int bit) {
682: if (bit == Raster.ALLOW_IMAGE_WRITE)
683: setFrequencyChangeMask(bit, 0x1);
684:
685: }
686:
687: }
|