All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.ogc.collada.impl.ColladaMeshShape Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind.ogc.collada.impl;

import com.jogamp.common.nio.Buffers;
import gov.nasa.worldwind.cache.GpuResourceCache;
import gov.nasa.worldwind.geom.Box;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.*;
import gov.nasa.worldwind.ogc.collada.*;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.terrain.Terrain;
import gov.nasa.worldwind.util.*;

import com.jogamp.opengl.*;
import java.awt.*;
import java.nio.FloatBuffer;
import java.util.*;
import java.util.List;

/**
 * Shape to render a COLLADA line or triangle mesh. An instance of this shape can render any number of {@link
 * ColladaLines} or {@link ColladaTriangles}, but a single instance cannot render both lines and triangles. New
 * instances are created by {@link #createTriangleMesh(java.util.List, gov.nasa.worldwind.ogc.collada.ColladaBindMaterial)
 * createTriangleMesh} and {@link #createLineMesh(java.util.List, gov.nasa.worldwind.ogc.collada.ColladaBindMaterial)
 * createLineMesh}.
 * 

* This shape supports only COLLADA line and triangle geometries. * * @author pabercrombie * @version $Id: ColladaMeshShape.java 2216 2014-08-11 20:29:24Z tgaskins $ */ public class ColladaMeshShape extends AbstractGeneralShape { /** * Class to represent an instance of the mesh to be drawn as an ordered renderable. We can't use the mesh itself as * the ordered renderable because it may be drawn multiple times with different transforms. */ public static class OrderedMeshShape implements OrderedRenderable { /** Shape to render. */ protected ColladaMeshShape mesh; /** Distance from the eye to the shape's reference position. */ protected double eyeDistance; /** Transform applied to this instance of the mesh. */ protected Matrix renderMatrix; /** * Create a new ordered renderable. * * @param mesh Mesh shape to render. * @param renderMatrix Transform matrix to apply when rendering the shape. * @param eyeDistance Distance from the eye position to the shape's reference position. */ public OrderedMeshShape(ColladaMeshShape mesh, Matrix renderMatrix, double eyeDistance) { this.mesh = mesh; this.eyeDistance = eyeDistance; this.renderMatrix = renderMatrix; } public double getDistanceFromEye() { return this.eyeDistance; } public void pick(DrawContext dc, Point pickPoint) { this.mesh.pick(dc, pickPoint, this.renderMatrix); } public void render(DrawContext dc) { this.mesh.render(dc, this.renderMatrix); } } /** * This class holds globe-specific data for this shape. It's managed via the shape-data cache in {@link * gov.nasa.worldwind.render.AbstractShape.AbstractShapeData}. */ protected static class ShapeData extends AbstractGeneralShape.ShapeData { /** * Construct a cache entry for this shape. * * @param dc the current draw context. * @param shape this shape. */ public ShapeData(DrawContext dc, AbstractGeneralShape shape) { super(dc, shape); } /** * Matrix to orient the shape on the surface of the globe. Cached result of {@link * gov.nasa.worldwind.globes.Globe#computeSurfaceOrientationAtPosition(gov.nasa.worldwind.geom.Position)} * evaluated at the reference position. */ protected Matrix surfaceOrientationMatrix; /** * Transform matrix to apply when rendering the shape. This matrix is determined by the COLLADA traversal * matrix, and {@link #surfaceOrientationMatrix}. */ protected Matrix renderMatrix; /** Cached reference center for the shape. */ protected Vec4 referenceCenter; } /** Geometry and attributes of a COLLADA {@code triangles} or {@code lines} element. */ protected static class Geometry { /** Collada element that defines this geometry. */ protected ColladaAbstractGeometry colladaGeometry; /** Offset (in vertices) into the coord, normal, and texcoord buffers of this coordinates for this geometry. */ protected int offset = -1; /** Texture applied to this geometry. */ protected WWTexture texture; /** Material applied to this geometry. */ protected Material material; /** * Indicates whether or not the geometry is double sided. If double sided, the geometry must be rendered with * backface culling disabled. This property is determined by the presence of a technique for the "GOOGLEEARTH" * profile that includes a double_sided field. * * @see ColladaMeshShape#isDoubleSided(gov.nasa.worldwind.ogc.collada.ColladaAbstractGeometry) */ protected boolean doubleSided; /** * Create a new geometry instance. * * @param geometry COLLADA geometry to render. */ public Geometry(ColladaAbstractGeometry geometry) { this.colladaGeometry = geometry; } } protected static class ExtentCacheKey { protected GlobeStateKey globeStateKey; protected Matrix matrix; public ExtentCacheKey(Globe globe, Matrix matrix) { this.globeStateKey = globe.getGlobeStateKey(); this.matrix = matrix; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ExtentCacheKey that = (ExtentCacheKey) o; if (globeStateKey != null ? !globeStateKey.equals(that.globeStateKey) : that.globeStateKey != null) { return false; } if (matrix != null ? !matrix.equals(that.matrix) : that.matrix != null) { return false; } return true; } @Override public int hashCode() { int result = globeStateKey != null ? globeStateKey.hashCode() : 0; result = 31 * result + (matrix != null ? matrix.hashCode() : 0); return result; } } /** OpenGL element type for this shape (GL.GL_LINES or GL.GL_TRIANGLES). */ protected int elementType; /** Number of vertices per shape. Two in the case of a line mesh, three in the case of a triangle mesh. */ protected int vertsPerShape; /** Total number of shapes (lines or triangles) in this mesh. Equal to the sum of the shapes in each geometry. */ protected int shapeCount; /** Material applied to this mesh. */ protected ColladaBindMaterial bindMaterial; /** Geometry objects that describe different parts of the mesh. */ protected List geometries; /** Cache of shape extents computed for different transform matrices. */ protected Map extentCache = new HashMap(); /** * The vertex data buffer for this shape data. The first part contains vertex coordinates, the second part contains * normals, and the third part contains texture coordinates. */ protected FloatBuffer coordBuffer; /** The slice of the {@link #coordBuffer} that contains normals. */ protected FloatBuffer normalBuffer; /** The index of the first normal in the {@link #coordBuffer}. */ protected int normalBufferPosition; /** Texture coordinates for all geometries in this shape. */ protected FloatBuffer textureCoordsBuffer; /** The index of the first texture coordinate in the {@link #coordBuffer}. */ protected int texCoordBufferPosition; /** * Create a triangle mesh shape. * * @param geometries COLLADA elements that defines geometry for this shape. Must contain at least one element. * @param bindMaterial Material applied to the mesh. May be null. */ public static ColladaMeshShape createTriangleMesh(List geometries, ColladaBindMaterial bindMaterial) { ColladaMeshShape shape = new ColladaMeshShape(geometries); shape.bindMaterial = bindMaterial; shape.elementType = GL.GL_TRIANGLES; shape.vertsPerShape = 3; return shape; } /** * Create a line mesh shape. * * @param geometries COLLADA elements that defines geometry for this shape. Must contain at least one element. * @param bindMaterial Material applied to the mesh. May be null. */ public static ColladaMeshShape createLineMesh(List geometries, ColladaBindMaterial bindMaterial) { ColladaMeshShape shape = new ColladaMeshShape(geometries); shape.bindMaterial = bindMaterial; shape.elementType = GL.GL_LINES; shape.vertsPerShape = 2; return shape; } /** * Create an instance of the shape. * * @param geometries Geometries to render. All geometries must be of the same type (either {@link ColladaTriangles} * or {@link ColladaLines}. */ protected ColladaMeshShape(List geometries) { if (WWUtil.isEmpty(geometries)) { String message = Logging.getMessage("generic.ListIsEmpty"); Logging.logger().severe(message); throw new IllegalStateException(message); } this.geometries = new ArrayList(geometries.size()); for (ColladaAbstractGeometry geometry : geometries) { this.geometries.add(new Geometry(geometry)); this.shapeCount += geometry.getCount(); } } /** * {@inheritDoc} *

* COLLADA shapes do not support intersection tests because the shape may be rendered multiple times with different * transform matrices. It's not possible to determine intersection without the transform matrix applied when the * shape is rendered. * * @return Always returns {@code null}. */ @Override public List intersect(Line line, Terrain terrain) throws InterruptedException { return null; } /** {@inheritDoc} Overridden to invalidate cached geometry when the model position is changed. */ @Override public void setModelPosition(Position modelPosition) { if (modelPosition != this.modelPosition) { this.modelPosition = modelPosition; this.reset(); } } ////////////////////////////////////////////////////////////////////// // Rendering ////////////////////////////////////////////////////////////////////// @Override protected OGLStackHandler beginDrawing(DrawContext dc, int attrMask) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. OGLStackHandler ogsh = super.beginDrawing(dc, attrMask); if (!dc.isPickingMode()) { // Push an identity texture matrix. This prevents drawSides() from leaking GL texture matrix state. The // texture matrix stack is popped from OGLStackHandler.pop(), in the finally block below. ogsh.pushTextureIdentity(gl); if (this.mustApplyLighting(dc, null)) { // We apply a scale transform on the modelview matrix, so the normal vectors must be re-normalized // before lighting is computed. gl.glEnable(GL2.GL_NORMALIZE); } } return ogsh; } /** * Render the mesh in a given orientation. * * @param dc Current draw context. * @param matrix Matrix to be multiply with the current modelview matrix to orient the mesh. */ public void render(DrawContext dc, Matrix matrix) { this.currentData = (AbstractShapeData) this.shapeDataCache.getEntry(dc.getGlobe()); if (this.currentData == null) { this.currentData = this.createCacheEntry(dc); this.shapeDataCache.addEntry(this.currentData); } ShapeData current = (ShapeData) this.currentData; current.renderMatrix = matrix; // Update current extent from cached extents. This must be done on each call to render because the same shape // may be drawn multiple times during a single frame with different transforms. Attempt to calculate the extent // if not available in the cache. It may not be possible to calculate the extent if the shape geometry has not // been built, in which case the extent will be computed by createMinimalGeometry. ExtentCacheKey extentCacheKey = new ExtentCacheKey(dc.getGlobe(), matrix); Extent extent = this.extentCache.get(extentCacheKey); if (extent == null) { extent = this.computeExtent(dc); this.extentCache.put(extentCacheKey, extent); } current.setExtent(extent); this.render(dc); } /** * Pick the mesh in a given orientation. * * @param dc Current draw context. * @param pickPoint Current pick point. * @param matrix Matrix to multiply with the current modelview matrix to orient the mesh. */ public void pick(DrawContext dc, Point pickPoint, Matrix matrix) { // This method is called only when ordered renderables are being drawn. if (dc == null) { String msg = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.pickSupport.clearPickList(); try { this.pickSupport.beginPicking(dc); this.render(dc, matrix); } finally { this.pickSupport.endPicking(dc); this.pickSupport.resolvePick(dc, pickPoint, this.pickLayer); } } /** * {@inheritDoc} Overridden because ColladaMeshShape uses OrderedMeshShape instead of adding itself to the ordered * renderable queue. */ @Override protected void drawBatched(DrawContext dc) { // Draw as many as we can in a batch to save ogl state switching. Object nextItem = dc.peekOrderedRenderables(); if (!dc.isPickingMode()) { while (nextItem != null && nextItem.getClass() == OrderedMeshShape.class) { OrderedMeshShape or = (OrderedMeshShape) nextItem; ColladaMeshShape shape = or.mesh; if (!shape.isEnableBatchRendering()) break; dc.pollOrderedRenderables(); // take it off the queue shape.doDrawOrderedRenderable(dc, this.pickSupport, or.renderMatrix); nextItem = dc.peekOrderedRenderables(); } } else if (this.isEnableBatchPicking()) { super.drawBatched(dc); while (nextItem != null && nextItem.getClass() == this.getClass()) { OrderedMeshShape or = (OrderedMeshShape) nextItem; ColladaMeshShape shape = or.mesh; if (!shape.isEnableBatchRendering() || !shape.isEnableBatchPicking()) break; if (shape.pickLayer != this.pickLayer) // batch pick only within a single layer break; dc.pollOrderedRenderables(); // take it off the queue shape.doDrawOrderedRenderable(dc, this.pickSupport, or.renderMatrix); nextItem = dc.peekOrderedRenderables(); } } } /** {@inheritDoc} */ protected boolean doMakeOrderedRenderable(DrawContext dc) { // Clear cached extents because we are creating new geometry. this.extentCache.clear(); // Do the minimum necessary to determine the model's reference point, extent and eye distance. this.createMinimalGeometry(dc, (ShapeData) this.getCurrent()); // If the shape is less that a pixel in size, don't render it. if (this.getCurrent().getExtent() == null || dc.isSmall(this.getExtent(), 1)) return false; if (!this.intersectsFrustum(dc)) return false; this.createFullGeometry(dc); return true; } /** * {@inheritDoc} Overridden because this shape uses {@link gov.nasa.worldwind.ogc.collada.impl.ColladaMeshShape.OrderedMeshShape} * to represent this drawn instance of the mesh in the ordered renderable queue. */ @Override protected void addOrderedRenderable(DrawContext dc) { ShapeData current = (ShapeData) this.getCurrent(); double eyeDistance = this.computeEyeDistance(dc); OrderedRenderable or = new OrderedMeshShape(this, current.renderMatrix, eyeDistance); dc.addOrderedRenderable(or); } /** * Draw the shape as an OrderedRenderable, using the specified transform matrix. * * @param dc Current draw context. * @param pickCandidates Pick candidates for this frame. * @param matrix Transform matrix to apply before trying shape. m */ protected void doDrawOrderedRenderable(DrawContext dc, PickSupport pickCandidates, Matrix matrix) { ShapeData current = (ShapeData) this.getCurrent(); current.renderMatrix = matrix; super.doDrawOrderedRenderable(dc, pickCandidates); } /** {@inheritDoc} Does nothing, all drawing is performed by {@link #doDrawInterior(gov.nasa.worldwind.render.DrawContext)}. */ @Override protected void doDrawOutline(DrawContext dc) { // Do nothing. All drawing is performed in doDrawInterior } /** {@inheritDoc} */ @Override protected void doDrawInterior(DrawContext dc) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. // Create an OpenGL stack handler to handle matrix stack push/pop. Explicitly track changes to the OpenGL // texture and cull face states in order to eliminate the need for attribute push/pop on a per mesh basis. OGLStackHandler stackHandler = new OGLStackHandler(); boolean texturesEnabled = false; boolean cullingEnabled = false; try { stackHandler.pushModelview(gl); this.setModelViewMatrix(dc); Material defaultMaterial = this.activeAttributes.getInteriorMaterial(); // Interior material is applied by super.prepareToDrawInterior. But, we may // need to change it if different geometry elements use different materials. Material activeMaterial = defaultMaterial; // When drawing with vertex arrays we can bind the vertex buffer once. When using vertex buffer objects // we need to check to make sure that the vbo is available each time through the loop because loading // textures may force vbos out of the cache (see loop below). if (!this.shouldUseVBOs(dc)) { FloatBuffer vb = this.coordBuffer; gl.glVertexPointer(ColladaAbstractGeometry.COORDS_PER_VERTEX, GL.GL_FLOAT, 0, vb.rewind()); } for (Geometry geometry : this.geometries) { Material nextMaterial = geometry.material != null ? geometry.material : defaultMaterial; // Apply new material if necessary if (!dc.isPickingMode() && !nextMaterial.equals(activeMaterial)) { this.applyMaterial(dc, nextMaterial); activeMaterial = nextMaterial; } if (!dc.isPickingMode() && this.mustApplyTexture(geometry) && this.getTexture(geometry).bind(dc)) // bind initiates retrieval { this.getTexture(geometry).applyInternalTransform(dc); if (!texturesEnabled) { gl.glEnable(GL.GL_TEXTURE_2D); gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); texturesEnabled = true; } gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT); gl.glTexCoordPointer(ColladaAbstractGeometry.TEX_COORDS_PER_VERTEX, GL.GL_FLOAT, 0, this.textureCoordsBuffer.rewind()); } else if (texturesEnabled) { gl.glDisable(GL.GL_TEXTURE_2D); gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY); texturesEnabled = false; } // If this geometry is double sided, then backface culling must be disabled. Otherwise backface culling // must be enabled, because some SketchUp models will not render correctly without it. if (geometry.doubleSided && cullingEnabled) { gl.glDisable(GL.GL_CULL_FACE); cullingEnabled = false; } else if (!geometry.doubleSided && !cullingEnabled) { gl.glEnable(GL.GL_CULL_FACE); cullingEnabled = true; } // Look up VBO IDs each time through the loop because binding a texture may bump a VBO out of the cache. // If VBOs are not used, the vertex array is bound once, before the loop. int[] vboIds = null; if (this.shouldUseVBOs(dc)) { vboIds = this.getVboIds(dc); if (vboIds == null) { FloatBuffer vb = this.coordBuffer; gl.glVertexPointer(ColladaAbstractGeometry.COORDS_PER_VERTEX, GL.GL_FLOAT, 0, vb.rewind()); } } if (vboIds != null) this.doDrawInteriorVBO(dc, geometry, vboIds); else this.doDrawInteriorVA(dc, geometry); } } finally { // Restore the OpenGL matrix stack state. stackHandler.pop(gl); // Restore the previous OpenGL texture state and cull face state. We do this in order to ensure that any // subsequent ColladaMeshShape instances processed during batch picking/rendering have the same initial // conditions as the first ColladaMeshShape. Without this restore, subsequent ColladaMeshShapes without a // texture will have the GL_TEXTURE_COORD_ARRAY state enabled during glDrawArrays. if (texturesEnabled) { gl.glDisable(GL.GL_TEXTURE_2D); gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY); } if (cullingEnabled) { gl.glDisable(GL.GL_CULL_FACE); } } } /** * Draw one geometry in the mesh interior using vertex arrays. * * @param dc Current draw context. * @param geometry Geometry to draw. */ protected void doDrawInteriorVA(DrawContext dc, Geometry geometry) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. if (geometry.offset == -1) return; if (!dc.isPickingMode() && this.mustApplyLighting(dc, null) && this.normalBuffer != null) gl.glNormalPointer(GL.GL_FLOAT, 0, this.normalBuffer.rewind()); gl.glDrawArrays(this.elementType, geometry.offset, geometry.colladaGeometry.getCount() * this.vertsPerShape); } /** * Draw one geometry in the mesh interior using vertex buffer objects. * * @param dc Current draw context. * @param geometry Geometry to draw. * @param vboIds Array of vertex buffer identifiers. The first element of the array identifies the buffer that * contains vertex coordinates and normal vectors. */ protected void doDrawInteriorVBO(DrawContext dc, Geometry geometry, int[] vboIds) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. if (geometry.offset == -1) return; try { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboIds[0]); gl.glVertexPointer(ColladaAbstractGeometry.COORDS_PER_VERTEX, GL.GL_FLOAT, 0, 0); if (!dc.isPickingMode() && this.mustApplyLighting(dc, null) && this.normalBuffer != null) { gl.glNormalPointer(GL.GL_FLOAT, 0, this.normalBufferPosition * Buffers.SIZEOF_FLOAT); } gl.glDrawArrays(this.elementType, geometry.offset, geometry.colladaGeometry.getCount() * this.vertsPerShape); } finally { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); } } /** * Called during drawing to set the modelview matrix to apply the correct position, scale and orientation for this * shape. * * @param dc the current DrawContext * * @throws IllegalArgumentException if draw context is null or the draw context GL is null */ protected void setModelViewMatrix(DrawContext dc) { if (dc.getGL() == null) { String message = Logging.getMessage("nullValue.DrawingContextGLIsNull"); Logging.logger().severe(message); throw new IllegalStateException(message); } Matrix matrix = dc.getView().getModelviewMatrix(); matrix = matrix.multiply(this.computeRenderMatrix(dc)); GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. gl.glMatrixMode(GL2.GL_MODELVIEW); double[] matrixArray = new double[16]; matrix.toArray(matrixArray, 0, false); gl.glLoadMatrixd(matrixArray, 0); } ////////////////////////////////////////////////////////////////////// // Geometry creation ////////////////////////////////////////////////////////////////////// /** {@inheritDoc} */ protected boolean isOrderedRenderableValid(DrawContext dc) { return this.coordBuffer != null; } /** {@inheritDoc} */ protected AbstractShapeData createCacheEntry(DrawContext dc) { return new ShapeData(dc, this); } /** * Compute enough geometry to determine this shape's extent, reference point and eye distance. *

* A {@link gov.nasa.worldwind.render.AbstractShape.AbstractShapeData} must be current when this method is called. * * @param dc the current draw context. * @param shapeData the current shape data for this shape. */ protected void createMinimalGeometry(DrawContext dc, ShapeData shapeData) { Vec4 refPt = this.computeReferencePoint(dc.getTerrain()); if (refPt == null) return; shapeData.setReferencePoint(refPt); shapeData.setEyeDistance(this.computeEyeDistance(dc, shapeData)); shapeData.setGlobeStateKey(dc.getGlobe().getGlobeStateKey(dc)); shapeData.setVerticalExaggeration(dc.getVerticalExaggeration()); if (this.coordBuffer == null) this.createVertexCoords(dc); if (shapeData.getExtent() == null) { Extent extent = this.computeExtent(dc); this.extentCache.put(new ExtentCacheKey(dc.getGlobe(), shapeData.renderMatrix), extent); shapeData.setExtent(extent); } } /** * Create full geometry for the shape, including normals and texture coordinates. * * @param dc Current draw context. */ protected void createFullGeometry(DrawContext dc) { if (this.normalBuffer == null && this.mustApplyLighting(dc, null)) this.createNormals(); if (this.textureCoordsBuffer == null && this.mustApplyTexture(dc)) this.createTexCoords(); for (Geometry geometry : this.geometries) { if (geometry.material == null) geometry.material = this.getMaterial(geometry); geometry.doubleSided = this.isDoubleSided(geometry.colladaGeometry); } } /** * Compute the shape's extent, using the active orientation matrix. * * @param dc Current draw context. * * @return The spatial extent of the shape, or null if the extent cannot be determined. */ protected Extent computeExtent(DrawContext dc) { if (this.coordBuffer == null) return null; // Compute a bounding box around the vertices in this shape. this.coordBuffer.rewind(); Box box = Box.computeBoundingBox(new BufferWrapper.FloatBufferWrapper(this.coordBuffer), ColladaAbstractGeometry.COORDS_PER_VERTEX); Matrix matrix = this.computeRenderMatrix(dc); // Compute the corners of the bounding box and transform with the active transform matrix. List extrema = new ArrayList(); Vec4[] corners = box.getCorners(); for (Vec4 corner : corners) { extrema.add(corner.transformBy4(matrix)); } if (extrema.isEmpty()) return null; // Compute the bounding box around the transformed corners. return Box.computeBoundingBox(extrema); } public Box getLocalExtent(ColladaTraversalContext tc) { if (tc == null) { String message = Logging.getMessage("nullValue.TraversalContextIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } int size = this.shapeCount * this.vertsPerShape * ColladaAbstractGeometry.COORDS_PER_VERTEX; FloatBuffer vertexBuffer = WWBufferUtil.newFloatBuffer(size, true); for (Geometry geometry : this.geometries) { geometry.colladaGeometry.getVertices(vertexBuffer); } // Compute a bounding box around the vertices in this shape. vertexBuffer.rewind(); Box box = Box.computeBoundingBox(new BufferWrapper.FloatBufferWrapper(vertexBuffer), ColladaAbstractGeometry.COORDS_PER_VERTEX); // Compute the corners of the bounding box and transform with the active transform matrix. List extrema = new ArrayList(); Vec4[] corners = box.getCorners(); for (Vec4 corner : corners) { extrema.add(corner.transformBy4(tc.peekMatrix())); } if (extrema.isEmpty()) return null; // Compute the bounding box around the transformed corners. return Box.computeBoundingBox(extrema); } /** * Create the shape's vertex coordinates. The coordinates are stored in {@link #coordBuffer}. * * @param dc Current draw context. */ protected void createVertexCoords(DrawContext dc) { int size = this.shapeCount * this.vertsPerShape * ColladaAbstractGeometry.COORDS_PER_VERTEX; // Capture the position at which normals buffer starts (in case there are normals) this.normalBufferPosition = size; if (this.mustApplyLighting(dc, null)) { size += (this.shapeCount * this.vertsPerShape * ColladaAbstractGeometry.COORDS_PER_VERTEX); } // Capture the position at which texture coordinate buffer starts (in case that textures are applied) this.texCoordBufferPosition = size; if (this.mustApplyTexture(dc)) { size += (this.shapeCount * this.vertsPerShape * ColladaAbstractGeometry.TEX_COORDS_PER_VERTEX); } if (this.coordBuffer != null && this.coordBuffer.capacity() >= size) this.coordBuffer.clear(); else this.coordBuffer = Buffers.newDirectFloatBuffer(size); for (Geometry geometry : this.geometries) { geometry.offset = this.coordBuffer.position() / this.vertsPerShape; geometry.colladaGeometry.getVertices(this.coordBuffer); } } /** Create this shape's vertex normals. The normals are stored in {@link #normalBuffer}. */ protected void createNormals() { this.coordBuffer.position(this.normalBufferPosition); this.normalBuffer = this.coordBuffer.slice(); for (Geometry geometry : this.geometries) { if (geometry.colladaGeometry.getNormalAccessor() != null) { geometry.colladaGeometry.getNormals(this.normalBuffer); } else { int thisSize = geometry.colladaGeometry.getCount() * this.vertsPerShape * ColladaAbstractGeometry.COORDS_PER_VERTEX; this.normalBuffer.position(this.normalBuffer.position() + thisSize); } } } /** Create this shape's texture coordinates. The texture coordinates are stored in {@link #textureCoordsBuffer}. */ protected void createTexCoords() { this.coordBuffer.position(this.texCoordBufferPosition); this.textureCoordsBuffer = this.coordBuffer.slice(); for (Geometry geometry : this.geometries) { if (this.mustApplyTexture(geometry)) { String semantic = this.getTexCoordSemantic(geometry); geometry.colladaGeometry.getTextureCoordinates(this.textureCoordsBuffer, semantic); } else { int thisSize = geometry.colladaGeometry.getCount() * this.vertsPerShape * ColladaAbstractGeometry.TEX_COORDS_PER_VERTEX; this.textureCoordsBuffer.position(this.textureCoordsBuffer.position() + thisSize); } } } /** {@inheritDoc} */ protected void fillVBO(DrawContext dc) { GL gl = dc.getGL(); ShapeData shapeData = (ShapeData) getCurrentData(); int[] vboIds = this.getVboIds(dc); if (vboIds == null) { int size = this.coordBuffer.limit() * Buffers.SIZEOF_FLOAT; vboIds = new int[1]; gl.glGenBuffers(vboIds.length, vboIds, 0); dc.getGpuResourceCache().put(shapeData.getVboCacheKey(), vboIds, GpuResourceCache.VBO_BUFFERS, size); } try { FloatBuffer vb = this.coordBuffer; gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboIds[0]); gl.glBufferData(GL.GL_ARRAY_BUFFER, vb.limit() * Buffers.SIZEOF_FLOAT, vb.rewind(), GL.GL_STATIC_DRAW); } finally { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); } } /** * Computes this shape's reference center. * * @param dc the current draw context. * * @return the computed reference center, or null if it cannot be computed. */ protected Vec4 computeReferenceCenter(DrawContext dc) { Position pos = this.getReferencePosition(); if (pos == null) return null; return this.computePoint(dc.getTerrain(), pos); } /** * Computes the minimum distance between this shape and the eye point. *

* A {@link gov.nasa.worldwind.render.AbstractShape.AbstractShapeData} must be current when this method is called. * * @param dc the current draw context. * * @return the minimum distance from the shape to the eye point. */ protected double computeEyeDistance(DrawContext dc) { Vec4 eyePoint = dc.getView().getEyePoint(); Vec4 refPt = this.computePoint(dc.getTerrain(), this.getModelPosition()); if (refPt != null) return refPt.distanceTo3(eyePoint); return 0; } /** * Computes the transform to use during rendering to orient the model. * * @param dc the current draw context * * @return the modelview transform for this shape. */ protected Matrix computeRenderMatrix(DrawContext dc) { ShapeData current = (ShapeData) this.getCurrent(); if (current.referenceCenter == null || current.isExpired(dc)) { current.referenceCenter = this.computeReferenceCenter(dc); Position refPosition = dc.getGlobe().computePositionFromPoint(current.referenceCenter); current.surfaceOrientationMatrix = dc.getGlobe().computeSurfaceOrientationAtPosition(refPosition); } return current.surfaceOrientationMatrix.multiply(current.renderMatrix); } ////////////////////////////////////////////////////////////////////// // Materials and textures ////////////////////////////////////////////////////////////////////// /** * {@inheritDoc} * * @return True if any geometry in this shape includes a texture. */ @Override protected boolean mustApplyTexture(DrawContext dc) { for (Geometry geometry : this.geometries) { if (this.mustApplyTexture(geometry)) return true; } return false; } /** * Indicates whether or not a texture must be applied to a geometry. * * @param geometry Geometry to test. * * @return True if the specified geometry includes a texture. */ protected boolean mustApplyTexture(Geometry geometry) { String semantic = this.getTexCoordSemantic(geometry); return geometry.colladaGeometry.getTexCoordAccessor(semantic) != null && this.getTexture(geometry) != null; } /** * Indicates the texture applied to this shape. * * @return The texture that must be applied to the shape, or null if there is no texture, or the texture is not * available. */ protected WWTexture getTexture(Geometry geometry) { if (geometry.texture != null) return geometry.texture; String source = this.getTextureSource(geometry.colladaGeometry); if (source != null) { Object o = geometry.colladaGeometry.getRoot().resolveReference(source); if (o != null) geometry.texture = new LazilyLoadedTexture(o); } return geometry.texture; } /** * Apply a material to the active draw context. * * @param dc Current draw context. * @param material Material to apply. */ protected void applyMaterial(DrawContext dc, Material material) { GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility. ShapeAttributes activeAttrs = this.getActiveAttributes(); double opacity = activeAttrs.getInteriorOpacity(); // We don't need to enable or disable lighting; that's handled by super.prepareToDrawInterior. if (this.mustApplyLighting(dc, activeAttrs)) { material.apply(gl, GL2.GL_FRONT_AND_BACK, (float) opacity); } else { Color sc = material.getDiffuse(); gl.glColor4ub((byte) sc.getRed(), (byte) sc.getGreen(), (byte) sc.getBlue(), (byte) (opacity < 1 ? (int) (opacity * 255 + 0.5) : 255)); } } /** * Indicates the material applied to a geometry. * * @param geometry Geometry for which to find material. * * @return Material to apply to the geometry. If the COLLADA document does not define a material, this method return * a default material. */ protected Material getMaterial(Geometry geometry) { ColladaInstanceMaterial myMaterialInstance = this.getInstanceMaterial(geometry); if (myMaterialInstance == null) return DEFAULT_INTERIOR_MATERIAL; // Attempt to resolve the instance. The material may not be immediately available. ColladaMaterial myMaterial = myMaterialInstance.get(); if (myMaterial == null) return DEFAULT_INTERIOR_MATERIAL; ColladaInstanceEffect myEffectInstance = myMaterial.getInstanceEffect(); if (myEffectInstance == null) return DEFAULT_INTERIOR_MATERIAL; // Attempt to resolve effect. The effect may not be immediately available. ColladaEffect myEffect = myEffectInstance.get(); if (myEffect == null) return DEFAULT_INTERIOR_MATERIAL; return myEffect.getMaterial(); } /** * Indicates the instance_material element for a geometry. * * @param geometry Geometry for which to find material. * * @return Material for the specified geometry, or null if the material cannot be resolved. */ protected ColladaInstanceMaterial getInstanceMaterial(Geometry geometry) { if (this.bindMaterial == null) return null; ColladaTechniqueCommon techniqueCommon = this.bindMaterial.getTechniqueCommon(); if (techniqueCommon == null) return null; String materialSource = geometry.colladaGeometry.getMaterial(); if (materialSource == null) return null; for (ColladaInstanceMaterial material : techniqueCommon.getMaterials()) { if (materialSource.equals(material.getSymbol())) return material; } return null; } /** * Indicates the semantic that identifies texture coordinates. This may be specified for each material using a * bind_vertex_input element. * * @param geometry Geometry for which to find semantic. * * @return The semantic string that identifies the texture coordinates, or null if the geometry does not define the * semantic. */ protected String getTexCoordSemantic(Geometry geometry) { ColladaEffect effect = this.getEffect(geometry.colladaGeometry); if (effect == null) return null; ColladaTexture texture = effect.getTexture(); if (texture == null) return null; String texcoord = texture.getTexCoord(); if (texcoord == null) return null; ColladaInstanceMaterial instanceMaterial = this.getInstanceMaterial(geometry); String inputSemantic = null; // Search bind_vertex_input to find the semantic that identifies the texture coords. for (ColladaBindVertexInput bind : instanceMaterial.getBindVertexInputs()) { if (texcoord.equals(bind.getSemantic())) inputSemantic = bind.getInputSemantic(); } return inputSemantic; } /** * Indicates the source (file path or URL) of the texture applied to a geometry. * * @param geometry Geometry for which to find texture source. * * @return The source of the texture, or null if it cannot be resolved. */ protected String getTextureSource(ColladaAbstractGeometry geometry) { ColladaTechniqueCommon techniqueCommon = this.bindMaterial.getTechniqueCommon(); if (techniqueCommon == null) return null; String materialSource = geometry.getMaterial(); if (materialSource == null) return null; ColladaInstanceMaterial myMaterialInstance = null; for (ColladaInstanceMaterial material : techniqueCommon.getMaterials()) { if (materialSource.equals(material.getSymbol())) { myMaterialInstance = material; break; } } if (myMaterialInstance == null) return null; // Attempt to resolve the instance. The material may not be immediately available. ColladaMaterial myMaterial = myMaterialInstance.get(); if (myMaterial == null) return null; ColladaInstanceEffect myEffectInstance = myMaterial.getInstanceEffect(); if (myEffectInstance == null) return null; // Attempt to resolve effect. The effect may not be immediately available. ColladaEffect myEffect = myEffectInstance.get(); if (myEffect == null) return null; ColladaTexture texture = myEffect.getTexture(); if (texture == null) return null; String imageRef = this.getImageRef(myEffect, texture); if (imageRef == null) return null; // imageRef identifiers an element in this or another document. If the string doesn't already contain a // # then treat the entire string as a fragment identifier in the current document. if (!imageRef.contains("#")) imageRef = "#" + imageRef; // imageRef identifiers an element (may be external). This element will give us the filename. Object o = geometry.getRoot().resolveReference(imageRef); if (o instanceof ColladaImage) return ((ColladaImage) o).getInitFrom(); return null; } /** * Indicates the reference string for an image. The image reference identifies an image element in this, or * another COLLADA file. For example, "#myImage". * * @param effect Effect that defines the texture. * @param texture Texture for which to find the image reference. * * @return The image reference, or null if it cannot be resolved. */ protected String getImageRef(ColladaEffect effect, ColladaTexture texture) { String sid = texture.getTexture(); ColladaNewParam param = effect.getParam(sid); if (param == null) return null; ColladaSampler2D sampler = param.getSampler2D(); if (sampler == null) return null; ColladaSource source = sampler.getSource(); if (source == null) return null; sid = source.getCharacters(); if (sid == null) return null; param = effect.getParam(sid); if (param == null) return null; ColladaSurface surface = param.getSurface(); if (surface != null) return surface.getInitFrom(); return null; } /** * Indicates the effect applied to a geometry. * * @param geometry Geometry for which to find effect. * * @return Effect applied to the specified geometry, or null if no effect is defined, or the effect is not * available. */ protected ColladaEffect getEffect(ColladaAbstractGeometry geometry) { ColladaTechniqueCommon techniqueCommon = this.bindMaterial.getTechniqueCommon(); if (techniqueCommon == null) return null; String materialSource = geometry.getMaterial(); if (materialSource == null) return null; ColladaInstanceMaterial myMaterialInstance = null; for (ColladaInstanceMaterial material : techniqueCommon.getMaterials()) { if (materialSource.equals(material.getSymbol())) { myMaterialInstance = material; break; } } if (myMaterialInstance == null) return null; // Attempt to resolve the instance. The material may not be immediately available. ColladaMaterial myMaterial = myMaterialInstance.get(); if (myMaterial == null) return null; ColladaInstanceEffect myEffectInstance = myMaterial.getInstanceEffect(); if (myEffectInstance == null) return null; // Attempt to resolve effect. The effect may not be immediately available. return myEffectInstance.get(); } /** * Indicates whether or not a geometry is double sided. A geometry is double sided if its effect element * contains a technique for the profile "GOOGLEEARTH", and the technique includes a double_sided * field. The double_sided field is not part of the COLLADA specification, but many COLLADA models packaged * in KML include the element. * * @param geometry Geometry to test. * * @return True if the geometry is marked as double sided. Otherwise false. */ protected boolean isDoubleSided(ColladaAbstractGeometry geometry) { ColladaEffect effect = this.getEffect(geometry); if (effect == null) return false; ColladaProfileCommon profile = effect.getProfileCommon(); if (profile == null) return false; ColladaExtra extra = profile.getExtra(); if (extra == null) return false; ColladaTechnique technique = (ColladaTechnique) extra.getField("technique"); if (technique == null || !"GOOGLEEARTH".equals(technique.getProfile())) return false; Integer i = (Integer) technique.getField("double_sided"); return i != null && i == 1; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy