Initial version 3 export support

This commit is contained in:
Pyogenics
2025-04-30 18:45:17 +01:00
parent 437d9f079c
commit 56a55c8516
4 changed files with 175 additions and 13 deletions

View File

@@ -139,7 +139,19 @@ class A3D:
stream.read(padding) stream.read(padding)
def writeRootBlock3(self, stream): def writeRootBlock3(self, stream):
raise RuntimeError("Version 3 files are not supported yet") buffer = BytesIO()
# Write data to the buffer
print("Writing root block")
self.writeMaterialBlock3(buffer)
self.writeMeshBlock3(buffer)
self.writeTransformBlock3(buffer)
self.writeObjectBlock3(buffer)
# Write buffer to stream
packStream("<2I", stream, A3D_ROOTBLOCK_SIGNATURE, buffer.tell())
buffer.seek(0, 0)
stream.write(buffer.read())
''' '''
Material data blocks Material data blocks
@@ -188,6 +200,24 @@ class A3D:
padding = calculatePadding(length) padding = calculatePadding(length)
stream.read(padding) stream.read(padding)
def writeMaterialBlock3(self, stream):
buffer = BytesIO()
# Write data to the buffer
print("Writing material block")
packStream("<I", buffer, len(self.materials))
for material in self.materials:
material.write3(buffer)
# Write buffer to stream
packStream("<2I", stream, A3D_MATERIALBLOCK_SIGNATURE, buffer.tell())
buffer.seek(0, 0)
stream.write(buffer.read())
# Padding
paddingSize = calculatePadding(buffer.tell())
stream.write(b"\x00" * paddingSize)
''' '''
Mesh data blocks Mesh data blocks
''' '''
@@ -235,6 +265,24 @@ class A3D:
padding = calculatePadding(length) padding = calculatePadding(length)
stream.read(padding) stream.read(padding)
def writeMeshBlock3(self, stream):
buffer = BytesIO()
# Write data to the buffer
print("Writing mesh block")
packStream("<I", buffer, len(self.meshes))
for mesh in self.meshes:
mesh.write3(buffer)
# Write buffer to stream
packStream("<2I", stream, A3D_MESHBLOCK_SIGNATURE, buffer.tell())
buffer.seek(0, 0)
stream.write(buffer.read())
# Padding
paddingSize = calculatePadding(buffer.tell())
stream.write(b"\x00" * paddingSize)
''' '''
Transform data blocks Transform data blocks
''' '''
@@ -279,7 +327,6 @@ class A3D:
# Read data # Read data
print(f"Reading transform block with {transformCount} transforms and length {length}") print(f"Reading transform block with {transformCount} transforms and length {length}")
transforms = []
for _ in range(transformCount): for _ in range(transformCount):
transform = A3DObjects.A3DTransform() transform = A3DObjects.A3DTransform()
transform.read3(stream) transform.read3(stream)
@@ -293,6 +340,26 @@ class A3D:
padding = calculatePadding(length) padding = calculatePadding(length)
stream.read(padding) stream.read(padding)
def writeTransformBlock3(self, stream):
buffer = BytesIO()
# Write data to the buffer
print("Writing transform block")
packStream("<I", buffer, len(self.transforms))
for transform in self.transforms:
transform.write3(buffer)
for parentID in self.transformParentIDs:
packStream("<i", buffer, parentID)
# Write buffer to stream
packStream("<2I", stream, A3D_TRANSFORMBLOCK_SIGNATURE, buffer.tell())
buffer.seek(0, 0)
stream.write(buffer.read())
# Padding
paddingSize = calculatePadding(buffer.tell())
stream.write(b"\x00" * paddingSize)
''' '''
Object data blocks Object data blocks
''' '''
@@ -339,3 +406,21 @@ class A3D:
# Padding # Padding
padding = calculatePadding(length) padding = calculatePadding(length)
stream.read(padding) stream.read(padding)
def writeObjectBlock3(self, stream):
buffer = BytesIO()
# Write data to the buffer
print("Writing object block")
packStream("<I", buffer, len(self.objects))
for objec in self.objects:
objec.write3(buffer)
# Write buffer to stream
packStream("<2I", stream, A3D_OBJECTBLOCK_SIGNATURE, buffer.tell())
buffer.seek(0, 0)
stream.write(buffer.read())
# Padding
paddingSize = calculatePadding(buffer.tell())
stream.write(b"\x00" * paddingSize)

View File

@@ -31,9 +31,10 @@ from .A3DObjects import (
) )
class A3DBlenderExporter: class A3DBlenderExporter:
def __init__(self, modelData, objects): def __init__(self, modelData, objects, version=2):
self.modelData = modelData self.modelData = modelData
self.objects = objects self.objects = objects
self.version = version
def exportData(self): def exportData(self):
print("Exporting blender data to A3D") print("Exporting blender data to A3D")
@@ -60,7 +61,7 @@ class A3DBlenderExporter:
materials[ma.name] = materialData materials[ma.name] = materialData
# Create mesh # Create mesh
mesh = self.buildA3DMesh(me) mesh = self.buildA3DMesh(me, ob)
meshes.append(mesh) meshes.append(mesh)
# Create transform # Create transform
transform = A3DObjects.A3DTransform() transform = A3DObjects.A3DTransform()
@@ -68,22 +69,34 @@ class A3DBlenderExporter:
rotationW, rotationX, rotationY, rotationZ = ob.rotation_quaternion rotationW, rotationX, rotationY, rotationZ = ob.rotation_quaternion
transform.rotation = (rotationX, rotationY, rotationZ, rotationW) transform.rotation = (rotationX, rotationY, rotationZ, rotationW)
transform.scale = ob.scale transform.scale = ob.scale
transform.name = ob.name
transforms[ob.name] = transform transforms[ob.name] = transform
# Create object # Create object
objec = A3DObjects.A3DObject() objec = A3DObjects.A3DObject()
objec.name = ob.name objec.name = ob.name
objec.meshID = len(meshes) - 1 objec.meshID = len(meshes) - 1
objec.transformID = len(transforms) - 1 objec.transformID = len(transforms) - 1
materialIDs = []
for ma in me.materials:
materialID = list(materials.keys()).index(ma.name)
materialIDs.append(materialID)
objec.materialCount = len(materialIDs)
objec.materialIDs = materialIDs
objects.append(objec) objects.append(objec)
# Create parentIDs # Create parentIDs
transformParentIDs = [] transformParentIDs = []
for ob in self.objects: for ob in self.objects:
parentOB = ob.parent parentOB = ob.parent
if (parentOB == None) or (parentOB.name not in transforms): if (parentOB == None) or (parentOB.name not in transforms):
transformParentIDs.append(0) #XXX: this is only for version 2 if self.version < 3:
transformParentIDs.append(0)
else:
transformParentIDs.append(-1)
else: else:
parentIndex = list(transforms.keys()).index(parentOB.name) parentIndex = list(transforms.keys()).index(parentOB.name)
transformParentIDs.append(parentIndex+1) if self.version < 3:
parentIndex += 1 # Version 2 uses 0 to signify empty parent
transformParentIDs.append(parentIndex)
self.modelData.materials = materials.values() self.modelData.materials = materials.values()
self.modelData.meshes = meshes self.modelData.meshes = meshes
@@ -91,7 +104,7 @@ class A3DBlenderExporter:
self.modelData.transformParentIDs = transformParentIDs self.modelData.transformParentIDs = transformParentIDs
self.modelData.objects = objects self.modelData.objects = objects
def buildA3DMesh(self, me): def buildA3DMesh(self, me, ob):
mesh = A3DObjects.A3DMesh() mesh = A3DObjects.A3DMesh()
mesh.vertexCount = len(me.vertices) mesh.vertexCount = len(me.vertices)
@@ -137,4 +150,12 @@ class A3DBlenderExporter:
mesh.submeshCount = len(submeshes) mesh.submeshCount = len(submeshes)
mesh.submeshes = submeshes mesh.submeshes = submeshes
# Bound box data
bounds = []
for bound in ob.bound_box:
x, y, z = bound
bounds.append((x, y, z))
mesh.bboxMax = max(bounds)
mesh.bboxMin = min(bounds)
return mesh return mesh

View File

@@ -20,7 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
''' '''
from .IOTools import unpackStream, packStream, readNullTerminatedString, writeNullTerminatedString, readLengthPrefixedString, calculatePadding from .IOTools import unpackStream, packStream, readNullTerminatedString, writeNullTerminatedString, readLengthPrefixedString, writeLengthPrefixedString, calculatePadding
class A3DMaterial: class A3DMaterial:
def __init__(self): def __init__(self):
@@ -48,6 +48,12 @@ class A3DMaterial:
print(f"[A3DMaterial name: {self.name} color: {self.color} diffuse map: {self.diffuseMap}]") print(f"[A3DMaterial name: {self.name} color: {self.color} diffuse map: {self.diffuseMap}]")
def write3(self, stream):
writeLengthPrefixedString(stream, self.name)
colorR, colorG, colorB = self.color
packStream("<3f", stream, colorR, colorG, colorB)
writeLengthPrefixedString(stream, self.diffuseMap)
class A3DMesh: class A3DMesh:
def __init__(self): def __init__(self):
self.name = "" self.name = ""
@@ -110,6 +116,24 @@ class A3DMesh:
print(f"[A3DMesh name: {self.name} bbox max: {self.bboxMax} bbox min: {self.bboxMin} vertex buffers: {len(self.vertexBuffers)} submeshes: {len(self.submeshes)}]") print(f"[A3DMesh name: {self.name} bbox max: {self.bboxMax} bbox min: {self.bboxMin} vertex buffers: {len(self.vertexBuffers)} submeshes: {len(self.submeshes)}]")
def write3(self, stream):
writeLengthPrefixedString(stream, self.name)
bboxMaxX, bboxMaxY, bboxMaxZ = self.bboxMax
packStream("<3f", stream, bboxMaxX, bboxMaxY, bboxMaxZ)
bboxMinX, bboxMinY, bboxMinZ = self.bboxMin
packStream("<3f", stream, bboxMinX, bboxMinY, bboxMinZ)
packStream("<f", stream, 0.0) # XXX: Unknown float value!
# Write vertex buffers
packStream("<2I", stream, self.vertexCount, self.vertexBufferCount)
for vertexBuffer in self.vertexBuffers:
vertexBuffer.write2(stream)
# Write submeshes
packStream("<I", stream, self.submeshCount)
for submesh in self.submeshes:
submesh.write3(stream)
A3D_VERTEXTYPE_COORDINATE = 1 A3D_VERTEXTYPE_COORDINATE = 1
A3D_VERTEXTYPE_UV1 = 2 A3D_VERTEXTYPE_UV1 = 2
A3D_VERTEXTYPE_NORMAL1 = 3 A3D_VERTEXTYPE_NORMAL1 = 3
@@ -179,11 +203,20 @@ class A3DSubmesh:
self.indices = list(unpackStream(f"<{self.indexCount}H", stream)) self.indices = list(unpackStream(f"<{self.indexCount}H", stream))
# Padding # Padding
padding = calculatePadding(self.indexCount*2) # Each index is 2 bytes paddingSize = calculatePadding(self.indexCount*2) # Each index is 2 bytes
stream.read(padding) stream.read(paddingSize)
print(f"[A3DSubmesh indices: {len(self.indices)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]") print(f"[A3DSubmesh indices: {len(self.indices)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
def write3(self, stream):
packStream("<I", stream, self.indexCount)
for index in self.indices:
packStream("<H", stream, index)
# Padding
paddingSize = calculatePadding(self.indexCount*2) # Each index is 2 bytes
stream.write(b"\x00" * paddingSize)
class A3DTransform: class A3DTransform:
def __init__(self): def __init__(self):
self.name = "" self.name = ""
@@ -214,6 +247,15 @@ class A3DTransform:
print(f"[A3DTransform name: {self.name} position: {self.position} rotation: {self.rotation} scale: {self.scale}]") print(f"[A3DTransform name: {self.name} position: {self.position} rotation: {self.rotation} scale: {self.scale}]")
def write3(self, stream):
writeLengthPrefixedString(stream, self.name)
positionX, positionY, positionZ = self.position
packStream("<3f", stream, positionX, positionY, positionZ)
rotationX, rotationY, rotationZ, rotationW = self.rotation
packStream("<4f", stream, rotationX, rotationY, rotationZ, rotationW)
scaleX, scaleY, scaleZ = self.scale
packStream("<3f", stream, scaleX, scaleY, scaleZ)
class A3DObject: class A3DObject:
def __init__(self): def __init__(self):
self.name = "" self.name = ""
@@ -242,3 +284,8 @@ class A3DObject:
self.materialIDs.append(materialID) self.materialIDs.append(materialID)
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]") print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]")
def write3(self, stream):
packStream("<3I", stream, self.meshID, self.transformID, self.materialCount)
for materialID in self.materialIDs:
packStream("<i", stream, materialID)

View File

@@ -57,3 +57,12 @@ def readLengthPrefixedString(stream):
stream.read(paddingSize) stream.read(paddingSize)
return string.decode("utf8", errors="ignore") return string.decode("utf8", errors="ignore")
def writeLengthPrefixedString(stream, string):
string = string.encode("utf-8")
packStream("<I", stream, len(string))
stream.write(string)
paddingSize = calculatePadding(len(string))
stream.write(b"\x00" * paddingSize)