From daf58e6c4b16ad9bd4996e279d878e0fbc673142 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Jul 2025 19:09:16 -0400 Subject: [PATCH 01/42] 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 8110151c..f6087935 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 e0884ddd4986ec3ce751810f8a9f62f063d15b1f Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 24 Jul 2025 21:29:12 -0400 Subject: [PATCH 02/42] 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 f976ee8bb0b11cf4079b444e2dd1ab404ebf21ad Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 22 Aug 2025 01:18:35 -0400 Subject: [PATCH 03/42] 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 c999a15d01c9a26b813008b4467b825744138330 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:21:24 -0400 Subject: [PATCH 04/42] 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 121e79f2ae157bf7aff67a790739b9cce49d146d Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:43:53 -0400 Subject: [PATCH 05/42] 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 ea05f772e21e67eec3c9b77c48c7ebaada2b5c2e Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:47:14 -0400 Subject: [PATCH 06/42] 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 cc31178783763c3304d01701c776d8265bfa49f4 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:55:35 -0400 Subject: [PATCH 07/42] 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 c305cb991e8345da567e1ddc92d49f49d6acaee4 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 02:56:02 -0400 Subject: [PATCH 08/42] 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 8f7fdac12f058fccaf67d7625b20db781ec5342a Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:11:09 -0400 Subject: [PATCH 09/42] 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 f70eaefe6eff046328943a7d54024dfaebeb56b7 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:15:30 -0400 Subject: [PATCH 10/42] 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 20f61f5bcc4e946487b098c379f8082c13237987 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:17:38 -0400 Subject: [PATCH 11/42] 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 2af1a04f12959bbaa709d6b899f217c06115513a Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 3 Oct 2025 03:23:35 -0400 Subject: [PATCH 12/42] 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 cb627c9bbb57d80398b025c411b97caa55df451f Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 9 Oct 2025 00:39:39 -0400 Subject: [PATCH 13/42] 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 c3b893e12c8ff373cb439910dae960f65ff5f2ca Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 11 Jan 2026 18:41:56 -0500 Subject: [PATCH 14/42] 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]; } From 090a1787318e18d1ba2c1812da2f83694bdcf3b8 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Tue, 15 Sep 2026 22:26:52 -0400 Subject: [PATCH 15/42] Fix writing to the wrong object. --- away3d/core/base/data/VertexDefinition.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index abe890d5..b0f076c9 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -52,7 +52,7 @@ class VertexDefinition // elsewhere. Instead of modifying it, make a copy. if (attribute.offset != -1 && attribute.offset != length) { - attributes[index] = attribute.clone(); + attributes[index] = attribute = attribute.clone(); } attribute.offset = length; From 739e5c1c9fbe1b33ad8b6b3b8d975fe45c88e7da Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Sep 2026 21:57:51 -0400 Subject: [PATCH 16/42] Fix hidden documentation. --- away3d/materials/methods/ShadingMethodBase.hx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/away3d/materials/methods/ShadingMethodBase.hx b/away3d/materials/methods/ShadingMethodBase.hx index eb4f06ff..065a12fd 100644 --- a/away3d/materials/methods/ShadingMethodBase.hx +++ b/away3d/materials/methods/ShadingMethodBase.hx @@ -20,7 +20,14 @@ import openfl.Vector; */ class ShadingMethodBase extends NamedAssetBase { + /** + * The shared registers created by the compiler and possibly used by methods. + */ public var sharedRegisters(get, set):ShaderRegisterData; + + /** + * Any passes required that render to a texture used by this method. + */ public var passes(get, never):Vector; private var _sharedRegisters:ShaderRegisterData; @@ -54,9 +61,6 @@ class ShadingMethodBase extends NamedAssetBase } - /** - * The shared registers created by the compiler and possibly used by methods. - */ @:allow(away3d) private function get_sharedRegisters():ShaderRegisterData { return _sharedRegisters; @@ -68,9 +72,6 @@ class ShadingMethodBase extends NamedAssetBase return value; } - /** - * Any passes required that render to a texture used by this method. - */ private function get_passes():Vector { return _passes; From 7e77f27b9e5c7c9e64bd6068c2c43ded63e21b59 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Sep 2026 22:21:04 -0400 Subject: [PATCH 17/42] Implement `ShadingMethodBase.attributes`. Methods can use this to alert other classes that they require certain custom attributes. These attributes will also automatically be tracked and deduplicated. --- .../materials/compilation/ShaderCompiler.hx | 1 + .../compilation/ShaderRegisterData.hx | 6 ++- away3d/materials/methods/ShadingMethodBase.hx | 51 ++++++++++++++++++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/away3d/materials/compilation/ShaderCompiler.hx b/away3d/materials/compilation/ShaderCompiler.hx index 127a9e9e..bbb80153 100644 --- a/away3d/materials/compilation/ShaderCompiler.hx +++ b/away3d/materials/compilation/ShaderCompiler.hx @@ -1003,6 +1003,7 @@ class ShaderCompiler for (i in 0...numMethods) { method = methods[i].method; data = methods[i].data; + method.initAttributes(_registerCache); _vertexCode += method.getVertexCode(data, _registerCache); if (data.needsGlobalVertexPos || data.needsGlobalFragmentPos) _registerCache.removeVertexTempUsage(_sharedRegisters.globalPositionVertex); diff --git a/away3d/materials/compilation/ShaderRegisterData.hx b/away3d/materials/compilation/ShaderRegisterData.hx index 901faf06..5893a8cf 100644 --- a/away3d/materials/compilation/ShaderRegisterData.hx +++ b/away3d/materials/compilation/ShaderRegisterData.hx @@ -122,8 +122,10 @@ class ShaderRegisterData public var bitangent:ShaderRegisterElement; /** - * Space for named registers other than the ones above. Will be null until - * defined by an effect method. + * Space for named registers other than the ones above. Null by default. + * + * If a method defines `attributes`, this will contain the corresponding + * register elements, indexed by attribute name. */ public var custom:Map = null; diff --git a/away3d/materials/methods/ShadingMethodBase.hx b/away3d/materials/methods/ShadingMethodBase.hx index 065a12fd..1052dd0f 100644 --- a/away3d/materials/methods/ShadingMethodBase.hx +++ b/away3d/materials/methods/ShadingMethodBase.hx @@ -3,17 +3,23 @@ package away3d.materials.methods; import away3d.*; import away3d.cameras.*; import away3d.core.base.*; +import away3d.core.base.data.VertexDefinition.AttributeDefinition; import away3d.core.managers.*; import away3d.events.*; import away3d.library.assets.*; import away3d.materials.compilation.*; import away3d.materials.passes.*; import away3d.textures.*; - import openfl.display3D.Context3DTextureFormat; import openfl.display3D.Context3DTextureFilter; import openfl.Vector; +#if haxe4 +import haxe.ds.ReadOnlyArray; +#else +import away3d.core.base.data.VertexDefinition.ReadOnlyArray; +#end + /** * ShadingMethodBase provides an abstract base method for shading methods, used by compiled passes to compile * the final shading program. @@ -30,17 +36,28 @@ class ShadingMethodBase extends NamedAssetBase */ public var passes(get, never):Vector; + /** + * One or more vertex attributes (other than the five default attributes) + * used by this method. + * + * When compiling the shader, these attributes will be allocated + * automatically and stored by name in `sharedRegisters.custom`. + */ + public var attributes(get, never):ReadOnlyArray; + private var _sharedRegisters:ShaderRegisterData; private var _passes:Vector; + private var _attributes:Array; /** * Create a new ShadingMethodBase object. * @param needsNormals Defines whether or not the method requires normals. * @param needsView Defines whether or not the method requires the view direction. */ - public function new() + public function new(?attributes:Array) { super(); + this._attributes = attributes; } /** @@ -52,6 +69,31 @@ class ShadingMethodBase extends NamedAssetBase } + /** + * Allocates registers for each attribute in `attributes` + * @param regCache The register cache for the pass currently being compiled. + */ + @:allow(away3d) private function initAttributes(regCache:ShaderRegisterCache):Void + { + if (_attributes != null) { + if (sharedRegisters.custom == null) { + sharedRegisters.custom = new Map(); + } + for (attribute in _attributes) { + switch (attribute.name) { + case "position", "normal", "tangent", "UV", "secondaryUV": + continue; + default: + } + if (!sharedRegisters.custom.exists(attribute.name)) + { + sharedRegisters.custom[attribute.name] + = regCache.getFreeVertexAttribute(); + } + } + } + } + /** * Initializes unchanging shader constants using the data from a MethodVO. * @param vo The MethodVO object linking this method with the pass currently being compiled. @@ -77,6 +119,11 @@ class ShadingMethodBase extends NamedAssetBase return _passes; } + private function get_attributes():ReadOnlyArray + { + return _attributes; + } + /** * Cleans up any resources used by the current object. */ From f785d2c074122e72f83ab0a9f9ba1313eeb8af76 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Sep 2026 22:38:30 -0400 Subject: [PATCH 18/42] Implement `IRenderable.activateVertexBufferByName()`. --- away3d/core/base/CompactSubGeometry.hx | 12 ++++++------ away3d/core/base/IRenderable.hx | 8 ++++++++ away3d/core/base/ISubGeometry.hx | 8 ++++++++ away3d/core/base/SubGeometry.hx | 17 +++++++++++++++++ away3d/core/base/SubMesh.hx | 8 ++++++++ away3d/entities/SegmentSet.hx | 4 ++++ away3d/entities/Sprite3D.hx | 5 +++++ away3d/primitives/SkyBox.hx | 4 ++++ 8 files changed, 60 insertions(+), 6 deletions(-) diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index c30c4e32..5e3cda34 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", index, stage3DProxy); + activateVertexBufferByName("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", index, stage3DProxy); + activateVertexBufferByName("UV", index, stage3DProxy); } public function activateSecondaryUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("secondaryUV", index, stage3DProxy); + activateVertexBufferByName("secondaryUV", index, stage3DProxy); } private function uploadData(contextIndex:Int):Void @@ -103,15 +103,15 @@ class CompactSubGeometry extends SubGeometryBase implements ISubGeometry public function activateVertexNormalBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("normal", index, stage3DProxy); + activateVertexBufferByName("normal", index, stage3DProxy); } public function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void { - activateSpecificVertexBuffer("tangent", index, stage3DProxy); + activateVertexBufferByName("tangent", index, stage3DProxy); } - public function activateSpecificVertexBuffer(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void { var attribute:AttributeDefinition = definition.get(attributeName); if (attribute == null) diff --git a/away3d/core/base/IRenderable.hx b/away3d/core/base/IRenderable.hx index 2c8e2888..c6e5b0e2 100644 --- a/away3d/core/base/IRenderable.hx +++ b/away3d/core/base/IRenderable.hx @@ -106,6 +106,14 @@ interface IRenderable extends IMaterialOwner */ function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void; + /** + * Assigns the attribute stream for the specified custom attribute + * @param attributeName The name of the attribute to activate + * @param index The attribute stream index for the vertex shader + * @param stage3DProxy The Stage3DProxy to assign the stream to + */ + function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void; + /** * Retrieves the IndexBuffer3D object that contains triangle indices. * @param context The Context3D for which we request the buffer diff --git a/away3d/core/base/ISubGeometry.hx b/away3d/core/base/ISubGeometry.hx index 030ba1cf..a8a1a6f1 100644 --- a/away3d/core/base/ISubGeometry.hx +++ b/away3d/core/base/ISubGeometry.hx @@ -81,6 +81,14 @@ interface ISubGeometry */ function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void; + /** + * Assigns the attribute stream for the specified custom attribute + * @param attributeName The name of the attribute to activate + * @param index The attribute stream index for the vertex shader + * @param stage3DProxy The Stage3DProxy to assign the stream to + */ + function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void; + /** * Retrieves the IndexBuffer3D object that contains triangle indices. * @param context The Context3D for which we request the buffer diff --git a/away3d/core/base/SubGeometry.hx b/away3d/core/base/SubGeometry.hx index 7c04cd77..282869dd 100644 --- a/away3d/core/base/SubGeometry.hx +++ b/away3d/core/base/SubGeometry.hx @@ -184,6 +184,23 @@ class SubGeometry extends SubGeometryBase implements ISubGeometry context.setVertexBufferAt(index, _vertexTangentBuffer[contextIndex], 0, Context3DVertexBufferFormat.FLOAT_3); } + public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + { + switch (attributeName) { + case "position": + activateVertexBuffer(index, stage3DProxy); + case "normal": + activateVertexNormalBuffer(index, stage3DProxy); + case "tangent": + activateVertexTangentBuffer(index, stage3DProxy); + case "UV": + activateUVBuffer(index, stage3DProxy); + case "secondaryUV": + activateSecondaryUVBuffer(index, stage3DProxy); + default: + } + } + override public function applyTransformation(transform:Matrix3D):Void { super.applyTransformation(transform); diff --git a/away3d/core/base/SubMesh.hx b/away3d/core/base/SubMesh.hx index 2bccdd5d..831c762b 100644 --- a/away3d/core/base/SubMesh.hx +++ b/away3d/core/base/SubMesh.hx @@ -346,6 +346,14 @@ class SubMesh implements IRenderable _subGeometry.activateSecondaryUVBuffer(index, stage3DProxy); } + /** + * @inheritDoc + */ + public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + { + _subGeometry.activateVertexBufferByName(attributeName, index, stage3DProxy); + } + /** * @inheritDoc */ diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index 7bed599b..70111f88 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -387,6 +387,10 @@ class SegmentSet extends Entity implements IRenderable { } + public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + { + } + private function reOrderIndices(subSetIndex:Int, index:Int):Void { var segRef:SegRef; diff --git a/away3d/entities/Sprite3D.hx b/away3d/entities/Sprite3D.hx index 5442db2e..e4695d8d 100644 --- a/away3d/entities/Sprite3D.hx +++ b/away3d/entities/Sprite3D.hx @@ -163,6 +163,11 @@ class Sprite3D extends Entity implements IRenderable _geometry.activateVertexTangentBuffer(index, stage3DProxy); } + public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + { + _geometry.activateVertexBufferByName(attributeName, index, stage3DProxy); + } + public function getIndexBuffer(stage3DProxy:Stage3DProxy):IndexBuffer3D { return _geometry.getIndexBuffer(stage3DProxy); diff --git a/away3d/primitives/SkyBox.hx b/away3d/primitives/SkyBox.hx index 305a1a44..e20169b9 100644 --- a/away3d/primitives/SkyBox.hx +++ b/away3d/primitives/SkyBox.hx @@ -109,6 +109,10 @@ class SkyBox extends Entity implements IRenderable { } + public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void + { + } + /** * @inheritDoc */ From 663ae421b8d8ce34838e699c06039f822cc39ec0 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Sep 2026 22:39:14 -0400 Subject: [PATCH 19/42] Automatically upload custom attributes. --- away3d/materials/methods/ShadingMethodBase.hx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/away3d/materials/methods/ShadingMethodBase.hx b/away3d/materials/methods/ShadingMethodBase.hx index 1052dd0f..0422356a 100644 --- a/away3d/materials/methods/ShadingMethodBase.hx +++ b/away3d/materials/methods/ShadingMethodBase.hx @@ -42,6 +42,11 @@ class ShadingMethodBase extends NamedAssetBase * * When compiling the shader, these attributes will be allocated * automatically and stored by name in `sharedRegisters.custom`. + * + * When rendering, this will call `IRenderable.activateVertexBufferByName()` + * for each attribute. This will upload the corresponding buffers if found; + * currently this is only supported by `CompactSubGeometry`, and the user is + * responsible for including the attributes in its `VertexDefinition`. */ public var attributes(get, never):ReadOnlyArray; @@ -189,7 +194,14 @@ class ShadingMethodBase extends NamedAssetBase */ @:allow(away3d) private function setRenderState(vo:MethodVO, renderable:IRenderable, stage3DProxy:Stage3DProxy, camera:Camera3D):Void { - + if (_attributes != null && sharedRegisters.custom != null) { + for (attribute in _attributes) { + var element:ShaderRegisterElement = sharedRegisters.custom[attribute.name]; + if (element != null) { + renderable.activateVertexBufferByName(attribute.name, element.index, stage3DProxy); + } + } + } } /** From 692b421a712d3ca82925037b789fa6425b1b1201 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 20 Sep 2026 22:51:35 -0400 Subject: [PATCH 20/42] Pass through constructor argument. --- away3d/materials/methods/EffectMethodBase.hx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/away3d/materials/methods/EffectMethodBase.hx b/away3d/materials/methods/EffectMethodBase.hx index 68d75c5a..00ee88e7 100644 --- a/away3d/materials/methods/EffectMethodBase.hx +++ b/away3d/materials/methods/EffectMethodBase.hx @@ -1,6 +1,7 @@ package away3d.materials.methods; import away3d.*; +import away3d.core.base.data.VertexDefinition.AttributeDefinition; import away3d.errors.*; import away3d.library.assets.*; import away3d.materials.compilation.*; @@ -13,9 +14,9 @@ class EffectMethodBase extends ShadingMethodBase implements IAsset { public var assetType(get, never):String; - public function new() + public function new(?attributes:Array) { - super(); + super(attributes); } /** From 518cf7be1d89725113c762ffacb274d56208557c Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 2 Oct 2026 22:04:58 -0400 Subject: [PATCH 21/42] Fix formatting. --- away3d/core/base/data/VertexDefinition.hx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index b0f076c9..6cfdfddf 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -44,14 +44,12 @@ class VertexDefinition 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 (attribute.offset != -1 && attribute.offset != length) { attributes[index] = attribute = attribute.clone(); } @@ -65,16 +63,14 @@ class VertexDefinition public function get(attributeName:String):AttributeDefinition { - for (attribute in attributes) - { - if (attribute.name == attributeName) - { + for (attribute in attributes) { + if (attribute.name == attributeName) { return attribute; } } return null; } - + public inline function toString():String { return Std.string(attributes); @@ -97,8 +93,7 @@ class AttributeDefinition this.name = name; this.length = length; - vertexBufferFormat = switch (length) - { + vertexBufferFormat = switch (length) { case 1: FLOAT_1; case 2: From b57315ed03aa405a4b2a1bb4cbe83e02a9e262ae Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Fri, 2 Oct 2026 22:23:59 -0400 Subject: [PATCH 22/42] Implement `VertexDefinition.merge()`. --- 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 6cfdfddf..a31ff6f0 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -31,6 +31,26 @@ class VertexDefinition */ public static var defaultVertexDefinition(default, null):VertexDefinition = new VertexDefinition(defaultAttributes); + /** + * Creates a new vertex definition containing all attributes from both + * `definitionA` and `definitionB`. If the same attribute is found in both, + * only the longer version is included. + */ + public static function merge(definitionA:VertexDefinition, definitionB:VertexDefinition):VertexDefinition + { + var attributes:Array = definitionA.attributes.copy(); + for (attributeB in definitionB.attributes) { + var attributeA:AttributeDefinition = definitionA.get(attributeB.name); + if (attributeA == null) { + attributes.push(attributeB); + } else if (attributeA.length < attributeB.length) { + var index:Int = attributes.indexOf(attributeA); + attributes[index >= 0 ? index : attributes.length] = attributeB; + } + } + return new VertexDefinition(attributes); + } + public var attributes(default, null):ReadOnlyArray; /** From 0775489c3bd91487ff9e041c294d39c4294049fe Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 19:16:54 -0400 Subject: [PATCH 23/42] Implement `VertexDefinition.concatUnique()`. This version avoids unnecessary allocations, and can be chained. --- away3d/core/base/data/VertexDefinition.hx | 47 +++++++++++++---------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/away3d/core/base/data/VertexDefinition.hx b/away3d/core/base/data/VertexDefinition.hx index a31ff6f0..7da309f8 100644 --- a/away3d/core/base/data/VertexDefinition.hx +++ b/away3d/core/base/data/VertexDefinition.hx @@ -31,26 +31,6 @@ class VertexDefinition */ public static var defaultVertexDefinition(default, null):VertexDefinition = new VertexDefinition(defaultAttributes); - /** - * Creates a new vertex definition containing all attributes from both - * `definitionA` and `definitionB`. If the same attribute is found in both, - * only the longer version is included. - */ - public static function merge(definitionA:VertexDefinition, definitionB:VertexDefinition):VertexDefinition - { - var attributes:Array = definitionA.attributes.copy(); - for (attributeB in definitionB.attributes) { - var attributeA:AttributeDefinition = definitionA.get(attributeB.name); - if (attributeA == null) { - attributes.push(attributeB); - } else if (attributeA.length < attributeB.length) { - var index:Int = attributes.indexOf(attributeA); - attributes[index >= 0 ? index : attributes.length] = attributeB; - } - } - return new VertexDefinition(attributes); - } - public var attributes(default, null):ReadOnlyArray; /** @@ -81,6 +61,33 @@ class VertexDefinition this.attributes = attributes; } + /** + * Returns a vertex definition containing this definition's attributes plus + * all unique attributes from the given array(s). If the same attribute is + * found multiple times, only the longer version will be included. If this + * already contains everything from `attributes`, it is returned unchanged. + */ + public function concatUnique(attributes:ReadOnlyArray):VertexDefinition + { + var result:Array = null; + for (newAttribute in attributes) { + var existing:AttributeDefinition = get(newAttribute.name); + if (existing != null && existing.length >= newAttribute.length) { + continue; + } + if (result == null) { + result = this.attributes.copy(); + } + if (existing == null) { + result.push(newAttribute); + } else { + var index:Int = result.indexOf(existing); + result[index >= 0 ? index : result.length] = newAttribute; + } + } + return result == null ? this : new VertexDefinition(result); + } + public function get(attributeName:String):AttributeDefinition { for (attribute in attributes) { From f4a884c8e5797e5d2a658206f56b15736120ac3a Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 20:37:47 -0400 Subject: [PATCH 24/42] Fix null pointer error. --- away3d/tools/helpers/ParticleGeometryHelper.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/away3d/tools/helpers/ParticleGeometryHelper.hx b/away3d/tools/helpers/ParticleGeometryHelper.hx index 574004cc..b429e1d6 100644 --- a/away3d/tools/helpers/ParticleGeometryHelper.hx +++ b/away3d/tools/helpers/ParticleGeometryHelper.hx @@ -46,6 +46,9 @@ class ParticleGeometryHelper var tempTangents:Vector3D = new Vector3D(); var tempUV:Point = new Point(); + if (vertexDefinition == null) { + vertexDefinition = VertexDefinition.defaultVertexDefinition; + } var positionDefinition:AttributeDefinition = vertexDefinition.get("position"); var normalDefinition:AttributeDefinition = vertexDefinition.get("normal"); var tangentDefinition:AttributeDefinition = vertexDefinition.get("tangent"); From e588fd030183f1127943e8091d5b20367c968752 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 24 Sep 2026 21:36:21 -0400 Subject: [PATCH 25/42] Reset shared registers when recompiling. --- .../materials/compilation/ShaderCompiler.hx | 2 +- .../compilation/ShaderRegisterData.hx | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/away3d/materials/compilation/ShaderCompiler.hx b/away3d/materials/compilation/ShaderCompiler.hx index bbb80153..a81848eb 100644 --- a/away3d/materials/compilation/ShaderCompiler.hx +++ b/away3d/materials/compilation/ShaderCompiler.hx @@ -310,7 +310,7 @@ class ShaderCompiler _vertexCode = ""; _fragmentCode = ""; - _sharedRegisters.custom = null; + _sharedRegisters.reset(); _sharedRegisters.localPosition = _registerCache.getFreeVertexVectorTemp(); _registerCache.addVertexTempUsages(_sharedRegisters.localPosition, 1); diff --git a/away3d/materials/compilation/ShaderRegisterData.hx b/away3d/materials/compilation/ShaderRegisterData.hx index 5893a8cf..87ea0d9a 100644 --- a/away3d/materials/compilation/ShaderRegisterData.hx +++ b/away3d/materials/compilation/ShaderRegisterData.hx @@ -133,4 +133,28 @@ class ShaderRegisterData { } + + public function reset():Void + { + normalVarying = null; + tangentVarying = null; + bitangentVarying = null; + uvVarying = null; + secondaryUVVarying = null; + viewDirVarying = null; + shadedTarget = null; + globalPositionVertex = null; + globalPositionVarying = null; + localPosition = null; + normalInput = null; + tangentInput = null; + animatedNormal = null; + animatedTangent = null; + commons = null; + projectionFragment = null; + normalFragment = null; + viewDirFragment = null; + bitangent = null; + custom = null; + } } From 3bb7db1e1ef52001105d43e63f016a3c50fdc38e Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 24 Sep 2026 21:38:02 -0400 Subject: [PATCH 26/42] Note when `localPosition` is and isn't used. --- away3d/materials/compilation/ShaderRegisterData.hx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/away3d/materials/compilation/ShaderRegisterData.hx b/away3d/materials/compilation/ShaderRegisterData.hx index 87ea0d9a..896f417d 100644 --- a/away3d/materials/compilation/ShaderRegisterData.hx +++ b/away3d/materials/compilation/ShaderRegisterData.hx @@ -53,6 +53,9 @@ class ShaderRegisterData /** * Vertex temp. Contains the vertex position in world space. To use this, * set either `needsGlobalVertexPos` or `needsGlobalFragmentPos`. + * + * If enabled (i.e., not null), this will be used instead of `localPosition` + * to calculate the vertex's screen position. */ public var globalPositionVertex:ShaderRegisterElement; @@ -64,6 +67,9 @@ class ShaderRegisterData /** * Vertex temp. Contains the vertex position in local space. + * + * By default, this will be used to calculate the vertex's screen position. + * However, `globalPositionVertex` will be used instead if enabled. */ public var localPosition:ShaderRegisterElement; From ea066ac72702ded626a88d94aaaa14e748820911 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Thu, 24 Sep 2026 21:43:25 -0400 Subject: [PATCH 27/42] Expose `sceneTransform` register to effect methods. Technically, the register only contains the first part of the matrix, and the next three registers contain the rest, but I didn't want to make the comment longer. --- away3d/materials/compilation/ShaderCompiler.hx | 6 +++--- away3d/materials/compilation/ShaderRegisterData.hx | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/away3d/materials/compilation/ShaderCompiler.hx b/away3d/materials/compilation/ShaderCompiler.hx index a81848eb..8f4b3bf5 100644 --- a/away3d/materials/compilation/ShaderCompiler.hx +++ b/away3d/materials/compilation/ShaderCompiler.hx @@ -446,13 +446,13 @@ class ShaderCompiler { _sharedRegisters.globalPositionVertex = _registerCache.getFreeVertexVectorTemp(); _registerCache.addVertexTempUsages(_sharedRegisters.globalPositionVertex, _dependencyCounter.globalPosDependencies); - var positionMatrixReg:ShaderRegisterElement = _registerCache.getFreeVertexConstant(); + _sharedRegisters.sceneTransform = _registerCache.getFreeVertexConstant(); _registerCache.getFreeVertexConstant(); _registerCache.getFreeVertexConstant(); _registerCache.getFreeVertexConstant(); - _sceneMatrixIndex = positionMatrixReg.index*4; + _sceneMatrixIndex = _sharedRegisters.sceneTransform.index*4; - _vertexCode += "m44 " + _sharedRegisters.globalPositionVertex + ", " + _sharedRegisters.localPosition + ", " + positionMatrixReg + "\n"; + _vertexCode += "m44 " + _sharedRegisters.globalPositionVertex + ", " + _sharedRegisters.localPosition + ", " + _sharedRegisters.sceneTransform + "\n"; if (_dependencyCounter.usesGlobalPosFragment) { _sharedRegisters.globalPositionVarying = _registerCache.getFreeVarying(); diff --git a/away3d/materials/compilation/ShaderRegisterData.hx b/away3d/materials/compilation/ShaderRegisterData.hx index 896f417d..bf6fcc0c 100644 --- a/away3d/materials/compilation/ShaderRegisterData.hx +++ b/away3d/materials/compilation/ShaderRegisterData.hx @@ -73,6 +73,16 @@ class ShaderRegisterData */ public var localPosition:ShaderRegisterElement; + /** + * Vertex constant. Contains the scene transform matrix, which converts + * from local to world space. To use this, set either `needsGlobalVertexPos` + * or `needsGlobalFragmentPos`. + * + * If this is enabled, `vc0` will contain only the view projection matrix. + * Otherwise, `vc0` will store both matrices combined. + */ + public var sceneTransform:ShaderRegisterElement; + /** * Vertex attribute. Contains the normal to the vertex, in local space. Not * assumed to be normalized. @@ -152,6 +162,7 @@ class ShaderRegisterData globalPositionVertex = null; globalPositionVarying = null; localPosition = null; + sceneTransform = null; normalInput = null; tangentInput = null; animatedNormal = null; From 90344587bdbfcf2ed6405f06e8643e472d764a2e Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Wed, 30 Sep 2026 16:34:55 -0400 Subject: [PATCH 28/42] Implement `getClipSpaceVertexCode()` to let methods modify the final vertex position. --- .../materials/compilation/ShaderCompiler.hx | 39 +++++++++++++++---- .../compilation/ShaderRegisterData.hx | 9 +++++ away3d/materials/methods/ShadingMethodBase.hx | 13 +++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/away3d/materials/compilation/ShaderCompiler.hx b/away3d/materials/compilation/ShaderCompiler.hx index 8f4b3bf5..ac8c1170 100644 --- a/away3d/materials/compilation/ShaderCompiler.hx +++ b/away3d/materials/compilation/ShaderCompiler.hx @@ -446,6 +446,7 @@ class ShaderCompiler { _sharedRegisters.globalPositionVertex = _registerCache.getFreeVertexVectorTemp(); _registerCache.addVertexTempUsages(_sharedRegisters.globalPositionVertex, _dependencyCounter.globalPosDependencies); + _sharedRegisters.sceneTransform = _registerCache.getFreeVertexConstant(); _registerCache.getFreeVertexConstant(); _registerCache.getFreeVertexConstant(); @@ -465,17 +466,22 @@ class ShaderCompiler */ private function compileProjectionCode():Void { + _sharedRegisters.clipSpacePosition = _registerCache.getFreeVertexVectorTemp(); + _registerCache.addVertexTempUsages(_sharedRegisters.clipSpacePosition, 1); + var pos:String = _dependencyCounter.globalPosDependencies > 0 || _forceSeperateMVP? _sharedRegisters.globalPositionVertex.toString() : _animationTargetRegisters[0]; - var code:String; + var projected:String = _sharedRegisters.clipSpacePosition.toString(); + _vertexCode += 'm44 $projected, $pos, vc0 \n'; + + compileMethodsClipSpace(); if (_dependencyCounter.projectionDependencies > 0) { - code = "m44 vt5, " + pos + ", vc0 \n" + - "mov " + _sharedRegisters.projectionFragment + ", vt5\n" + - "mov op, vt5\n"; - } else - code = "m44 op, " + pos + ", vc0 \n"; + _vertexCode += 'mov ${_sharedRegisters.projectionFragment}, $projected\n'; + } + + _vertexCode += 'mov op, $projected\n'; - _vertexCode += code; + _registerCache.removeVertexTempUsage(_sharedRegisters.clipSpacePosition); } /** @@ -984,7 +990,7 @@ class ShaderCompiler } /** - * Compiles the code for the methods. + * Compiles the code for the methods, except post-projection code. */ private function compileMethods():Void { @@ -1026,6 +1032,23 @@ class ShaderCompiler } } + /** + * Compiles the methods' post-projection code (`getClipSpaceVertexCode()`). + */ + private function compileMethodsClipSpace():Void + { + var methods:Vector = _methodSetup._methods; + var numMethods:Int = methods.length; + var method:EffectMethodBase; + var data:MethodVO; + + for (i in 0...numMethods) { + method = methods[i].method; + data = methods[i].data; + _vertexCode += method.getClipSpaceVertexCode(data, _registerCache); + } + } + /** * Indices for the light probe diffuse textures. */ diff --git a/away3d/materials/compilation/ShaderRegisterData.hx b/away3d/materials/compilation/ShaderRegisterData.hx index bf6fcc0c..9f2f214b 100644 --- a/away3d/materials/compilation/ShaderRegisterData.hx +++ b/away3d/materials/compilation/ShaderRegisterData.hx @@ -73,6 +73,15 @@ class ShaderRegisterData */ public var localPosition:ShaderRegisterElement; + /** + * Vertex temp. Contains the vertex position in clip space. Only available + * during `getClipSpaceVertexCode()`. + * + * To convert this from clip space to screen space, divide `xyz` by `w`. Be + * sure to multiply by `w` again later, because OpenGL expects clip space. + */ + public var clipSpacePosition:ShaderRegisterElement; + /** * Vertex constant. Contains the scene transform matrix, which converts * from local to world space. To use this, set either `needsGlobalVertexPos` diff --git a/away3d/materials/methods/ShadingMethodBase.hx b/away3d/materials/methods/ShadingMethodBase.hx index 0422356a..e2b740b7 100644 --- a/away3d/materials/methods/ShadingMethodBase.hx +++ b/away3d/materials/methods/ShadingMethodBase.hx @@ -172,6 +172,19 @@ class ShadingMethodBase extends NamedAssetBase return ""; } + /** + * Get additional vertex shader code for this method. This code will be run + * after projecting the vertex to clip space, and can access and modify + * `sharedRegisters.clipSpacePosition`. + * @param vo The MethodVO object linking this method with the pass currently being compiled. + * @param regCache The register cache used during the compilation. + * @private + */ + @:allow(away3d) private function getClipSpaceVertexCode(vo:MethodVO, regCache:ShaderRegisterCache):String + { + return ""; + } + /** * Sets the render state for this method. * From 0f17edc8cb1a47263e74a8ebd15982185532756a Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Wed, 30 Sep 2026 16:32:25 -0400 Subject: [PATCH 29/42] Reset `ShaderCompiler`'s cache on each compile. This currently doesn't matter because compilers are never reused, but that could change someday. --- away3d/materials/compilation/ShaderCompiler.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/away3d/materials/compilation/ShaderCompiler.hx b/away3d/materials/compilation/ShaderCompiler.hx index ac8c1170..576d5952 100644 --- a/away3d/materials/compilation/ShaderCompiler.hx +++ b/away3d/materials/compilation/ShaderCompiler.hx @@ -198,7 +198,6 @@ class ShaderCompiler { _registerCache = new ShaderRegisterCache(profile); _registerCache.vertexAttributesOffset = 1; - _registerCache.reset(); } /** @@ -311,6 +310,7 @@ class ShaderCompiler _fragmentCode = ""; _sharedRegisters.reset(); + _registerCache.reset(); _sharedRegisters.localPosition = _registerCache.getFreeVertexVectorTemp(); _registerCache.addVertexTempUsages(_sharedRegisters.localPosition, 1); From 67950a4c7fffb25fa4cee1794478e30a3683feaa Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 00:14:58 -0400 Subject: [PATCH 30/42] Re-implement `SegmentSet` using an effect method. This allows applying other materials and methods to the set, though these interactions are untested and not well supported. Per-segment colors are temporarily disabled, though `ColorMaterial` can be used to set a color globally. This also fixes some bugs with the line width. This means the lines are no longer twice as wide as `thickness` (though a temporary `-D away3d_double_line_width` flag exists to reverse this). --- away3d/core/base/CompactSubGeometry.hx | 6 + away3d/core/partition/NodeBase.hx | 2 +- away3d/entities/SegmentSet.hx | 855 +++++++------------- away3d/materials/OrthoSegmentMaterial.hx | 27 - away3d/materials/methods/SegmentMethod.hx | 118 +++ away3d/materials/passes/OrthoSegmentPass.hx | 178 ---- away3d/primitives/WireframePrimitiveBase.hx | 8 +- away3d/primitives/data/Segment.hx | 32 +- 8 files changed, 419 insertions(+), 807 deletions(-) delete mode 100644 away3d/materials/OrthoSegmentMaterial.hx create mode 100644 away3d/materials/methods/SegmentMethod.hx delete mode 100644 away3d/materials/passes/OrthoSegmentPass.hx diff --git a/away3d/core/base/CompactSubGeometry.hx b/away3d/core/base/CompactSubGeometry.hx index 5e3cda34..65ce4589 100644 --- a/away3d/core/base/CompactSubGeometry.hx +++ b/away3d/core/base/CompactSubGeometry.hx @@ -12,6 +12,12 @@ import openfl.Vector; class CompactSubGeometry extends SubGeometryBase implements ISubGeometry { + /** + * The maximum number of vertices in a single subgeometry. Any more than + * this can't be indexed, because indices are unsigned shorts. + */ + public static inline var MAX_NUM_VERTICES:Int = 65536; + public var definition(default, null):VertexDefinition; public var numVertices(get, never):Int; diff --git a/away3d/core/partition/NodeBase.hx b/away3d/core/partition/NodeBase.hx index 6297322b..4acee672 100644 --- a/away3d/core/partition/NodeBase.hx +++ b/away3d/core/partition/NodeBase.hx @@ -171,7 +171,7 @@ class NodeBase _childNodes[i++].acceptTraverser(traverser); if (_debugPrimitive != null) - traverser.applyRenderable(_debugPrimitive); + traverser.applyEntity(_debugPrimitive); } } diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index 70111f88..c6f93077 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -1,680 +1,389 @@ package away3d.entities; -import away3d.animators.IAnimator; -import away3d.bounds.BoundingSphere; -import away3d.bounds.BoundingVolumeBase; import away3d.cameras.Camera3D; -import away3d.core.base.IRenderable; -import away3d.core.managers.Stage3DProxy; -import away3d.core.partition.EntityNode; -import away3d.core.partition.RenderableNode; -import away3d.materials.MaterialBase; -import away3d.materials.SegmentMaterial; -import away3d.primitives.LineSegment; -import away3d.primitives.data.Segment; +import away3d.core.base.CompactSubGeometry; +import away3d.core.base.data.VertexDefinition; +import away3d.core.base.Geometry; +import away3d.core.base.ISubGeometry; +import away3d.entities.Entity; +import away3d.entities.Mesh; +import away3d.events.GeometryEvent; import away3d.library.assets.Asset3DType; - -import openfl.display3D.Context3D; -import openfl.display3D.Context3DVertexBufferFormat; -import openfl.display3D.IndexBuffer3D; -import openfl.display3D.VertexBuffer3D; +import away3d.materials.methods.SegmentMethod; +import away3d.materials.SinglePassMaterialBase; +import away3d.materials.utils.DefaultMaterialManager; +import away3d.primitives.data.Segment; import openfl.geom.Matrix; import openfl.geom.Matrix3D; import openfl.geom.Vector3D; import openfl.Vector; -class SegmentSet extends Entity implements IRenderable -{ - private static var LIMIT:Int = 3*0xFFFF; - +class SegmentSet extends Mesh { + public static var requiredAttributes(get, null):VertexDefinition; + private static function get_requiredAttributes():VertexDefinition { + if (requiredAttributes == null) { + requiredAttributes = new VertexDefinition([ + new AttributeDefinition("position", 3), + new AttributeDefinition("opposite", 3), + new AttributeDefinition("halfWidth", 1) + ]); + } + return requiredAttributes; + } + public var segmentCount(get, never):Int; - @:allow(away3d) private var subSetCount(get, never):Int; - @:allow(away3d) private var hasData(get, never):Bool; - public var numTriangles(get, never):Int; - public var sourceEntity(get, never):Entity; - public var castsShadows(get, never):Bool; - public var material(get, set):MaterialBase; - public var animator(get, never):IAnimator; - public var uvTransform(get, never):Matrix; - public var uvTransform2(get, never):Matrix; - public var vertexData(get, never):Vector; - public var indexData(get, never):Vector; - public var UVData(get, never):Vector; - public var numVertices(get, never):Int; - public var vertexStride(get, never):Int; - public var vertexNormalData(get, never):Vector; - public var vertexTangentData(get, never):Vector; - public var vertexOffset(get, never):Int; - public var vertexNormalOffset(get, never):Int; - public var vertexTangentOffset(get, never):Int; - - private var _activeSubSet:SubSet; - private var _subSets:Vector; - private var _subSetCount:Int = 0; - private var _numIndices:Int = 0; - private var _material:MaterialBase; - private var _animator:IAnimator; - private var _hasData:Bool; - - private var _segments:Map; - private var _indexSegments:Int = 0; - - /** - * Creates a new SegmentSet object. - */ - public function new() - { - super(); - - _subSetCount = 0; - _subSets = new Vector(); - addSubSet(); - - _segments = new Map(); - material = new SegmentMaterial(); + private inline function get_segmentCount():Int { + var count:Int = 0; + for (subGeometry in _subGeometries) { + count += subGeometry.numVertices >> 2; + } + return count; } - - /** - * Adds a new segment to the SegmentSet. - * - * @param segment the segment to add - */ + + private var _segments:Array; + + private var _subGeometries:Array; + + private var _vertexDefinition:VertexDefinition; + private var _positionOffset:Int; + private var _oppositeOffset:Int; + private var _halfWidthOffset:Int; + private var _colorOffset:Int = -1; + + public function new(?material:SinglePassMaterialBase, ?vertexDefinition:VertexDefinition) { + if (material == null) { + material = DefaultMaterialManager.getDefaultMaterial(this); + } + material.addMethod(new SegmentMethod()); + + super(new Geometry(), material); + + _segments = []; + _subGeometries = []; + + _castsShadows = false; + _mouseEnabled = false; + + if (vertexDefinition == null) { + vertexDefinition = VertexDefinition.defaultVertexDefinition; + } + _vertexDefinition = VertexDefinition.merge(vertexDefinition, requiredAttributes); + + _positionOffset = _vertexDefinition.get("position").offset; + _oppositeOffset = _vertexDefinition.get("opposite").offset; + _halfWidthOffset = _vertexDefinition.get("halfWidth").offset; + var color:AttributeDefinition = _vertexDefinition.get("color"); + if (color != null) _colorOffset = color.offset; + } + public function addSegment(segment:Segment):Void { - segment.segmentsBase = this; - - _hasData = true; - - var subSetIndex:Int = _subSets.length - 1; - var subSet:SubSet = _subSets[subSetIndex]; - - if (subSet.vertices.length + 44 > LIMIT) { - subSet = addSubSet(); - subSetIndex++; + var subGeometry:CompactSubGeometry = _subGeometries[_subGeometries.length - 1]; + + if (subGeometry == null || subGeometry.numVertices + 4 * _vertexDefinition.length >= CompactSubGeometry.MAX_NUM_VERTICES) { + subGeometry = new CompactSubGeometry(_vertexDefinition); + _subGeometries.push(subGeometry); + geometry.addSubGeometry(subGeometry); } - - segment.index = subSet.vertices.length; - segment.subSetIndex = subSetIndex; - + + var index:Int = subGeometry.numVertices; + + segment.index = _segments.length; + _segments.push(segment); + segment.subSetIndex = _subGeometries.length - 1; + updateSegment(segment); - - var index:Int = subSet.lineCount << 2; - - subSet.indices.push(index); - subSet.indices.push(index + 1); - subSet.indices.push(index + 2); - subSet.indices.push(index + 3); - subSet.indices.push(index + 2); - subSet.indices.push(index + 1); - subSet.numVertices = Std.int(subSet.vertices.length / 11); - subSet.numIndices = subSet.indices.length; - subSet.lineCount++; - subSet.indexBufferDirty=true; - - var segRef:SegRef = new SegRef(); - segRef.index = index; - segRef.subSetIndex = subSetIndex; - segRef.segment = segment; - - _segments.set(_indexSegments, segRef); - - _indexSegments++; + + var indices:Vector = subGeometry.indexData; + if (indices == null) indices = new Vector(); + indices.push(index + 0); + indices.push(index + 1); + indices.push(index + 2); + indices.push(index + 3); + indices.push(index + 2); + indices.push(index + 1); + subGeometry.updateIndexData(indices); } - - /** - * Removes a segment from the SegmentSet by its index in the set. - * - * @param index The index of the segment to remove - * @param dispose If the segment must be disposed as well. Default is false; - * - * Removing a Segment by an index when segment is unknown - * index of the segment is relative to the order it was added to the segmentSet. - * If a segment was removed from or added to the segmentSet, a segment index may have changed. - * The index of each Segment is updated when one is added or removed. - * If 2 segments are added, segment #1 has index 0, segment #2 has index 1 - * if segment #1 is removed, segment#2 will get index 0 instead of 1. - */ - public function removeSegmentByIndex(index:Int, dispose:Bool = false):Void + + override public function dispose():Void { - var segRef:SegRef; - if (index >= _indexSegments) - return; - - if (_segments.exists(index)) - segRef = _segments[index]; - else - return; - - var subSet:SubSet; - if (_subSets[segRef.subSetIndex] == null) - return; - var subSetIndex:Int = segRef.subSetIndex; - subSet = _subSets[segRef.subSetIndex]; - - var segment:Segment = segRef.segment; - var indices:Vector = subSet.indices; - - var ind:Int = index*6; - for (i in ind...indices.length) - indices[i] -= 4; - - subSet.indices.splice(index*6, 6); - subSet.vertices.splice(index*44, 44); - subSet.numVertices = Std.int(subSet.vertices.length/11); - subSet.numIndices = indices.length; - subSet.vertexBufferDirty = true; - subSet.indexBufferDirty = true; - subSet.lineCount--; - - if (dispose) { - segment.dispose(); - segment = null; - - } else { - segment.index = -1; - segment.segmentsBase = null; - } - - if (subSet.lineCount == 0) { - - if (subSetIndex == 0) - _hasData = false; - - else { - subSet.dispose(); - _subSets[subSetIndex] = null; - _subSets.splice(subSetIndex, 1); + removeAllSegments(); + super.dispose(); + _segments = null; + _material = null; + _subGeometries = null; + } + + override private function onSubGeometryRemoved(event:GeometryEvent):Void + { + for (i => subGeometry in _subGeometries) { + if (subGeometry == event.subGeometry) { + _subGeometries.splice(i, 1); + break; } } - - reOrderIndices(subSetIndex, index); - - segRef = null; - _segments.remove(_indexSegments); - _indexSegments--; + + super.onSubGeometryRemoved(event); } - - /** - * Removes a segment from the SegmentSet. - * - * @param segment The segment to remove - * @param dispose If the segment must be disposed as well. Default is false; - */ - public function removeSegment(segment:Segment, dispose:Bool = false):Void + + public function getSegment(index:Int):Segment { - if (segment.index == -1) - return; - removeSegmentByIndex(Std.int(segment.index/44)); + return _segments[index]; } - + /** - * Empties the segmentSet from all its segments data + * Removes all line segments from this set. */ public function removeAllSegments():Void { - var subSet:SubSet; - for (i in 0..._subSetCount) { - subSet = _subSets[i]; - subSet.vertices = null; - subSet.indices = null; - if (subSet.vertexBuffer != null) - Stage3DProxy.disposeVertexBuffer(subSet.vertexBuffer); - if (subSet.indexBuffer != null) - Stage3DProxy.disposeIndexBuffer(subSet.indexBuffer); - subSet = null; + var subGeometries:Vector = geometry.subGeometries; + while (subGeometries.length > 0) { + subGeometries.pop().dispose(); } - - for (segReg in _segments) - segReg = null; - _segments = null; - - _subSetCount = 0; - _activeSubSet = null; - _indexSegments = 0; - _subSets = new Vector(); - _segments = new Map(); - - addSubSet(); - - _hasData = false; + _subGeometries = []; + + while (_subMeshes.length > 0) { + _subMeshes.pop().dispose(); + } + + invalidateBounds(); } - + /** - * @returns a segment object from a given index. + * Removes a segment from the SegmentSet. + * + * @param segment The segment to remove + * @param dispose Whether to dispose the segment */ - public function getSegment(index:Int):Segment + public function removeSegment(segment:Segment, dispose:Bool = false):Void { - if (index > _indexSegments - 1) - return null; - - return _segments[index].segment; + if (segment.index >= 0) + { + removeSegmentByIndex(Std.int(segment.index / (4 * _vertexDefinition.length)), dispose); + } } - + /** - * @returns howmany segments are in the SegmentSet + * Removes a segment from the SegmentSet by its index in the set. All + * following segments will have their index decreased. + * + * @param index The index of the segment to remove + * @param dispose Whether to dispose the removed segment */ - private function get_segmentCount():Int + public function removeSegmentByIndex(index:Int, dispose:Bool = false):Void { - return _indexSegments; - } - - private function get_subSetCount():Int { - return _subSetCount; + var subGeometry:CompactSubGeometry = null; + for (s in _subGeometries) { + var segmentCount:Int = s.numVertices >> 2; + if (index < segmentCount) { + subGeometry = s; + break; + } else { + index -= segmentCount; + } + } + if (subGeometry == null) + return; + + var verticesPerSegment:Int = 4 * _vertexDefinition.length; + var vertices:Vector = subGeometry.vertexData; + vertices.splice(index * verticesPerSegment, verticesPerSegment); + subGeometry.updateData(vertices); + + var indices:Vector = subGeometry.indexData; + indices.length -= 6; + subGeometry.updateIndexData(indices); + + var segment:Segment = _segments[index]; + segment.index = -1; + segment.segmentsBase = null; + if (dispose) { + segment.dispose(); + } + + _segments.splice(index, 1); + for (i in index..._segments.length) { + _segments[i].index = i * verticesPerSegment; + } + + if (subGeometry.numVertices == 0) { + geometry.removeSubGeometry(subGeometry); + + for (i in index..._segments.length) { + _segments[i].subSetIndex--; + } + } } @:allow(away3d) private function updateSegment(segment:Segment):Void { - //to do: add support for curve segment - var start:Vector3D = segment._start; - var end:Vector3D = segment._end; - var startX:Float = start.x, startY:Float = start.y, startZ:Float = start.z; - var endX:Float = end.x, endY:Float = end.y, endZ:Float = end.z; - var startR:Float = segment._startR, startG:Float = segment._startG, startB:Float = segment._startB; - var endR:Float = segment._endR, endG:Float = segment._endG, endB:Float = segment._endB; + var position:Vector3D = segment._start; + var opposite:Vector3D = segment._end; + + #if away3d_double_line_width + //Previous versions of `SegmentSet` implicitly doubled the line width. + //This can be re-enabled to ease the transition, but is deprecated. + var halfWidth:Float = segment.thickness; + #else + var halfWidth:Float = segment._halfThickness; + #end + + var subGeometry:CompactSubGeometry = _subGeometries[segment.subSetIndex]; var index:Int = segment.index; - var t:Float = segment.thickness; - - var subSet:SubSet = _subSets[segment.subSetIndex]; - var vertices:Vector = subSet.vertices; - - vertices[index++] = startX; - vertices[index++] = startY; - vertices[index++] = startZ; - vertices[index++] = endX; - vertices[index++] = endY; - vertices[index++] = endZ; - vertices[index++] = t; - vertices[index++] = startR; - vertices[index++] = startG; - vertices[index++] = startB; - vertices[index++] = 1; - - vertices[index++] = endX; - vertices[index++] = endY; - vertices[index++] = endZ; - vertices[index++] = startX; - vertices[index++] = startY; - vertices[index++] = startZ; - vertices[index++] = -t; - vertices[index++] = endR; - vertices[index++] = endG; - vertices[index++] = endB; - vertices[index++] = 1; - - vertices[index++] = startX; - vertices[index++] = startY; - vertices[index++] = startZ; - vertices[index++] = endX; - vertices[index++] = endY; - vertices[index++] = endZ; - vertices[index++] = -t; - vertices[index++] = startR; - vertices[index++] = startG; - vertices[index++] = startB; - vertices[index++] = 1; - - vertices[index++] = endX; - vertices[index++] = endY; - vertices[index++] = endZ; - vertices[index++] = startX; - vertices[index++] = startY; - vertices[index++] = startZ; - vertices[index++] = t; - vertices[index++] = endR; - vertices[index++] = endG; - vertices[index++] = endB; - vertices[index++] = 1; - - subSet.vertexBufferDirty = true; - - invalidateBounds(); - } - - private function get_hasData():Bool - { - return _hasData; - } - - public function getIndexBuffer(stage3DProxy:Stage3DProxy):IndexBuffer3D - { - if (_activeSubSet.indexContext3D != stage3DProxy.context3D || _activeSubSet.indexBufferDirty) { - _activeSubSet.indexBuffer = stage3DProxy.createIndexBuffer(_activeSubSet.numIndices); - _activeSubSet.indexBuffer.uploadFromVector(_activeSubSet.indices, 0, _activeSubSet.numIndices); - _activeSubSet.indexBufferDirty = false; - _activeSubSet.indexContext3D = stage3DProxy.context3D; - } - - return _activeSubSet.indexBuffer; - } - - public function activateVertexBuffer(index:Int, stage3DProxy:Stage3DProxy):Void - { - var subSet:SubSet = _subSets[index]; - - _activeSubSet = subSet; - _numIndices = subSet.numIndices; - - if (subSet.vertexContext3D != stage3DProxy.context3D || subSet.vertexBufferDirty) { - if (subSet.vertexBuffer != null) Stage3DProxy.disposeVertexBuffer(subSet.vertexBuffer); - subSet.vertexBuffer = stage3DProxy.createVertexBuffer(subSet.numVertices, 11); - subSet.vertexBuffer.uploadFromVector(subSet.vertices, 0, subSet.numVertices); - subSet.vertexBufferDirty = false; - subSet.vertexContext3D = stage3DProxy.context3D; + for (i in 0...segment.subSetIndex) { + index -= _subGeometries[i].numVertices >> 2; } - - var vertexBuffer:VertexBuffer3D = subSet.vertexBuffer; - var context3d:Context3D = stage3DProxy._context3D; - - context3d.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3); - context3d.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3); - context3d.setVertexBufferAt(2, vertexBuffer, 6, Context3DVertexBufferFormat.FLOAT_1); - context3d.setVertexBufferAt(3, vertexBuffer, 7, Context3DVertexBufferFormat.FLOAT_4); - } - - public function activateUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void - { - } - - public function activateVertexNormalBuffer(index:Int, stage3DProxy:Stage3DProxy):Void - { - } - - public function activateVertexTangentBuffer(index:Int, stage3DProxy:Stage3DProxy):Void - { - } - - public function activateSecondaryUVBuffer(index:Int, stage3DProxy:Stage3DProxy):Void - { - } - - public function activateVertexBufferByName(attributeName:String, index:Int, stage3DProxy:Stage3DProxy):Void - { - } - - private function reOrderIndices(subSetIndex:Int, index:Int):Void - { - var segRef:SegRef; - - for (i in index..._indexSegments - 1) { - segRef = _segments[i + 1]; - segRef.index = i; - if (segRef.subSetIndex == subSetIndex) - segRef.segment.index -= 44; - _segments[i] = segRef; + + var vertices:Vector = subGeometry.vertexData; + if (vertices == null) vertices = new Vector(); + var stride:Int = _vertexDefinition.length; + var startIndex:Int = index * 4 * stride; + index = startIndex; + + for (i in 0...4) { + if (i == 2) { + position = segment._end; + opposite = segment._start; + } + + vertices[index + _positionOffset + 0] = position.x; + vertices[index + _positionOffset + 1] = position.y; + vertices[index + _positionOffset + 2] = position.z; + + vertices[index + _oppositeOffset + 0] = opposite.x; + vertices[index + _oppositeOffset + 1] = opposite.y; + vertices[index + _oppositeOffset + 2] = opposite.z; + + index += stride; } - - } - - private function addSubSet():SubSet - { - var subSet:SubSet = new SubSet(); - _subSets.push(subSet); - - subSet.vertices = new Vector(); - subSet.numVertices = 0; - subSet.indices = new Vector(); - subSet.numIndices = 0; - subSet.vertexBufferDirty = true; - subSet.indexBufferDirty = true; - subSet.lineCount = 0; - - _subSetCount++; - - return subSet; - } - - /** - * @inheritDoc - */ - override public function dispose():Void - { - super.dispose(); - removeAllSegments(); - _segments = null; - _material = null; - var subSet:SubSet = _subSets[0]; - subSet.vertices = null; - subSet.indices = null; - _subSets = null; - } - - /** - * @inheritDoc - */ - override private function get_mouseEnabled():Bool - { - return false; - } - - /** - * @inheritDoc - */ - override private function getDefaultBoundingVolume():BoundingVolumeBase - { - return new BoundingSphere(); - } - - /** - * @inheritDoc - */ - override private function updateBounds():Void - { - var subSet:SubSet; - var len:Int; - var v:Float; - var index:Int; - - var minX:Float = Math.POSITIVE_INFINITY; - var minY:Float = Math.POSITIVE_INFINITY; - var minZ:Float = Math.POSITIVE_INFINITY; - var maxX:Float = Math.NEGATIVE_INFINITY; - var maxY:Float = Math.NEGATIVE_INFINITY; - var maxZ:Float = Math.NEGATIVE_INFINITY; - var vertices:Vector; - - var i:Int = 0; - while (i < _subSetCount) { - subSet = _subSets[i]; - index = 0; - vertices = subSet.vertices; - len = vertices.length; - - if (len == 0) { - ++i; - continue; + + index = startIndex; + vertices[index + _halfWidthOffset] = halfWidth; + vertices[index + stride + _halfWidthOffset] = -halfWidth; + vertices[index + stride * 2 + _halfWidthOffset] = -halfWidth; + vertices[index + stride * 3 + _halfWidthOffset] = halfWidth; + + if (_colorOffset >= 0) { + for (_ in 0...2) { + vertices[index + _colorOffset + 0] = segment._startR; + vertices[index + _colorOffset + 1] = segment._startG; + vertices[index + _colorOffset + 2] = segment._startB; + index += stride; } - - while (index < len) { - - v = vertices[index++]; - if (v < minX) - minX = v; - else if (v > maxX) - maxX = v; - - v = vertices[index++]; - if (v < minY) - minY = v; - else if (v > maxY) - maxY = v; - - v = vertices[index++]; - if (v < minZ) - minZ = v; - else if (v > maxZ) - maxZ = v; - - index += 8; + for (_ in 0...2) { + vertices[index + _colorOffset + 0] = segment._endR; + vertices[index + _colorOffset + 1] = segment._endG; + vertices[index + _colorOffset + 2] = segment._endB; + index += stride; } - ++i; - } - - if (minX != Math.POSITIVE_INFINITY) - _bounds.fromExtremes(minX, minY, minZ, maxX, maxY, maxZ); - - else { - var min:Float = .5; - _bounds.fromExtremes(-min, -min, -min, min, min, min); } - - _boundsInvalid = false; + + subGeometry.updateData(vertices); } - - /** - * @inheritDoc - */ - override private function createEntityPartitionNode():EntityNode + + override private function get_assetType():String { - return new RenderableNode(this); + return Asset3DType.SEGMENT_SET; } - + + // Backwards compatibility + + @:noCompletion @:dox(hide) public var numTriangles(get, never):Int; + @:noCompletion @:dox(hide) public var sourceEntity(get, never):Entity; + @:noCompletion @:dox(hide) public var uvTransform(get, never):Matrix; + @:noCompletion @:dox(hide) public var uvTransform2(get, never):Matrix; + @:noCompletion @:dox(hide) public var vertexData(get, never):Vector; + @:noCompletion @:dox(hide) public var indexData(get, never):Vector; + @:noCompletion @:dox(hide) public var UVData(get, never):Vector; + @:noCompletion @:dox(hide) public var numVertices(get, never):Int; + @:noCompletion @:dox(hide) public var vertexStride(get, never):Int; + @:noCompletion @:dox(hide) public var vertexNormalData(get, never):Vector; + @:noCompletion @:dox(hide) public var vertexTangentData(get, never):Vector; + @:noCompletion @:dox(hide) public var vertexOffset(get, never):Int; + @:noCompletion @:dox(hide) public var vertexNormalOffset(get, never):Int; + @:noCompletion @:dox(hide) public var vertexTangentOffset(get, never):Int; + private function get_numTriangles():Int { - return Std.int(_numIndices/3); + var count:Int = 0; + for (subMesh in _subMeshes) { + count += subMesh.numTriangles; + } + return count; } - + private function get_sourceEntity():Entity { return this; } - - private function get_castsShadows():Bool - { - return false; - } - - private function get_material():MaterialBase - { - return _material; - } - - private function get_animator():IAnimator - { - return _animator; - } - - private function set_material(value:MaterialBase):MaterialBase - { - if (value == _material) - return value; - if (_material != null) - _material.removeOwner(this); - _material = value; - if (_material != null) - _material.addOwner(this); - return value; - } - + private function get_uvTransform():Matrix { return null; } - + private function get_uvTransform2():Matrix { return null; } - + private function get_vertexData():Vector { return null; } - + private function get_indexData():Vector { return null; } - + private function get_UVData():Vector { return null; } - + private function get_numVertices():Int { - return 0; + var count:Int = 0; + for (subMesh in _subMeshes) { + count += subMesh.numVertices; + } + return count; } - + private function get_vertexStride():Int { return 11; } - + private function get_vertexNormalData():Vector { return null; } - + private function get_vertexTangentData():Vector { return null; } - + private function get_vertexOffset():Int { return 0; } - + private function get_vertexNormalOffset():Int { return 0; } - + private function get_vertexTangentOffset():Int { return 0; } - - override private function get_assetType():String - { - return Asset3DType.SEGMENT_SET; - } - + + @:noCompletion @:dox(hide) public function getRenderSceneTransform(camera:Camera3D):Matrix3D { return _sceneTransform; } } - -@:final class SegRef -{ - - public var index:Int; - public var subSetIndex:Int; - public var segment:Segment; - - public function new() {} - -} - -@:final class SubSet -{ - - public var vertices:Vector; - public var numVertices:Int; - - public var indices:Vector; - public var numIndices:Int; - - public var vertexBufferDirty:Bool; - public var indexBufferDirty:Bool; - - public var vertexContext3D:Context3D; - public var indexContext3D:Context3D; - - public var vertexBuffer:VertexBuffer3D; - public var indexBuffer:IndexBuffer3D; - public var lineCount:Int; - - public function new() {} - - public function dispose():Void - { - vertices = null; - if (vertexBuffer != null) - Stage3DProxy.disposeVertexBuffer(vertexBuffer); - if (indexBuffer != null) - Stage3DProxy.disposeIndexBuffer(indexBuffer); - } - -} \ No newline at end of file diff --git a/away3d/materials/OrthoSegmentMaterial.hx b/away3d/materials/OrthoSegmentMaterial.hx deleted file mode 100644 index efdfcd1a..00000000 --- a/away3d/materials/OrthoSegmentMaterial.hx +++ /dev/null @@ -1,27 +0,0 @@ -package away3d.materials; - -import away3d.materials.passes.OrthoSegmentPass; - -/** - * OrthoSegmentMaterial is a material exclusively used to render wireframe objects - * - * @see away3d.entities.Lines - */ -class OrthoSegmentMaterial extends MaterialBase -{ - private var _screenPass:OrthoSegmentPass; - - /** - * Creates a new OrthoSegmentMaterial object. - * - * @param thickness The thickness of the wireframe lines. - */ - public function new(thickness:Float = 1.25) - { - super(); - - bothSides = true; - addPass(_screenPass = new OrthoSegmentPass(thickness)); - _screenPass.material = this; - } -} \ No newline at end of file diff --git a/away3d/materials/methods/SegmentMethod.hx b/away3d/materials/methods/SegmentMethod.hx new file mode 100644 index 00000000..4de07bf7 --- /dev/null +++ b/away3d/materials/methods/SegmentMethod.hx @@ -0,0 +1,118 @@ +package away3d.materials.methods; + +import away3d.core.base.data.VertexDefinition.AttributeDefinition; +import away3d.core.managers.Stage3DProxy; +import away3d.materials.compilation.ShaderRegisterCache; +import away3d.materials.compilation.ShaderRegisterElement; +import away3d.materials.methods.EffectMethodBase; +import away3d.materials.methods.MethodVO; +import openfl.Vector; + +class SegmentMethod extends EffectMethodBase +{ + /** + * Half of the line segment's width. + */ + public static var HALF_WIDTH(default, null):AttributeDefinition = new AttributeDefinition("halfWidth", 1); + + /** + * For each vertex, the position of the opposite end of the line segment. + */ + public static var OPPOSITE(default, null):AttributeDefinition = new AttributeDefinition("opposite", 3); + + public function new() + { + super([OPPOSITE, HALF_WIDTH]); + } + + private override function activate(vo:MethodVO, stage3DProxy:Stage3DProxy):Void + { + var index:Int = vo.vertexConstantsIndex; + var data:Vector = vo.vertexData; + + var width:Float = stage3DProxy.width; + var height:Float = stage3DProxy.height; + if (stage3DProxy.scissorRect != null) { + width = stage3DProxy.scissorRect.width; + height = stage3DProxy.scissorRect.height; + } + if (width < 1) width = 1; + if (height < 1) height = 1; + + //One pixel in screen coordinates. This can be either `2 / height` or + //`2 / width`, with the only difference being the order of operations in + //the "Find the line's direction" section. + data[index] = 2 / height; //onePixel + + data[index + 1] = width / height; //aspectRatio + data[index + 2] = height / width; //invAspectRatio + } + + private override function getClipSpaceVertexCode(vo:MethodVO, regCache:ShaderRegisterCache):String + { + var halfWidth:String = sharedRegisters.custom[HALF_WIDTH.name] + ".x"; + + var constants:ShaderRegisterElement = regCache.getFreeVertexConstant(); + vo.vertexConstantsIndex = constants.index * 4; + var onePixel:String = constants + ".x"; + var aspectRatio:String = constants + ".y"; + var invAspectRatio:String = constants + ".z"; + + var code:String = ""; + + var vertexPosition:ShaderRegisterElement = regCache.getFreeVertexVectorTemp(); + regCache.addVertexTempUsages(vertexPosition, 1); + code += 'div $vertexPosition.xyz, ${ sharedRegisters.clipSpacePosition }.xyz, ${ sharedRegisters.clipSpacePosition }.w\n'; + + var oppositePosition:ShaderRegisterElement = regCache.getFreeVertexVectorTemp(); + regCache.addVertexTempUsages(oppositePosition, 1); + if (sharedRegisters.sceneTransform != null) { + code += 'm44 $oppositePosition, ${ sharedRegisters.custom[OPPOSITE.name] }, ${ sharedRegisters.sceneTransform }\n' + + 'm44 $oppositePosition, $oppositePosition, vc0\n'; + } else { + code += 'm44 $oppositePosition, ${ sharedRegisters.custom[OPPOSITE.name] }, vc0\n'; + } + + //Convert from clip space to (relative) screen space. + //Normally, converting from clip space requires dividing a point by its + //own w, but we only need the relative angle between them. Multiplying + //each point by the other's w gets that and saves the division. + code += 'mul $vertexPosition.xyz, ${ sharedRegisters.clipSpacePosition }.xyz, $oppositePosition.w\n' + + 'mul $oppositePosition.xyz, $oppositePosition.xyz, ${ sharedRegisters.clipSpacePosition }.w\n'; + + //Find the line's direction in screen coordinates. + var direction:ShaderRegisterElement = regCache.getFreeVertexVectorTemp(); + regCache.addVertexTempUsages(direction, 1); + code += 'sub $direction.xy, $oppositePosition.xy, $vertexPosition.xy\n' + + 'mul $direction.x, $direction.x, $aspectRatio\n' + + 'nrm $direction.xy, $direction.xy\n' + + 'mul $direction.y, $direction.y, $invAspectRatio\n'; + + //Rotate `direction` to be perpendicular to the line. + code += 'mov $direction.z, $direction.x\n' + + 'mov $direction.x, $direction.y\n' + + 'neg $direction.y, $direction.z\n'; + + //Prepare to move the vertex by `halfWidth * onePixel` in clip space. + //This vertex's mirror will move the same distance in the opposite + //direction, so the line will be drawn at its full width. + var distance:String = '$direction.z'; + code += 'mul $distance, $halfWidth, $onePixel\n' + + 'mul $distance, $distance, ${ sharedRegisters.clipSpacePosition }.w\n' + + 'mul $direction.xy, $direction.xy, $distance\n'; + + //Move the vertex. + code += 'add ${ sharedRegisters.clipSpacePosition }.xy, ${ sharedRegisters.clipSpacePosition }.xy, $direction.xy\n'; + + regCache.removeVertexTempUsage(vertexPosition); + regCache.removeVertexTempUsage(oppositePosition); + regCache.removeVertexTempUsage(direction); + + return code; + } + + private override function getFragmentCode(vo:MethodVO, regCache:ShaderRegisterCache, targetReg:ShaderRegisterElement):String + { + return ""; + } +} diff --git a/away3d/materials/passes/OrthoSegmentPass.hx b/away3d/materials/passes/OrthoSegmentPass.hx deleted file mode 100644 index 86133378..00000000 --- a/away3d/materials/passes/OrthoSegmentPass.hx +++ /dev/null @@ -1,178 +0,0 @@ -package away3d.materials.passes; - -import away3d.*; -import away3d.cameras.*; -import away3d.cameras.lenses.*; -import away3d.core.base.*; -import away3d.core.managers.*; -import away3d.entities.*; - -import openfl.display3D.*; -import openfl.geom.*; -import openfl.Vector; - -/** - * OrthoSegmentPass is a material pass that draws wireframe segments. - */ -class OrthoSegmentPass extends MaterialPassBase -{ - private static var ONE_VECTOR:Vector = new Vector([ 1, 1, 1, 1 ]); - private static var FRONT_VECTOR:Vector = new Vector([ 0, 0, 1, 1 ]); - - private var _constants:Vector = new Vector(4, true); - private var _calcMatrix:Matrix3D; - private var _thickness:Float; - - /** - * Creates a new OrthoSegmentPass object. - * - * @param thickness the thickness of the segments to be drawn. - */ - public function new(thickness:Float) - { - _calcMatrix = new Matrix3D(); - - _thickness = thickness; - _constants[1] = 1/255; - - super(); - } - - /** - * @inheritDoc - */ - private override function getVertexCode():String - { - return "m44 vt0, va0, vc8 \n" + // transform Q0 to eye space - "m44 vt1, va1, vc8 \n" + // transform Q1 to eye space - "sub vt2, vt1, vt0 \n" + // L = Q1 - Q0 - - // test if behind camera near plane - // if 0 - Q0.z < Camera.near then the point needs to be clipped - //"neg vt5.x, vt0.z \n" + // 0 - Q0.z - "slt vt5.x, vt0.z, vc7.z \n" + // behind = ( 0 - Q0.z < -Camera.near ) ? 1 : 0 - "sub vt5.y, vc5.x, vt5.x \n" + // !behind = 1 - behind - - // p = point on the plane (0,0,-near) - // n = plane normal (0,0,-1) - // D = Q1 - Q0 - // t = ( dot( n, ( p - Q0 ) ) / ( dot( n, d ) - - // solve for t where line crosses Camera.near - "add vt4.x, vt0.z, vc7.z \n" + // Q0.z + ( -Camera.near ) - "sub vt4.y, vt0.z, vt1.z \n" + // Q0.z - Q1.z - - // fix divide by zero for horizontal lines - "seq vt4.z, vt4.y vc6.x \n" + // offset = (Q0.z - Q1.z)==0 ? 1 : 0 - "add vt4.y, vt4.y, vt4.z \n" + // ( Q0.z - Q1.z ) + offset - - "div vt4.z, vt4.x, vt4.y \n" + // t = ( Q0.z - near ) / ( Q0.z - Q1.z ) - - "mul vt4.xyz, vt4.zzz, vt2.xyz \n" + // t(L) - "add vt3.xyz, vt0.xyz, vt4.xyz \n" + // Qclipped = Q0 + t(L) - "mov vt3.w, vc5.x \n" + // Qclipped.w = 1 - - // If necessary, replace Q0 with new Qclipped - "mul vt0, vt0, vt5.yyyy \n" + // !behind * Q0 - "mul vt3, vt3, vt5.xxxx \n" + // behind * Qclipped - "add vt0, vt0, vt3 \n" + // newQ0 = Q0 + Qclipped - - // calculate side vector for line - "sub vt2, vt1, vt0 \n" + // L = Q1 - Q0 - "nrm vt2.xyz, vt2.xyz \n" + // normalize( L ) - "crs vt3.xyz, vt2, vc6 \n" + // S = L x D - "nrm vt3.xyz, vt3.xyz \n" + // normalize( S ) - - // face the side vector properly for the given point - "mul vt3.xyz, vt3.xyz, va2.xxx \n" + // S *= weight - "mov vt3.w, vc5.x \n" + // S.w = 1 - - // calculate the amount required to move at the point's distance to correspond to the line's pixel width - // scale the side vector by that amount - "mov vt4.x, vc6.z \n" + // distance = dot( view ) - "mul vt4.x, vt4.x, vc7.x \n" + // distance *= vpsod - "mul vt3.xyz, vt3.xyz, vt4.xxx \n" + // S.xyz *= pixelScaleFactor - - // add scaled side vector to Q0 and transform to clip space - "add vt0.xyz, vt0.xyz, vt3.xyz \n" + // Q0 + S - - "m44 op, vt0, vc0 \n" + // transform Q0 to clip space - - // interpolate color - "mov v0, va3 \n"; - } - - /** - * @inheritDoc - */ - private override function getFragmentCode(animationCode:String):String - { - return "mov oc, v0\n"; - } - - /** - * @inheritDoc - * todo: keep maps in dictionary per renderable - */ - private override function render(renderable:IRenderable, stage3DProxy:Stage3DProxy, camera:Camera3D, viewProjection:Matrix3D):Void - { - var context:Context3D = stage3DProxy._context3D; - _calcMatrix.copyFrom(renderable.sourceEntity.sceneTransform); - _calcMatrix.append(camera.inverseSceneTransform); - - var subSetCount:UInt = cast(renderable, SegmentSet).subSetCount; - - if (cast (renderable, SegmentSet).hasData) { - for (i in 0...subSetCount) { - renderable.activateVertexBuffer(i, stage3DProxy); - context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 8, _calcMatrix, true); - context.drawTriangles(renderable.getIndexBuffer(stage3DProxy), 0, renderable.numTriangles); - } - } - } - - /** - * @inheritDoc - */ - private override function activate(stage3DProxy:Stage3DProxy, camera:Camera3D):Void - { - var context:Context3D = stage3DProxy._context3D; - super.activate(stage3DProxy, camera); - - if (stage3DProxy.scissorRect != null) - _constants[0] = _thickness*0.5*cast(camera.lens, OrthographicLens).projectionHeight/stage3DProxy.scissorRect.height; - else - _constants[0] = _thickness*0.5*cast(camera.lens, OrthographicLens).projectionHeight/stage3DProxy.height; - - // value to convert distance from camera to model length per pixel width - _constants[2] = camera.lens.near; - - context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, ONE_VECTOR); - context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 6, FRONT_VECTOR); - context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 7, _constants); - - // projection matrix - if (stage3DProxy.renderTarget == null) - context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, camera.lens.matrix, true); - else - { - //TODO: to find a better way - _calcMatrix.copyFrom(camera.lens.matrix); - var rttBufferManager:RTTBufferManager = RTTBufferManager.getInstance(stage3DProxy); - _calcMatrix.appendScale(rttBufferManager.textureRatioX, rttBufferManager.textureRatioY, 1); - context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _calcMatrix, true); - } - } - - /** - * @inheritDoc - */ - private override function deactivate(stage3DProxy:Stage3DProxy):Void - { - var context:Context3D = stage3DProxy._context3D; - context.setVertexBufferAt(0, null); - context.setVertexBufferAt(1, null); - context.setVertexBufferAt(2, null); - context.setVertexBufferAt(3, null); - } -} \ No newline at end of file diff --git a/away3d/primitives/WireframePrimitiveBase.hx b/away3d/primitives/WireframePrimitiveBase.hx index 15acc5c4..63f6aa0d 100644 --- a/away3d/primitives/WireframePrimitiveBase.hx +++ b/away3d/primitives/WireframePrimitiveBase.hx @@ -34,8 +34,8 @@ class WireframePrimitiveBase extends SegmentSet private function set_color(value:Int):Int { _color = value; - for (segRef in _segments) { - segRef.segment.startColor = segRef.segment.endColor = value; + for (segment in _segments) { + segment.startColor = segment.endColor = value; } return value; } @@ -48,8 +48,8 @@ class WireframePrimitiveBase extends SegmentSet private function set_thickness(value:Float):Float { _thickness = value; - for (segRef in _segments) { - segRef.segment.thickness = segRef.segment.thickness = value; + for (segment in _segments) { + segment.thickness = value; } return value; } diff --git a/away3d/primitives/data/Segment.hx b/away3d/primitives/data/Segment.hx index 5c08d7df..3484d852 100644 --- a/away3d/primitives/data/Segment.hx +++ b/away3d/primitives/data/Segment.hx @@ -16,7 +16,7 @@ class Segment public var segmentsBase(never, set):SegmentSet; public var _segmentsBase:SegmentSet; - public var _thickness:Float; + public var _halfThickness:Float; public var _start:Vector3D; public var _end:Vector3D; public var _startR:Float; @@ -36,7 +36,7 @@ class Segment // TODO: not yet used: for CurveSegment support anchor = null; - _thickness = thickness*.5; + _halfThickness = thickness*.5; // TODO: add support for curve using anchor v1 // Prefer removing v1 from this, and make Curve a separate class extending Segment? (- David) _start = start; @@ -58,13 +58,10 @@ class Segment if (_endColor != colorEnd) endColor = colorEnd; - _thickness = thickness*.5; + _halfThickness = thickness*.5; update(); } - /** - * Defines the starting vertex. - */ private function get_start():Vector3D { return _start; @@ -77,9 +74,6 @@ class Segment return value; } - /** - * Defines the ending vertex. - */ private function get_end():Vector3D { return _end; @@ -92,24 +86,18 @@ class Segment return value; } - /** - * Defines the ending vertex. - */ private function get_thickness():Float { - return _thickness*2; + return _halfThickness*2; } private function set_thickness(value:Float):Float { - _thickness = value*.5; + _halfThickness = value*.5; update(); return value; } - /** - * Defines the startColor - */ private function get_startColor():Int { return _startColor; @@ -127,9 +115,6 @@ class Segment return color; } - /** - * Defines the endColor - */ private function get_endColor():Int { return _endColor; @@ -181,10 +166,9 @@ class Segment return segBase; } - private function update():Void + private inline function update():Void { - if (_segmentsBase == null) - return; - _segmentsBase.updateSegment(this); + if (_segmentsBase != null) + _segmentsBase.updateSegment(this); } } \ No newline at end of file From 0345caa11c4dfda76a5a2b5c354077a455c042d6 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 20:30:40 -0400 Subject: [PATCH 31/42] Fix missing segment colors. --- away3d/entities/SegmentSet.hx | 39 ++++++++++++-- away3d/materials/methods/VertexColorMethod.hx | 54 +++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 away3d/materials/methods/VertexColorMethod.hx diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index c6f93077..0ab57d1e 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -10,6 +10,7 @@ import away3d.entities.Mesh; import away3d.events.GeometryEvent; import away3d.library.assets.Asset3DType; import away3d.materials.methods.SegmentMethod; +import away3d.materials.methods.VertexColorMethod; import away3d.materials.SinglePassMaterialBase; import away3d.materials.utils.DefaultMaterialManager; import away3d.primitives.data.Segment; @@ -18,6 +19,9 @@ import openfl.geom.Matrix3D; import openfl.geom.Vector3D; import openfl.Vector; +/** + * A collection of 3D line segments. + */ class SegmentSet extends Mesh { public static var requiredAttributes(get, null):VertexDefinition; private static function get_requiredAttributes():VertexDefinition { @@ -50,7 +54,16 @@ class SegmentSet extends Mesh { private var _halfWidthOffset:Int; private var _colorOffset:Int = -1; - public function new(?material:SinglePassMaterialBase, ?vertexDefinition:VertexDefinition) { + /** + * Creates a new `SegmentSet`. + * @param material The material to use. + * @param overrideMaterialColors Whether to use individual `Segment` colors + * instead of the material's color or texture. If false, `Segment` colors + * will be ignored. + * @param vertexDefinition A custom vertex definition containing any other + * attributes needed by `material`. + */ + public function new(?material:SinglePassMaterialBase, ?overrideMaterialColors:Bool = true, ?vertexDefinition:VertexDefinition) { if (material == null) { material = DefaultMaterialManager.getDefaultMaterial(this); } @@ -67,13 +80,31 @@ class SegmentSet extends Mesh { if (vertexDefinition == null) { vertexDefinition = VertexDefinition.defaultVertexDefinition; } - _vertexDefinition = VertexDefinition.merge(vertexDefinition, requiredAttributes); + _vertexDefinition = vertexDefinition.concatUnique(requiredAttributes.attributes); _positionOffset = _vertexDefinition.get("position").offset; _oppositeOffset = _vertexDefinition.get("opposite").offset; _halfWidthOffset = _vertexDefinition.get("halfWidth").offset; - var color:AttributeDefinition = _vertexDefinition.get("color"); - if (color != null) _colorOffset = color.offset; + + if (overrideMaterialColors) { + _vertexDefinition = _vertexDefinition.concatUnique([VertexColorMethod.COLOR]); + + var color:AttributeDefinition = _vertexDefinition.get("color"); + if (color != null) { + _colorOffset = color.offset; + } + + var hasColorMethod:Bool = false; + for (i in 0...material.numMethods) { + if (Std.isOfType(material.getMethodAt(i), VertexColorMethod)) { + hasColorMethod = true; + break; + } + } + if (!hasColorMethod) { + material.addMethod(new VertexColorMethod(color.length > 3)); + } + } } public function addSegment(segment:Segment):Void diff --git a/away3d/materials/methods/VertexColorMethod.hx b/away3d/materials/methods/VertexColorMethod.hx new file mode 100644 index 00000000..a4d08bb1 --- /dev/null +++ b/away3d/materials/methods/VertexColorMethod.hx @@ -0,0 +1,54 @@ +package away3d.materials.methods; + +import away3d.materials.compilation.ShaderRegisterCache; +import openfl.display.BlendMode; +import away3d.core.base.data.VertexDefinition.AttributeDefinition; +import away3d.materials.compilation.ShaderRegisterElement; + +/** + * A method that applies per-vertex colors from the "color" attribute. Overwites + * colors from other sources (e.g., the color applied by `ColorMaterial`). + */ +class VertexColorMethod extends EffectMethodBase +{ + /** + * The vertex's color, in RGB format. + */ + public static var COLOR(default, null):AttributeDefinition = new AttributeDefinition("color", 3); + + /** + * The vertex's color, in RGBA format. + */ + public static var COLOR_WITH_ALPHA(default, null):AttributeDefinition = new AttributeDefinition("color", 4); + + /** + * Whether this method supports alpha blending. If this is true, the + * material must have an appropriate `blendMode` (typically `LAYER`). + */ + public var alphaEnabled(default, null):Bool; + + private var colorVarying:ShaderRegisterElement; + + public function new(?enableAlpha:Bool = false) + { + super(enableAlpha ? [COLOR_WITH_ALPHA] : [COLOR]); + alphaEnabled = enableAlpha; + } + + override private function getVertexCode(vo:MethodVO, regCache:ShaderRegisterCache):String + { + var color:ShaderRegisterElement = sharedRegisters.custom["color"]; + colorVarying = regCache.getFreeVarying(); + + return 'mov $colorVarying, $color\n'; + } + + override private function getFragmentCode(vo:MethodVO, regCache:ShaderRegisterCache, targetReg:ShaderRegisterElement):String + { + if (alphaEnabled) { + return 'mov $targetReg, $colorVarying\n'; + } else { + return 'mov $targetReg.xyz, $colorVarying.xyz\n'; + } + } +} From 935b1d91ab88f63f82f969fa710d83e055d007b7 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 20:59:53 -0400 Subject: [PATCH 32/42] Fix segments not updating. --- away3d/entities/SegmentSet.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index 0ab57d1e..16ff50d8 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -121,6 +121,7 @@ class SegmentSet extends Mesh { segment.index = _segments.length; _segments.push(segment); + segment.segmentsBase = this; segment.subSetIndex = _subGeometries.length - 1; updateSegment(segment); From b34e07b8623950fe38dee4f3ba95bc677efee2f2 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 21:00:40 -0400 Subject: [PATCH 33/42] Simplify `WireframePrimitiveBase`. Instead of having to set the color of every segment, a single-color material can be used. --- away3d/primitives/WireframePrimitiveBase.hx | 29 ++++++++------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/away3d/primitives/WireframePrimitiveBase.hx b/away3d/primitives/WireframePrimitiveBase.hx index 63f6aa0d..f0ece67b 100644 --- a/away3d/primitives/WireframePrimitiveBase.hx +++ b/away3d/primitives/WireframePrimitiveBase.hx @@ -3,6 +3,7 @@ package away3d.primitives; import away3d.bounds.BoundingVolumeBase; import away3d.entities.SegmentSet; import away3d.errors.AbstractMethodError; +import away3d.materials.ColorMaterial; import away3d.primitives.data.Segment; import openfl.geom.Vector3D; @@ -13,30 +14,27 @@ class WireframePrimitiveBase extends SegmentSet public var thickness(get, set):Float; private var _geomDirty:Bool = true; - private var _color:Int; + private var _colorMaterial:ColorMaterial; private var _thickness:Float; public function new(color:Int = 0xffffff, thickness:Float = 1) { if (thickness <= 0) thickness = 1; - _color = color; + _colorMaterial = new ColorMaterial(color); _thickness = thickness; mouseEnabled = mouseChildren = false; - super(); + super(_colorMaterial, false); } private function get_color():Int { - return _color; + return _colorMaterial.color; } private function set_color(value:Int):Int { - _color = value; - for (segment in _segments) { - segment.startColor = segment.endColor = value; - } + _colorMaterial.color = value; return value; } @@ -89,17 +87,12 @@ class WireframePrimitiveBase extends SegmentSet var s:Vector3D, e:Vector3D; if ((segment = getSegment(index)) != null) { - s = segment.start; - e = segment.end; - s.x = v0.x; - s.y = v0.y; - s.z = v0.z; - e.x = v1.x; - e.y = v1.y; - e.z = v1.z; - segment.updateSegment(s, e, null, _color, _color, _thickness); + segment.start.copyFrom(v0); + segment.end.copyFrom(v1); + segment._halfThickness = _thickness * 0.5; + updateSegment(segment); } else - addSegment(new LineSegment(v0.clone(), v1.clone(), _color, _color, _thickness)); + addSegment(new LineSegment(v0.clone(), v1.clone(), _thickness)); } override private function updateMouseChildren():Void From 6de5e41bb16dfa69fceead5c1defead62d5eb5d8 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 21:15:16 -0400 Subject: [PATCH 34/42] Fix debug wireframes being displayed with 0.5 width. --- away3d/bounds/AxisAlignedBoundingBox.hx | 2 +- away3d/bounds/BoundingSphere.hx | 2 +- away3d/bounds/NullBounds.hx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/away3d/bounds/AxisAlignedBoundingBox.hx b/away3d/bounds/AxisAlignedBoundingBox.hx index e9dff26b..b49da21b 100644 --- a/away3d/bounds/AxisAlignedBoundingBox.hx +++ b/away3d/bounds/AxisAlignedBoundingBox.hx @@ -256,7 +256,7 @@ class AxisAlignedBoundingBox extends BoundingVolumeBase override private function createBoundingRenderable():WireframePrimitiveBase { - return new WireframeCube(1, 1, 1, 0xffffff, 0.5); + return new WireframeCube(1, 1, 1, 0xffffff); } override public function classifyToPlane(plane:Plane3D):Int diff --git a/away3d/bounds/BoundingSphere.hx b/away3d/bounds/BoundingSphere.hx index 25822b58..7c65893c 100644 --- a/away3d/bounds/BoundingSphere.hx +++ b/away3d/bounds/BoundingSphere.hx @@ -175,7 +175,7 @@ class BoundingSphere extends BoundingVolumeBase override private function createBoundingRenderable():WireframePrimitiveBase { - return new WireframeSphere(1, 16, 12, 0xffffff, 0.5); + return new WireframeSphere(1, 16, 12, 0xffffff); } override public function classifyToPlane(plane:Plane3D):Int diff --git a/away3d/bounds/NullBounds.hx b/away3d/bounds/NullBounds.hx index 38a153aa..c4ee0883 100644 --- a/away3d/bounds/NullBounds.hx +++ b/away3d/bounds/NullBounds.hx @@ -33,7 +33,7 @@ class NullBounds extends BoundingVolumeBase override private function createBoundingRenderable():WireframePrimitiveBase { - return if (_renderable != null) _renderable; else new WireframeSphere(100, 16, 12, 0xffffff, 0.5); + return if (_renderable != null) _renderable; else new WireframeSphere(100, 16, 12, 0xffffff); } /** From 3c77465ec78d53b8ba1877aadd40ec78ec9e9e98 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 21:56:19 -0400 Subject: [PATCH 35/42] Implement `hasMethodOfType()`. This can help avoid adding redundant methods to a material. --- away3d/materials/MultiPassMaterialBase.hx | 16 ++++++++++++++++ away3d/materials/SinglePassMaterialBase.hx | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/away3d/materials/MultiPassMaterialBase.hx b/away3d/materials/MultiPassMaterialBase.hx index 2de6d063..243f5bb0 100644 --- a/away3d/materials/MultiPassMaterialBase.hx +++ b/away3d/materials/MultiPassMaterialBase.hx @@ -314,6 +314,22 @@ class MultiPassMaterialBase extends MaterialBase return (_effectsPass != null)? _effectsPass.hasMethod(method) : false; } + /** + * Queries whether an effect method of the given type was added to the material. + * + * @param type The type of method to be queried. + * @return true if that type of method was added to the material, false otherwise. + */ + public function hasMethodOfType(type:Class):Bool + { + for (i in 0...numMethods) { + if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (getMethodAt(i), type)) { + return true; + } + } + return false; + } + /** * Returns the method added at the given index. * @param index The index of the method to retrieve. diff --git a/away3d/materials/SinglePassMaterialBase.hx b/away3d/materials/SinglePassMaterialBase.hx index 9bf33539..5a3295ae 100644 --- a/away3d/materials/SinglePassMaterialBase.hx +++ b/away3d/materials/SinglePassMaterialBase.hx @@ -276,6 +276,22 @@ class SinglePassMaterialBase extends MaterialBase return _screenPass.hasMethod(method); } + /** + * Queries whether an effect method of the given type was added to the material. + * + * @param type The type of method to be queried. + * @return true if that type of method was added to the material, false otherwise. + */ + public function hasMethodOfType(type:Class):Bool + { + for (i in 0...numMethods) { + if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (getMethodAt(i), type)) { + return true; + } + } + return false; + } + /** * Returns the method added at the given index. * @param index The index of the method to retrieve. From 0b0f1f866412afa31f6a4103af623c2f0f6ae859 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 21:56:57 -0400 Subject: [PATCH 36/42] Avoid adding redundant methods. This also fixes compatibility with Haxe 4.1 and earlier. --- away3d/entities/SegmentSet.hx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index 16ff50d8..f0da70b6 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -67,7 +67,9 @@ class SegmentSet extends Mesh { if (material == null) { material = DefaultMaterialManager.getDefaultMaterial(this); } - material.addMethod(new SegmentMethod()); + if (material.hasMethodOfType(SegmentMethod)) { + material.addMethod(new SegmentMethod()); + } super(new Geometry(), material); @@ -94,14 +96,7 @@ class SegmentSet extends Mesh { _colorOffset = color.offset; } - var hasColorMethod:Bool = false; - for (i in 0...material.numMethods) { - if (Std.isOfType(material.getMethodAt(i), VertexColorMethod)) { - hasColorMethod = true; - break; - } - } - if (!hasColorMethod) { + if (!material.hasMethodOfType(VertexColorMethod)) { material.addMethod(new VertexColorMethod(color.length > 3)); } } From 2204fd0fe3c3bf395f876fddfaf9eebb6c5110a5 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 22:00:43 -0400 Subject: [PATCH 37/42] Remove key-value iterator for backwards compatibility. --- away3d/entities/SegmentSet.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index f0da70b6..f6086315 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -143,7 +143,8 @@ class SegmentSet extends Mesh { override private function onSubGeometryRemoved(event:GeometryEvent):Void { - for (i => subGeometry in _subGeometries) { + for (i in 0..._subGeometries.length) { + var subGeometry:CompactSubGeometry = _subGeometries[i]; if (subGeometry == event.subGeometry) { _subGeometries.splice(i, 1); break; From 1e89ab3d3e6d14a03e2adc2d9a0e2922948b8f28 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 22:07:11 -0400 Subject: [PATCH 38/42] Allow skipping color arguments, hopefully. --- away3d/primitives/LineSegment.hx | 2 +- away3d/primitives/data/Segment.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/away3d/primitives/LineSegment.hx b/away3d/primitives/LineSegment.hx index 0859b7d9..e6ea3538 100644 --- a/away3d/primitives/LineSegment.hx +++ b/away3d/primitives/LineSegment.hx @@ -20,7 +20,7 @@ class LineSegment extends Segment * @param color1 Ending colour of the line segment * @param thickness Thickness of the line */ - public function new(v0:Vector3D, v1:Vector3D, color0:Int = 0x333333, color1:Int = 0x333333, thickness:Float = 1) + public function new(v0:Vector3D, v1:Vector3D, ?color0:Int = 0x333333, ?color1:Int = 0x333333, ?thickness:Float = 1) { super(v0, v1, null, color0, color1, thickness); } diff --git a/away3d/primitives/data/Segment.hx b/away3d/primitives/data/Segment.hx index 3484d852..fed1a5b2 100644 --- a/away3d/primitives/data/Segment.hx +++ b/away3d/primitives/data/Segment.hx @@ -31,7 +31,7 @@ class Segment public var _startColor:Int; public var _endColor:Int; - public function new(start:Vector3D, end:Vector3D, anchor:Vector3D, colorStart:Int = 0x333333, colorEnd:Int = 0x333333, thickness:Float = 1) + public function new(start:Vector3D, end:Vector3D, anchor:Vector3D, ?colorStart:Int = 0x333333, ?colorEnd:Int = 0x333333, ?thickness:Float = 1) { // TODO: not yet used: for CurveSegment support anchor = null; From 54b0a31c777c9af786d99c2d20f160b8a61f88c2 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 22:09:31 -0400 Subject: [PATCH 39/42] Remove `SegmentPass`. --- away3d/materials/passes/SegmentPass.hx | 180 ------------------------- 1 file changed, 180 deletions(-) delete mode 100644 away3d/materials/passes/SegmentPass.hx diff --git a/away3d/materials/passes/SegmentPass.hx b/away3d/materials/passes/SegmentPass.hx deleted file mode 100644 index 0a9723a1..00000000 --- a/away3d/materials/passes/SegmentPass.hx +++ /dev/null @@ -1,180 +0,0 @@ -package away3d.materials.passes; - -import away3d.cameras.Camera3D; -import away3d.core.base.IRenderable; -import away3d.core.managers.RTTBufferManager; -import away3d.core.managers.Stage3DProxy; -import away3d.entities.SegmentSet; - -import openfl.display3D.Context3D; -import openfl.display3D.Context3DProgramType; -import openfl.geom.Matrix3D; -import openfl.Vector; - -/** - * SegmentPass is a material pass that draws wireframe segments. - */ -class SegmentPass extends MaterialPassBase -{ - private static var ONE_VECTOR:Vector = Vector.ofArray([ 1.0, 1.0, 1.0, 1.0 ]); - private static var FRONT_VECTOR:Vector = Vector.ofArray([ 0.0, 0.0, -1.0, 0.0 ]); - - private var _constants:Vector = new Vector(4, true); - private var _calcMatrix:Matrix3D; - private var _thickness:Float; - - /** - * Creates a new SegmentPass object. - * - * @param thickness the thickness of the segments to be drawn. - */ - public function new(thickness:Float) - { - _calcMatrix = new Matrix3D(); - - _thickness = thickness; - _constants[1] = 1/255; - - super(); - } - - /** - * @inheritDoc - */ - @:allow(away3d) override private function getVertexCode():String - { - return "m44 vt0, va0, vc8 \n" + // transform Q0 to eye space - "m44 vt1, va1, vc8 \n" + // transform Q1 to eye space - "sub vt2, vt1, vt0 \n" + // L = Q1 - Q0 - - // test if behind camera near plane - // if 0 - Q0.z < Camera.near then the point needs to be clipped - //"neg vt5.x, vt0.z \n" + // 0 - Q0.z - "slt vt5.x, vt0.z, vc7.z \n" + // behind = ( 0 - Q0.z < -Camera.near ) ? 1 : 0 - "sub vt5.y, vc5.x, vt5.x \n" + // !behind = 1 - behind - - // p = point on the plane (0,0,-near) - // n = plane normal (0,0,-1) - // D = Q1 - Q0 - // t = ( dot( n, ( p - Q0 ) ) / ( dot( n, d ) - - // solve for t where line crosses Camera.near - "add vt4.x, vt0.z, vc7.z \n" + // Q0.z + ( -Camera.near ) - "sub vt4.y, vt0.z, vt1.z \n" + // Q0.z - Q1.z - - // fix divide by zero for horizontal lines - "seq vt4.z, vt4.y vc6.x \n" + // offset = (Q0.z - Q1.z)==0 ? 1 : 0 - "add vt4.y, vt4.y, vt4.z \n" + // ( Q0.z - Q1.z ) + offset - - "div vt4.z, vt4.x, vt4.y \n" + // t = ( Q0.z - near ) / ( Q0.z - Q1.z ) - - "mul vt4.xyz, vt4.zzz, vt2.xyz \n" + // t(L) - "add vt3.xyz, vt0.xyz, vt4.xyz \n" + // Qclipped = Q0 + t(L) - "mov vt3.w, vc5.x \n" + // Qclipped.w = 1 - - // If necessary, replace Q0 with new Qclipped - "mul vt0, vt0, vt5.yyyy \n" + // !behind * Q0 - "mul vt3, vt3, vt5.xxxx \n" + // behind * Qclipped - "add vt0, vt0, vt3 \n" + // newQ0 = Q0 + Qclipped - - // calculate side vector for line - "sub vt2, vt1, vt0 \n" + // L = Q1 - Q0 - "nrm vt2.xyz, vt2.xyz \n" + // normalize( L ) - "nrm vt5.xyz, vt0.xyz \n" + // D = normalize( Q1 ) - "mov vt5.w, vc5.x \n" + // D.w = 1 - "crs vt3.xyz, vt2, vt5 \n" + // S = L x D - "nrm vt3.xyz, vt3.xyz \n" + // normalize( S ) - - // face the side vector properly for the given point - "mul vt3.xyz, vt3.xyz, va2.xxx \n" + // S *= weight - "mov vt3.w, vc5.x \n" + // S.w = 1 - - // calculate the amount required to move at the point's distance to correspond to the line's pixel width - // scale the side vector by that amount - "dp3 vt4.x, vt0, vc6 \n" + // distance = dot( view ) - "mul vt4.x, vt4.x, vc7.x \n" + // distance *= vpsod - "mul vt3.xyz, vt3.xyz, vt4.xxx \n" + // S.xyz *= pixelScaleFactor - - // add scaled side vector to Q0 and transform to clip space - "add vt0.xyz, vt0.xyz, vt3.xyz \n" + // Q0 + S - - "m44 op, vt0, vc0 \n" + // transform Q0 to clip space - - // interpolate color - "mov v0, va3 \n"; - } - - /** - * @inheritDoc - */ - @:allow(away3d) override private function getFragmentCode(animationCode:String):String - { - return "mov oc, v0\n"; - } - - /** - * @inheritDoc - * todo: keep maps in dictionary per renderable - */ - @:allow(away3d) override private function render(renderable:IRenderable, stage3DProxy:Stage3DProxy, camera:Camera3D, viewProjection:Matrix3D):Void - { - var context:Context3D = stage3DProxy._context3D; - _calcMatrix.copyFrom(renderable.sourceEntity.sceneTransform); - _calcMatrix.append(camera.inverseSceneTransform); - - var subSetCount:Int = cast(renderable, SegmentSet).subSetCount; - - if (cast(renderable, SegmentSet).hasData) { - for (i in 0...subSetCount) { - renderable.activateVertexBuffer(i, stage3DProxy); - context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 8, _calcMatrix, true); - stage3DProxy.drawTriangles(renderable.getIndexBuffer(stage3DProxy), 0, renderable.numTriangles); - } - } - } - - /** - * @inheritDoc - */ - @:allow(away3d) override private function activate(stage3DProxy:Stage3DProxy, camera:Camera3D):Void - { - var context:Context3D = stage3DProxy._context3D; - super.activate(stage3DProxy, camera); - - if (stage3DProxy.scissorRect != null) - _constants[0] = _thickness/Math.min(stage3DProxy.scissorRect.width, stage3DProxy.scissorRect.height); - else - _constants[0] = _thickness/Math.min(stage3DProxy.width, stage3DProxy.height); - - // value to convert distance from camera to model length per pixel width - _constants[2] = camera.lens.near; - - context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, ONE_VECTOR); - context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 6, FRONT_VECTOR); - context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 7, _constants); - - // projection matrix - if (stage3DProxy.renderTarget == null) - context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, camera.lens.matrix, true); - else - { - //TODO: to find a better way - _calcMatrix.copyFrom(camera.lens.matrix); - var rttBufferManager:RTTBufferManager = RTTBufferManager.getInstance(stage3DProxy); - _calcMatrix.appendScale(rttBufferManager.textureRatioX, rttBufferManager.textureRatioY, 1); - context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _calcMatrix, true); - } - } - - /** - * @inheritDoc - */ - @:allow(away3d) override private function deactivate(stage3DProxy:Stage3DProxy):Void - { - var context:Context3D = stage3DProxy._context3D; - context.setVertexBufferAt(0, null); - context.setVertexBufferAt(1, null); - context.setVertexBufferAt(2, null); - context.setVertexBufferAt(3, null); - } -} \ No newline at end of file From b26428a0df4145e75093c502509d356bf3800e2e Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 22:09:52 -0400 Subject: [PATCH 40/42] Fix type error. --- away3d/core/partition/ViewVolume.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/away3d/core/partition/ViewVolume.hx b/away3d/core/partition/ViewVolume.hx index cbef4ba4..2f7a55ec 100644 --- a/away3d/core/partition/ViewVolume.hx +++ b/away3d/core/partition/ViewVolume.hx @@ -86,7 +86,7 @@ class ViewVolume extends NodeBase { if (traverser.enterNode(this)) { if (_debugPrimitive != null) - traverser.applyRenderable(_debugPrimitive); + traverser.applyEntity(_debugPrimitive); if (!_active) return; From c8bc74a97e4454f70fe02cbca5f1c1a013070ac0 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 22:11:00 -0400 Subject: [PATCH 41/42] Also remove `SegmentMaterial`. --- away3d/materials/SegmentMaterial.hx | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 away3d/materials/SegmentMaterial.hx diff --git a/away3d/materials/SegmentMaterial.hx b/away3d/materials/SegmentMaterial.hx deleted file mode 100644 index 20238f0d..00000000 --- a/away3d/materials/SegmentMaterial.hx +++ /dev/null @@ -1,27 +0,0 @@ -package away3d.materials; - -import away3d.materials.passes.SegmentPass; - -/** - * SegmentMaterial is a material exclusively used to render wireframe objects - * - * @see away3d.entities.Lines - */ -class SegmentMaterial extends MaterialBase -{ - private var _screenPass:SegmentPass; - - /** - * Creates a new SegmentMaterial object. - * - * @param thickness The thickness of the wireframe lines. - */ - public function new(thickness:Float = 1.25) - { - super(); - - bothSides = true; - addPass(_screenPass = new SegmentPass(thickness)); - _screenPass.material = this; - } -} \ No newline at end of file From 8b167384d08c92ec51c5f4eae7c4f1b8494e15eb Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sat, 3 Oct 2026 22:23:24 -0400 Subject: [PATCH 42/42] Fix segments not getting rendered. --- away3d/entities/SegmentSet.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/away3d/entities/SegmentSet.hx b/away3d/entities/SegmentSet.hx index f6086315..56390d29 100644 --- a/away3d/entities/SegmentSet.hx +++ b/away3d/entities/SegmentSet.hx @@ -67,7 +67,7 @@ class SegmentSet extends Mesh { if (material == null) { material = DefaultMaterialManager.getDefaultMaterial(this); } - if (material.hasMethodOfType(SegmentMethod)) { + if (!material.hasMethodOfType(SegmentMethod)) { material.addMethod(new SegmentMethod()); }