mirror of
https://github.com/MapMakersAndProgrammers/io_scene_a3d.git
synced 2025-10-26 09:59:11 -07:00
Initial version 3 export support
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user