Skip to content

Commit 696a65e

Browse files
committed
Add A3D version 3 support
1 parent e8fd653 commit 696a65e

3 files changed

Lines changed: 174 additions & 7 deletions

File tree

io_scene_a3d/A3D.py

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
SOFTWARE.
2121
'''
2222

23-
from .IOTools import unpackStream, readNullTerminatedString
23+
from .IOTools import unpackStream, readNullTerminatedString, calculatePadding
2424
from . import A3DObjects
2525

2626
'''
@@ -83,7 +83,20 @@ def readRootBlock2(self, stream):
8383
self.readObjectBlock2(stream)
8484

8585
def readRootBlock3(self, stream):
86-
raise RuntimeError("Version 3 files are not supported yet")
86+
# Verify signature
87+
signature, length = unpackStream("<2I", stream)
88+
if signature != A3D_ROOTBLOCK_SIGNATURE:
89+
raise RuntimeError(f"Invalid root data block signature: {signature}")
90+
91+
# Read data
92+
self.readMaterialBlock3(stream)
93+
self.readMeshBlock3(stream)
94+
self.readTransformBlock3(stream)
95+
self.readObjectBlock3(stream)
96+
97+
# Padding
98+
padding = calculatePadding(length)
99+
stream.read(padding)
87100

88101
'''
89102
Material data blocks
@@ -101,6 +114,23 @@ def readMaterialBlock2(self, stream):
101114
material.read2(stream)
102115
self.materials.append(material)
103116

117+
def readMaterialBlock3(self, stream):
118+
# Verify signature
119+
signature, length, materialCount = unpackStream("<3I", stream)
120+
if signature != A3D_MATERIALBLOCK_SIGNATURE:
121+
raise RuntimeError(f"Invalid material data block signature: {signature}")
122+
123+
# Read data
124+
print(f"Reading material block with {materialCount} materials and length {length}")
125+
for _ in range(materialCount):
126+
material = A3DObjects.A3DMaterial()
127+
material.read3(stream)
128+
self.materials.append(material)
129+
130+
# Padding
131+
padding = calculatePadding(length)
132+
stream.read(padding)
133+
104134
'''
105135
Mesh data blocks
106136
'''
@@ -117,6 +147,23 @@ def readMeshBlock2(self, stream):
117147
mesh.read2(stream)
118148
self.meshes.append(mesh)
119149

150+
def readMeshBlock3(self, stream):
151+
# Verify signature
152+
signature, length, meshCount = unpackStream("<3I", stream)
153+
if signature != A3D_MESHBLOCK_SIGNATURE:
154+
raise RuntimeError(f"Invalid mesh data block signature: {signature}")
155+
156+
# Read data
157+
print(f"Reading mesh block with {meshCount} meshes and length {length}")
158+
for _ in range(meshCount):
159+
mesh = A3DObjects.A3DMesh()
160+
mesh.read3(stream)
161+
self.meshes.append(mesh)
162+
163+
# Padding
164+
padding = calculatePadding(length)
165+
stream.read(padding)
166+
120167
'''
121168
Transform data blocks
122169
'''
@@ -139,6 +186,29 @@ def readTransformBlock2(self, stream):
139186
transformID, = unpackStream("<I", stream)
140187
self.transforms[transformID] = transforms[transformI]
141188

189+
def readTransformBlock3(self, stream):
190+
# Verify signature
191+
signature, length, transformCount = unpackStream("<3I", stream)
192+
if signature != A3D_TRANSFORMBLOCK_SIGNATURE:
193+
print(f"{stream.tell()}")
194+
raise RuntimeError(f"Invalid transform data block signature: {signature}")
195+
196+
# Read data
197+
print(f"Reading transform block with {transformCount} transforms and length {length}")
198+
transforms = []
199+
for _ in range(transformCount):
200+
transform = A3DObjects.A3DTransform()
201+
transform.read3(stream)
202+
transforms.append(transform)
203+
# Read and assign transform ids
204+
for transformI in range(transformCount):
205+
transformID, = unpackStream("<I", stream)
206+
self.transforms[transformID] = transforms[transformI]
207+
208+
# Padding
209+
padding = calculatePadding(length)
210+
stream.read(padding)
211+
142212
'''
143213
Object data blocks
144214
'''
@@ -155,3 +225,21 @@ def readObjectBlock2(self, stream):
155225
objec = A3DObjects.A3DObject()
156226
objec.read2(stream)
157227
self.objects.append(objec)
228+
229+
def readObjectBlock3(self, stream):
230+
# Verify signature
231+
signature, length, objectCount = unpackStream("<3I", stream)
232+
if signature != A3D_OBJECTBLOCK_SIGNATURE:
233+
print(f"{stream.tell()}")
234+
raise RuntimeError(f"Invalid object data block signature: {signature}")
235+
236+
# Read data
237+
print(f"Reading object block with {objectCount} objects and length {length}")
238+
for _ in range(objectCount):
239+
objec = A3DObjects.A3DObject()
240+
objec.read3(stream)
241+
self.objects.append(objec)
242+
243+
# Padding
244+
padding = calculatePadding(length)
245+
stream.read(padding)

io_scene_a3d/A3DObjects.py

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
SOFTWARE.
2121
'''
2222

23-
from .IOTools import unpackStream, readNullTerminatedString
23+
from .IOTools import unpackStream, readNullTerminatedString, readLengthPrefixedString, calculatePadding
2424

2525
class A3DMaterial:
2626
def __init__(self):
@@ -36,10 +36,17 @@ def read2(self, stream):
3636
print(f"[A3DMaterial name: {self.name} color: {self.color} diffuse map: {self.diffuseMap}]")
3737

3838
def read3(self, stream):
39-
pass
39+
self.name = readLengthPrefixedString(stream)
40+
self.color = unpackStream("<3f", stream)
41+
self.diffuseMap = readLengthPrefixedString(stream)
42+
43+
print(f"[A3DMaterial name: {self.name} color: {self.color} diffuse map: {self.diffuseMap}]")
4044

4145
class A3DMesh:
4246
def __init__(self):
47+
self.name = ""
48+
self.bboxMax = None
49+
self.bboxMin = None
4350
self.vertexBuffers = []
4451
self.submeshes = []
4552

@@ -58,7 +65,31 @@ def read2(self, stream):
5865
submesh.read2(stream)
5966
self.submeshes.append(submesh)
6067

61-
print(f"[A3DMesh vertex buffers: {len(self.vertexBuffers)} submeshes: {len(self.submeshes)}]")
68+
print(f"[A3DMesh name: {self.name} bbox max: {self.bboxMax} bbox min: {self.bboxMin} vertex buffers: {len(self.vertexBuffers)} submeshes: {len(self.submeshes)}]")
69+
70+
def read3(self, stream):
71+
# Read mesh info
72+
self.name = readLengthPrefixedString(stream)
73+
# XXX: bbox order maybe incorrect, check this (might be min then max and not max then min)
74+
self.bboxMax = unpackStream("<3f", stream)
75+
self.bboxMin = unpackStream("<3f", stream)
76+
stream.read(4) # XXX: Unknown float value
77+
78+
# Read vertex buffers
79+
vertexCount, bufferCount = unpackStream("<2I", stream)
80+
for _ in range(bufferCount):
81+
vertexBuffer = A3DVertexBuffer()
82+
vertexBuffer.read2(vertexCount, stream)
83+
self.vertexBuffers.append(vertexBuffer)
84+
85+
# Read submeshes
86+
submeshCount, = unpackStream("<I", stream)
87+
for _ in range(submeshCount):
88+
submesh = A3DSubmesh()
89+
submesh.read3(stream)
90+
self.submeshes.append(submesh)
91+
92+
print(f"[A3DMesh name: {self.name} bbox max: {self.bboxMax} bbox min: {self.bboxMin} vertex buffers: {len(self.vertexBuffers)} submeshes: {len(self.submeshes)}]")
6293

6394
A3D_VERTEXTYPE_COORDINATE = 1
6495
A3D_VERTEXTYPE_UV1 = 2
@@ -93,6 +124,7 @@ def read2(self, vertexCount, stream):
93124

94125
class A3DSubmesh:
95126
def __init__(self):
127+
self.indices = []
96128
self.faces = []
97129
self.smoothingGroups = []
98130
self.materialID = None
@@ -107,10 +139,24 @@ def read2(self, stream):
107139
self.smoothingGroups.append(smoothingGroup)
108140
self.materialID, = unpackStream("<H", stream)
109141

110-
print(f"[A3DSubmesh faces: {len(self.faces)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
142+
print(f"[A3DSubmesh indices: {len(self.indices)} faces: {len(self.faces)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
143+
144+
def read3(self, stream):
145+
# Read indices
146+
indexCount, = unpackStream("<I", stream)
147+
for _ in range(indexCount):
148+
index, = unpackStream("<H", stream)
149+
self.indices.append(index)
150+
151+
# Padding
152+
padding = calculatePadding(indexCount*2) # Each index is 2 bytes
153+
stream.read(padding)
154+
155+
print(f"[A3DSubmesh indices: {len(self.indices)} faces: {len(self.faces)} smoothing groups: {len(self.smoothingGroups)} materialID: {self.materialID}]")
111156

112157
class A3DTransform:
113158
def __init__(self):
159+
self.name = ""
114160
self.position = (0.0, 0.0, 0.0)
115161
self.rotation = (0.0, 0.0, 0.0, 0.0)
116162
self.scale = (0.0, 0.0, 0.0)
@@ -122,14 +168,33 @@ def read2(self, stream):
122168

123169
print(f"[A3DTransform position: {self.position} rotation: {self.rotation} scale: {self.scale}]")
124170

171+
def read3(self, stream):
172+
self.name = readLengthPrefixedString(stream)
173+
self.position = unpackStream("<3f", stream)
174+
self.rotation = unpackStream("<4f", stream)
175+
self.scale = unpackStream("<3f", stream)
176+
177+
print(f"[A3DTransform name: {self.name} position: {self.position} rotation: {self.rotation} scale: {self.scale}]")
178+
125179
class A3DObject:
126180
def __init__(self):
127181
self.name = ""
128182
self.meshID = None
129183
self.transformID = None
184+
self.materialIDs = []
130185

131186
def read2(self, stream):
132187
self.name = readNullTerminatedString(stream)
133188
self.meshID, self.transformID = unpackStream("<2I", stream)
134189

135-
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID}]")
190+
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]")
191+
192+
def read3(self, stream):
193+
self.meshID, self.transformID, materialCount = unpackStream("<3I", stream)
194+
195+
# Read material IDs
196+
for _ in range(materialCount):
197+
materialID, = unpackStream("<I", stream)
198+
self.materialIDs.append(materialID)
199+
200+
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]")

io_scene_a3d/IOTools.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,17 @@ def readNullTerminatedString(stream):
3434
string += char
3535
char = stream.read(1)
3636
return string.decode("utf8")
37+
38+
def calculatePadding(length):
39+
# (it basically works with rounding)
40+
paddingSize = (((length + 3) // 4) * 4) - length
41+
return paddingSize
42+
43+
def readLengthPrefixedString(stream):
44+
length, = unpackStream("<I", stream)
45+
string = stream.read(length)
46+
47+
paddingSize = calculatePadding(length)
48+
stream.read(paddingSize)
49+
50+
return string.decode("utf8")

0 commit comments

Comments
 (0)