summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3djs/primitives.js
diff options
context:
space:
mode:
authorgspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
committergspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
commit05b47f7a8c5451f858dc220df0e3a97542edace6 (patch)
treea2273d619f0625c9d44d40842845ccce2eac1045 /o3d/samples/o3djs/primitives.js
parent5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff)
downloadchromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't yet build in it's new home. We'll change that shortly. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
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);
+};
+