Add A3D version 3 support

This commit is contained in:
Pyogenics
2024-11-24 18:34:10 +00:00
parent e8fd653c80
commit 696a65e5a2
3 changed files with 174 additions and 7 deletions

View File

@@ -20,7 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
from .IOTools import unpackStream, readNullTerminatedString
from .IOTools import unpackStream, readNullTerminatedString, calculatePadding
from . import A3DObjects
'''
@@ -83,7 +83,20 @@ class A3D:
self.readObjectBlock2(stream)
def readRootBlock3(self, stream):
raise RuntimeError("Version 3 files are not supported yet")
# Verify signature
signature, length = unpackStream("<2I", stream)
if signature != A3D_ROOTBLOCK_SIGNATURE:
raise RuntimeError(f"Invalid root data block signature: {signature}")
# Read data
self.readMaterialBlock3(stream)
self.readMeshBlock3(stream)
self.readTransformBlock3(stream)
self.readObjectBlock3(stream)
# Padding
padding = calculatePadding(length)
stream.read(padding)
'''
Material data blocks
@@ -101,6 +114,23 @@ class A3D:
material.read2(stream)
self.materials.append(material)
def readMaterialBlock3(self, stream):
# Verify signature
signature, length, materialCount = unpackStream("<3I", stream)
if signature != A3D_MATERIALBLOCK_SIGNATURE:
raise RuntimeError(f"Invalid material data block signature: {signature}")
# Read data
print(f"Reading material block with {materialCount} materials and length {length}")
for _ in range(materialCount):
material = A3DObjects.A3DMaterial()
material.read3(stream)
self.materials.append(material)
# Padding
padding = calculatePadding(length)
stream.read(padding)
'''
Mesh data blocks
'''
@@ -117,6 +147,23 @@ class A3D:
mesh.read2(stream)
self.meshes.append(mesh)
def readMeshBlock3(self, stream):
# Verify signature
signature, length, meshCount = unpackStream("<3I", stream)
if signature != A3D_MESHBLOCK_SIGNATURE:
raise RuntimeError(f"Invalid mesh data block signature: {signature}")
# Read data
print(f"Reading mesh block with {meshCount} meshes and length {length}")
for _ in range(meshCount):
mesh = A3DObjects.A3DMesh()
mesh.read3(stream)
self.meshes.append(mesh)
# Padding
padding = calculatePadding(length)
stream.read(padding)
'''
Transform data blocks
'''
@@ -139,6 +186,29 @@ class A3D:
transformID, = unpackStream("<I", stream)
self.transforms[transformID] = transforms[transformI]
def readTransformBlock3(self, stream):
# Verify signature
signature, length, transformCount = unpackStream("<3I", stream)
if signature != A3D_TRANSFORMBLOCK_SIGNATURE:
print(f"{stream.tell()}")
raise RuntimeError(f"Invalid transform data block signature: {signature}")
# Read data
print(f"Reading transform block with {transformCount} transforms and length {length}")
transforms = []
for _ in range(transformCount):
transform = A3DObjects.A3DTransform()
transform.read3(stream)
transforms.append(transform)
# Read and assign transform ids
for transformI in range(transformCount):
transformID, = unpackStream("<I", stream)
self.transforms[transformID] = transforms[transformI]
# Padding
padding = calculatePadding(length)
stream.read(padding)
'''
Object data blocks
'''
@@ -155,3 +225,21 @@ class A3D:
objec = A3DObjects.A3DObject()
objec.read2(stream)
self.objects.append(objec)
def readObjectBlock3(self, stream):
# Verify signature
signature, length, objectCount = unpackStream("<3I", stream)
if signature != A3D_OBJECTBLOCK_SIGNATURE:
print(f"{stream.tell()}")
raise RuntimeError(f"Invalid object data block signature: {signature}")
# Read data
print(f"Reading object block with {objectCount} objects and length {length}")
for _ in range(objectCount):
objec = A3DObjects.A3DObject()
objec.read3(stream)
self.objects.append(objec)
# Padding
padding = calculatePadding(length)
stream.read(padding)

View File

@@ -20,7 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
from .IOTools import unpackStream, readNullTerminatedString
from .IOTools import unpackStream, readNullTerminatedString, readLengthPrefixedString, calculatePadding
class A3DMaterial:
def __init__(self):
@@ -36,10 +36,17 @@ class A3DMaterial:
print(f"[A3DMaterial name: {self.name} color: {self.color} diffuse map: {self.diffuseMap}]")
def read3(self, stream):
pass
self.name = readLengthPrefixedString(stream)
self.color = unpackStream("<3f", stream)
self.diffuseMap = readLengthPrefixedString(stream)
print(f"[A3DMaterial name: {self.name} color: {self.color} diffuse map: {self.diffuseMap}]")
class A3DMesh:
def __init__(self):
self.name = ""
self.bboxMax = None
self.bboxMin = None
self.vertexBuffers = []
self.submeshes = []
@@ -58,7 +65,31 @@ class A3DMesh:
submesh.read2(stream)
self.submeshes.append(submesh)
print(f"[A3DMesh 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 read3(self, stream):
# Read mesh info
self.name = readLengthPrefixedString(stream)
# XXX: bbox order maybe incorrect, check this (might be min then max and not max then min)
self.bboxMax = unpackStream("<3f", stream)
self.bboxMin = unpackStream("<3f", stream)
stream.read(4) # XXX: Unknown float value
# Read vertex buffers
vertexCount, bufferCount = unpackStream("<2I", stream)
for _ in range(bufferCount):
vertexBuffer = A3DVertexBuffer()
vertexBuffer.read2(vertexCount, stream)
self.vertexBuffers.append(vertexBuffer)
# Read submeshes
submeshCount, = unpackStream("<I", stream)
for _ in range(submeshCount):
submesh = A3DSubmesh()
submesh.read3(stream)
self.submeshes.append(submesh)
print(f"[A3DMesh name: {self.name} bbox max: {self.bboxMax} bbox min: {self.bboxMin} vertex buffers: {len(self.vertexBuffers)} submeshes: {len(self.submeshes)}]")
A3D_VERTEXTYPE_COORDINATE = 1
A3D_VERTEXTYPE_UV1 = 2
@@ -93,6 +124,7 @@ class A3DVertexBuffer:
class A3DSubmesh:
def __init__(self):
self.indices = []
self.faces = []
self.smoothingGroups = []
self.materialID = None
@@ -107,10 +139,24 @@ class A3DSubmesh:
self.smoothingGroups.append(smoothingGroup)
self.materialID, = unpackStream("<H", stream)
print(f"[A3DSubmesh faces: {len(self.faces)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
print(f"[A3DSubmesh indices: {len(self.indices)} faces: {len(self.faces)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
def read3(self, stream):
# Read indices
indexCount, = unpackStream("<I", stream)
for _ in range(indexCount):
index, = unpackStream("<H", stream)
self.indices.append(index)
# Padding
padding = calculatePadding(indexCount*2) # Each index is 2 bytes
stream.read(padding)
print(f"[A3DSubmesh indices: {len(self.indices)} faces: {len(self.faces)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
class A3DTransform:
def __init__(self):
self.name = ""
self.position = (0.0, 0.0, 0.0)
self.rotation = (0.0, 0.0, 0.0, 0.0)
self.scale = (0.0, 0.0, 0.0)
@@ -122,14 +168,33 @@ class A3DTransform:
print(f"[A3DTransform position: {self.position} rotation: {self.rotation} scale: {self.scale}]")
def read3(self, stream):
self.name = readLengthPrefixedString(stream)
self.position = unpackStream("<3f", stream)
self.rotation = unpackStream("<4f", stream)
self.scale = unpackStream("<3f", stream)
print(f"[A3DTransform name: {self.name} position: {self.position} rotation: {self.rotation} scale: {self.scale}]")
class A3DObject:
def __init__(self):
self.name = ""
self.meshID = None
self.transformID = None
self.materialIDs = []
def read2(self, stream):
self.name = readNullTerminatedString(stream)
self.meshID, self.transformID = unpackStream("<2I", stream)
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID}]")
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]")
def read3(self, stream):
self.meshID, self.transformID, materialCount = unpackStream("<3I", stream)
# Read material IDs
for _ in range(materialCount):
materialID, = unpackStream("<I", stream)
self.materialIDs.append(materialID)
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]")

View File

@@ -34,3 +34,17 @@ def readNullTerminatedString(stream):
string += char
char = stream.read(1)
return string.decode("utf8")
def calculatePadding(length):
# (it basically works with rounding)
paddingSize = (((length + 3) // 4) * 4) - length
return paddingSize
def readLengthPrefixedString(stream):
length, = unpackStream("<I", stream)
string = stream.read(length)
paddingSize = calculatePadding(length)
stream.read(paddingSize)
return string.decode("utf8")