aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/CompressedGeometryRetained.java
blob: aba0277c8be8b599ed8a32ea7361d37c118df78f (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
/*
 * Copyright 1998-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.Point3d;

/**
 * The compressed geometry object is used to store geometry in a
 * compressed format.  Using compressed geometry reduces the amount
 * of memory needed by a Java 3D application and increases the speed
 * objects can be sent over the network.  Once geometry decompression
 * hardware support becomes available, increased rendering performance
 * will also result from the use of compressed geometry.
 */
class CompressedGeometryRetained extends GeometryRetained {

    // If not in by-reference mode, a 48-byte header as defined by the
    // GL_SUNX_geometry_compression OpenGL extension is always concatenated to
    // the beginning of the compressed geometry data and copied along with the
    // it into a contiguous array.  This allows hardware decompression using
    // the obsolete experimental GL_SUNX_geometry_compression extension if
    // that is all that is available.
    //
    // This is completely distinct and not to be confused with the cgHeader
    // field on the non-retained side, although much of the data is
    // essentially the same.
    private static final int HEADER_LENGTH = 48 ;

    // These are the header locations examined.
    private static final int HEADER_MAJOR_VERSION_OFFSET = 0 ;
    private static final int HEADER_MINOR_VERSION_OFFSET = 1 ;
    private static final int HEADER_MINOR_MINOR_VERSION_OFFSET = 2 ;
    private static final int HEADER_BUFFER_TYPE_OFFSET = 3 ;
    private static final int HEADER_BUFFER_DATA_OFFSET = 4 ;

    // The OpenGL compressed geometry extensions use bits instead of
    // enumerations to represent the type of compressed geometry.
    static final byte TYPE_POINT = 1 ;
    static final byte TYPE_LINE = 2 ;
    static final byte TYPE_TRIANGLE = 4 ;

    // Version number of this compressed geometry object.
    int majorVersionNumber ;
    int minorVersionNumber ;
    int minorMinorVersionNumber ;

    // These fields are used by the native execute() method.
    int packedVersion ;
    int bufferType ;
    int bufferContents ;
    int renderFlags ;
    int offset ;
    int size ;
    byte[] compressedGeometry ;

    // True if by-reference data access mode is in effect.
    private boolean byReference = false ;

    // A reference to the original byte array with which this object was
    // created.  If hardware decompression is available but it doesn't support
    // by-reference semantics, then an internal copy of the original byte array
    // is made even when by-reference semantics have been requested.
    private byte[] originalCompressedGeometry = null ;

    // True if the platform supports hardware decompression.
    private static boolean hardwareDecompression = false ;

    // This field retains a reference to the GeometryRetained object used for
    // geometry-based picking.  It is normally the same reference as the
    // mirror geometry used for rendering unless hardware decompression is
    // supported.
    private GeometryRetained pickGeometry = null ;

    /**
     * Formerly native method that returns availability of a native by-reference
     * rendering API for compressed geometry.
     */
    private boolean decompressByRef(Context ctx) {
	return false;
    }

    /**
     * Formerly native method that returns availability of hardware
     * rendering (and acceleration) for compressed geometry of the
     * given version.
     */
    private boolean decompressHW(Context ctx, int majorVersion, int minorVersion) {
	return false;
    }

    /**
     * Formerly native method that does HW compressed geometry rendering
     */
    private void execute(Context ctx, int version, int bufferType,
			 int bufferContents, int renderFlags,
			 int offset, int size, byte[] geometry) {

	assert false : "This method should never be called!";
    }

    /**
     * Method for calling native execute() method on behalf of the J3D renderer.
     */
    @Override
    void execute(Canvas3D cv, RenderAtom ra, boolean isNonUniformScale,
		 boolean updateAlpha, float alpha,
                 int screen, boolean ignoreVertexColors) {

	// XXXX: alpha udpate
	execute(cv.ctx, packedVersion, bufferType, bufferContents,
		renderFlags, offset, size, compressedGeometry) ;
    }

    /**
     * The package-scoped constructor.
     */
    CompressedGeometryRetained() {
	this.geoType = GEO_TYPE_COMPRESSED ;

	// Compressed geometry is always bounded by [-1..1] on each axis, so
	// set that as the initial bounding box.
	geoBounds.setUpper( 1.0, 1.0, 1.0) ;
	geoBounds.setLower(-1.0,-1.0,-1.0) ;
    }

    /**
     * Compressed geometry is immutable so this method does nothing.
     */
    @Override
    void computeBoundingBox() {
    }

    /**
     * Update this object.  Compressed geometry is immutable so there's
     * nothing to do.
     */
    @Override
    void update() {
	isDirty = 0 ;
    }

    /**
     * Return true if the data access mode is by-reference.
     */
    boolean isByReference() {
	return this.byReference ;
    }

    private void createByCopy(byte[] geometry) {
	// Always copy a header along with the compressed geometry into a
	// contiguous array in order to support hardware acceleration with the
	// GL_SUNX_geometry_compression extension.  The header is unnecessary
	// if only the newer GL_SUN_geometry_compression API needs support.
	compressedGeometry = new byte[HEADER_LENGTH + this.size] ;

	compressedGeometry[HEADER_MAJOR_VERSION_OFFSET] =
	    (byte)this.majorVersionNumber ;

	compressedGeometry[HEADER_MINOR_VERSION_OFFSET] =
	    (byte)this.minorVersionNumber ;

	compressedGeometry[HEADER_MINOR_MINOR_VERSION_OFFSET] =
	    (byte)this.minorMinorVersionNumber ;

	compressedGeometry[HEADER_BUFFER_TYPE_OFFSET] =
	    (byte)this.bufferType ;

	compressedGeometry[HEADER_BUFFER_DATA_OFFSET] =
	    (byte)this.bufferContents ;

	System.arraycopy(geometry, this.offset,
			 compressedGeometry, HEADER_LENGTH, this.size) ;

	this.offset = HEADER_LENGTH ;
    }

    /**
     * Creates the retained compressed geometry data.  Data from the header is
     * always copied; the compressed geometry is copied as well if the data
     * access mode is not by-reference.
     *
     * @param hdr the compressed geometry header
     * @param geometry the compressed geometry
     * @param byReference if true then by-reference semantics requested
     */
    void createCompressedGeometry(CompressedGeometryHeader hdr,
				  byte[] geometry, boolean byReference) {

	this.byReference = byReference ;

	if (hdr.lowerBound != null)
	    this.geoBounds.setLower(hdr.lowerBound) ;

	if (hdr.upperBound != null)
	    this.geoBounds.setUpper(hdr.upperBound) ;

	geoBounds.getCenter(this.centroid);
	recompCentroid = false;
	this.majorVersionNumber = hdr.majorVersionNumber ;
	this.minorVersionNumber = hdr.minorVersionNumber ;
	this.minorMinorVersionNumber = hdr.minorMinorVersionNumber ;

	this.packedVersion =
	    (hdr.majorVersionNumber << 24) |
	    (hdr.minorVersionNumber << 16) |
	    (hdr.minorMinorVersionNumber << 8) ;

	switch(hdr.bufferType) {
	case CompressedGeometryHeader.POINT_BUFFER:
	    this.bufferType = TYPE_POINT ;
	    break ;
	case CompressedGeometryHeader.LINE_BUFFER:
	    this.bufferType = TYPE_LINE ;
	    break ;
	case CompressedGeometryHeader.TRIANGLE_BUFFER:
	    this.bufferType = TYPE_TRIANGLE ;
	    break ;
	}

	this.bufferContents = hdr.bufferDataPresent ;
	this.renderFlags = 0 ;

	this.size = hdr.size ;
	this.offset = hdr.start ;

	if (byReference) {
	    // Assume we can use the given reference, but maintain a second
	    // reference in case a copy is later needed.
	    this.compressedGeometry = geometry ;
	    this.originalCompressedGeometry = geometry ;
	} else {
	    // Copy the original data into a format that can be used by both
	    // the software and native hardware decompressors.
	    createByCopy(geometry) ;
	    this.originalCompressedGeometry = null ;
	}
    }

    /**
     * Decompress this object into a GeometryArrayRetained if hardware
     * decompression is not available.  Once decompressed the resulting
     * geometry replaces the geometry reference in the associated RenderAtom
     * as well as the mirror geometry reference in this object.
     */
    GeometryRetained getGeometry(boolean forceDecompression, Canvas3D cv ) {

	if (forceDecompression) {
	    // forceDecompression is set to true if lighting is disabled and
	    // ignoreVertexColors is true, since there is no way for openGL to
	    // ignore vertexColors in this case.
	    GeometryDecompressorRetained gdr =
		new GeometryDecompressorRetained() ;

	    mirrorGeometry = gdr.decompress(this) ;
	    gdr.getBoundingBox(geoBounds) ;
	    pickGeometry = mirrorGeometry ;
	}
	else {
	    // Return this object if hardware decompression is available.
	    if (hardwareDecompression)
		return this;

	    // Check to see if hardware decompression is available.
	    if (decompressHW(cv.ctx, majorVersionNumber, minorVersionNumber)) {
		hardwareDecompression = true ;

		// If hardware can't handle by-reference, punt to by-copy.
		if (isByReference() && !decompressByRef(cv.ctx)) {
		    createByCopy(compressedGeometry) ;
		}

		return this;
	    }

	    // Decompress the data into a GeometryArrayRetained representation
	    // for the mirror geometry reference.
	    GeometryDecompressorRetained gdr =
		new GeometryDecompressorRetained() ;

	    mirrorGeometry = gdr.decompress(this) ;
	    gdr.getBoundingBox(geoBounds) ;

	    // The mirror geometry contains a superset of the pick geometry
	    // data. Since hardware decompression isn't available, there's no
	    // need to retain separate pick geometry.
	    pickGeometry = mirrorGeometry ;
	}

	return mirrorGeometry ;
    }

    /**
     * This method always decompresses the geometry and retains the result in
     * order to support geometry-based picking and collision detection.  The
     * returned GeometryRetained object will contain only positions and
     * connections.
     */
    GeometryRetained getPickGeometry() {
	// Return the pick geometry if available.
	if (pickGeometry != null)
	    return pickGeometry ;

	// Decompress the data into a GeometryArrayRetained representation for
	// the pick geometry reference.  Retain it and its bounding box.
	GeometryDecompressorRetained gdr = new GeometryDecompressorRetained() ;
	gdr.setDecompressPositionsOnly(true) ;

	pickGeometry = gdr.decompress(this) ;
	gdr.getBoundingBox(geoBounds) ;
	return pickGeometry ;
    }

    //
    // The following intersect() methods are used to implement geometry-based
    // picking and collision.
    //
    @Override
    boolean intersect(PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt,
                      GeometryRetained geom, int geomIndex) {
	GeometryRetained geomR = getPickGeometry() ;
	return (geomR != null ?
		geomR.intersect(pickShape, pickInfo, flags, iPnt, geom, geomIndex) : false);
    }

    @Override
    boolean intersect(Bounds targetBound) {
	GeometryRetained geom = getPickGeometry() ;
	return (geom != null ? geom.intersect(targetBound) : false);
    }

    @Override
    boolean intersect(Transform3D thisToOtherVworld,  GeometryRetained g) {
	GeometryRetained geom = getPickGeometry() ;
	return (geom != null ?
		geom.intersect(thisToOtherVworld, g) : false);
    }

    @Override
    boolean intersect(Point3d[] pnts) {
	GeometryRetained geom = getPickGeometry() ;
	return (geom != null ? geom.intersect(pnts) : false);
    }

    /**
     * Return a vertex format mask that's compatible with GeometryArray
     * objects.
     */
    @Override
    int getVertexFormat() {
	int vertexFormat = GeometryArray.COORDINATES ;

	if ((this.bufferContents &
	     CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
	    vertexFormat |= GeometryArray.NORMALS ;

	if ((this.bufferContents &
	     CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
	    vertexFormat |= GeometryArray.COLOR ;

	if ((this.bufferContents &
	     CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
	    vertexFormat |= GeometryArray.WITH_ALPHA ;

	return vertexFormat ;
    }

    /**
     * Return a buffer type that's compatible with CompressedGeometryHeader.
     */
    int getBufferType() {
	switch(this.bufferType) {
	case TYPE_POINT:
	    return CompressedGeometryHeader.POINT_BUFFER ;
	case TYPE_LINE:
	    return CompressedGeometryHeader.LINE_BUFFER ;
	default:
	case TYPE_TRIANGLE:
	    return CompressedGeometryHeader.TRIANGLE_BUFFER ;
	}
    }

    /**
     * Copies compressed geometry data into the given array of bytes.
     * The internal header information is not copied.
     *
     * @param buff array of bytes into which to copy compressed geometry
     */
    void copy(byte[] buff) {
	System.arraycopy(compressedGeometry, offset, buff, 0, size) ;
    }

    /**
     * Returns a reference to the original compressed geometry byte array,
     * which may have been copied even if by-reference semantics have been
     * requested.  It will be null if byCopy is in effect.
     *
     * @return reference to array of bytes containing the compressed geometry.
     */
    byte[] getReference() {
	return originalCompressedGeometry ;
    }

    /**
     * Copies all retained data for cloneNodeComponent() on the non-retained
     * side.  This is unlike GeometryArray subclasses which just call the
     * public API constructors and then duplicateNodeComponent() to invoke the
     * GeometryArray implementation of duplicateAttributes(), since the
     * CompressedGeometry class directly subclasses Geometry and calling the
     * public constructors would cause a lot of redundant data copying.
     */
    void duplicate(CompressedGeometryRetained cgr) {
	cgr.majorVersionNumber = this.majorVersionNumber ;
	cgr.minorVersionNumber = this.minorVersionNumber ;
	cgr.minorMinorVersionNumber = this.minorMinorVersionNumber ;

	cgr.packedVersion = this.packedVersion ;
	cgr.bufferType = this.bufferType ;
	cgr.bufferContents = this.bufferContents ;
	cgr.renderFlags = this.renderFlags ;

	cgr.offset = this.offset ;
	cgr.size= this.size ;

	cgr.geoBounds.setLower(this.geoBounds.lower) ;
	cgr.geoBounds.setUpper(this.geoBounds.upper) ;
	cgr.pickGeometry = this.pickGeometry ;
	cgr.byReference = this.byReference ;

	if (this.byReference) {
	    // Copy references only.
	    cgr.compressedGeometry = this.compressedGeometry ;
	    cgr.originalCompressedGeometry = this.originalCompressedGeometry ;
	} else {
	    // Copy entire byte array including 48-byte native OpenGL header.
	    cgr.compressedGeometry = new byte[this.compressedGeometry.length] ;
	    System.arraycopy(this.compressedGeometry, 0,
			     cgr.compressedGeometry, 0,
			     this.compressedGeometry.length) ;
	    cgr.originalCompressedGeometry = null ;
	}
    }

    @Override
    int getClassType() {
	return COMPRESS_TYPE;
    }
}