/** * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. * You may add additional accurate notices of copyright ownership. * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ package alternativa.engine3d.resources { import alternativa.engine3d.alternativa3d; import alternativa.engine3d.core.BoundBox; import alternativa.engine3d.core.RayIntersectionData; import alternativa.engine3d.core.Resource; import alternativa.engine3d.core.Transform3D; import alternativa.engine3d.core.VertexAttributes; import alternativa.engine3d.core.VertexStream; import flash.display3D.Context3D; import flash.display3D.IndexBuffer3D; import flash.display3D.VertexBuffer3D; import flash.geom.Point; import flash.geom.Vector3D; import flash.utils.ByteArray; import flash.utils.Endian; use namespace alternativa3d; /** * Resource, that stores data about geometry of object. All data are stored for each vertex. * So, you can set any set of parameters. And this set will be defined for each vertex of geometry. * It will be useful to divide parameters by some vertexBuffers in order to update these data at * memory of GPU, independently of each other (vertexBuffer can be updated at once only). * For this, you can store groups of parameters in different streams. Based on them vertexBuffers will be formed on uploading to GPU. * When new stream is formed, are specified the parameters, that will be stored in it. * * @example This code creates stream on properties: x,y,z,u,v and forms a triangle by three vertices. *
getAttributeValues by parameter name, e.g.:
* geometry.getAttributeValues(VertexAttributes.POSITION)
* returns vector from coordinates: <Number>[x1,y1,z1,x2,y2,z2,x3,y3,z3].
*/
public class Geometry extends Resource {
/**
* @private
*/
alternativa3d var _vertexStreams:Vector. Vector.<uint>([vertex_id_1,vertex_id_2,vertex_id_3,vertex_id_4,vertex_id_5,vertex_id_6]);.
*/
public function get indices():Vector.Geometry.
*/
public function clone():Geometry
{
var geometry:Geometry = new Geometry();
geometry.clonePropertiesFrom(this);
return geometry;
}
/**
* Copies basic properties of Geometry. This method calls from clone() method.
* @param source Geometry, properties of which will be copied.
*/
protected function clonePropertiesFrom(source:Geometry):void {
var n:int = source._vertexStreams.length;
for (var i:int = 0; i < n; ++i)
{
addVertexStream(source.getVertexStreamAttributes(i));
_vertexStreams[i].data.length = source._vertexStreams[i].data.length;
_vertexStreams[i].data.writeBytes(source._vertexStreams[i].data);
_vertexStreams[i].data.position = source._vertexStreams[i].data.position;
}
_numVertices = source._numVertices;
_indices = source._indices.slice();
}
/**
* Calculation of vertex normals.
*/
public function calculateNormals():void {
if (!hasAttribute(VertexAttributes.POSITION)) throw new Error("Vertices positions is required to calculate normals");
var normals:Array = [];
var positionsStream:VertexStream = _attributesStreams[VertexAttributes.POSITION];
var positionsData:ByteArray = positionsStream.data;
var positionsOffset:int = _attributesOffsets[VertexAttributes.POSITION]*4;
var stride:int = positionsStream.attributes.length*4;
var numIndices:int = _indices.length;
var normal:Vector3D;
var i:int;
// Normals calculations
for (i = 0; i < numIndices; i += 3) {
var vertIndexA:int = _indices[i];
var vertIndexB:int = _indices[i + 1];
var vertIndexC:int = _indices[i + 2];
// v1
positionsData.position = vertIndexA*stride + positionsOffset;
var ax:Number = positionsData.readFloat();
var ay:Number = positionsData.readFloat();
var az:Number = positionsData.readFloat();
// v2
positionsData.position = vertIndexB*stride + positionsOffset;
var bx:Number = positionsData.readFloat();
var by:Number = positionsData.readFloat();
var bz:Number = positionsData.readFloat();
// v3
positionsData.position = vertIndexC*stride + positionsOffset;
var cx:Number = positionsData.readFloat();
var cy:Number = positionsData.readFloat();
var cz:Number = positionsData.readFloat();
// v2-v1
var abx:Number = bx - ax;
var aby:Number = by - ay;
var abz:Number = bz - az;
// v3-v1
var acx:Number = cx - ax;
var acy:Number = cy - ay;
var acz:Number = cz - az;
var normalX:Number = acz*aby - acy*abz;
var normalY:Number = acx*abz - acz*abx;
var normalZ:Number = acy*abx - acx*aby;
var normalLen:Number = Math.sqrt(normalX*normalX + normalY*normalY + normalZ*normalZ);
if (normalLen > 0) {
normalX /= normalLen;
normalY /= normalLen;
normalZ /= normalLen;
} else {
trace("degenerated triangle", i/3);
}
// v1 normal
normal = normals[vertIndexA];
if (normal == null) {
normals[vertIndexA] = new Vector3D(normalX, normalY, normalZ);
} else {
normal.x += normalX;
normal.y += normalY;
normal.z += normalZ;
}
// v2 normal
normal = normals[vertIndexB];
if (normal == null) {
normals[vertIndexB] = new Vector3D(normalX, normalY, normalZ);
} else {
normal.x += normalX;
normal.y += normalY;
normal.z += normalZ;
}
// v3 normal
normal = normals[vertIndexC];
if (normal == null) {
normals[vertIndexC] = new Vector3D(normalX, normalY, normalZ);
} else {
normal.x += normalX;
normal.y += normalY;
normal.z += normalZ;
}
}
if (hasAttribute(VertexAttributes.NORMAL)) {
var normalsOffset:int = _attributesOffsets[VertexAttributes.NORMAL]*4;
var normalsStream:VertexStream = _attributesStreams[VertexAttributes.NORMAL];
var normalsBuffer:ByteArray = normalsStream.data;
var normalsBufferStride:uint = normalsStream.attributes.length*4;
for (i = 0; i < _numVertices; i++) {
normal = normals[i];
if (normal == null) continue;
normal.normalize();
normalsBuffer.position = i*normalsBufferStride + normalsOffset;
normalsBuffer.writeFloat(normal.x);
normalsBuffer.writeFloat(normal.y);
normalsBuffer.writeFloat(normal.z);
}
} else {
// Write normals to ByteArray
var resultByteArray:ByteArray = new ByteArray();
resultByteArray.endian = Endian.LITTLE_ENDIAN;
for (i = 0; i < _numVertices; i++) {
normal = normals[i] || new Vector3D(0,0,1);
normal.normalize();
resultByteArray.writeBytes(positionsData, i*stride, stride);
resultByteArray.writeFloat(normal.x);
resultByteArray.writeFloat(normal.y);
resultByteArray.writeFloat(normal.z);
}
positionsStream.attributes.push(VertexAttributes.NORMAL);
positionsStream.attributes.push(VertexAttributes.NORMAL);
positionsStream.attributes.push(VertexAttributes.NORMAL);
positionsStream.data = resultByteArray;
positionsData.clear();
_attributesOffsets[VertexAttributes.NORMAL] = stride/4;
_attributesStreams[VertexAttributes.NORMAL] = positionsStream;
_attributesStrides[VertexAttributes.NORMAL] = 3;
}
}
/**
* Calculation of tangents and bi-normals. Normals of geometry must be calculated.
*/
public function calculateTangents(uvChannel:int):void {
if (!hasAttribute(VertexAttributes.POSITION)) throw new Error("Vertices positions is required to calculate normals");
if (!hasAttribute(VertexAttributes.NORMAL)) throw new Error("Vertices normals is required to calculate tangents, call calculateNormals first");
if (!hasAttribute(VertexAttributes.TEXCOORDS[uvChannel])) throw new Error("Specified uv channel does not exist in geometry");
var tangents:Array = [];
var positionsStream:VertexStream = _attributesStreams[VertexAttributes.POSITION];
var positionsData:ByteArray = positionsStream.data;
var positionsOffset:int = _attributesOffsets[VertexAttributes.POSITION]*4;
var positionsStride:int = positionsStream.attributes.length*4;
var normalsStream:VertexStream = _attributesStreams[VertexAttributes.NORMAL];
var normalsData:ByteArray = normalsStream.data;
var normalsOffset:int = _attributesOffsets[VertexAttributes.NORMAL]*4;
var normalsStride:int = normalsStream.attributes.length*4;
var uvsStream:VertexStream = _attributesStreams[VertexAttributes.TEXCOORDS[uvChannel]];
var uvsData:ByteArray = uvsStream.data;
var uvsOffset:int = _attributesOffsets[VertexAttributes.TEXCOORDS[uvChannel]]*4;
var uvsStride:int = uvsStream.attributes.length*4;
var numIndices:int = _indices.length;
var normal:Vector3D;
var tangent:Vector3D;
var i:int;
for (i = 0; i < numIndices; i += 3) {
var vertIndexA:int = _indices[i];
var vertIndexB:int = _indices[i + 1];
var vertIndexC:int = _indices[i + 2];
// a.xyz
positionsData.position = vertIndexA*positionsStride + positionsOffset;
var ax:Number = positionsData.readFloat();
var ay:Number = positionsData.readFloat();
var az:Number = positionsData.readFloat();
// b.xyz
positionsData.position = vertIndexB*positionsStride + positionsOffset;
var bx:Number = positionsData.readFloat();
var by:Number = positionsData.readFloat();
var bz:Number = positionsData.readFloat();
// c.xyz
positionsData.position = vertIndexC*positionsStride + positionsOffset;
var cx:Number = positionsData.readFloat();
var cy:Number = positionsData.readFloat();
var cz:Number = positionsData.readFloat();
// a.uv
uvsData.position = vertIndexA*uvsStride + uvsOffset;
var au:Number = uvsData.readFloat();
var av:Number = uvsData.readFloat();
// b.uv
uvsData.position = vertIndexB*uvsStride + uvsOffset;
var bu:Number = uvsData.readFloat();
var bv:Number = uvsData.readFloat();
// c.uv
uvsData.position = vertIndexC*uvsStride + uvsOffset;
var cu:Number = uvsData.readFloat();
var cv:Number = uvsData.readFloat();
// a.nrm
normalsData.position = vertIndexA*normalsStride + normalsOffset;
var anx:Number = normalsData.readFloat();
var any:Number = normalsData.readFloat();
var anz:Number = normalsData.readFloat();
// b.nrm
normalsData.position = vertIndexB*normalsStride + normalsOffset;
var bnx:Number = normalsData.readFloat();
var bny:Number = normalsData.readFloat();
var bnz:Number = normalsData.readFloat();
// c.nrm
normalsData.position = vertIndexC*normalsStride + normalsOffset;
var cnx:Number = normalsData.readFloat();
var cny:Number = normalsData.readFloat();
var cnz:Number = normalsData.readFloat();
// v2-v1
var abx:Number = bx - ax;
var aby:Number = by - ay;
var abz:Number = bz - az;
// v3-v1
var acx:Number = cx - ax;
var acy:Number = cy - ay;
var acz:Number = cz - az;
var abu:Number = bu - au;
var abv:Number = bv - av;
var acu:Number = cu - au;
var acv:Number = cv - av;
var r:Number = 1/(abu*acv - acu*abv);
var tangentX:Number = r*(acv*abx - acx*abv);
var tangentY:Number = r*(acv*aby - abv*acy);
var tangentZ:Number = r*(acv*abz - abv*acz);
tangent = tangents[vertIndexA];
if (tangent == null) {
tangents[vertIndexA] = new Vector3D(
tangentX - anx*(anx*tangentX + any*tangentY + anz*tangentZ),
tangentY - any*(anx*tangentX + any*tangentY + anz*tangentZ),
tangentZ - anz*(anx*tangentX + any*tangentY + anz*tangentZ));
} else {
tangent.x += tangentX - anx*(anx*tangentX + any*tangentY + anz*tangentZ);
tangent.y += tangentY - any*(anx*tangentX + any*tangentY + anz*tangentZ);
tangent.z += tangentZ - anz*(anx*tangentX + any*tangentY + anz*tangentZ);
}
tangent = tangents[vertIndexB];
if (tangent == null) {
tangents[vertIndexB] = new Vector3D(
tangentX - bnx*(bnx*tangentX + bny*tangentY + bnz*tangentZ),
tangentY - bny*(bnx*tangentX + bny*tangentY + bnz*tangentZ),
tangentZ - bnz*(bnx*tangentX + bny*tangentY + bnz*tangentZ));
} else {
tangent.x += tangentX - bnx*(bnx*tangentX + bny*tangentY + bnz*tangentZ);
tangent.y += tangentY - bny*(bnx*tangentX + bny*tangentY + bnz*tangentZ);
tangent.z += tangentZ - bnz*(bnx*tangentX + bny*tangentY + bnz*tangentZ);
}
tangent = tangents[vertIndexC];
if (tangent == null) {
tangents[vertIndexC] = new Vector3D(
tangentX - cnx*(cnx*tangentX + cny*tangentY + cnz*tangentZ),
tangentY - cny*(cnx*tangentX + cny*tangentY + cnz*tangentZ),
tangentZ - cnz*(cnx*tangentX + cny*tangentY + cnz*tangentZ));
} else {
tangent.x += tangentX - cnx*(cnx*tangentX + cny*tangentY + cnz*tangentZ);
tangent.y += tangentY - cny*(cnx*tangentX + cny*tangentY + cnz*tangentZ);
tangent.z += tangentZ - cnz*(cnx*tangentX + cny*tangentY + cnz*tangentZ);
}
}
if (hasAttribute(VertexAttributes.TANGENT4)) {
var tangentsOffset:int = _attributesOffsets[VertexAttributes.TANGENT4]*4;
var tangentsStream:VertexStream = _attributesStreams[VertexAttributes.TANGENT4];
var tangentsBuffer:ByteArray = tangentsStream.data;
var tangentsBufferStride:uint = tangentsStream.attributes.length*4;
for (i = 0; i < _numVertices; i++) {
tangent = tangents[i];
tangent.normalize();
tangentsBuffer.position = i*tangentsBufferStride + tangentsOffset;
tangentsBuffer.writeFloat(tangent.x);
tangentsBuffer.writeFloat(tangent.y);
tangentsBuffer.writeFloat(tangent.z);
tangentsBuffer.writeFloat(-1);
}
} else {
// Write normals to ByteArray
var resultByteArray:ByteArray = new ByteArray();
resultByteArray.endian = Endian.LITTLE_ENDIAN;
for (i = 0; i < _numVertices; i++) {
tangent = tangents[i];
tangent.normalize();
resultByteArray.writeBytes(positionsData, i*positionsStride, positionsStride);
resultByteArray.writeFloat(tangent.x);
resultByteArray.writeFloat(tangent.y);
resultByteArray.writeFloat(tangent.z);
resultByteArray.writeFloat(-1);
}
positionsStream.attributes.push(VertexAttributes.TANGENT4);
positionsStream.attributes.push(VertexAttributes.TANGENT4);
positionsStream.attributes.push(VertexAttributes.TANGENT4);
positionsStream.attributes.push(VertexAttributes.TANGENT4);
positionsStream.data = resultByteArray;
positionsData.clear();
_attributesOffsets[VertexAttributes.TANGENT4] = positionsStride/4;
_attributesStreams[VertexAttributes.TANGENT4] = positionsStream;
_attributesStrides[VertexAttributes.TANGENT4] = 4;
}
}
/**
* Adds a stream for set of parameters, that can be updated independently of the other sets of parameters.
* @param attributes List of parameters. Types of parameters are get from VertexAttributes.
* @return Index of stream, that has been created.
*/
public function addVertexStream(attributes:Array):int {
var numMappings:int = attributes.length;
if (numMappings < 1) {
throw new Error("Must be at least one attribute to create the buffer.");
}
var vBuffer:VertexStream = new VertexStream();
var newBufferIndex:int = _vertexStreams.length;
var attribute:uint = attributes[0];
var stride:int = 1;
for (var i:int = 1; i <= numMappings; i++) {
var next:uint = (i < numMappings) ? attributes[i] : 0;
if (next != attribute) {
// Last item will enter here forcibly.
if (attribute != 0) {
if (attribute < _attributesStreams.length && _attributesStreams[attribute] != null) {
throw new Error("Attribute " + attribute + " already used in this geometry.");
}
var numStandartFloats:int = VertexAttributes.getAttributeStride(attribute);
if (numStandartFloats != 0 && numStandartFloats != stride) {
throw new Error("Standard attributes must be predefined size.");
}
if (_attributesStreams.length < attribute) {
_attributesStreams.length = attribute + 1;
_attributesOffsets.length = attribute + 1;
_attributesStrides.length = attribute + 1;
}
var startIndex:int = i - stride;
_attributesStreams[attribute] = vBuffer;
_attributesOffsets[attribute] = startIndex;
_attributesStrides[attribute] = stride;
}
stride = 1;
} else {
stride++;
}
attribute = next;
}
vBuffer.attributes = attributes.slice();
// vBuffer.data = new Vector.findVertexStreamByAttribute.
*
* @param attribute Type of attribute. List of types of attributes placed at VertexAttributes.
* @return Offset.
*
* @see #findVertexStreamByAttribute
* @see VertexAttributes
*/
public function getAttributeOffset(attribute:uint):int {
var vBuffer:VertexStream = (attribute < _attributesStreams.length) ? _attributesStreams[attribute] : null;
if (vBuffer == null) {
throw new Error("Attribute not found.");
}
return _attributesOffsets[attribute];
}
/**
* Sets value for attribute.
* If buffer has not been initialized, then it initialized with zeros automatically.
*
* @param attribute
* @param values
*/
public function setAttributeValues(attribute:uint, values:Vector.