mirror of
https://github.com/MapMakersAndProgrammers/Alternativa3D.git
synced 2025-10-26 01:49:07 -07:00
You are all going to love this pull request! It fixes an extreme inefficiency in 'Geometry.rayIntersection' caused by using the 'indices' property rather than the '_indices' member. This was making three copies of the entire index array for EVERY triangle. Ray intersections with complex models is now actually usable. Enjoy! Chris.
1050 lines
35 KiB
ActionScript
1050 lines
35 KiB
ActionScript
/**
|
||
* 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.
|
||
* <listing version="3.0">
|
||
* var attributes:Array = [];
|
||
* attributes[0] = VertexAttributes.POSITION;
|
||
* attributes[1] = VertexAttributes.POSITION;
|
||
* attributes[2] = VertexAttributes.POSITION;
|
||
* attributes[3] = VertexAttributes.TEXCOORDS[0];
|
||
* attributes[4] = VertexAttributes.TEXCOORDS[0];
|
||
* var geometry = new Geometry();
|
||
* geometry.addVertexStream(attributes);
|
||
* geometry.numVertices = 3;
|
||
* geometry.setAttributeValues(VertexAttributes.POSITION, new <Number>[x1,y1,z1,x2,y2,z2,x3,y3,z3]);
|
||
* geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], new <Number>[u1,v1,u2,v2,u3,v3]);
|
||
* geometry.indices = Vector.<uint>([0,1,2]);
|
||
* </listing>
|
||
* To get access to data, you can use method <code>getAttributeValues</code> by parameter name, e.g.:
|
||
* <code>geometry.getAttributeValues(VertexAttributes.POSITION)</code>
|
||
* returns vector from coordinates: <Number>[x1,y1,z1,x2,y2,z2,x3,y3,z3].
|
||
*/
|
||
public class Geometry extends Resource {
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var _vertexStreams:Vector.<VertexStream> = new Vector.<VertexStream>();
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var _indexBuffer:IndexBuffer3D;
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var _numVertices:int;
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var _indices:Vector.<uint> = new Vector.<uint>();
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var _attributesStreams:Vector.<VertexStream> = new Vector.<VertexStream>();
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d var _attributesOffsets:Vector.<int> = new Vector.<int>();
|
||
|
||
private var _attributesStrides:Vector.<int> = new Vector.<int>();
|
||
|
||
/**
|
||
* Creates a new instance.
|
||
* @param numVertices Number of vertices.
|
||
*/
|
||
public function Geometry(numVertices:int = 0) {
|
||
this._numVertices = numVertices;
|
||
}
|
||
|
||
/**
|
||
* Number of triangles, that are contained in geometry.
|
||
*/
|
||
public function get numTriangles():int {
|
||
return _indices.length/3;
|
||
}
|
||
|
||
/**
|
||
* Indexes of vertices for specifying of triangles of surface.
|
||
* Example of specifying of surface, that consists of two triangles: <code> Vector.<uint>([vertex_id_1,vertex_id_2,vertex_id_3,vertex_id_4,vertex_id_5,vertex_id_6]);</code>.
|
||
*/
|
||
public function get indices():Vector.<uint> {
|
||
return _indices.slice();
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set indices(value:Vector.<uint>):void {
|
||
if (value == null) {
|
||
_indices.length = 0;
|
||
} else {
|
||
_indices = value.slice()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Number of vertices of geometry.
|
||
*/
|
||
public function get numVertices():int {
|
||
return _numVertices;
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
public function set numVertices(value:int):void {
|
||
if (_numVertices != value) {
|
||
// Change buffers.
|
||
for each (var vBuffer:VertexStream in _vertexStreams) {
|
||
var numMappings:int = vBuffer.attributes.length;
|
||
vBuffer.data.length = 4*numMappings*value;
|
||
}
|
||
_numVertices = value;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns a copy of object.
|
||
* @return A copy of this <code>Geometry</code>.
|
||
*/
|
||
public function clone():Geometry
|
||
{
|
||
var geometry:Geometry = new Geometry();
|
||
geometry.clonePropertiesFrom(this);
|
||
return geometry;
|
||
}
|
||
|
||
/**
|
||
* Copies basic properties of <code>Geometry</code>. This method calls from <code>clone()</code> method.
|
||
* @param source <code>Geometry</code>, 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 <code>VertexAttributes</code>.
|
||
* @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.<Number>(numMappings*_numVertices);
|
||
vBuffer.data = new ByteArray();
|
||
vBuffer.data.endian = Endian.LITTLE_ENDIAN;
|
||
vBuffer.data.length = 4*numMappings*_numVertices;
|
||
_vertexStreams[newBufferIndex] = vBuffer;
|
||
return newBufferIndex;
|
||
}
|
||
|
||
/**
|
||
* Number of vertex-streams.
|
||
*/
|
||
public function get numVertexStreams():int {
|
||
return _vertexStreams.length;
|
||
}
|
||
|
||
/**
|
||
* returns mapping of stream by index.
|
||
* @param index index of stream.
|
||
* @return mapping.
|
||
*/
|
||
public function getVertexStreamAttributes(index:int):Array {
|
||
return _vertexStreams[index].attributes.slice();
|
||
}
|
||
|
||
/**
|
||
* Check the existence of attribute in all streams.
|
||
* @param attribute Attribute, that is checked.
|
||
* @return
|
||
*/
|
||
public function hasAttribute(attribute:uint):Boolean {
|
||
return attribute < _attributesStreams.length && _attributesStreams[attribute] != null;
|
||
}
|
||
|
||
/**
|
||
* Returns index of stream, that contains needed attribute.
|
||
*
|
||
* @param attribute
|
||
*
|
||
* @return -1 if attribute is not found.
|
||
*/
|
||
public function findVertexStreamByAttribute(attribute:uint):int {
|
||
var vBuffer:VertexStream = (attribute < _attributesStreams.length) ? _attributesStreams[attribute] : null;
|
||
if (vBuffer != null) {
|
||
for (var i:int = 0; i < _vertexStreams.length; i++) {
|
||
if (_vertexStreams[i] == vBuffer) {
|
||
return i;
|
||
}
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/**
|
||
* Offset of attribute at stream, with which this attribute is stored. You can find index of stream using <code>findVertexStreamByAttribute</code>.
|
||
*
|
||
* @param attribute Type of attribute. List of types of attributes placed at <code>VertexAttributes</code>.
|
||
* @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.<Number>):void {
|
||
var vBuffer:VertexStream = (attribute < _attributesStreams.length) ? _attributesStreams[attribute] : null;
|
||
if (vBuffer == null) {
|
||
throw new Error("Attribute not found.");
|
||
}
|
||
var stride:int = _attributesStrides[attribute];
|
||
if (values == null || values.length != stride*_numVertices) {
|
||
throw new Error("Values count must be the same.");
|
||
}
|
||
var numMappings:int = vBuffer.attributes.length;
|
||
var data:ByteArray = vBuffer.data;
|
||
var offset:int = _attributesOffsets[attribute];
|
||
// Copy values
|
||
for (var i:int = 0; i < _numVertices; i++) {
|
||
var srcIndex:int = stride*i;
|
||
data.position = 4*(numMappings*i + offset);
|
||
for (var j:int = 0; j < stride; j++) {
|
||
data.writeFloat(values[int(srcIndex + j)]);
|
||
}
|
||
}
|
||
}
|
||
|
||
public function getAttributeValues(attribute:uint):Vector.<Number> {
|
||
var vBuffer:VertexStream = (attribute < _attributesStreams.length) ? _attributesStreams[attribute] : null;
|
||
if (vBuffer == null) {
|
||
throw new Error("Attribute not found.");
|
||
}
|
||
var data:ByteArray = vBuffer.data;
|
||
var stride:int = _attributesStrides[attribute];
|
||
var result:Vector.<Number> = new Vector.<Number>(stride*_numVertices);
|
||
var numMappings:int = vBuffer.attributes.length;
|
||
var offset:int = _attributesOffsets[attribute];
|
||
// Copy values
|
||
for (var i:int = 0; i < _numVertices; i++) {
|
||
data.position = 4*(numMappings*i + offset);
|
||
var dstIndex:int = stride*i;
|
||
for (var j:int = 0; j < stride; j++) {
|
||
result[int(dstIndex + j)] = data.readFloat();
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Check for existence of resource in video memory.
|
||
*/
|
||
override public function get isUploaded():Boolean {
|
||
return _indexBuffer != null;
|
||
}
|
||
|
||
/**
|
||
* @inheritDoc
|
||
*/
|
||
override public function upload(context3D:Context3D):void {
|
||
var vBuffer:VertexStream;
|
||
var i:int;
|
||
var numBuffers:int = _vertexStreams.length;
|
||
if (_indexBuffer != null) {
|
||
// Clear old resources
|
||
_indexBuffer.dispose();
|
||
_indexBuffer = null;
|
||
for (i = 0; i < numBuffers; i++) {
|
||
vBuffer = _vertexStreams[i];
|
||
if (vBuffer.buffer != null) {
|
||
vBuffer.buffer.dispose();
|
||
vBuffer.buffer = null;
|
||
}
|
||
}
|
||
}
|
||
if (_indices.length <= 0 || _numVertices <= 0) {
|
||
return;
|
||
}
|
||
|
||
for (i = 0; i < numBuffers; i++) {
|
||
vBuffer = _vertexStreams[i];
|
||
var numMappings:int = vBuffer.attributes.length;
|
||
var data:ByteArray = vBuffer.data;
|
||
if (data == null) {
|
||
throw new Error("Cannot upload without vertex buffer data.");
|
||
}
|
||
vBuffer.buffer = context3D.createVertexBuffer(_numVertices, numMappings);
|
||
vBuffer.buffer.uploadFromByteArray(data, 0, 0, _numVertices);
|
||
}
|
||
var numIndices:int = _indices.length;
|
||
_indexBuffer = context3D.createIndexBuffer(numIndices);
|
||
_indexBuffer.uploadFromVector(_indices, 0, numIndices);
|
||
}
|
||
|
||
/**
|
||
* @inheritDoc
|
||
*/
|
||
override public function dispose():void {
|
||
if (_indexBuffer != null) {
|
||
_indexBuffer.dispose();
|
||
_indexBuffer = null;
|
||
var numBuffers:int = _vertexStreams.length;
|
||
for (var i:int = 0; i < numBuffers; i++) {
|
||
var vBuffer:VertexStream = _vertexStreams[i];
|
||
vBuffer.buffer.dispose();
|
||
vBuffer.buffer = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Updates values of index-buffer in video memory.
|
||
* @param data List of values.
|
||
* @param startOffset Offset.
|
||
* @param count Count of updated values.
|
||
*/
|
||
public function updateIndexBufferInContextFromVector(data:Vector.<uint>, startOffset:int, count:int):void {
|
||
if (_indexBuffer == null) {
|
||
throw new Error("Geometry must be uploaded.");
|
||
}
|
||
_indexBuffer.uploadFromVector(data, startOffset, count);
|
||
}
|
||
|
||
/**
|
||
* Updates values of index-buffer in video memory.
|
||
* @param data Data
|
||
* @param startOffset Offset
|
||
* @param count Number of updated values.
|
||
*/
|
||
public function updateIndexBufferInContextFromByteArray(data:ByteArray, byteArrayOffset:int, startOffset:int, count:int):void {
|
||
if (_indexBuffer == null) {
|
||
throw new Error("Geometry must be uploaded.");
|
||
}
|
||
_indexBuffer.uploadFromByteArray(data, byteArrayOffset, startOffset, count);
|
||
}
|
||
|
||
/**
|
||
* Updates values of vertex-buffer in video memory.
|
||
* @param data List of values.
|
||
* @param startVertex Offset.
|
||
* @param numVertices Number of updated values.
|
||
*/
|
||
public function updateVertexBufferInContextFromVector(index:int, data:Vector.<Number>, startVertex:int, numVertices:int):void {
|
||
if (_indexBuffer == null) {
|
||
throw new Error("Geometry must be uploaded.");
|
||
}
|
||
_vertexStreams[index].buffer.uploadFromVector(data, startVertex, numVertices);
|
||
}
|
||
|
||
/**
|
||
* Updates values of vertex-buffer in video memory.
|
||
* @param data Data
|
||
* @param startVertex Offset.
|
||
* @param numVertices Number of updated values.
|
||
*/
|
||
public function updateVertexBufferInContextFromByteArray(index:int, data:ByteArray, byteArrayOffset:int, startVertex:int, numVertices:int):void {
|
||
if (_indexBuffer == null) {
|
||
throw new Error("Geometry must be uploaded.");
|
||
}
|
||
_vertexStreams[index].buffer.uploadFromByteArray(data, byteArrayOffset, startVertex, numVertices);
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d function intersectRay(origin:Vector3D, direction:Vector3D, indexBegin:uint, numTriangles:uint):RayIntersectionData {
|
||
var ox:Number = origin.x;
|
||
var oy:Number = origin.y;
|
||
var oz:Number = origin.z;
|
||
var dx:Number = direction.x;
|
||
var dy:Number = direction.y;
|
||
var dz:Number = direction.z;
|
||
|
||
var nax:Number;
|
||
var nay:Number;
|
||
var naz:Number;
|
||
var nau:Number;
|
||
var nav:Number;
|
||
|
||
var nbx:Number;
|
||
var nby:Number;
|
||
var nbz:Number;
|
||
var nbu:Number;
|
||
var nbv:Number;
|
||
|
||
var ncx:Number;
|
||
var ncy:Number;
|
||
var ncz:Number;
|
||
var ncu:Number;
|
||
var ncv:Number;
|
||
|
||
var nrmX:Number;
|
||
var nrmY:Number;
|
||
var nrmZ:Number;
|
||
|
||
var point:Vector3D;
|
||
var minTime:Number = 1e+22;
|
||
var posAttribute:int = VertexAttributes.POSITION;
|
||
var uvAttribute:int = VertexAttributes.TEXCOORDS[0];
|
||
var positionStream:VertexStream;
|
||
if (VertexAttributes.POSITION >= _attributesStreams.length || (positionStream = _attributesStreams[posAttribute]) == null) {
|
||
throw new Error("Raycast require POSITION attribute");
|
||
}
|
||
var positionBuffer:ByteArray = positionStream.data;
|
||
// Offset of position attribute.
|
||
const positionOffset:uint = _attributesOffsets[posAttribute]*4;
|
||
// Length of vertex on bytes.
|
||
var stride:uint = positionStream.attributes.length*4;
|
||
|
||
var uvStream:VertexStream;
|
||
var hasUV:Boolean = uvAttribute < _attributesStreams.length && (uvStream = _attributesStreams[uvAttribute]) != null;
|
||
var uvBuffer:ByteArray;
|
||
var uvOffset:uint;
|
||
var uvStride:uint;
|
||
if (hasUV) {
|
||
uvBuffer = uvStream.data;
|
||
uvOffset = _attributesOffsets[uvAttribute]*4;
|
||
uvStride = uvStream.attributes.length*4;
|
||
}
|
||
|
||
if (numTriangles*3 > _indices.length) {
|
||
throw new ArgumentError("index is out of bounds");
|
||
}
|
||
for (var i:int = indexBegin, count:int = indexBegin + numTriangles*3; i < count; i += 3) {
|
||
var indexA:uint = _indices[i];
|
||
var indexB:uint = _indices[int(i + 1)];
|
||
var indexC:uint = _indices[int(i + 2)];
|
||
positionBuffer.position = indexA*stride + positionOffset;
|
||
var ax:Number = positionBuffer.readFloat();
|
||
var ay:Number = positionBuffer.readFloat();
|
||
var az:Number = positionBuffer.readFloat();
|
||
var au:Number;
|
||
var av:Number;
|
||
positionBuffer.position = indexB*stride + positionOffset;
|
||
var bx:Number = positionBuffer.readFloat();
|
||
var by:Number = positionBuffer.readFloat();
|
||
var bz:Number = positionBuffer.readFloat();
|
||
var bu:Number;
|
||
var bv:Number;
|
||
|
||
positionBuffer.position = indexC*stride + positionOffset;
|
||
var cx:Number = positionBuffer.readFloat();
|
||
var cy:Number = positionBuffer.readFloat();
|
||
var cz:Number = positionBuffer.readFloat();
|
||
var cu:Number;
|
||
var cv:Number;
|
||
|
||
if (hasUV) {
|
||
uvBuffer.position = indexA*uvStride + uvOffset;
|
||
au = uvBuffer.readFloat();
|
||
av = uvBuffer.readFloat();
|
||
|
||
uvBuffer.position = indexB*uvStride + uvOffset;
|
||
bu = uvBuffer.readFloat();
|
||
bv = uvBuffer.readFloat();
|
||
|
||
uvBuffer.position = indexC*uvStride + uvOffset;
|
||
cu = uvBuffer.readFloat();
|
||
cv = uvBuffer.readFloat();
|
||
}
|
||
|
||
var abx:Number = bx - ax;
|
||
var aby:Number = by - ay;
|
||
var abz:Number = bz - az;
|
||
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 len:Number = normalX*normalX + normalY*normalY + normalZ*normalZ;
|
||
if (len > 0.001) {
|
||
len = 1/Math.sqrt(len);
|
||
normalX *= len;
|
||
normalY *= len;
|
||
normalZ *= len;
|
||
}
|
||
var dot:Number = dx*normalX + dy*normalY + dz*normalZ;
|
||
if (dot < 0) {
|
||
var offset:Number = ox*normalX + oy*normalY + oz*normalZ - (ax*normalX + ay*normalY + az*normalZ);
|
||
if (offset > 0) {
|
||
var time:Number = -offset/dot;
|
||
if (point == null || time < minTime) {
|
||
var rx:Number = ox + dx*time;
|
||
var ry:Number = oy + dy*time;
|
||
var rz:Number = oz + dz*time;
|
||
abx = bx - ax;
|
||
aby = by - ay;
|
||
abz = bz - az;
|
||
acx = rx - ax;
|
||
acy = ry - ay;
|
||
acz = rz - az;
|
||
if ((acz*aby - acy*abz)*normalX + (acx*abz - acz*abx)*normalY + (acy*abx - acx*aby)*normalZ >= 0) {
|
||
abx = cx - bx;
|
||
aby = cy - by;
|
||
abz = cz - bz;
|
||
acx = rx - bx;
|
||
acy = ry - by;
|
||
acz = rz - bz;
|
||
if ((acz*aby - acy*abz)*normalX + (acx*abz - acz*abx)*normalY + (acy*abx - acx*aby)*normalZ >= 0) {
|
||
abx = ax - cx;
|
||
aby = ay - cy;
|
||
abz = az - cz;
|
||
acx = rx - cx;
|
||
acy = ry - cy;
|
||
acz = rz - cz;
|
||
if ((acz*aby - acy*abz)*normalX + (acx*abz - acz*abx)*normalY + (acy*abx - acx*aby)*normalZ >= 0) {
|
||
if (time < minTime) {
|
||
minTime = time;
|
||
if (point == null) point = new Vector3D();
|
||
point.x = rx;
|
||
point.y = ry;
|
||
point.z = rz;
|
||
nax = ax;
|
||
nay = ay;
|
||
naz = az;
|
||
nau = au;
|
||
nav = av;
|
||
nrmX = normalX;
|
||
nbx = bx;
|
||
nby = by;
|
||
nbz = bz;
|
||
nbu = bu;
|
||
nbv = bv;
|
||
nrmY = normalY;
|
||
ncx = cx;
|
||
ncy = cy;
|
||
ncz = cz;
|
||
ncu = cu;
|
||
ncv = cv;
|
||
nrmZ = normalZ;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (point != null) {
|
||
var res:RayIntersectionData = new RayIntersectionData();
|
||
res.point = point;
|
||
res.time = minTime;
|
||
if (hasUV) {
|
||
// Calculation of UV.
|
||
abx = nbx - nax;
|
||
aby = nby - nay;
|
||
abz = nbz - naz;
|
||
var abu:Number = nbu - nau;
|
||
var abv:Number = nbv - nav;
|
||
|
||
acx = ncx - nax;
|
||
acy = ncy - nay;
|
||
acz = ncz - naz;
|
||
var acu:Number = ncu - nau;
|
||
var acv:Number = ncv - nav;
|
||
|
||
// Calculation of uv-transformation matrix.
|
||
var det:Number = -nrmX*acy*abz + acx*nrmY*abz + nrmX*aby*acz - abx*nrmY*acz - acx*aby*nrmZ + abx*acy*nrmZ;
|
||
var ima:Number = (-nrmY*acz + acy*nrmZ)/det;
|
||
var imb:Number = (nrmX*acz - acx*nrmZ)/det;
|
||
var imc:Number = (-nrmX*acy + acx*nrmY)/det;
|
||
var imd:Number = (nax*nrmY*acz - nrmX*nay*acz - nax*acy*nrmZ + acx*nay*nrmZ + nrmX*acy*naz - acx*nrmY*naz)/det;
|
||
var ime:Number = (nrmY*abz - aby*nrmZ)/det;
|
||
var imf:Number = (-nrmX*abz + abx*nrmZ)/det;
|
||
var img:Number = (nrmX*aby - abx*nrmY)/det;
|
||
var imh:Number = (nrmX*nay*abz - nax*nrmY*abz + nax*aby*nrmZ - abx*nay*nrmZ - nrmX*aby*naz + abx*nrmY*naz)/det;
|
||
var ma:Number = abu*ima + acu*ime;
|
||
var mb:Number = abu*imb + acu*imf;
|
||
var mc:Number = abu*imc + acu*img;
|
||
var md:Number = abu*imd + acu*imh + nau;
|
||
var me:Number = abv*ima + acv*ime;
|
||
var mf:Number = abv*imb + acv*imf;
|
||
var mg:Number = abv*imc + acv*img;
|
||
var mh:Number = abv*imd + acv*imh + nav;
|
||
// UV
|
||
res.uv = new Point(ma*point.x + mb*point.y + mc*point.z + md, me*point.x + mf*point.y + mg*point.z + mh);
|
||
}
|
||
|
||
return res;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d function getVertexBuffer(attribute:int):VertexBuffer3D {
|
||
if (attribute < _attributesStreams.length) {
|
||
var stream:VertexStream = _attributesStreams[attribute];
|
||
return stream != null ? stream.buffer : null;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
alternativa3d function updateBoundBox(boundBox:BoundBox, transform:Transform3D = null):void {
|
||
var vBuffer:VertexStream = (VertexAttributes.POSITION < _attributesStreams.length) ? _attributesStreams[VertexAttributes.POSITION] : null;
|
||
if (vBuffer == null) {
|
||
throw new Error("Cannot calculate BoundBox without data.");
|
||
}
|
||
var offset:int = _attributesOffsets[VertexAttributes.POSITION];
|
||
var numMappings:int = vBuffer.attributes.length;
|
||
var data:ByteArray = vBuffer.data;
|
||
|
||
for (var i:int = 0; i < _numVertices; i++) {
|
||
data.position = 4*(numMappings*i + offset);
|
||
var vx:Number = data.readFloat();
|
||
var vy:Number = data.readFloat();
|
||
var vz:Number = data.readFloat();
|
||
var x:Number, y:Number, z:Number;
|
||
if (transform != null) {
|
||
x = transform.a*vx + transform.b*vy + transform.c*vz + transform.d;
|
||
y = transform.e*vx + transform.f*vy + transform.g*vz + transform.h;
|
||
z = transform.i*vx + transform.j*vy + transform.k*vz + transform.l;
|
||
} else {
|
||
x = vx;
|
||
y = vy;
|
||
z = vz;
|
||
}
|
||
if (x < boundBox.minX) boundBox.minX = x;
|
||
if (x > boundBox.maxX) boundBox.maxX = x;
|
||
if (y < boundBox.minY) boundBox.minY = y;
|
||
if (y > boundBox.maxY) boundBox.maxY = y;
|
||
if (z < boundBox.minZ) boundBox.minZ = z;
|
||
if (z > boundBox.maxZ) boundBox.maxZ = z;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|