diff options
Diffstat (limited to 'o3d')
-rw-r--r-- | o3d/samples/o3djs/element.js | 68 | ||||
-rw-r--r-- | o3d/samples/o3djs/picking.js | 38 | ||||
-rw-r--r-- | o3d/samples/picking.html | 57 |
3 files changed, 148 insertions, 15 deletions
diff --git a/o3d/samples/o3djs/element.js b/o3d/samples/o3djs/element.js index aedcacb..0c4593b 100644 --- a/o3d/samples/o3djs/element.js +++ b/o3d/samples/o3djs/element.js @@ -141,4 +141,72 @@ o3djs.element.duplicateElement = function(pack, sourceElement) { return newElement; }; +/** + * Gets the normal for specific triangle in a Primitive in that Primitive's + * local space. + * + * NOTE: THIS FUNCTION IS SLOW! If you want to do collisions you should use a + * different solution. + * + * @param {!o3d.Primitive} primitive Primitive to get normal from. The + * primitive MUST be a TRIANGLELIST or a TRIANGLESTRIP and it must have a + * POSITION,0 stream. + * @param {number} index Index of triangle. + * @param {boolean} opt_winding The winding of the triangles of the + * Primitive. False = Clockwise, True = Counterclockwise. The default is + * false. This is only used for Primitives that have no normals. + * @return {!o3djs.math.Vector3} The normal for the triangle. + */ +o3djs.element.getNormalForTriangle = function(primitive, index, opt_winding) { + // Check that we can do this + var primitiveType = primitive.primitiveType; + if (primitiveType != o3djs.base.o3d.Primitive.TRIANGLELIST && + primitiveType != o3djs.base.o3d.Primitive.TRIANGLESTRIP) { + throw 'primitive is not a TRIANGLELIST or TRIANGLESTRIP'; + } + + var indexBuffer = primitive.indexBuffer; + var vertexIndex = (primitiveType == o3djs.base.o3d.Primitive.TRIANGLELIST) ? + (index * 3) : (index + 2); + var vertexIndices; + if (indexBuffer) { + var indexField = indexBuffer.fields[0]; + vertexIndices = indexField.getAt(vertexIndex, 3); + } else { + vertexIndices = [vertexIndex, vertexIndex + 1, vertexIndex + 2] + } + + var normalStream = primitive.streamBank.getVertexStream( + o3djs.base.o3d.Stream.NORMAL, 0); + if (normalStream) { + var normalField = normalStream.field; + // Look up the 3 normals that make the triangle. + var summedNormal = [0, 0, 0]; + for (var ii = 0; ii < 3; ++ii) { + var normal = normalField.getAt(vertexIndices[ii], 1); + summedNormal = o3djs.math.addVector(summedNormal, normal); + } + return o3djs.math.normalize(summedNormal); + } else { + var positionStream = primitive.streamBank.getVertexStream( + o3djs.base.o3d.Stream.POSITION, 0); + if (!positionStream) { + throw 'no POSITION,0 stream in primitive'; + } + var positionField = positionStream.field; + // Lookup the 3 positions that make the triangle. + var positions = []; + for (var ii = 0; ii < 3; ++ii) { + positions[ii] = positionField.getAt(vertexIndices[ii], 1); + } + + // Compute a face normal from the positions. + var v0 = o3djs.math.normalize(o3djs.math.subVector(positions[1], + positions[0])); + var v1 = o3djs.math.normalize(o3djs.math.subVector(positions[2], + positions[1])); + return opt_winding ? o3djs.math.cross(v1, v0) : o3djs.math.cross(v0, v1); + } +}; + diff --git a/o3d/samples/o3djs/picking.js b/o3d/samples/o3djs/picking.js index a40eb5d..06e3765 100644 --- a/o3d/samples/o3djs/picking.js +++ b/o3d/samples/o3djs/picking.js @@ -70,18 +70,20 @@ o3djs.picking.Ray = goog.typedef; /** * Creates a new PickInfo. - * @param {!o3djs.picking.ShapeInfo} shapeInfo The ShapeInfo that - * was picked. - * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Information - * about the pick. - * @param {!o3djs.math.Vector3} worldIntersectionPosition - * world position of intersection. + * @param {!o3d.Element} element The Element that was picked. + * @param {!o3djs.picking.ShapeInfo} shapeInfo The ShapeInfo that was picked. + * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Information about the + * pick. + * @param {!o3djs.math.Vector3} worldIntersectionPosition world position of + * intersection. * @return {!o3djs.picking.PickInfo} The new PickInfo. */ -o3djs.picking.createPickInfo = function(shapeInfo, +o3djs.picking.createPickInfo = function(element, + shapeInfo, rayIntersectionInfo, worldIntersectionPosition) { - return new o3djs.picking.PickInfo(shapeInfo, + return new o3djs.picking.PickInfo(element, + shapeInfo, rayIntersectionInfo, worldIntersectionPosition); }; @@ -219,17 +221,24 @@ o3djs.picking.dumpRayIntersectionInfo = function(label, /** * Creates a new PickInfo. Used to return picking information. * @constructor - * @param {!o3djs.picking.ShapeInfo} shapeInfo The ShapeInfo that - * was picked. - * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Information - * about the pick. + * @param {!o3d.Element} element The Element that was picked. + * @param {!o3djs.picking.ShapeInfo} shapeInfo The ShapeInfo that was picked. + * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Information about the + * pick. * @param {!o3djs.math.Vector3} worldIntersectionPosition world position of * intersection. */ -o3djs.picking.PickInfo = function(shapeInfo, +o3djs.picking.PickInfo = function(element, + shapeInfo, rayIntersectionInfo, worldIntersectionPosition) { /** + * The Element that was picked (Primitive). + * @type {!o3d.Element} + */ + this.element = element; + + /** * The ShapeInfo that was picked. * @type {!o3djs.picking.ShapeInfo} */ @@ -335,7 +344,8 @@ o3djs.picking.ShapeInfo.prototype.pick = function(worldRay) { if (rayIntersectionInfo.intersected) { var worldIntersectionPosition = o3djs.math.matrix4.transformPoint( worldMatrix, rayIntersectionInfo.position); - return o3djs.picking.createPickInfo(this, + return o3djs.picking.createPickInfo(element, + this, rayIntersectionInfo, worldIntersectionPosition); } diff --git a/o3d/samples/picking.html b/o3d/samples/picking.html index cd9ddd1..ae62ebd 100644 --- a/o3d/samples/picking.html +++ b/o3d/samples/picking.html @@ -56,7 +56,7 @@ O3D Picking Example. <!-- Include default javascript library functions--> <script type="text/javascript" src="o3djs/base.js"></script> <!-- Our javascript code --> -<script type="text/javascript"> +<script type="text/javascript" id="o3d"> o3djs.require('o3djs.util'); o3djs.require('o3djs.math'); o3djs.require('o3djs.rendergraph'); @@ -64,6 +64,7 @@ o3djs.require('o3djs.pack'); o3djs.require('o3djs.camera'); o3djs.require('o3djs.picking'); o3djs.require('o3djs.scene'); +o3djs.require('o3djs.debug'); // Events // init() once the page has finished loading. @@ -71,6 +72,9 @@ o3djs.require('o3djs.scene'); window.onload = init; window.onunload = unload; +// constants +var NORMAL_SCALE_FACTOR = 2.0; + // global variables var g_o3d; var g_math; @@ -79,6 +83,9 @@ var g_pack; var g_viewInfo; var g_treeInfo; // information about the transform graph. var g_pickInfoElem; +var g_debugHelper; +var g_debugLineGroup; +var g_debugLine; var g_selectedInfo = null; var g_flashTimer = 0; var g_highlightMaterial; @@ -135,13 +142,53 @@ function pick(e) { unSelectAll(); // Update the entire tree in case anything moved. + // NOTE: This function is very SLOW! + // If you really want to use picking you should manually update only those + // transforms and shapes that moved, were added, or deleted by writing your + // own picking library. You should also make sure that you are only + // considering things that are pickable. By that I mean if you have a scene of + // a meadow with trees, grass, bushes, and animals and the only thing the user + // can pick is the animals then put the animals on their own sub branch of the + // transform graph and only pick against that subgraph. + // Even better, make a separate transform graph with only cubes on it to + // represent the animals and use that instead of the actual animals. g_treeInfo.update(); var pickInfo = g_treeInfo.pick(worldRay); if (pickInfo) { select(pickInfo); g_pickInfoElem.innerHTML = pickInfo.shapeInfo.shape.name; + + // Display the normal + // NOTE: Lookup the normal with this function is very SLOW!! + // If you need performance, for a game or something, you should consider + // other methods. + var normal = o3djs.element.getNormalForTriangle( + pickInfo.element, + pickInfo.rayIntersectionInfo.primitiveIndex); + + // Convert the normal from local to world space. + normal = g_math.matrix4.transformNormal( + pickInfo.shapeInfo.parent.transform.worldMatrix, + normal); + + // Remove the scale from the normal. + normal = g_math.normalize(normal); + + // Get the world position of the collision. + var worldPosition = pickInfo.worldIntersectionPosition; + + // Add the normal to it to get a point in space above it with some + // multiplier to scale it. + var normalSpot = g_math.addVector( + worldPosition, + g_math.mulVectorScalar(normal, NORMAL_SCALE_FACTOR)); + + // Move our debug line to show the normal + g_debugLine.setVisible(true); + g_debugLine.setEndPoints(worldPosition, normalSpot); } else { + g_debugLine.setVisible(false); g_pickInfoElem.innerHTML = '--nothing--'; } } @@ -238,6 +285,14 @@ function initStep2(clientElements) { g_client.root, g_client.renderGraphRoot); + // Use the debug library to create a line we can position to show + // the normal. + g_debugHelper = o3djs.debug.createDebugHelper(g_client.createPack(), + g_viewInfo); + g_debugLineGroup = g_debugHelper.createDebugLineGroup(g_client.root); + g_debugLine = g_debugLineGroup.addLine(); + g_debugLine.setColor([0,1,0,1]); + // Create a material for highlighting. g_highlightMaterial = g_pack.createObject('Material'); g_highlightMaterial.drawList = g_viewInfo.performanceDrawList; |