From 61fa82b1c6f0360c1312fe379d253741b85aada0 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Jul 2025 19:09:16 -0400 Subject: [PATCH 01/14] Implement `VertexDefinition`, allowing users to change which attributes `CompactSubGeometry` includes. Most users likely don't need "secondary UVs", for instance, and could save memory by omitting them. Others might want to add a custom 4-length attribute. --- away3d/core/base/CompactSubGeometry.hx | 210 +++++++++++------- away3d/core/base/data/VertexDefinition.hx | 98 ++++++++ away3d/loaders/parsers/DAEParser.hx | 2 +- .../tools/helpers/ParticleGeometryHelper.hx | 158 ++++++++----- away3d/tools/utils/GeomUtil.hx | 4 +- 5 files changed, 333 insertions(+), 139 deletions(-) create mode 100644 away3d/core/base/data/VertexDefinition.hx diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index 9bff9af2..bf60fa71 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -1,6 +1,6 @@ package away3d.core.base; - +import away3d.core.base.data.VertexDefinition; import away3d.core.managers.Stage3DProxy; import openfl.display3D.Context3D; @@ -12,6 +12,8 @@ import openfl.Vector; class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { + public final definition:VertexDefinition; + public var numVertices(get, never):Int; public var secondaryUVStride(get, never):Int; public var secondaryUVOffset(get, never):Int; @@ -27,11 +29,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry private var _isolatedVertexPositionData:Vector; private var _isolatedVertexPositionDataDirty:Bool; - public function new() + public function new(?definition:VertexDefinition) { super(); _autoDeriveVertexNormals = false; _autoDeriveVertexTangents = false; + this.definition = definition != null ? definition : VertexDefinition.defaultVertexDefinition; } private function get_numVertices():Int @@ -59,7 +62,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry _isolatedVertexPositionDataDirty = true; _vertexData = data; - var numVertices:Int = Std.int(_vertexData.length/13); + var numVertices:Int = Std.int(_vertexData.length/definition.length); if (numVertices != _numVertices) disposeVertexBuffers(_vertexBuffer); _numVertices = numVertices; @@ -74,6 +77,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { + final attribute:AttributeDefinition = definition.get("position"); + if (attribute == null) + { + return; + } + var contextIndex:Int = stage3DProxy._stage3DIndex; var context:Context3D = stage3DProxy._context3D; @@ -85,11 +94,17 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry if (_activeDataInvalid) uploadData(contextIndex); - context.setVertexBufferAt(index, _activeBuffer, 0, Context3DVertexBufferFormat.FLOAT_3); + context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); } public function activateUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { + final attribute:AttributeDefinition = definition.get("UV"); + if (attribute == null) + { + return; + } + var contextIndex:Int = stage3DProxy._stage3DIndex; var context:Context3D = stage3DProxy._context3D; @@ -106,11 +121,17 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry if (_activeDataInvalid) uploadData(contextIndex); - context.setVertexBufferAt(index, _activeBuffer, 9, Context3DVertexBufferFormat.FLOAT_2); + context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); } public function activateSecondaryUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { + final attribute:AttributeDefinition = definition.get("secondaryUV"); + if (attribute == null) + { + return; + } + var contextIndex:Int = stage3DProxy._stage3DIndex; var context:Context3D = stage3DProxy._context3D; @@ -122,7 +143,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry if (_activeDataInvalid) uploadData(contextIndex); - context.setVertexBufferAt(index, _activeBuffer, 11, Context3DVertexBufferFormat.FLOAT_2); + context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); } private function uploadData(contextIndex:Int):Void @@ -133,6 +154,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexNormalBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { + final attribute:AttributeDefinition = definition.get("normal"); + if (attribute == null) + { + return; + } + var contextIndex:Int = stage3DProxy._stage3DIndex; var context:Context3D = stage3DProxy._context3D; @@ -144,11 +171,17 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry if (_activeDataInvalid) uploadData(contextIndex); - context.setVertexBufferAt(index, _activeBuffer, 3, Context3DVertexBufferFormat.FLOAT_3); + context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); } public function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { + final attribute:AttributeDefinition = definition.get("tangent"); + if (attribute == null) + { + return; + } + var contextIndex:Int = stage3DProxy._stage3DIndex; var context:Context3D = stage3DProxy._context3D; @@ -160,12 +193,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry if (_activeDataInvalid) uploadData(contextIndex); - context.setVertexBufferAt(index, _activeBuffer, 6, Context3DVertexBufferFormat.FLOAT_3); + context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); } private function createBuffer(contextIndex:Int, context:Context3D, stage3DProxy:Stage3DProxy):Void { - _vertexBuffer[contextIndex] = _activeBuffer = stage3DProxy.createVertexBuffer(_numVertices, 13); + _vertexBuffer[contextIndex] = _activeBuffer = stage3DProxy.createVertexBuffer(_numVertices, definition.length); _bufferContext[contextIndex] = _activeContext = context; _vertexDataInvalid[contextIndex] = _activeDataInvalid = true; } @@ -257,52 +290,52 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry override private function get_vertexStride():Int { - return 13; + return definition.length; } override private function get_vertexNormalStride():Int { - return 13; + return definition.length; } override private function get_vertexTangentStride():Int { - return 13; + return definition.length; } override private function get_UVStride():Int { - return 13; + return definition.length; } private function get_secondaryUVStride():Int { - return 13; + return definition.length; } override private function get_vertexOffset():Int { - return 0; + return definition.get("position")?.offset ?? 0; } override private function get_vertexNormalOffset():Int { - return 3; + return definition.get("normal")?.offset ?? 0; } override private function get_vertexTangentOffset():Int { - return 6; + return definition.get("tangent")?.offset ?? 0; } override private function get_UVOffset():Int { - return 9; + return definition.get("UV")?.offset ?? 0; } private function get_secondaryUVOffset():Int { - return 11; + return definition.get("secondaryUV")?.offset ?? 0; } override public function dispose():Void @@ -327,15 +360,15 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function cloneWithSeperateBuffers():SubGeometry { var clone:SubGeometry = new SubGeometry(); - clone.updateVertexData(_isolatedVertexPositionData != null? _isolatedVertexPositionData : _isolatedVertexPositionData = stripBuffer(0, 3)); + clone.updateVertexData(get_vertexPositionData()); clone.autoDeriveVertexNormals = _autoDeriveVertexNormals; clone.autoDeriveVertexTangents = _autoDeriveVertexTangents; if (!_autoDeriveVertexNormals) - clone.updateVertexNormalData(stripBuffer(3, 3)); + clone.updateVertexNormalData(isolateAttribute("normal")); if (!_autoDeriveVertexTangents) - clone.updateVertexTangentData(stripBuffer(6, 3)); - clone.updateUVData(stripBuffer(9, 2)); - clone.updateSecondaryUVData(stripBuffer(11, 2)); + clone.updateVertexTangentData(isolateAttribute("tangent")); + clone.updateUVData(isolateAttribute("UV")); + clone.updateSecondaryUVData(isolateAttribute("secondaryUV")); clone.updateIndexData(indexData.concat()); return clone; } @@ -343,26 +376,38 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry override private function get_vertexPositionData():Vector { if (_isolatedVertexPositionDataDirty || _isolatedVertexPositionData == null) { - _isolatedVertexPositionData = stripBuffer(0, 3); + _isolatedVertexPositionData = isolateAttribute("position"); _isolatedVertexPositionDataDirty = false; } return _isolatedVertexPositionData; } /** - * Isolate and returns a Vector.Number of a specific buffer type - * - * - stripBuffer(0, 3), return only the vertices - * - stripBuffer(3, 3): return only the normals - * - stripBuffer(6, 3): return only the tangents - * - stripBuffer(9, 2): return only the uv's - * - stripBuffer(11, 2): return only the secondary uv's + * Isolates and returns all data for the attribute with the given name, + * typically one of "position", "normal", "tangent", "UV", or "secondaryUV". + */ + public function isolateAttribute(name:String):Vector + { + final attribute:AttributeDefinition = definition.get(name); + if (attribute != null) + { + return stripBuffer(attribute.offset, attribute.length); + } + else + { + return null; + } + } + + /** + * Isolates and returns a specific subset of this geometry. + * @see `isolateAttribute` */ public function stripBuffer(offset:Int, numEntries:Int):Vector { var data:Vector = new Vector(_numVertices*numEntries); var i:Int = 0, j:Int = offset; - var skip:Int = 13 - numEntries; + var skip:Int = definition.length - numEntries; for (v in 0..._numVertices) { for (k in 0...numEntries) @@ -373,60 +418,73 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry return data; } - public function fromVectors(verts:Vector, uvs:Vector, normals:Vector, tangents:Vector):Void + public function setAttributeData(attributeName:String, data:Vector):Void { - var vertLen:Int = Std.int(verts.length/3*13); - - var index:Int = 0; - var v:Int = 0; - var n:Int = 0; - var t:Int = 0; - var u:Int = 0; + if (_setAttributeData(attributeName, data)) + { + updateData(_vertexData); + } + } + + private function _setAttributeData(attributeName:String, data:Vector):Bool + { + final attribute:AttributeDefinition = definition.get(attributeName); + if (data == null || attribute == null) + { + return false; + } - var data:Vector = new Vector(vertLen, true); + final attributeLength:Int = attribute.length; + final vertexLength:Int = _vertexData.length; - while (index < vertLen) { - data[index++] = verts[v++]; - data[index++] = verts[v++]; - data[index++] = verts[v++]; - - if (normals != null && normals.length > 0) { - data[index++] = normals[n++]; - data[index++] = normals[n++]; - data[index++] = normals[n++]; - } else { - data[index++] = 0; - data[index++] = 0; - data[index++] = 0; + var inputIndex:Int = 0; + var outputIndex:Int = attribute.offset; + while (inputIndex + attributeLength < data.length + && outputIndex + attributeLength < _vertexData.length) + { + for (i in 0...attributeLength) + { + _vertexData[outputIndex + i] = data[inputIndex + i]; } - if (tangents != null && tangents.length > 0) { - data[index++] = tangents[t++]; - data[index++] = tangents[t++]; - data[index++] = tangents[t++]; - } else { - data[index++] = 0; - data[index++] = 0; - data[index++] = 0; + inputIndex += attributeLength; + outputIndex += vertexLength; + } + + return true; + } + + public function fromVectors(positions:Vector, uvs:Vector, normals:Vector, tangents:Vector):Void + { + if (positions != null) + { + final newLength:Int = Std.int(positions.length / 3 * definition.length); + if (newLength < _vertexData.length) + { + _vertexData = _vertexData.slice(0, newLength); } - - if (uvs != null && uvs.length > 0) { - data[index++] = uvs[u]; - data[index++] = uvs[u + 1]; - // use same secondary uvs as primary - data[index++] = uvs[u++]; - data[index++] = uvs[u++]; - } else { - data[index++] = 0; - data[index++] = 0; - data[index++] = 0; - data[index++] = 0; + else if (newLength > _vertexData.length) + { + final newData:Vector = new Vector(newLength, true); + for (i in 0..._vertexData.length) + { + newData[i] = _vertexData[i]; + } + _vertexData = newData; } + + _setAttributeData("position", positions); } + _setAttributeData("normal", normals); autoDeriveVertexNormals = !(normals != null && normals.length > 0); + + _setAttributeData("tangent", tangents); autoDeriveVertexTangents = !(tangents != null && tangents.length > 0); + + _setAttributeData("UV", uvs); autoGenerateDummyUVs = !(uvs != null && uvs.length > 0); - updateData(data); + + updateData(_vertexData); } } \ No newline at end of file diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx new file mode 100644 index 00000000..054e4c2f --- /dev/null +++ b/away3d/core/base/data/VertexDefinition.hx @@ -0,0 +1,98 @@ +package away3d.core.base.data; + +import openfl.display3D.Context3DVertexBufferFormat; +import haxe.ds.ReadOnlyArray; + +using Lambda; + +/** + * The contents of a single vertex in a `CompactSubGeometry`. + */ +class VertexDefinition { + /** + * The default attributes used by `CompactSubGeometry`: position, normal, + * tangent, UV, and secondaryUV. + */ + public static final defaultAttributes:ReadOnlyArray = [ + new AttributeDefinition("position", 3), + new AttributeDefinition("normal", 3), + new AttributeDefinition("tangent", 3), + new AttributeDefinition("UV", 2), + new AttributeDefinition("secondaryUV", 2) + ]; + + /** + * A definition using `defaultAttributes`: position, normal, tangent, UV, + * and secondaryUV. + */ + public static final defaultVertexDefinition:VertexDefinition = new VertexDefinition(defaultAttributes); + + public final attributes:ReadOnlyArray; + + /** + * The combined length of all attributes; the total number of float values + * stored per vertex. + */ + public final length:Int; + + public function new(attributes:ReadOnlyArray) { + var attributes:Array = attributes.copy(); + var length:Int = 0; + + for(index => attribute in attributes) { + //If an offset was already set, the attribute is most likely in use + //elsewhere. Instead of modifying it, make a copy. + if(attribute.offset != -1 && attribute.offset != length) { + attributes[index] = attribute.clone(); + } + + attribute.offset = length; + length += attribute.length; + } + + this.length = length; + this.attributes = attributes; + } + + public function get(attributeName:String):AttributeDefinition { + for(attribute in attributes) { + if(attribute.name == attributeName) { + return attribute; + } + } + return null; + } +} + +class AttributeDefinition { + public final length:Int; + + @:allow(away3d.core.base.data.VertexDefinition) + public var offset(default, null):Int = -1; + + public final name:String; + + public final vertexBufferFormat:Context3DVertexBufferFormat; + + public inline function new(name:String, length:Int) { + this.name = name; + this.length = length; + + vertexBufferFormat = switch (length) { + case 1: + FLOAT_1; + case 2: + FLOAT_2; + case 3: + FLOAT_3; + case 4: + FLOAT_4; + default: + throw length + " should be 1-4"; + }; + } + + public inline function clone():AttributeDefinition { + return new AttributeDefinition(name, length); + } +} diff --git a/away3d/loaders/parsers/DAEParser.hx b/away3d/loaders/parsers/DAEParser.hx index 63f425d4..3fb828be 100644 --- a/away3d/loaders/parsers/DAEParser.hx +++ b/away3d/loaders/parsers/DAEParser.hx @@ -492,7 +492,7 @@ class DAEParser extends ParserBase for (i in 0...base.subGeometries.length) { sub = cast(base.subGeometries[i], CompactSubGeometry); vertexData = sub.vertexData.concat(); - for (v in 0...Std.int(vertexData.length/13)) { + for (v in 0...Std.int(vertexData.length/sub.definition.length)) { j = sub.vertexOffset + v*sub.vertexStride; vertexData[j] = morph.method == "NORMALIZED"? startWeight*sub.vertexData[j] : sub.vertexData[j]; for (k in 0...morph.targets.length) diff --git a/away3d/tools/helpers/ParticleGeometryHelper.hx b/away3d/tools/helpers/ParticleGeometryHelper.hx index 5a87b0b8..edb434b2 100644 --- a/away3d/tools/helpers/ParticleGeometryHelper.hx +++ b/away3d/tools/helpers/ParticleGeometryHelper.hx @@ -3,6 +3,7 @@ package away3d.tools.helpers; import away3d.core.base.ParticleGeometry; import away3d.core.base.CompactSubGeometry; import away3d.core.base.data.ParticleData; +import away3d.core.base.data.VertexDefinition; import away3d.core.base.Geometry; import away3d.core.base.ISubGeometry; import away3d.tools.helpers.data.ParticleGeometryTransform; @@ -20,7 +21,7 @@ class ParticleGeometryHelper { public static inline var MAX_VERTEX:Int = 65535; - public static function generateGeometry(geometries:Vector, transforms:Vector = null):ParticleGeometry + public static function generateGeometry(geometries:Vector, transforms:Vector = null, ?vertexDefinition:VertexDefinition):ParticleGeometry { var verticesVector:Vector> = new Vector>(); var indicesVector:Vector> = new Vector>(); @@ -45,6 +46,11 @@ class ParticleGeometryHelper var tempTangents:Vector3D = new Vector3D(); var tempUV:Point = new Point(); + final positionDefinition:AttributeDefinition = vertexDefinition.get("position"); + final normalDefinition:AttributeDefinition = vertexDefinition.get("normal"); + final tangentDefinition:AttributeDefinition = vertexDefinition.get("tangent"); + final uvDefinition:AttributeDefinition = vertexDefinition.get("UV"); + for (i in 0...numParticles) { sourceSubGeometries = geometries[i].subGeometries; numSubGeometries = sourceSubGeometries.length; @@ -54,7 +60,7 @@ class ParticleGeometryHelper sub2SubMap.push(subGeometries.length); verticesVector.push(new Vector()); indicesVector.push(new Vector()); - subGeometries.push(new CompactSubGeometry()); + subGeometries.push(new CompactSubGeometry(vertexDefinition)); vertexCounters.push(0); } @@ -66,7 +72,7 @@ class ParticleGeometryHelper sub2SubMap[srcIndex] = subGeometries.length; verticesVector.push(new Vector()); indicesVector.push(new Vector()); - subGeometries.push(new CompactSubGeometry()); + subGeometries.push(new CompactSubGeometry(vertexDefinition)); vertexCounters.push(0); } @@ -88,13 +94,17 @@ class ParticleGeometryHelper vertexCounters[j] += sourceSubGeometry.numVertices; var k:Int; - var tempLen:Int; var compact:CompactSubGeometry = #if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end(sourceSubGeometry, CompactSubGeometry) ? cast sourceSubGeometry : null; var product:Int; var sourceVertices:Vector; + var attributesDone:Array = []; + + final inStride:Int = compact.definition.length; + final outStride:Int = subGeometry.definition.length; + final startIndex:Int = vertices.length; + vertices.length += outStride * compact.numVertices; if (compact != null) { - tempLen = compact.numVertices; compact.numTriangles; sourceVertices = compact.vertexData; @@ -104,64 +114,94 @@ class ParticleGeometryHelper var invVertexTransform:Matrix3D = particleGeometryTransform.invVertexTransform; var UVTransform:Matrix = particleGeometryTransform.UVTransform; - for (k in 0...tempLen) { + final inPositionDefinition:AttributeDefinition = compact.definition.get("position"); + final inNormalDefinition:AttributeDefinition = compact.definition.get("normal"); + final inTangentDefinition:AttributeDefinition = compact.definition.get("tangent"); + final inUVDefinition:AttributeDefinition = compact.definition.get("UV"); + + for (k in 0...compact.numVertices) { /* * 0 - 2: vertex position X, Y, Z * 3 - 5: normal X, Y, Z * 6 - 8: tangent X, Y, Z * 9 - 10: U V - * 11 - 12: Secondary U V*/ - product = k*13; - tempVertex.x = sourceVertices[product]; - tempVertex.y = sourceVertices[product + 1]; - tempVertex.z = sourceVertices[product + 2]; - tempNormal.x = sourceVertices[product + 3]; - tempNormal.y = sourceVertices[product + 4]; - tempNormal.z = sourceVertices[product + 5]; - tempTangents.x = sourceVertices[product + 6]; - tempTangents.y = sourceVertices[product + 7]; - tempTangents.z = sourceVertices[product + 8]; - tempUV.x = sourceVertices[product + 9]; - tempUV.y = sourceVertices[product + 10]; - if (vertexTransform != null) { - tempVertex = vertexTransform.transformVector(tempVertex); - tempNormal = invVertexTransform.deltaTransformVector(tempNormal); - tempTangents = invVertexTransform.deltaTransformVector(tempNormal); + * 11 - 12: Secondary U V + */ + product = k*compact.definition.length; + + if (inPositionDefinition != null && positionDefinition != null && vertexTransform != null) { + tempVertex.x = sourceVertices[product + inPositionDefinition.offset]; + tempVertex.y = sourceVertices[product + inPositionDefinition.offset + 1]; + tempVertex.z = sourceVertices[product + inPositionDefinition.offset + 2]; + vertexTransform.transformVectorToOutput(tempVertex, tempVertex); + vertices[startIndex + k * vertexDefinition.length + positionDefinition.offset] = tempVertex.x; + vertices[startIndex + k * vertexDefinition.length + positionDefinition.offset + 1] = tempVertex.y; + vertices[startIndex + k * vertexDefinition.length + positionDefinition.offset + 2] = tempVertex.z; + + attributesDone.push("position"); + } + + if (inNormalDefinition != null && normalDefinition != null && vertexTransform != null) { + tempNormal.x = sourceVertices[product + inNormalDefinition.offset]; + tempNormal.y = sourceVertices[product + inNormalDefinition.offset + 1]; + tempNormal.z = sourceVertices[product + inNormalDefinition.offset + 2]; + invVertexTransform.deltaTransformVectorToOutput(tempNormal, tempNormal); + vertices[startIndex + k * vertexDefinition.length + normalDefinition.offset] = tempNormal.x; + vertices[startIndex + k * vertexDefinition.length + normalDefinition.offset + 1] = tempNormal.y; + vertices[startIndex + k * vertexDefinition.length + normalDefinition.offset + 2] = tempNormal.z; + + attributesDone.push("normal"); + } + + if (inTangentDefinition != null && tangentDefinition != null && vertexTransform != null) { + tempTangents.x = sourceVertices[product + inTangentDefinition.offset]; + tempTangents.y = sourceVertices[product + inTangentDefinition.offset + 1]; + tempTangents.z = sourceVertices[product + inTangentDefinition.offset + 2]; + invVertexTransform.deltaTransformVectorToOutput(tempTangents, tempTangents); + vertices[startIndex + k * vertexDefinition.length + tangentDefinition.offset] = tempTangents.x; + vertices[startIndex + k * vertexDefinition.length + tangentDefinition.offset + 1] = tempTangents.y; + vertices[startIndex + k * vertexDefinition.length + tangentDefinition.offset + 2] = tempTangents.z; + + attributesDone.push("tangent"); } - if (UVTransform != null) - tempUV = UVTransform.transformPoint(tempUV); - //this is faster than that only push one data - vertices.push(tempVertex.x); - vertices.push(tempVertex.y); - vertices.push(tempVertex.z); - vertices.push(tempNormal.x); - vertices.push(tempNormal.y); - vertices.push(tempNormal.z); - vertices.push(tempTangents.x); - vertices.push(tempTangents.y); - vertices.push(tempTangents.z); - vertices.push(tempUV.x); - vertices.push(tempUV.y); - vertices.push(sourceVertices[product + 11]); - vertices.push(sourceVertices[product + 12]); + + if (inUVDefinition != null && uvDefinition != null && UVTransform != null) { + tempUV.x = sourceVertices[product + inUVDefinition.offset]; + tempUV.y = sourceVertices[product + inUVDefinition.offset + 1]; + UVTransform.transformPointToOutput(tempUV, tempUV); + vertices[startIndex + k * vertexDefinition.length + uvDefinition.offset] = tempUV.x; + vertices[startIndex + k * vertexDefinition.length + uvDefinition.offset + 1] = tempUV.y; + + attributesDone.push("UV"); + } + + } + } + + for (outAttribute in subGeometry.definition.attributes) { + if (attributesDone.indexOf(outAttribute.name) >= 0) { + continue; } - } else { - for (k in 0...tempLen) { - product = k*13; - //this is faster than that only push one data - vertices.push(sourceVertices[product]); - vertices.push(sourceVertices[product + 1]); - vertices.push(sourceVertices[product + 2]); - vertices.push(sourceVertices[product + 3]); - vertices.push(sourceVertices[product + 4]); - vertices.push(sourceVertices[product + 5]); - vertices.push(sourceVertices[product + 6]); - vertices.push(sourceVertices[product + 7]); - vertices.push(sourceVertices[product + 8]); - vertices.push(sourceVertices[product + 9]); - vertices.push(sourceVertices[product + 10]); - vertices.push(sourceVertices[product + 11]); - vertices.push(sourceVertices[product + 12]); + + final sourceAttribute:AttributeDefinition = compact.definition.get(outAttribute.name); + + if (sourceAttribute == null) { + throw 'Input data does not include attribute "${ outAttribute.name }". It defines "' + + [for(attribute in compact.definition.attributes) attribute.name].join('", "') + '".'; + } + + if (outAttribute.length != sourceAttribute.length) { + throw 'Length mismatch for attribute "${ outAttribute.name }": source has length ${ sourceAttribute.length }, destination needs length ${ outAttribute.name }.'; + } + + final length:Int = outAttribute.length; + final inOffset:Int = sourceAttribute.offset; + final outOffset:Int = outAttribute.offset; + + for (k in 0...compact.numVertices) { + for (l in 0...length) { + vertices[startIndex + k * outStride + outOffset + l] = sourceVertices[k * inStride + inOffset + l]; + } } } } else { @@ -169,8 +209,7 @@ class ParticleGeometryHelper } var sourceIndices:Vector = sourceSubGeometry.indexData; - tempLen = sourceSubGeometry.numTriangles; - for (k in 0...tempLen) { + for (k in 0...sourceSubGeometry.numTriangles) { product = k*3; indices.push(sourceIndices[product] + vertexCounter); indices.push(sourceIndices[product + 1] + vertexCounter); @@ -183,8 +222,7 @@ class ParticleGeometryHelper particleGeometry.particles = particles; particleGeometry.numParticles = numParticles; - numParticles = subGeometries.length; - for (i in 0...numParticles) { + for (i in 0...subGeometries.length) { subGeometry = subGeometries[i]; subGeometry.updateData(verticesVector[i]); subGeometry.updateIndexData(indicesVector[i]); diff --git a/away3d/tools/utils/GeomUtil.hx b/away3d/tools/utils/GeomUtil.hx index 2e43e5ec..6168c6a1 100644 --- a/away3d/tools/utils/GeomUtil.hx +++ b/away3d/tools/utils/GeomUtil.hx @@ -189,8 +189,8 @@ class GeomUtil /* * Combines a set of separate raw buffers into an interleaved one, compatible - * with CompactSubGeometry. SubGeometry uses separate buffers, whereas CompactSubGeometry - * uses a single, combined buffer. + * with a default CompactSubGeometry. SubGeometry uses separate buffers, whereas + * CompactSubGeometry uses a single, combined buffer. */ public static function interleaveBuffers(numVertices:Int, vertices:Vector = null, normals:Vector = null, tangents:Vector = null, uvs:Vector = null, suvs:Vector = null):Vector { From 1b7b5d26d5d4643e700d41e2a6d6704d13c54421 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 24 Jul 2025 21:29:12 -0400 Subject: [PATCH 02/14] Clean up. --- away3d/tools/helpers/ParticleGeometryHelper.hx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/away3d/tools/helpers/ParticleGeometryHelper.hx b/away3d/tools/helpers/ParticleGeometryHelper.hx index edb434b2..14790b2d 100644 --- a/away3d/tools/helpers/ParticleGeometryHelper.hx +++ b/away3d/tools/helpers/ParticleGeometryHelper.hx @@ -120,13 +120,6 @@ class ParticleGeometryHelper final inUVDefinition:AttributeDefinition = compact.definition.get("UV"); for (k in 0...compact.numVertices) { - /* - * 0 - 2: vertex position X, Y, Z - * 3 - 5: normal X, Y, Z - * 6 - 8: tangent X, Y, Z - * 9 - 10: U V - * 11 - 12: Secondary U V - */ product = k*compact.definition.length; if (inPositionDefinition != null && positionDefinition != null && vertexTransform != null) { @@ -174,7 +167,6 @@ class ParticleGeometryHelper attributesDone.push("UV"); } - } } From cd6cb81b480b0af73c87228a780c049ae6bef3ef Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 22 Aug 2025 01:18:35 -0400 Subject: [PATCH 03/14] Clone `CompactSubGeometry.definition`. Without that, the clone will interpret all its data incorrectly. --- away3d/core/base/CompactSubGeometry.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index bf60fa71..b107e52b 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -274,7 +274,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function clone():ISubGeometry { - var clone:CompactSubGeometry = new CompactSubGeometry(); + var clone:CompactSubGeometry = new CompactSubGeometry(definition); clone._autoDeriveVertexNormals = _autoDeriveVertexNormals; clone._autoDeriveVertexTangents = _autoDeriveVertexTangents; clone.updateData(_vertexData.concat()); From 54327249cf83fb990152e64ef3e1df62f1749f3e Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:21:24 -0400 Subject: [PATCH 04/14] Implement `activateSpecificVertexBuffer()`. This reduces duplicate code and allows activating custom attributes. --- away3d/core/base/CompactSubGeometry.hx | 83 ++++---------------------- 1 file changed, 10 insertions(+), 73 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index b107e52b..d6e4cabd 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -77,73 +77,22 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - final attribute:AttributeDefinition = definition.get("position"); - if (attribute == null) - { - return; - } - - var contextIndex:Int = stage3DProxy._stage3DIndex; - var context:Context3D = stage3DProxy._context3D; - - if (contextIndex != _contextIndex) - updateActiveBuffer(contextIndex); - - if (_activeBuffer == null || _activeContext != context) - createBuffer(contextIndex, context, stage3DProxy); - if (_activeDataInvalid) - uploadData(contextIndex); - - context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); + activateSpecificVertexBuffer("position"); } public function activateUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - final attribute:AttributeDefinition = definition.get("UV"); - if (attribute == null) - { - return; - } - - var contextIndex:Int = stage3DProxy._stage3DIndex; - var context:Context3D = stage3DProxy._context3D; - if (_uvsDirty && _autoGenerateUVs) { _vertexData = updateDummyUVs(_vertexData); invalidateBuffers(_vertexDataInvalid); } - if (contextIndex != _contextIndex) - updateActiveBuffer(contextIndex); - - if (_activeBuffer == null || _activeContext != context) - createBuffer(contextIndex, context, stage3DProxy); - if (_activeDataInvalid) - uploadData(contextIndex); - - context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); + activateSpecificVertexBuffer("UV"); } public function activateSecondaryUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - final attribute:AttributeDefinition = definition.get("secondaryUV"); - if (attribute == null) - { - return; - } - - var contextIndex:Int = stage3DProxy._stage3DIndex; - var context:Context3D = stage3DProxy._context3D; - - if (contextIndex != _contextIndex) - updateActiveBuffer(contextIndex); - - if (_activeBuffer == null || _activeContext != context) - createBuffer(contextIndex, context, stage3DProxy); - if (_activeDataInvalid) - uploadData(contextIndex); - - context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); + activateSpecificVertexBuffer("secondaryUV"); } private function uploadData(contextIndex:Int):Void @@ -154,29 +103,17 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexNormalBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - final attribute:AttributeDefinition = definition.get("normal"); - if (attribute == null) - { - return; - } - - var contextIndex:Int = stage3DProxy._stage3DIndex; - var context:Context3D = stage3DProxy._context3D; - - if (contextIndex != _contextIndex) - updateActiveBuffer(contextIndex); - - if (_activeBuffer == null || _activeContext != context) - createBuffer(contextIndex, context, stage3DProxy); - if (_activeDataInvalid) - uploadData(contextIndex); - - context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); + activateSpecificVertexBuffer("normal"); } public function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - final attribute:AttributeDefinition = definition.get("tangent"); + activateSpecificVertexBuffer("tangent"); + } + + public function activateSpecificVertexBuffer(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + { + final attribute:AttributeDefinition = definition.get(attributeName); if (attribute == null) { return; From a18ed728d1ca6ae7804a996d44de4b8baee01654 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:43:53 -0400 Subject: [PATCH 05/14] Remove `?.` and `??` for backwards compatibility. --- away3d/core/base/CompactSubGeometry.hx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index d6e4cabd..abc96d54 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -252,27 +252,33 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry override private function get_vertexOffset():Int { - return definition.get("position")?.offset ?? 0; + return getAttributeOffset("position"); } override private function get_vertexNormalOffset():Int { - return definition.get("normal")?.offset ?? 0; + return getAttributeOffset("normal"); } override private function get_vertexTangentOffset():Int { - return definition.get("tangent")?.offset ?? 0; + return getAttributeOffset("tangent"); } override private function get_UVOffset():Int { - return definition.get("UV")?.offset ?? 0; + return getAttributeOffset("UV"); } private function get_secondaryUVOffset():Int { - return definition.get("secondaryUV")?.offset ?? 0; + return getAttributeOffset("secondaryUV"); + } + + private inline function getAttributeOffset(attributeName:String):Int + { + final attribute:AttributeDefinition = definition.get(attributeName); + return attribute != null ? attribute.offset : 0; } override public function dispose():Void From 3dc65842a669627940365b0cc96b0487d5c0d0af Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:47:14 -0400 Subject: [PATCH 06/14] Add missing arguments. --- away3d/core/base/CompactSubGeometry.hx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index abc96d54..e1ac686e 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -77,7 +77,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("position"); + activateSpecificVertexBuffer("position", index, stage3DProxy); } public function activateUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void @@ -87,12 +87,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry invalidateBuffers(_vertexDataInvalid); } - activateSpecificVertexBuffer("UV"); + activateSpecificVertexBuffer("UV", index, stage3DProxy); } public function activateSecondaryUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("secondaryUV"); + activateSpecificVertexBuffer("secondaryUV", index, stage3DProxy); } private function uploadData(contextIndex:Int):Void @@ -103,12 +103,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexNormalBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("normal"); + activateSpecificVertexBuffer("normal", index, stage3DProxy); } public function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("tangent"); + activateSpecificVertexBuffer("tangent", index, stage3DProxy); } public function activateSpecificVertexBuffer(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void From 402bfbcfe68733373f57017f2191ee13ce1db3f5 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:55:35 -0400 Subject: [PATCH 07/14] Avoid `final`, for backwards compatibility. --- away3d/core/base/CompactSubGeometry.hx | 18 +++++++++--------- away3d/core/base/data/VertexDefinition.hx | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index e1ac686e..dc726b80 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -12,7 +12,7 @@ import openfl.Vector; class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { - public final definition:VertexDefinition; + public var definition(default, null):VertexDefinition; public var numVertices(get, never):Int; public var secondaryUVStride(get, never):Int; @@ -113,7 +113,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateSpecificVertexBuffer(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void { - final attribute:AttributeDefinition = definition.get(attributeName); + var attribute:AttributeDefinition = definition.get(attributeName); if (attribute == null) { return; @@ -277,7 +277,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry private inline function getAttributeOffset(attributeName:String):Int { - final attribute:AttributeDefinition = definition.get(attributeName); + var attribute:AttributeDefinition = definition.get(attributeName); return attribute != null ? attribute.offset : 0; } @@ -331,7 +331,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry */ public function isolateAttribute(name:String):Vector { - final attribute:AttributeDefinition = definition.get(name); + var attribute:AttributeDefinition = definition.get(name); if (attribute != null) { return stripBuffer(attribute.offset, attribute.length); @@ -371,14 +371,14 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry private function _setAttributeData(attributeName:String, data:Vector):Bool { - final attribute:AttributeDefinition = definition.get(attributeName); + var attribute:AttributeDefinition = definition.get(attributeName); if (data == null || attribute == null) { return false; } - final attributeLength:Int = attribute.length; - final vertexLength:Int = _vertexData.length; + var attributeLength:Int = attribute.length; + var vertexLength:Int = _vertexData.length; var inputIndex:Int = 0; var outputIndex:Int = attribute.offset; @@ -401,14 +401,14 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { if (positions != null) { - final newLength:Int = Std.int(positions.length / 3 * definition.length); + var newLength:Int = Std.int(positions.length / 3 * definition.length); if (newLength < _vertexData.length) { _vertexData = _vertexData.slice(0, newLength); } else if (newLength > _vertexData.length) { - final newData:Vector = new Vector(newLength, true); + var newData:Vector = new Vector(newLength, true); for (i in 0..._vertexData.length) { newData[i] = _vertexData[i]; diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index 054e4c2f..ef944300 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -13,7 +13,7 @@ class VertexDefinition { * The default attributes used by `CompactSubGeometry`: position, normal, * tangent, UV, and secondaryUV. */ - public static final defaultAttributes:ReadOnlyArray = [ + public static var defaultAttributes(default, null):ReadOnlyArray = [ new AttributeDefinition("position", 3), new AttributeDefinition("normal", 3), new AttributeDefinition("tangent", 3), @@ -25,15 +25,15 @@ class VertexDefinition { * A definition using `defaultAttributes`: position, normal, tangent, UV, * and secondaryUV. */ - public static final defaultVertexDefinition:VertexDefinition = new VertexDefinition(defaultAttributes); + public static var defaultVertexDefinition(default, null):VertexDefinition = new VertexDefinition(defaultAttributes); - public final attributes:ReadOnlyArray; + public var attributes(default, null):ReadOnlyArray; /** * The combined length of all attributes; the total number of float values * stored per vertex. */ - public final length:Int; + public var length(default, null):Int; public function new(attributes:ReadOnlyArray) { var attributes:Array = attributes.copy(); @@ -65,14 +65,14 @@ class VertexDefinition { } class AttributeDefinition { - public final length:Int; + public var length(default, null):Int; @:allow(away3d.core.base.data.VertexDefinition) public var offset(default, null):Int = -1; - public final name:String; + public var name(default, null):String; - public final vertexBufferFormat:Context3DVertexBufferFormat; + public var vertexBufferFormat(default, null):Context3DVertexBufferFormat; public inline function new(name:String, length:Int) { this.name = name; From 4c68d07025aa4c17ef9c435802f5e29953ded3ec Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:56:02 -0400 Subject: [PATCH 08/14] Avoid `keyValueIterator()`, for backwards compatibility. --- away3d/core/base/data/VertexDefinition.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index ef944300..4bae04e9 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -39,7 +39,9 @@ class VertexDefinition { var attributes:Array = attributes.copy(); var length:Int = 0; - for(index => attribute in attributes) { + for(index in 0...attributes.length) { + var attribute:AttributeDefinition = attributes[index]; + //If an offset was already set, the attribute is most likely in use //elsewhere. Instead of modifying it, make a copy. if(attribute.offset != -1 && attribute.offset != length) { From 89fb58d8e4187a0b1db30bded3d1aa2898c49088 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:11:09 -0400 Subject: [PATCH 09/14] Only import `ReadOnlyArray` in Haxe 4+. --- away3d/core/base/data/VertexDefinition.hx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index 4bae04e9..bd9f3c84 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -1,7 +1,10 @@ package away3d.core.base.data; import openfl.display3D.Context3DVertexBufferFormat; + +#if haxe4 import haxe.ds.ReadOnlyArray; +#end using Lambda; @@ -98,3 +101,20 @@ class AttributeDefinition { return new AttributeDefinition(name, length); } } + +#if !haxe4 +@:forward(copy, filter, indexOf, iterator, join, lastIndexOf, map, slice, toString) +abstract ReadOnlyArray(Array) from Array to Iterable +{ + public var length(get, never):Int; + + inline function get_length() + return this.length; + + @:arrayAccess inline function get(i:Int) + return this[i]; + + public inline function concat(a:ReadOnlyArray):Array + return this.concat(cast a); +} +#end From 2abebaa67a136d3372ce0b60222ae64255fae71b Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:15:30 -0400 Subject: [PATCH 10/14] Run formatter. --- away3d/core/base/CompactSubGeometry.hx | 136 +++++++++++----------- away3d/core/base/data/VertexDefinition.hx | 67 ++++++----- 2 files changed, 107 insertions(+), 96 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index dc726b80..6c64b8eb 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -13,11 +13,11 @@ import openfl.Vector; class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { public var definition(default, null):VertexDefinition; - + public var numVertices(get, never):Int; public var secondaryUVStride(get, never):Int; public var secondaryUVOffset(get, never):Int; - + private var _vertexDataInvalid:Vector = new Vector(8, true); private var _vertexBuffer:Vector = new Vector(8); private var _bufferContext:Vector = new Vector(8); @@ -28,7 +28,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry private var _activeDataInvalid:Bool; private var _isolatedVertexPositionData:Vector; private var _isolatedVertexPositionDataDirty:Bool; - + public function new(?definition:VertexDefinition) { super(); @@ -36,12 +36,12 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry _autoDeriveVertexTangents = false; this.definition = definition != null ? definition : VertexDefinition.defaultVertexDefinition; } - + private function get_numVertices():Int { return _numVertices; } - + /** * Updates the vertex data. All vertex properties are contained in a single Vector, and the order is as follows: * 0 - 2: vertex position X, Y, Z @@ -56,61 +56,61 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry _vertexNormalsDirty = true; if (_autoDeriveVertexTangents) _vertexTangentsDirty = true; - + _faceNormalsDirty = true; _faceTangentsDirty = true; _isolatedVertexPositionDataDirty = true; - + _vertexData = data; var numVertices:Int = Std.int(_vertexData.length/definition.length); if (numVertices != _numVertices) disposeVertexBuffers(_vertexBuffer); _numVertices = numVertices; - + if (_numVertices == 0) throw new Error("Bad data: geometry can't have zero triangles"); - + invalidateBuffers(_vertexDataInvalid); - + invalidateBounds(); } - + public function activateVertexBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { activateSpecificVertexBuffer("position", index, stage3DProxy); } - + public function activateUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { if (_uvsDirty && _autoGenerateUVs) { _vertexData = updateDummyUVs(_vertexData); invalidateBuffers(_vertexDataInvalid); } - + activateSpecificVertexBuffer("UV", index, stage3DProxy); } - + public function activateSecondaryUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { activateSpecificVertexBuffer("secondaryUV", index, stage3DProxy); } - + private function uploadData(contextIndex:Int):Void { _activeBuffer.uploadFromVector(_vertexData, 0, _numVertices); _vertexDataInvalid[contextIndex] = _activeDataInvalid = false; } - + public function activateVertexNormalBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { activateSpecificVertexBuffer("normal", index, stage3DProxy); } - + public function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { activateSpecificVertexBuffer("tangent", index, stage3DProxy); } - + public function activateSpecificVertexBuffer(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void { var attribute:AttributeDefinition = definition.get(attributeName); @@ -118,28 +118,28 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { return; } - + var contextIndex:Int = stage3DProxy._stage3DIndex; var context:Context3D = stage3DProxy._context3D; - + if (contextIndex != _contextIndex) updateActiveBuffer(contextIndex); - + if (_activeBuffer == null || _activeContext != context) createBuffer(contextIndex, context, stage3DProxy); if (_activeDataInvalid) uploadData(contextIndex); - + context.setVertexBufferAt(index, _activeBuffer, attribute.offset, attribute.vertexBufferFormat); } - + private function createBuffer(contextIndex:Int, context:Context3D, stage3DProxy:Stage3DProxy):Void { _vertexBuffer[contextIndex] = _activeBuffer = stage3DProxy.createVertexBuffer(_numVertices, definition.length); _bufferContext[contextIndex] = _activeContext = context; _vertexDataInvalid[contextIndex] = _activeDataInvalid = true; } - + private function updateActiveBuffer(contextIndex:Int):Void { _contextIndex = contextIndex; @@ -147,7 +147,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry _activeBuffer = _vertexBuffer[contextIndex]; _activeContext = _bufferContext[contextIndex]; } - + override private function get_vertexData():Vector { if (_autoDeriveVertexNormals && _vertexNormalsDirty) @@ -158,13 +158,13 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry _vertexData = updateDummyUVs(_vertexData); return _vertexData; } - + override private function updateVertexNormals(target:Vector):Vector { invalidateBuffers(_vertexDataInvalid); return super.updateVertexNormals(target); } - + override private function updateVertexTangents(target:Vector):Vector { if (_vertexNormalsDirty) @@ -172,22 +172,22 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry invalidateBuffers(_vertexDataInvalid); return super.updateVertexTangents(target); } - + override private function get_vertexNormalData():Vector { if (_autoDeriveVertexNormals && _vertexNormalsDirty) _vertexData = updateVertexNormals(_vertexData); - + return _vertexData; } - + override private function get_vertexTangentData():Vector { if (_autoDeriveVertexTangents && _vertexTangentsDirty) _vertexData = updateVertexTangents(_vertexData); return _vertexData; } - + override private function get_UVData():Vector { if (_uvsDirty && _autoGenerateUVs) { @@ -196,19 +196,19 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry } return _vertexData; } - + override public function applyTransformation(transform:Matrix3D):Void { super.applyTransformation(transform); invalidateBuffers(_vertexDataInvalid); } - + override public function scale(scale:Float):Void { super.scale(scale); invalidateBuffers(_vertexDataInvalid); } - + public function clone():ISubGeometry { var clone:CompactSubGeometry = new CompactSubGeometry(definition); @@ -218,88 +218,88 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry clone.updateIndexData(_indices.concat()); return clone; } - + override public function scaleUV(scaleU:Float = 1, scaleV:Float = 1):Void { super.scaleUV(scaleU, scaleV); invalidateBuffers(_vertexDataInvalid); } - + override private function get_vertexStride():Int { return definition.length; } - + override private function get_vertexNormalStride():Int { return definition.length; } - + override private function get_vertexTangentStride():Int { return definition.length; } - + override private function get_UVStride():Int { return definition.length; } - + private function get_secondaryUVStride():Int { return definition.length; } - + override private function get_vertexOffset():Int { return getAttributeOffset("position"); } - + override private function get_vertexNormalOffset():Int { return getAttributeOffset("normal"); } - + override private function get_vertexTangentOffset():Int { return getAttributeOffset("tangent"); } - + override private function get_UVOffset():Int { return getAttributeOffset("UV"); } - + private function get_secondaryUVOffset():Int { return getAttributeOffset("secondaryUV"); } - + private inline function getAttributeOffset(attributeName:String):Int { var attribute:AttributeDefinition = definition.get(attributeName); return attribute != null ? attribute.offset : 0; } - + override public function dispose():Void { super.dispose(); disposeVertexBuffers(_vertexBuffer); _vertexBuffer = null; } - + override private function disposeVertexBuffers(buffers:Vector):Void { super.disposeVertexBuffers(buffers); _activeBuffer = null; } - + override private function invalidateBuffers(invalid:Vector):Void { super.invalidateBuffers(invalid); _activeDataInvalid = true; } - + public function cloneWithSeperateBuffers():SubGeometry { var clone:SubGeometry = new SubGeometry(); @@ -315,7 +315,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry clone.updateIndexData(indexData.concat()); return clone; } - + override private function get_vertexPositionData():Vector { if (_isolatedVertexPositionDataDirty || _isolatedVertexPositionData == null) { @@ -324,7 +324,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry } return _isolatedVertexPositionData; } - + /** * Isolates and returns all data for the attribute with the given name, * typically one of "position", "normal", "tangent", "UV", or "secondaryUV". @@ -341,7 +341,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry return null; } } - + /** * Isolates and returns a specific subset of this geometry. * @see `isolateAttribute` @@ -351,16 +351,16 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry var data:Vector = new Vector(_numVertices*numEntries); var i:Int = 0, j:Int = offset; var skip:Int = definition.length - numEntries; - + for (v in 0..._numVertices) { for (k in 0...numEntries) data[i++] = _vertexData[j++]; j += skip; } - + return data; } - + public function setAttributeData(attributeName:String, data:Vector):Void { if (_setAttributeData(attributeName, data)) @@ -368,7 +368,7 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry updateData(_vertexData); } } - + private function _setAttributeData(attributeName:String, data:Vector):Bool { var attribute:AttributeDefinition = definition.get(attributeName); @@ -376,10 +376,10 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { return false; } - + var attributeLength:Int = attribute.length; var vertexLength:Int = _vertexData.length; - + var inputIndex:Int = 0; var outputIndex:Int = attribute.offset; while (inputIndex + attributeLength < data.length @@ -389,14 +389,14 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { _vertexData[outputIndex + i] = data[inputIndex + i]; } - + inputIndex += attributeLength; outputIndex += vertexLength; } - + return true; } - + public function fromVectors(positions:Vector, uvs:Vector, normals:Vector, tangents:Vector):Void { if (positions != null) @@ -415,19 +415,19 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry } _vertexData = newData; } - + _setAttributeData("position", positions); } - + _setAttributeData("normal", normals); autoDeriveVertexNormals = !(normals != null && normals.length > 0); - + _setAttributeData("tangent", tangents); autoDeriveVertexTangents = !(tangents != null && tangents.length > 0); - + _setAttributeData("UV", uvs); autoGenerateDummyUVs = !(uvs != null && uvs.length > 0); - + updateData(_vertexData); } -} \ No newline at end of file +} diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index bd9f3c84..660cbdab 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -11,7 +11,8 @@ using Lambda; /** * The contents of a single vertex in a `CompactSubGeometry`. */ -class VertexDefinition { +class VertexDefinition +{ /** * The default attributes used by `CompactSubGeometry`: position, normal, * tangent, UV, and secondaryUV. @@ -23,45 +24,51 @@ class VertexDefinition { new AttributeDefinition("UV", 2), new AttributeDefinition("secondaryUV", 2) ]; - + /** * A definition using `defaultAttributes`: position, normal, tangent, UV, * and secondaryUV. */ public static var defaultVertexDefinition(default, null):VertexDefinition = new VertexDefinition(defaultAttributes); - + public var attributes(default, null):ReadOnlyArray; - + /** * The combined length of all attributes; the total number of float values * stored per vertex. */ public var length(default, null):Int; - - public function new(attributes:ReadOnlyArray) { + + public function new(attributes:ReadOnlyArray) + { var attributes:Array = attributes.copy(); var length:Int = 0; - - for(index in 0...attributes.length) { + + for (index in 0...attributes.length) + { var attribute:AttributeDefinition = attributes[index]; - - //If an offset was already set, the attribute is most likely in use - //elsewhere. Instead of modifying it, make a copy. - if(attribute.offset != -1 && attribute.offset != length) { + + // If an offset was already set, the attribute is most likely in use + // elsewhere. Instead of modifying it, make a copy. + if (attribute.offset != -1 && attribute.offset != length) + { attributes[index] = attribute.clone(); } - + attribute.offset = length; length += attribute.length; } - + this.length = length; this.attributes = attributes; } - - public function get(attributeName:String):AttributeDefinition { - for(attribute in attributes) { - if(attribute.name == attributeName) { + + public function get(attributeName:String):AttributeDefinition + { + for (attribute in attributes) + { + if (attribute.name == attributeName) + { return attribute; } } @@ -69,21 +76,24 @@ class VertexDefinition { } } -class AttributeDefinition { +class AttributeDefinition +{ public var length(default, null):Int; - + @:allow(away3d.core.base.data.VertexDefinition) public var offset(default, null):Int = -1; - + public var name(default, null):String; - + public var vertexBufferFormat(default, null):Context3DVertexBufferFormat; - - public inline function new(name:String, length:Int) { + + public inline function new(name:String, length:Int) + { this.name = name; this.length = length; - - vertexBufferFormat = switch (length) { + + vertexBufferFormat = switch (length) + { case 1: FLOAT_1; case 2: @@ -96,8 +106,9 @@ class AttributeDefinition { throw length + " should be 1-4"; }; } - - public inline function clone():AttributeDefinition { + + public inline function clone():AttributeDefinition + { return new AttributeDefinition(name, length); } } From 0a4ec37dd246accc8100007812ccd4caec774fb9 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:17:38 -0400 Subject: [PATCH 11/14] Avoid `final`, for backwards compatibility. --- .../tools/helpers/ParticleGeometryHelper.hx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/away3d/tools/helpers/ParticleGeometryHelper.hx b/away3d/tools/helpers/ParticleGeometryHelper.hx index 14790b2d..9f945974 100644 --- a/away3d/tools/helpers/ParticleGeometryHelper.hx +++ b/away3d/tools/helpers/ParticleGeometryHelper.hx @@ -46,10 +46,10 @@ class ParticleGeometryHelper var tempTangents:Vector3D = new Vector3D(); var tempUV:Point = new Point(); - final positionDefinition:AttributeDefinition = vertexDefinition.get("position"); - final normalDefinition:AttributeDefinition = vertexDefinition.get("normal"); - final tangentDefinition:AttributeDefinition = vertexDefinition.get("tangent"); - final uvDefinition:AttributeDefinition = vertexDefinition.get("UV"); + var positionDefinition:AttributeDefinition = vertexDefinition.get("position"); + var normalDefinition:AttributeDefinition = vertexDefinition.get("normal"); + var tangentDefinition:AttributeDefinition = vertexDefinition.get("tangent"); + var uvDefinition:AttributeDefinition = vertexDefinition.get("UV"); for (i in 0...numParticles) { sourceSubGeometries = geometries[i].subGeometries; @@ -99,9 +99,9 @@ class ParticleGeometryHelper var sourceVertices:Vector; var attributesDone:Array = []; - final inStride:Int = compact.definition.length; - final outStride:Int = subGeometry.definition.length; - final startIndex:Int = vertices.length; + var inStride:Int = compact.definition.length; + var outStride:Int = subGeometry.definition.length; + var startIndex:Int = vertices.length; vertices.length += outStride * compact.numVertices; if (compact != null) { @@ -114,10 +114,10 @@ class ParticleGeometryHelper var invVertexTransform:Matrix3D = particleGeometryTransform.invVertexTransform; var UVTransform:Matrix = particleGeometryTransform.UVTransform; - final inPositionDefinition:AttributeDefinition = compact.definition.get("position"); - final inNormalDefinition:AttributeDefinition = compact.definition.get("normal"); - final inTangentDefinition:AttributeDefinition = compact.definition.get("tangent"); - final inUVDefinition:AttributeDefinition = compact.definition.get("UV"); + var inPositionDefinition:AttributeDefinition = compact.definition.get("position"); + var inNormalDefinition:AttributeDefinition = compact.definition.get("normal"); + var inTangentDefinition:AttributeDefinition = compact.definition.get("tangent"); + var inUVDefinition:AttributeDefinition = compact.definition.get("UV"); for (k in 0...compact.numVertices) { product = k*compact.definition.length; @@ -175,7 +175,7 @@ class ParticleGeometryHelper continue; } - final sourceAttribute:AttributeDefinition = compact.definition.get(outAttribute.name); + var sourceAttribute:AttributeDefinition = compact.definition.get(outAttribute.name); if (sourceAttribute == null) { throw 'Input data does not include attribute "${ outAttribute.name }". It defines "' @@ -186,9 +186,9 @@ class ParticleGeometryHelper throw 'Length mismatch for attribute "${ outAttribute.name }": source has length ${ sourceAttribute.length }, destination needs length ${ outAttribute.name }.'; } - final length:Int = outAttribute.length; - final inOffset:Int = sourceAttribute.offset; - final outOffset:Int = outAttribute.offset; + var length:Int = outAttribute.length; + var inOffset:Int = sourceAttribute.offset; + var outOffset:Int = outAttribute.offset; for (k in 0...compact.numVertices) { for (l in 0...length) { From a36df59d4c49a4b97508a684d4c33a13d760830c Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:23:35 -0400 Subject: [PATCH 12/14] Restore Flash support. --- away3d/tools/helpers/ParticleGeometryHelper.hx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/away3d/tools/helpers/ParticleGeometryHelper.hx b/away3d/tools/helpers/ParticleGeometryHelper.hx index 9f945974..574004cc 100644 --- a/away3d/tools/helpers/ParticleGeometryHelper.hx +++ b/away3d/tools/helpers/ParticleGeometryHelper.hx @@ -126,7 +126,11 @@ class ParticleGeometryHelper tempVertex.x = sourceVertices[product + inPositionDefinition.offset]; tempVertex.y = sourceVertices[product + inPositionDefinition.offset + 1]; tempVertex.z = sourceVertices[product + inPositionDefinition.offset + 2]; + #if flash + tempVertex = vertexTransform.transformVector(tempVertex); + #else vertexTransform.transformVectorToOutput(tempVertex, tempVertex); + #end vertices[startIndex + k * vertexDefinition.length + positionDefinition.offset] = tempVertex.x; vertices[startIndex + k * vertexDefinition.length + positionDefinition.offset + 1] = tempVertex.y; vertices[startIndex + k * vertexDefinition.length + positionDefinition.offset + 2] = tempVertex.z; @@ -138,7 +142,11 @@ class ParticleGeometryHelper tempNormal.x = sourceVertices[product + inNormalDefinition.offset]; tempNormal.y = sourceVertices[product + inNormalDefinition.offset + 1]; tempNormal.z = sourceVertices[product + inNormalDefinition.offset + 2]; + #if flash + tempNormal = invVertexTransform.deltaTransformVector(tempNormal); + #else invVertexTransform.deltaTransformVectorToOutput(tempNormal, tempNormal); + #end vertices[startIndex + k * vertexDefinition.length + normalDefinition.offset] = tempNormal.x; vertices[startIndex + k * vertexDefinition.length + normalDefinition.offset + 1] = tempNormal.y; vertices[startIndex + k * vertexDefinition.length + normalDefinition.offset + 2] = tempNormal.z; @@ -150,7 +158,11 @@ class ParticleGeometryHelper tempTangents.x = sourceVertices[product + inTangentDefinition.offset]; tempTangents.y = sourceVertices[product + inTangentDefinition.offset + 1]; tempTangents.z = sourceVertices[product + inTangentDefinition.offset + 2]; + #if flash + tempTangents = invVertexTransform.deltaTransformVector(tempTangents); + #else invVertexTransform.deltaTransformVectorToOutput(tempTangents, tempTangents); + #end vertices[startIndex + k * vertexDefinition.length + tangentDefinition.offset] = tempTangents.x; vertices[startIndex + k * vertexDefinition.length + tangentDefinition.offset + 1] = tempTangents.y; vertices[startIndex + k * vertexDefinition.length + tangentDefinition.offset + 2] = tempTangents.z; @@ -161,7 +173,11 @@ class ParticleGeometryHelper if (inUVDefinition != null && uvDefinition != null && UVTransform != null) { tempUV.x = sourceVertices[product + inUVDefinition.offset]; tempUV.y = sourceVertices[product + inUVDefinition.offset + 1]; + #if flash + tempUV = UVTransform.transformPoint(tempUV); + #else UVTransform.transformPointToOutput(tempUV, tempUV); + #end vertices[startIndex + k * vertexDefinition.length + uvDefinition.offset] = tempUV.x; vertices[startIndex + k * vertexDefinition.length + uvDefinition.offset + 1] = tempUV.y; From b24b14f302de6fa36ff200b6d464c2fd2340efa2 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 9 Oct 2025 00:39:39 -0400 Subject: [PATCH 13/14] Implement `VertexDefinition.toString()`. --- away3d/core/base/data/VertexDefinition.hx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index 660cbdab..abe890d5 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -74,6 +74,11 @@ class VertexDefinition } return null; } + + public inline function toString():String + { + return Std.string(attributes); + } } class AttributeDefinition @@ -111,6 +116,11 @@ class AttributeDefinition { return new AttributeDefinition(name, length); } + + public inline function toString():String + { + return name + ":FLOAT_" + length; + } } #if !haxe4 From 2187fb9dd063cfac6429f5262b2272dc9346aaf5 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 11 Jan 2026 18:41:56 -0500 Subject: [PATCH 14/14] Account for `_vertexData` being null. --- away3d/core/base/CompactSubGeometry.hx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index 6c64b8eb..c30c4e32 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -377,6 +377,11 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry return false; } + if (_vertexData == null) + { + _vertexData = new Vector(definition.length * Std.int(data.length / attribute.length)); + } + var attributeLength:Int = attribute.length; var vertexLength:Int = _vertexData.length; @@ -402,14 +407,15 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry if (positions != null) { var newLength:Int = Std.int(positions.length / 3 * definition.length); - if (newLength < _vertexData.length) + var oldLength:Int = _vertexData != null ? _vertexData.length : 0; + if (newLength < oldLength) { _vertexData = _vertexData.slice(0, newLength); } - else if (newLength > _vertexData.length) + else if (newLength > oldLength) { var newData:Vector = new Vector(newLength, true); - for (i in 0..._vertexData.length) + for (i in 0...oldLength) { newData[i] = _vertexData[i]; }