summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3djs/primitives.js
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/samples/o3djs/primitives.js')
-rw-r--r--o3d/samples/o3djs/primitives.js1698
1 files changed, 1698 insertions, 0 deletions
diff --git a/o3d/samples/o3djs/primitives.js b/o3d/samples/o3djs/primitives.js
new file mode 100644
index 0000000..d3df686
--- /dev/null
+++ b/o3d/samples/o3djs/primitives.js
@@ -0,0 +1,1698 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions to create geometric primitives for
+ * o3d. It puts them in the "primitives" module on the o3djs object.
+ *
+ * For more information about o3d see http://code.google.com/p/o3d
+ *
+ *
+ * Requires base.js
+ */
+
+o3djs.provide('o3djs.primitives');
+
+o3djs.require('o3djs.math');
+
+/**
+ * A Module for creating primitives.
+ * @namespace
+ */
+o3djs.primitives = o3djs.primitives || {};
+
+
+/**
+ * Sets the bounding box and zSortPoint for a primitive based on its vertices
+ *
+ * @param {!o3d.Primitive} primitive Primitive to set culling info for.
+ */
+o3djs.primitives.setCullingInfo = function(primitive) {
+ var box = primitive.getBoundingBox(0);
+ primitive.boundingBox = box;
+ var minExtent = box.minExtent;
+ var maxExtent = box.maxExtent;
+ primitive.zSortPoint = o3djs.math.divVectorScalar(
+ o3djs.math.addVector(minExtent, maxExtent), 2);
+};
+
+/**
+ * Used to store the elements of a stream.
+ * @param {number} numComponents The number of numerical components per
+ * element.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @constructor
+ */
+o3djs.primitives.VertexStreamInfo = function(numComponents,
+ semantic,
+ opt_semanticIndex) {
+ /**
+ * The number of numerical components per element.
+ * @type {number}
+ */
+ this.numComponents = numComponents;
+
+ /**
+ * The semantic of the stream.
+ * @type {!o3d.Stream.Semantic}
+ */
+ this.semantic = semantic;
+
+ /**
+ * The semantic index of the stream.
+ * @type {number}
+ */
+ this.semanticIndex = opt_semanticIndex || 0;
+
+ /**
+ * The elements of the stream.
+ * @type {!Array.<number>}
+ */
+ this.elements = [];
+
+ /**
+ * Adds an element to this VertexStreamInfo. The number of values passed must
+ * match the number of components for this VertexStreamInfo.
+ * @param {number} value1 First value.
+ * @param {number} opt_value2 Second value.
+ * @param {number} opt_value3 Third value.
+ * @param {number} opt_value4 Fourth value.
+ */
+ this.addElement = function(value1, opt_value2, opt_value3, opt_value4) { };
+
+ /**
+ * Sets an element on this VertexStreamInfo. The number of values passed must
+ * match the number of components for this VertexStreamInfo.
+ * @param {number} index Index of element to set.
+ * @param {number} value1 First value.
+ * @param {number} opt_value2 Second value.
+ * @param {number} opt_value3 Third value.
+ * @param {number} opt_value4 Fourth value.
+ */
+ this.setElement = function(
+ index, value1, opt_value2, opt_value3, opt_value4) { };
+
+ /**
+ * Adds an element to this VertexStreamInfo. The number of values in the
+ * vector must match the number of components for this VertexStreamInfo.
+ * @param {!Array.<number>} vector Array of values for element.
+ */
+ this.addElementVector = function(vector) { }; // replaced below.
+
+ /**
+ * Sets an element on this VertexStreamInfo. The number of values in the
+ * vector must match the number of components for this VertexStreamInfo.
+ * @param {number} index Index of element to set.
+ * @param {!Array.<number>} vector Array of values for element.
+ */
+ this.setElementVector = function(index, vector) { }; // replaced below.
+
+ /**
+ * Sets an element on this VertexStreamInfo. The number of values in the
+ * vector will match the number of components for this VertexStreamInfo.
+ * @param {number} index Index of element to set.
+ * @return {!Array.<number>} Array of values for element.
+ */
+ this.getElementVector = function(index) { return []; }; // replaced below.
+
+ switch (numComponents) {
+ case 1:
+ this.addElement = function(value) {
+ this.elements.push(value);
+ }
+ this.getElement = function(index) {
+ return this.elements[index];
+ }
+ this.setElement = function(index, value) {
+ this.elements[index] = value;
+ }
+ break;
+ case 2:
+ this.addElement = function(value0, value1) {
+ this.elements.push(value0, value1);
+ }
+ this.addElementVector = function(vector) {
+ this.elements.push(vector[0], vector[1]);
+ }
+ this.getElementVector = function(index) {
+ return this.elements.slice(index * numComponents,
+ (index + 1) * numComponents);
+ }
+ this.setElement = function(index, value0, value1) {
+ this.elements[index * numComponents + 0] = value0;
+ this.elements[index * numComponents + 1] = value1;
+ }
+ this.setElementVector = function(index, vector) {
+ this.elements[index * numComponents + 0] = vector[0];
+ this.elements[index * numComponents + 1] = vector[1];
+ }
+ break;
+ case 3:
+ this.addElement = function(value0, value1, value2) {
+ this.elements.push(value0, value1, value2);
+ }
+ this.addElementVector = function(vector) {
+ this.elements.push(vector[0], vector[1], vector[2]);
+ }
+ this.getElementVector = function(index) {
+ return this.elements.slice(index * numComponents,
+ (index + 1) * numComponents);
+ }
+ this.setElement = function(index, value0, value1, value2) {
+ this.elements[index * numComponents + 0] = value0;
+ this.elements[index * numComponents + 1] = value1;
+ this.elements[index * numComponents + 2] = value2;
+ }
+ this.setElementVector = function(index, vector) {
+ this.elements[index * numComponents + 0] = vector[0];
+ this.elements[index * numComponents + 1] = vector[1];
+ this.elements[index * numComponents + 2] = vector[2];
+ }
+ break;
+ case 4:
+ this.addElement = function(value0, value1, value2, value3) {
+ this.elements.push(value0, value1, value2, value3);
+ }
+ this.addElementVector = function(vector) {
+ this.elements.push(vector[0], vector[1], vector[2], vector[3]);
+ }
+ this.getElementVector = function(index) {
+ return this.elements.slice(index * numComponents,
+ (index + 1) * numComponents);
+ }
+ this.setElement = function(index, value0, value1, value2, value3) {
+ this.elements[index * numComponents + 0] = value0;
+ this.elements[index * numComponents + 1] = value1;
+ this.elements[index * numComponents + 2] = value2;
+ this.elements[index * numComponents + 3] = value3;
+ }
+ this.setElementVector = function(index, vector) {
+ this.elements[index * numComponents + 0] = vector[0];
+ this.elements[index * numComponents + 1] = vector[1];
+ this.elements[index * numComponents + 2] = vector[2];
+ this.elements[index * numComponents + 3] = vector[3];
+ }
+ break;
+ default:
+ throw 'A stream must contain between 1 and 4 components';
+ }
+};
+
+/**
+ * Get the number of elements in the stream.
+ * @return {number} The number of elements in the stream.
+ */
+o3djs.primitives.VertexStreamInfo.prototype.numElements = function() {
+ return this.elements.length / this.numComponents;
+};
+
+/**
+ * Create a VertexStreamInfo.
+ * @param {number} numComponents The number of numerical components per
+ * element.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @return {!o3djs.primitives.VertexStreamInfo} The new stream.
+ */
+o3djs.primitives.createVertexStreamInfo = function(numComponents,
+ semantic,
+ opt_semanticIndex) {
+ return new o3djs.primitives.VertexStreamInfo(numComponents,
+ semantic,
+ opt_semanticIndex);
+};
+
+/**
+ * VertexInfo. Used to store vertices and indices.
+ * @constructor
+ */
+o3djs.primitives.VertexInfo = function() {
+ this.streams = [];
+ this.indices = [];
+};
+
+/**
+ * Add a new stream to the VertexInfo, replacing it with a new empty one
+ * if it already exists.
+ * @param {number} numComponents The number of components per vector.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @return {!o3djs.primitives.VertexStreamInfo} The new stream.
+ */
+o3djs.primitives.VertexInfo.prototype.addStream = function(
+ numComponents,
+ semantic,
+ opt_semanticIndex) {
+ this.removeStream(semantic, opt_semanticIndex);
+ var stream = o3djs.primitives.createVertexStreamInfo(
+ numComponents,
+ semantic,
+ opt_semanticIndex);
+ this.streams.push(stream);
+ return stream;
+};
+
+/**
+ * Find a stream in the VertexInfo.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @return {o3djs.primitives.VertexStreamInfo} The stream or null if it
+ * is not present.
+ */
+o3djs.primitives.VertexInfo.prototype.findStream = function(
+ semantic,
+ opt_semanticIndex) {
+ opt_semanticIndex = opt_semanticIndex || 0;
+ for (var i = 0; i < this.streams.length; ++i) {
+ if (this.streams[i].semantic === semantic &&
+ this.streams[i].semanticIndex == opt_semanticIndex) {
+ return this.streams[i];
+ }
+ }
+ return null;
+};
+
+/**
+ * Remove a stream from the VertexInfo. Does nothing if a matching stream
+ * does not exist.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ */
+o3djs.primitives.VertexInfo.prototype.removeStream = function(
+ semantic,
+ opt_semanticIndex) {
+ opt_semanticIndex = opt_semanticIndex || 0;
+ for (var i = 0; i < this.streams.length; ++i) {
+ if (this.streams[i].semantic === semantic &&
+ this.streams[i].semanticIndex == opt_semanticIndex) {
+ this.streams.splice(i, 1);
+ return;
+ }
+ }
+};
+
+/**
+ * Returns the number of triangles represented by the VertexInfo.
+ * @return {number} The number of triangles represented by VertexInfo.
+ */
+o3djs.primitives.VertexInfo.prototype.numTriangles = function() {
+ return this.indices.length / 3;
+};
+
+/**
+ * Adds a triangle.
+ * @param {number} index1 The index of the first vertex of the triangle.
+ * @param {number} index2 The index of the second vertex of the triangle.
+ * @param {number} index3 The index of the third vertex of the triangle.
+ */
+o3djs.primitives.VertexInfo.prototype.addTriangle = function(
+ index1, index2, index3) {
+ this.indices.push(index1, index2, index3);
+};
+
+/**
+ * Gets the vertex indices of the triangle at the given triangle index.
+ * @param {number} triangleIndex The index of the triangle.
+ * @return {!Array.<number>} An array of three triangle indices.
+ */
+o3djs.primitives.VertexInfo.prototype.getTriangle = function(
+ triangleIndex) {
+ var indexIndex = triangleIndex * 3;
+ return [this.indices[indexIndex + 0],
+ this.indices[indexIndex + 1],
+ this.indices[indexIndex + 2]];
+};
+
+/**
+ * Sets the vertex indices of thye triangle at the given triangle index.
+ * @param {number} triangleIndex The index of the triangle.
+ * @param {number} index1 The index of the first vertex of the triangle.
+ * @param {number} index2 The index of the second vertex of the triangle.
+ * @param {number} index3 The index of the third vertex of the triangle.
+ */
+o3djs.primitives.VertexInfo.prototype.setTriangle = function(
+ triangleIndex, index1, index2, index3) {
+ var indexIndex = triangleIndex * 3;
+ this.indices[indexIndex + 0] = index1;
+ this.indices[indexIndex + 1] = index2;
+ this.indices[indexIndex + 2] = index3;
+};
+
+/**
+ * Validates that all the streams contain the same number of elements, that
+ * all the indices are within range and that a position stream is present.
+ */
+o3djs.primitives.VertexInfo.prototype.validate = function() {
+ // Check the position stream is present.
+ var positionStream = this.findStream(o3djs.base.o3d.Stream.POSITION);
+ if (!positionStream)
+ throw 'POSITION stream is missing';
+
+ // Check all the streams have the same number of elements.
+ var numElements = positionStream.numElements();
+ for (var s = 0; s < this.streams.length; ++s) {
+ if (this.streams[s].numElements() !== numElements) {
+ throw 'Stream ' + s + ' contains ' + this.streams[s].numElements() +
+ ' elements whereas the POSITION stream contains ' + numElements;
+ }
+ }
+
+ // Check all the indices are in range.
+ for (var i = 0; i < this.indices.length; ++i) {
+ if (this.indices[i] < 0 || this.indices[i] >= numElements) {
+ throw 'The index ' + this.indices[i] + ' is out of range [0, ' +
+ numElements + ']';
+ }
+ }
+};
+
+/**
+ * Creates a shape from a VertexInfo
+ * @param {!o3d.Pack} pack Pack to create objects in.
+ * @param {!o3d.Material} material to use.
+ * @return {!o3d.Shape} The created shape.
+ */
+o3djs.primitives.VertexInfo.prototype.createShape = function(
+ pack,
+ material) {
+ this.validate();
+
+ var positionStream = this.findStream(o3djs.base.o3d.Stream.POSITION);
+ var numVertices = positionStream.numElements();
+
+ // create a shape and primitive for the vertices.
+ var shape = pack.createObject('Shape');
+ var primitive = pack.createObject('Primitive');
+ var streamBank = pack.createObject('StreamBank');
+ primitive.owner = shape;
+ primitive.streamBank = streamBank;
+ primitive.material = material;
+ primitive.numberPrimitives = this.indices.length / 3;
+ primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST;
+ primitive.numberVertices = numVertices;
+ primitive.createDrawElement(pack, null);
+
+ // Calculate the tangent and binormal or provide defaults or fail if the
+ // effect requires either and they are not present.
+ var streamInfos = material.effect.getStreamInfo();
+ for (var s = 0; s < streamInfos.length; ++s) {
+ var semantic = streamInfos[s].semantic;
+ var semanticIndex = streamInfos[s].semanticIndex;
+
+ var requiredStream = this.findStream(semantic, semanticIndex);
+ if (!requiredStream) {
+ switch (semantic) {
+ case o3djs.base.o3d.Stream.TANGENT:
+ case o3djs.base.o3d.Stream.BINORMAL:
+ this.addTangentStreams(semanticIndex);
+ break;
+ case o3djs.base.o3d.Stream.COLOR:
+ requiredStream = this.addStream(4, semantic, semanticIndex);
+ for (var i = 0; i < numVertices; ++i) {
+ requiredStream.addElement(1, 1, 1, 1);
+ }
+ break;
+ default:
+ throw 'Missing stream for semantic ' + semantic +
+ ' with semantic index ' + semanticIndex;
+ }
+ }
+ }
+
+ // These next few lines take our javascript streams and load them into a
+ // 'buffer' where the 3D hardware can find them. We have to do this
+ // because the 3D hardware can't 'see' javascript data until we copy it to
+ // a buffer.
+ var vertexBuffer = pack.createObject('VertexBuffer');
+ var fields = [];
+ for (var s = 0; s < this.streams.length; ++s) {
+ var stream = this.streams[s];
+ var fieldType = (stream.semantic == o3djs.base.o3d.Stream.COLOR &&
+ stream.numComponents == 4) ? 'UByteNField' : 'FloatField';
+ fields[s] = vertexBuffer.createField(fieldType, stream.numComponents);
+ streamBank.setVertexStream(stream.semantic,
+ stream.semanticIndex,
+ fields[s],
+ 0);
+ }
+ vertexBuffer.allocateElements(numVertices);
+ for (var s = 0; s < this.streams.length; ++s) {
+ fields[s].setAt(0, this.streams[s].elements);
+ }
+
+ var indexBuffer = pack.createObject('IndexBuffer');
+ indexBuffer.set(this.indices);
+ primitive.indexBuffer = indexBuffer;
+ o3djs.primitives.setCullingInfo(primitive);
+ return shape;
+};
+
+/**
+ * Reorients the vertices, positions and normals, of this vertexInfo by the
+ * given matrix. In other words, it multiplies each vertex by the given matrix
+ * and each normal by the inverse-transpose of the given matrix.
+ * @param {!o3djs.math.Matrix4} matrix Matrix by which to multiply.
+ */
+o3djs.primitives.VertexInfo.prototype.reorient = function(matrix) {
+ var math = o3djs.math;
+ var matrixInverse = math.inverse(math.matrix4.getUpper3x3(matrix));
+
+ for (var s = 0; s < this.streams.length; ++s) {
+ var stream = this.streams[s];
+ if (stream.numComponents == 3) {
+ var numElements = stream.numElements();
+ switch (stream.semantic) {
+ case o3djs.base.o3d.Stream.POSITION:
+ for (var i = 0; i < numElements; ++i) {
+ stream.setElementVector(i,
+ math.matrix4.transformPoint(matrix,
+ stream.getElementVector(i)));
+ }
+ break;
+ case o3djs.base.o3d.Stream.NORMAL:
+ for (var i = 0; i < numElements; ++i) {
+ stream.setElementVector(i,
+ math.matrix4.transformNormal(matrix,
+ stream.getElementVector(i)));
+ }
+ break;
+ case o3djs.base.o3d.Stream.TANGENT:
+ case o3djs.base.o3d.Stream.BINORMAL:
+ for (var i = 0; i < numElements; ++i) {
+ stream.setElementVector(i,
+ math.matrix4.transformDirection(matrix,
+ stream.getElementVector(i)));
+ }
+ break;
+ }
+ }
+ }
+};
+
+/**
+ * Calculate tangents and binormals based on the positions, normals and
+ * texture coordinates found in existing streams.
+ * @param {number} opt_semanticIndex The semantic index of the texture
+ * coordinate to use and the tangent and binormal streams to add. Defaults
+ * to zero.
+ */
+o3djs.primitives.VertexInfo.prototype.addTangentStreams =
+ function(opt_semanticIndex) {
+ opt_semanticIndex = opt_semanticIndex || 0;
+ var math = o3djs.math;
+
+ this.validate();
+
+ // Find and validate the position, normal and texture coordinate frames.
+ var positionStream = this.findStream(o3djs.base.o3d.Stream.POSITION);
+ if (!positionStream)
+ throw 'Cannot calculate tangent frame because POSITION stream is missing';
+ if (positionStream.numComponents != 3)
+ throw 'Cannot calculate tangent frame because POSITION stream is not 3D';
+
+ var normalStream = this.findStream(o3djs.base.o3d.Stream.NORMAL);
+ if (!normalStream)
+ throw 'Cannot calculate tangent frame because NORMAL stream is missing';
+ if (normalStream.numComponents != 3)
+ throw 'Cannot calculate tangent frame because NORMAL stream is not 3D';
+
+ var texCoordStream = this.findStream(o3djs.base.o3d.Stream.TEXCOORD,
+ opt_semanticIndex);
+ if (!texCoordStream)
+ throw 'Cannot calculate tangent frame because TEXCOORD stream ' +
+ opt_semanticIndex + ' is missing';
+
+ // Maps from position, normal key to tangent and binormal matrix.
+ var tangentFrames = {};
+
+ // Rounds a vector to integer components.
+ function roundVector(v) {
+ return [Math.round(v[0]), Math.round(v[1]), Math.round(v[2])];
+ }
+
+ // Generates a key for the tangentFrames map from a position and normal
+ // vector. Rounds position and normal to allow some tolerance.
+ function tangentFrameKey(position, normal) {
+ return roundVector(math.mulVectorScalar(position, 100)) + ',' +
+ roundVector(math.mulVectorScalar(normal, 100));
+ }
+
+ // Accumulates into the tangent and binormal matrix at the approximate
+ // position and normal.
+ function addTangentFrame(position, normal, tangent, binormal) {
+ var key = tangentFrameKey(position, normal);
+ var frame = tangentFrames[key];
+ if (!frame) {
+ frame = [[0, 0, 0], [0, 0, 0]];
+ }
+ frame = math.addMatrix(frame, [tangent, binormal]);
+ tangentFrames[key] = frame;
+ }
+
+ // Get the tangent and binormal matrix at the approximate position and
+ // normal.
+ function getTangentFrame(position, normal) {
+ var key = tangentFrameKey(position, normal);
+ return tangentFrames[key];
+ }
+
+ var numTriangles = this.numTriangles();
+ for (var triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex) {
+ // Get the vertex indices, uvs and positions for the triangle.
+ var vertexIndices = this.getTriangle(triangleIndex);
+ var uvs = [];
+ var positions = [];
+ var normals = [];
+ for (var i = 0; i < 3; ++i) {
+ var vertexIndex = vertexIndices[i];
+ uvs[i] = texCoordStream.getElementVector(vertexIndex);
+ positions[i] = positionStream.getElementVector(vertexIndex);
+ normals[i] = normalStream.getElementVector(vertexIndex);
+ }
+
+ // Calculate the tangent and binormal for the triangle using method
+ // described in Maya documentation appendix A: tangent and binormal
+ // vectors.
+ var tangent = [0, 0, 0];
+ var binormal = [0, 0, 0];
+ for (var axis = 0; axis < 3; ++axis) {
+ var edge1 = [positions[1][axis] - positions[0][axis],
+ uvs[1][0] - uvs[0][0], uvs[1][1] - uvs[0][1]];
+ var edge2 = [positions[2][axis] - positions[0][axis],
+ uvs[2][0] - uvs[0][0], uvs[2][1] - uvs[0][1]];
+ var edgeCross = math.normalize(math.cross(edge1, edge2));
+ if (edgeCross[0] == 0) {
+ edgeCross[0] = 1;
+ }
+ tangent[axis] = -edgeCross[1] / edgeCross[0];
+ binormal[axis] = -edgeCross[2] / edgeCross[0];
+ }
+
+ // Normalize the tangent and binornmal.
+ var tangentLength = math.length(tangent);
+ if (tangentLength > 0.001) {
+ tangent = math.mulVectorScalar(tangent, 1 / tangentLength);
+ }
+ var binormalLength = math.length(binormal);
+ if (binormalLength > 0.001) {
+ binormal = math.mulVectorScalar(binormal, 1 / binormalLength);
+ }
+
+ // Accumulate the tangent and binormal into the tangent frame map.
+ for (var i = 0; i < 3; ++i) {
+ addTangentFrame(positions[i], normals[i], tangent, binormal);
+ }
+ }
+
+ // Add the tangent and binormal streams.
+ var tangentStream = this.addStream(3,
+ o3djs.base.o3d.Stream.TANGENT,
+ opt_semanticIndex);
+ var binormalStream = this.addStream(3,
+ o3djs.base.o3d.Stream.BINORMAL,
+ opt_semanticIndex);
+
+ // Extract the tangent and binormal for each vertex.
+ var numVertices = positionStream.numElements();
+ for (var vertexIndex = 0; vertexIndex < numVertices; ++vertexIndex) {
+ var position = positionStream.getElementVector(vertexIndex);
+ var normal = normalStream.getElementVector(vertexIndex);
+ var frame = getTangentFrame(position, normal);
+
+ // Orthonormalize the tangent with respect to the normal.
+ var tangent = frame[0];
+ tangent = math.subVector(
+ tangent, math.mulVectorScalar(normal, math.dot(normal, tangent)));
+ var tangentLength = math.length(tangent);
+ if (tangentLength > 0.001) {
+ tangent = math.mulVectorScalar(tangent, 1 / tangentLength);
+ }
+
+ // Orthonormalize the binormal with respect to the normal and the tangent.
+ var binormal = frame[1];
+ binormal = math.subVector(
+ binormal, math.mulVectorScalar(tangent, math.dot(tangent, binormal)));
+ binormal = math.subVector(
+ binormal, math.mulVectorScalar(normal, math.dot(normal, binormal)));
+ var binormalLength = math.length(binormal);
+ if (binormalLength > 0.001) {
+ binormal = math.mulVectorScalar(binormal, 1 / binormalLength);
+ }
+
+ tangentStream.setElementVector(vertexIndex, tangent);
+ binormalStream.setElementVector(vertexIndex, binormal);
+ }
+};
+
+/**
+ * Creates a new VertexInfo.
+ * @return {!o3djs.primitives.VertexInfo} The new VertexInfo.
+ */
+o3djs.primitives.createVertexInfo = function() {
+ return new o3djs.primitives.VertexInfo();
+};
+
+/**
+ * Creates sphere vertices.
+ * The created sphere has position, normal and uv streams.
+ *
+ * @param {number} radius radius of the sphere.
+ * @param {number} subdivisionsAxis number of steps around the sphere.
+ * @param {number} subdivisionsHeight number of vertically on the sphere.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created sphere vertices.
+ */
+o3djs.primitives.createSphereVertices = function(radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix) {
+ if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
+ throw RangeError('subdivisionAxis and subdivisionHeight must be > 0');
+ }
+
+ // We are going to generate our sphere by iterating through its
+ // spherical coordinates and generating 2 triangles for each quad on a
+ // ring of the sphere.
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Generate the individual vertices in our vertex buffer.
+ for (var y = 0; y <= subdivisionsHeight; y++) {
+ for (var x = 0; x <= subdivisionsAxis; x++) {
+ // Generate a vertex based on its spherical coordinates
+ var u = x / subdivisionsAxis;
+ var v = y / subdivisionsHeight;
+ var theta = 2 * Math.PI * u;
+ var phi = Math.PI * v;
+ var sinTheta = Math.sin(theta);
+ var cosTheta = Math.cos(theta);
+ var sinPhi = Math.sin(phi);
+ var cosPhi = Math.cos(phi);
+ var ux = cosTheta * sinPhi;
+ var uy = cosPhi;
+ var uz = sinTheta * sinPhi;
+ positionStream.addElement(radius * ux, radius * uy, radius * uz);
+ normalStream.addElement(ux, uy, uz);
+ texCoordStream.addElement(1 - u, 1 - v);
+ }
+ }
+ var numVertsAround = subdivisionsAxis + 1;
+
+ for (var x = 0; x < subdivisionsAxis; x++) {
+ for (var y = 0; y < subdivisionsHeight; y++) {
+ // Make triangle 1 of quad.
+ vertexInfo.addTriangle(
+ (y + 0) * numVertsAround + x,
+ (y + 0) * numVertsAround + x + 1,
+ (y + 1) * numVertsAround + x);
+
+ // Make triangle 2 of quad.
+ vertexInfo.addTriangle(
+ (y + 1) * numVertsAround + x,
+ (y + 0) * numVertsAround + x + 1,
+ (y + 1) * numVertsAround + x + 1);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a sphere.
+ * The created sphere has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create sphere elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius radius of the sphere.
+ * @param {number} subdivisionsAxis number of steps around the sphere.
+ * @param {number} subdivisionsHeight number of vertically on the sphere.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created sphere.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createSphere = function(pack,
+ material,
+ radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createSphereVertices(
+ radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Array of the indices of corners of each face of a cube.
+ * @private
+ * @type {!Array.<!Array.<number>>}
+ */
+o3djs.primitives.CUBE_FACE_INDICES_ = [
+ [3, 7, 5, 1],
+ [0, 4, 6, 2],
+ [6, 7, 3, 2],
+ [0, 1, 5, 4],
+ [5, 7, 6, 4],
+ [2, 3, 1, 0]
+];
+
+/**
+ * Creates the vertices and indices for a cube. The
+ * cube will be created around the origin. (-size / 2, size / 2)
+ * The created cube has position, normal and uv streams.
+ *
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created cube vertices.
+ */
+o3djs.primitives.createCubeVertices = function(size, opt_matrix) {
+ var k = size / 2;
+
+ var cornerVertices = [
+ [-k, -k, -k],
+ [+k, -k, -k],
+ [-k, +k, -k],
+ [+k, +k, -k],
+ [-k, -k, +k],
+ [+k, -k, +k],
+ [-k, +k, +k],
+ [+k, +k, +k]
+ ];
+
+ var faceNormals = [
+ [+1, +0, +0],
+ [-1, +0, +0],
+ [+0, +1, +0],
+ [+0, -1, +0],
+ [+0, +0, +1],
+ [+0, +0, -1]
+ ];
+
+ var uvCoords = [
+ [0, 0],
+ [1, 0],
+ [1, 1],
+ [0, 1]
+ ];
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ for (var f = 0; f < 6; ++f) {
+ var faceIndices = o3djs.primitives.CUBE_FACE_INDICES_[f];
+ for (var v = 0; v < 4; ++v) {
+ var position = cornerVertices[faceIndices[v]];
+ var normal = faceNormals[f];
+ var uv = uvCoords[v];
+
+ // Each face needs all four vertices because the normals and texture
+ // coordinates are not all the same.
+ positionStream.addElementVector(position);
+ normalStream.addElementVector(normal);
+ texCoordStream.addElementVector(uv);
+
+ // Two triangles make a square face.
+ var offset = 4 * f;
+ vertexInfo.addTriangle(offset + 0, offset + 1, offset + 2);
+ vertexInfo.addTriangle(offset + 0, offset + 2, offset + 3);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a cube.
+ * The cube will be created around the origin. (-size / 2, size / 2)
+ * The created cube has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create cube elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created cube.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createCube = function(pack,
+ material,
+ size,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCubeVertices(size, opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates a box. The box will be created around the origin.
+ * The created box has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create Box elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} width Width of the box.
+ * @param {number} height Height of the box.
+ * @param {number} depth Depth of the box.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created Box.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createBox = function(pack,
+ material,
+ width,
+ height,
+ depth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCubeVertices(1);
+ vertexInfo.reorient([[width, 0, 0, 0],
+ [0, height, 0, 0],
+ [0, 0, depth, 0],
+ [0, 0, 0, 1]]);
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates a cube with varying vertex colors. The cube will be created
+ * around the origin. (-size / 2, size / 2)
+ *
+ * @param {!o3d.Pack} pack Pack to create cube elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created cube.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createRainbowCube = function(pack,
+ material,
+ size,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCubeVertices(size, opt_matrix);
+ var colorStream = vertexInfo.addStream(
+ 4, o3djs.base.o3d.Stream.COLOR);
+
+ var colors = [
+ [1, 0, 0, 1],
+ [0, 1, 0, 1],
+ [0, 0, 1, 1],
+ [1, 1, 0, 1],
+ [0, 1, 1, 1],
+ [1, 0, 1, 1],
+ [0, .5, .3, 1],
+ [.3, 0, .5, 1]
+ ];
+
+ var vertices = vertexInfo.vertices;
+ for (var f = 0; f < 6; ++f) {
+ var faceIndices = o3djs.primitives.CUBE_FACE_INDICES_[f];
+ for (var v = 0; v < 4; ++v) {
+ var color = colors[faceIndices[v]];
+ colorStream.addElementVector(color);
+ }
+ }
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates disc vertices. The disc will be in the xz plane, centered
+ * at the origin. When creating, at least 3 divisions, or pie pieces, need
+ * to be specified, otherwise the triangles making up the disc will be
+ * degenerate. You can also specify the number of radial pieces (opt_stacks).
+ * A value of 1 for opt_stacks will give you a simple disc of pie pieces. If
+ * you want to create an annulus by omitting some of the center stacks, you
+ * can specify the stack at which to start creating triangles. Finally,
+ * stackPower allows you to have the widths increase or decrease as you move
+ * away from the center. This is particularly useful when using the disc as a
+ * ground plane with a fixed camera such that you don't need the resolution of
+ * small triangles near the perimeter. For example, a value of 2 will produce
+ * stacks whose ouside radius increases with the square of the stack index. A
+ * value of 1 will give uniform stacks.
+ *
+ * @param {number} radius Radius of the ground plane.
+ * @param {number} divisions Number of triangles in the ground plane
+ * (at least 3).
+ * @param {number} opt_stacks Number of radial divisions (default=1).
+ * @param {number} opt_startStack Which radial division to start dividing at.
+ * @param {number} opt_stackPower Power to raise stack size to for decreasing
+ * width.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created plane vertices.
+ */
+o3djs.primitives.createDiscVertices = function(radius,
+ divisions,
+ opt_stacks,
+ opt_startStack,
+ opt_stackPower,
+ opt_matrix) {
+ if (divisions < 3) {
+ throw RangeError('divisions must be at least 3');
+ }
+
+ var stacks = opt_stacks ? opt_stacks : 1;
+ var startStack = opt_startStack ? opt_startStack : 0;
+ var stackPower = opt_stackPower ? opt_stackPower : 1;
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Initialize the center vertex.
+ // x y z nx ny nz r g b a u v
+ var firstIndex = 0;
+
+ if (startStack == 0) {
+ positionStream.addElement(0, 0, 0);
+ normalStream.addElement(0, 1, 0);
+ texCoordStream.addElement(0, 0);
+ firstIndex++;
+ }
+
+ // Build the disk one stack at a time.
+ for (var currentStack = Math.max(startStack, 1);
+ currentStack <= stacks;
+ ++currentStack) {
+ var stackRadius = radius * Math.pow(currentStack / stacks, stackPower);
+
+ for (var i = 0; i < divisions; ++i) {
+ var theta = 2.0 * Math.PI * i / divisions;
+ var x = stackRadius * Math.cos(theta);
+ var z = stackRadius * Math.sin(theta);
+
+ positionStream.addElement(x, 0, z);
+ normalStream.addElement(0, 1, 0);
+ texCoordStream.addElement(x, z);
+
+ if (currentStack > startStack) {
+ // a, b, c and d are the indices of the vertices of a quad. unless
+ // the current stack is the one closest to the center, in which case
+ // the vertices a and b connect to the center vertex.
+ var a = firstIndex + (i + 1) % divisions;
+ var b = firstIndex + i;
+ if (currentStack > 1) {
+ var c = firstIndex + i - divisions;
+ var d = firstIndex + (i + 1) % divisions - divisions;
+
+ // Make a quad of the vertices a, b, c, d.
+ vertexInfo.addTriangle(a, b, c);
+ vertexInfo.addTriangle(a, c, d);
+ } else {
+ // Make a single triangle of a, b and the center.
+ vertexInfo.addTriangle(0, a, b);
+ }
+ }
+ }
+
+ firstIndex += divisions;
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a disc shape. The disc will be in the xz plane, centered
+ * at the origin. When creating, at least 3 divisions, or pie pieces, need
+ * to be specified, otherwise the triangles making up the disc will be
+ * degenerate. You can also specify the number of radial pieces (opt_stacks).
+ * A value of 1 for opt_stacks will give you a simple disc of pie pieces. If
+ * you want to create an annulus by omitting some of the center stacks, you
+ * can specify the stack at which to start creating triangles. Finally,
+ * stackPower allows you to have the widths increase or decrease as you move
+ * away from the center. This is particularly useful when using the disc as a
+ * ground plane with a fixed camera such that you don't need the resolution of
+ * small triangles near the perimeter. For example, a value of 2 will produce
+ * stacks whose ouside radius increases with the square of the stack index. A
+ * value of 1 will give uniform stacks.
+ *
+ * @param {!o3d.Pack} pack Pack to create disc elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius Radius of the disc.
+ * @param {number} divisions Number of triangles in the disc (at least 3).
+ * @param {number} stacks Number of radial divisions.
+ * @param {number} startStack Which radial division to start dividing at.
+ * @param {number} stackPower Power to raise stack size to for decreasing width.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created disc.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createDisc = function(pack, material,
+ radius, divisions, stacks,
+ startStack, stackPower,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createDiscVertices(radius, divisions,
+ stacks,
+ startStack,
+ stackPower,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates cylinder vertices. The cylinder will be created around the origin
+ * along the y-axis. The created cylinder has position, normal and uv streams.
+ *
+ * @param {number} radius Radius of cylinder.
+ * @param {number} height Height of cylinder.
+ * @param {number} radialSubdivisions The number of subdivisions around the
+ * cylinder.
+ * @param {number} verticalSubdivisions The number of subdivisions down the
+ * cylinder.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {o3djs.primitives.VertexInfo} The created cylinder vertices.
+ */
+o3djs.primitives.createCylinderVertices = function(radius,
+ height,
+ radialSubdivisions,
+ verticalSubdivisions,
+ opt_matrix) {
+ if (radialSubdivisions < 1) {
+ throw RangeError('radialSubdivisions must be 1 or greater');
+ }
+
+ if (verticalSubdivisions < 1) {
+ throw RangeError('verticalSubdivisions must be 1 or greater');
+ }
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ var indices = [];
+ var vertices = [];
+ var vertsAroundEdge = radialSubdivisions + 1;
+
+ for (var yy = -2; yy <= verticalSubdivisions + 2; ++yy) {
+ var ringRadius = radius;
+ var v = yy / verticalSubdivisions
+ var y = height * v;
+ if (yy < 0) {
+ y = 0;
+ v = 1;
+ } else if (yy > verticalSubdivisions) {
+ y = height;
+ v = 1;
+ }
+ if (yy == -2 || yy == verticalSubdivisions + 2) {
+ ringRadius = 0;
+ v = 0;
+ }
+ y -= height / 2;
+ for (var ii = 0; ii < vertsAroundEdge; ++ii) {
+ var sin = Math.sin(ii * Math.PI * 2 / radialSubdivisions);
+ var cos = Math.cos(ii * Math.PI * 2 / radialSubdivisions);
+ positionStream.addElement(sin * ringRadius, y, cos * ringRadius);
+ normalStream.addElement(
+ (yy < 0 || yy > verticalSubdivisions) ? 0 : sin,
+ (yy < 0) ? -1 : (yy > verticalSubdivisions ? 1 : 0),
+ (yy < 0 || yy > verticalSubdivisions) ? 0 : cos);
+ texCoordStream.addElement(ii / radialSubdivisions, v);
+ }
+ }
+
+ var trisAround = radialSubdivisions * 2;
+ for (var yy = 0; yy < verticalSubdivisions + 4; ++yy) {
+ for (var ii = 0; ii < radialSubdivisions; ++ii) {
+ vertexInfo.addTriangle(vertsAroundEdge * (yy + 0) + 0 + ii,
+ vertsAroundEdge * (yy + 0) + 1 + ii,
+ vertsAroundEdge * (yy + 1) + 1 + ii);
+ vertexInfo.addTriangle(vertsAroundEdge * (yy + 0) + 0 + ii,
+ vertsAroundEdge * (yy + 1) + 1 + ii,
+ vertsAroundEdge * (yy + 1) + 0 + ii);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates cylinder a cylinder shape. The cylinder will be created around the
+ * origin along the y-axis. The created cylinder has position, normal and uv
+ * streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create cylinder elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius Radius of cylinder.
+ * @param {number} depth Depth of cylinder.
+ * @param {number} radialSubdivisions The number of subdivisions around the
+ * cylinder.
+ * @param {number} verticalSubdivisions The number of subdivisions down the
+ * cylinder.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created cylinder.
+ */
+o3djs.primitives.createCylinder = function(pack,
+ material,
+ radius,
+ depth,
+ radialSubdivisions,
+ verticalSubdivisions,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCylinderVertices(
+ radius,
+ depth,
+ radialSubdivisions,
+ verticalSubdivisions,
+ opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates wedge vertices, wedge being an extruded triangle. The wedge will be
+ * created around the 3 2d points passed in and extruded along the z axis. The
+ * created wedge has position, normal and uv streams.
+ *
+ * @param {!Array.<!Array.<number>>} inPoints Array of 2d points in the format
+ * [[x1, y1], [x2, y2], [x3, y3]] that describe a 2d triangle.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created cylinder vertices.
+ */
+o3djs.primitives.createWedgeVertices = function(inPoints, depth,
+ opt_matrix) {
+ var math = o3djs.math;
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ var z1 = -depth * 0.5;
+ var z2 = depth * 0.5;
+ var face = [];
+ var indices = [];
+ var points = [[inPoints[0][0], inPoints[0][1]],
+ [inPoints[1][0], inPoints[1][1]],
+ [inPoints[2][0], inPoints[2][1]]];
+
+ face[0] = math.cross(
+ math.normalize([points[1][0] - points[0][0],
+ points[1][1] - points[0][1],
+ z1 - z1]),
+ math.normalize([points[1][0] - points[1][0],
+ points[1][1] - points[1][1],
+ z2 - z1]));
+ face[1] = math.cross(
+ math.normalize([points[2][0] - points[1][0],
+ points[2][1] - points[1][1],
+ z1 - z1]),
+ math.normalize([points[2][0] - points[2][0],
+ points[2][1] - points[2][1],
+ z2 - z1]));
+ face[2] = math.cross(
+ [points[0][0] - points[2][0], points[0][1] - points[2][1], z1 - z1],
+ [points[0][0] - points[0][0], points[0][1] - points[0][1], z2 - z1]);
+
+ positionStream.addElement(points[0][0], points[0][1], z1);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[1][0], points[1][1], z1);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[2][0], points[2][1], z1);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(0, 0);
+ // back
+ positionStream.addElement(points[0][0], points[0][1], z2);
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[1][0], points[1][1], z2);
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[2][0], points[2][1], z2);
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(0, 0);
+ // face 0
+ positionStream.addElement(points[0][0], points[0][1], z1);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[1][0], points[1][1], z1);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(points[1][0], points[1][1], z2);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[0][0], points[0][1], z2);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(1, 1);
+ // face 1
+ positionStream.addElement(points[1][0], points[1][1], z1);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[2][0], points[2][1], z1);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(points[2][0], points[2][1], z2);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[1][0], points[1][1], z2);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(1, 1);
+ // face 2
+ positionStream.addElement(points[2][0], points[2][1], z1);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[0][0], points[0][1], z1);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(points[0][0], points[0][1], z2);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[2][0], points[2][1], z2);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(1, 1);
+
+ var indices = [0, 2, 1,
+ 3, 4, 5,
+ 6, 7, 8,
+ 6, 8, 9,
+ 10, 11, 12,
+ 10, 12, 13,
+ 14, 15, 16,
+ 14, 16, 17];
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a wedge shape. A wedge being an extruded triangle. The wedge will
+ * be created around the 3 2d points passed in and extruded along the z-axis.
+ * The created wedge has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create wedge elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {!Array.<!Array.<number>>} points Array of 2d points in the format
+ * [[x1, y1], [x2, y2], [x3, y3]] that describe a 2d triangle.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created wedge.
+ */
+o3djs.primitives.createWedge = function(pack,
+ material,
+ points,
+ depth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createWedgeVertices(points,
+ depth,
+ opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates prism vertices by extruding a polygon. The prism will be created
+ * around the 2d points passed in and extruded along the z axis. The end caps
+ * of the prism are constructed using a triangle fan originating at point 0,
+ * so a non-convex polygon might not get the desired shape, but it will if it
+ * is convex with respect to point 0. Texture coordinates map each face of
+ * the wall exactly to the unit square. Texture coordinates on the front
+ * and back faces are scaled such that the bounding rectangle of the polygon
+ * is mapped to the unit square. The created prism has position, normal,
+ * uv streams.
+ *
+ * @param {!Array.<!Array.<number>>} points Array of 2d points in the format
+ * [[x1, y1], [x2, y2], [x3, y3],...] that describe a 2d polygon.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created cylinder vertices.
+ */
+o3djs.primitives.createPrismVertices = function(points,
+ depth,
+ opt_matrix) {
+ if (points.length < 3) {
+ throw RangeError('there must be 3 or more points');
+ }
+
+ var backZ = -0.5 * depth;
+ var frontZ = 0.5 * depth;
+ var normals = [];
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Normals for the wall faces.
+ var n = points.length;
+
+ for (var i = 0; i < n; ++i) {
+ var j = (i + 1) % n;
+ var x = points[j][0] - points[i][0];
+ var y = points[j][1] - points[i][1];
+ var length = Math.sqrt(x * x + y * y);
+ normals[i] = [y / length, -x / length, 0];
+ }
+
+ // Compute the minimum and maxiumum x and y coordinates of points in the
+ // polygon.
+ var minX = points[0][0];
+ var minY = points[0][1];
+ var maxX = points[0][0];
+ var maxY = points[0][1];
+ for (var i = 1; i < n; ++i) {
+ var x = points[i][0];
+ var y = points[i][1];
+ minX = Math.min(minX, x);
+ minY = Math.min(minY, y);
+ maxX = Math.max(maxX, x);
+ maxY = Math.max(maxY, y);
+ }
+
+ // Scale the x and y coordinates of the points of the polygon to fit the
+ // bounding rectangle, and use the scaled coordinates for the uv
+ // of the front and back cap.
+ var frontUV = [];
+ var backUV = [];
+ var rangeX = maxX - minX;
+ var rangeY = maxY - minY;
+ for (var i = 0; i < n; ++i) {
+ frontUV[i] = [
+ (points[i][0] - minX) / rangeX,
+ (points[i][1] - minY) / rangeY
+ ];
+ backUV[i] = [
+ (maxX - points[i][0]) / rangeX,
+ (points[i][1] - minY) / rangeY
+ ];
+ }
+
+ for (var i = 0; i < n; ++i) {
+ var j = (i + 1) % n;
+ // Vertex on the back face.
+ positionStream.addElement(points[i][0], points[i][1], backZ);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(backUV[i][0], backUV[i][1]);
+
+ // Vertex on the front face.
+ positionStream.addElement(points[i][0], points[i][1], frontZ),
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(frontUV[i][0], frontUV[i][1]);
+
+ // Vertices for a quad on the wall.
+ positionStream.addElement(points[i][0], points[i][1], backZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(0, 1);
+
+ positionStream.addElement(points[j][0], points[j][1], backZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(0, 0);
+
+ positionStream.addElement(points[j][0], points[j][1], frontZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(1, 0);
+
+ positionStream.addElement(points[i][0], points[i][1], frontZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(1, 1);
+
+ if (i > 0 && i < n - 1) {
+ // Triangle for the back face.
+ vertexInfo.addTriangle(0, 6 * (i + 1), 6 * i);
+
+ // Triangle for the front face.
+ vertexInfo.addTriangle(1, 6 * i + 1, 6 * (i + 1) + 1);
+ }
+
+ // Quad on the wall.
+ vertexInfo.addTriangle(6 * i + 2, 6 * i + 3, 6 * i + 4);
+ vertexInfo.addTriangle(6 * i + 2, 6 * i + 4, 6 * i + 5);
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a prism shape by extruding a polygon. The prism will be created
+ * around the 2d points passed in an array and extruded along the z-axis.
+ * The end caps of the prism are constructed using a triangle fan originating
+ * at the first point, so a non-convex polygon might not get the desired
+ * shape, but it will if it is convex with respect to the first point.
+ * Texture coordinates map each face of the wall exactly to the unit square.
+ * Texture coordinates on the front and back faces are scaled such that the
+ * bounding rectangle of the polygon is mapped to the unit square.
+ * The created prism has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create wedge elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {!Array.<!Array.<number>>} points Array of 2d points in the format:
+ * [[x1, y1], [x2, y2], [x3, y3],...] that describe a 2d polygon.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created wedge.
+ */
+o3djs.primitives.createPrism = function(pack,
+ material,
+ points,
+ depth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createPrismVertices(points,
+ depth,
+ opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates XZ plane vertices.
+ * The created plane has position, normal and uv streams.
+ *
+ * @param {number} width Width of the plane.
+ * @param {number} depth Depth of the plane.
+ * @param {number} subdivisionsWidth Number of steps across the plane.
+ * @param {number} subdivisionsDepth Number of steps down the plane.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created plane vertices.
+ */
+o3djs.primitives.createPlaneVertices = function(width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix) {
+ if (subdivisionsWidth <= 0 || subdivisionsDepth <= 0) {
+ throw RangeError('subdivisionWidth and subdivisionDepth must be > 0');
+ }
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Generate the individual vertices in our vertex buffer.
+ for (var z = 0; z <= subdivisionsDepth; z++) {
+ for (var x = 0; x <= subdivisionsWidth; x++) {
+ var u = x / subdivisionsWidth;
+ var v = z / subdivisionsDepth;
+ positionStream.addElement(width * u - width * 0.5,
+ 0,
+ depth * v - depth * 0.5);
+ normalStream.addElement(0, 1, 0);
+ texCoordStream.addElement(u, 1 - v);
+ }
+ }
+
+ var numVertsAcross = subdivisionsWidth + 1;
+
+ for (var z = 0; z < subdivisionsDepth; z++) {
+ for (var x = 0; x < subdivisionsWidth; x++) {
+ // triangle 1 of quad
+ vertexInfo.addTriangle(
+ (z + 0) * numVertsAcross + x,
+ (z + 1) * numVertsAcross + x,
+ (z + 0) * numVertsAcross + x + 1);
+
+ // triangle 2 of quad
+ vertexInfo.addTriangle(
+ (z + 1) * numVertsAcross + x,
+ (z + 1) * numVertsAcross + x + 1,
+ (z + 0) * numVertsAcross + x + 1);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates an XZ plane.
+ * The created plane has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create plane elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} width Width of the plane.
+ * @param {number} depth Depth of the plane.
+ * @param {number} subdivisionsWidth Number of steps across the plane.
+ * @param {number} subdivisionsDepth Number of steps down the plane.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created plane.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createPlane = function(pack,
+ material,
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createPlaneVertices(
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates an XZ fade plane, where the alpha channel of the color stream
+ * fades from 1 to 0.
+ * The created plane has position, normal, uv and vertex color streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create plane elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} width Width of the plane.
+ * @param {number} depth Depth of the plane.
+ * @param {number} subdivisionsWidth Number of steps across the plane.
+ * @param {number} subdivisionsDepth Number of steps down the plane.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created plane.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createFadePlane = function(pack,
+ material,
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createPlaneVertices(
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix);
+ var colorStream = vertexInfo.addStream(4, o3djs.base.o3d.Stream.COLOR);
+ for (var z = 0; z <= subdivisionsDepth; z++) {
+ var alpha = z / subdivisionsDepth;
+ for (var x = 0; x <= subdivisionsWidth; x++) {
+ colorStream.addElement(1, 1, 1, alpha);
+ }
+ }
+ return vertexInfo.createShape(pack, material);
+};
+