/** * 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. * * 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]); * * To get access to data, you can use method 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. = new Vector.(); /** * @private */ alternativa3d var _indexBuffer:IndexBuffer3D; /** * @private */ alternativa3d var _numVertices:int; /** * @private */ alternativa3d var _indices:Vector. = new Vector.(); /** * @private */ alternativa3d var _attributesStreams:Vector. = new Vector.(); /** * @private */ alternativa3d var _attributesOffsets:Vector. = new Vector.(); private var _attributesStrides:Vector. = new Vector.(); /** * 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: 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. { return _indices.slice(); } /** * @private */ public function set indices(value:Vector.):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 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.(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 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.):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. { 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. = new Vector.(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., 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., 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; } } } }