Skip to content

Commit e4e395b

Browse files
committed
Initial blender importing
1 parent 643d23d commit e4e395b

4 files changed

Lines changed: 187 additions & 9 deletions

File tree

io_scene_a3d/A3D.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def readTransformBlock3(self, stream):
203203
# Read and assign transform ids
204204
for transformI in range(transformCount):
205205
transformID, = unpackStream("<I", stream)
206-
self.transforms[transformID] = transforms[transformI]
206+
self.transforms[transformI] = transforms[transformI] #XXX: The IDs seem to be incorrect and instead map to index?
207207

208208
# Padding
209209
padding = calculatePadding(length)

io_scene_a3d/A3DObjects.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ def __init__(self):
135135
self.indexCount = 0
136136

137137
def read2(self, stream):
138-
self.indexCount, = unpackStream("<I", stream)*3
138+
self.indexCount, = unpackStream("<I", stream) # This is just the face count so multiply it by 3
139+
self.indexCount *= 3
139140
self.indices = list(unpackStream(f"<{self.indexCount}H", stream))
140141
self.smoothingGroups = list(unpackStream(f"<{self.indexCount//3}I", stream))
141142
self.materialID, = unpackStream("<H", stream)
@@ -195,7 +196,7 @@ def read3(self, stream):
195196

196197
# Read material IDs
197198
for _ in range(self.materialCount):
198-
materialID, = unpackStream("<I", stream)
199+
materialID, = unpackStream("<i", stream)
199200
self.materialIDs.append(materialID)
200201

201202
print(f"[A3DObject name: {self.name} meshID: {self.meshID} transformID: {self.transformID} materialIDs: {len(self.materialIDs)}]")

io_scene_a3d/__init__.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import bmesh
2+
import bpy
3+
from bpy.types import Operator
4+
from bpy.props import StringProperty, BoolProperty
5+
from bpy_extras.io_utils import ImportHelper
6+
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
7+
8+
from .A3D import A3D
9+
from .A3DObjects import (
10+
A3D_VERTEXTYPE_COORDINATE,
11+
A3D_VERTEXTYPE_UV1,
12+
A3D_VERTEXTYPE_NORMAL1,
13+
A3D_VERTEXTYPE_UV2,
14+
A3D_VERTEXTYPE_COLOR,
15+
A3D_VERTEXTYPE_NORMAL2
16+
)
17+
18+
'''
19+
Operators
20+
'''
21+
class ImportA3D(Operator, ImportHelper):
22+
bl_idname = "import_scene.alternativa"
23+
bl_label = "Import A3D"
24+
bl_description = "Import an A3D model"
25+
26+
filter_glob: StringProperty(default="*.a3d", options={'HIDDEN'})
27+
28+
def invoke(self, context, event):
29+
return ImportHelper.invoke(self, context, event)
30+
31+
def execute(self, context):
32+
filepath = self.filepath
33+
34+
# Read the file
35+
print(f"Reading A3D data from {filepath}")
36+
modelData = A3D()
37+
with open(filepath, "rb") as file:
38+
modelData.read(file)
39+
40+
# Import data into blender
41+
print("Importing mesh data into blender")
42+
# Create materials
43+
materials = []
44+
for material in modelData.materials:
45+
ma = bpy.data.materials.new(material.name)
46+
maWrapper = PrincipledBSDFWrapper(ma, is_readonly=False, use_nodes=True)
47+
maWrapper.base_color = material.color
48+
maWrapper.roughness = 1.0
49+
50+
materials.append(ma)
51+
# Build meshes
52+
meshes = []
53+
for mesh in modelData.meshes:
54+
me = bpy.data.meshes.new(mesh.name)
55+
56+
# Gather all vertex data
57+
coordinates = []
58+
uv1 = []
59+
normal1 = []
60+
uv2 = []
61+
colors = []
62+
normal2 = []
63+
for vertexBuffer in mesh.vertexBuffers:
64+
if vertexBuffer.bufferType == A3D_VERTEXTYPE_COORDINATE:
65+
coordinates += vertexBuffer.data
66+
elif vertexBuffer.bufferType == A3D_VERTEXTYPE_UV1:
67+
uv1 += vertexBuffer.data
68+
elif vertexBuffer.bufferType == A3D_VERTEXTYPE_NORMAL1:
69+
normal1 += vertexBuffer.data
70+
elif vertexBuffer.bufferType == A3D_VERTEXTYPE_UV2:
71+
uv2 += vertexBuffer.data
72+
elif vertexBuffer.bufferType == A3D_VERTEXTYPE_COLOR:
73+
colors += vertexBuffer.data
74+
elif vertexBuffer.bufferType == A3D_VERTEXTYPE_NORMAL2:
75+
normal2 += vertexBuffer.data
76+
77+
# Add blender vertices
78+
blenderVertexIndices = []
79+
blenderVertices = []
80+
blenderUV1s = []
81+
blenderUV2s = []
82+
for submesh in mesh.submeshes:
83+
polygonCount = len(submesh.indices) // 3
84+
me.vertices.add(polygonCount*3)
85+
me.loops.add(polygonCount*3)
86+
me.polygons.add(polygonCount)
87+
88+
for indexI in range(submesh.indexCount):
89+
index = submesh.indices[indexI]
90+
blenderVertexIndices.append(indexI)
91+
blenderVertices += list(coordinates[index])
92+
blenderUV1s.append(uv1[index])
93+
#blenderUV2s += uv2[index]
94+
me.vertices.foreach_set("co", blenderVertices)
95+
me.polygons.foreach_set("loop_start", range(0, len(blenderVertices)//3, 3))
96+
me.loops.foreach_set("vertex_index", blenderVertexIndices)
97+
98+
# UVs
99+
if len(uv1) != 0:
100+
uvData = me.uv_layers.new(name="UV1").data
101+
for polygonI, po in enumerate(me.polygons):
102+
indexI = polygonI * 3
103+
uvData[po.loop_start].uv = blenderUV1s[blenderVertexIndices[indexI]]
104+
uvData[po.loop_start+1].uv = blenderUV1s[blenderVertexIndices[indexI+1]]
105+
uvData[po.loop_start+2].uv = blenderUV1s[blenderVertexIndices[indexI+2]]
106+
107+
# Apply materials (version 2)
108+
faceIndexBase = 0
109+
for submeshI, submesh in enumerate(mesh.submeshes):
110+
if submesh.materialID == None:
111+
continue
112+
me.materials.append(materials[submesh.materialID])
113+
for faceI in range(submesh.indexCount//3):
114+
me.polygons[faceI+faceIndexBase].material_index = submeshI
115+
faceIndexBase += submesh.indexCount//3
116+
117+
# Finalise
118+
me.validate()
119+
me.update()
120+
meshes.append(me)
121+
# Create objects
122+
for objec in modelData.objects:
123+
me = meshes[objec.meshID]
124+
mesh = modelData.meshes[objec.meshID]
125+
transform = modelData.transforms[objec.transformID]
126+
127+
# Select a name for the blender object
128+
name = ""
129+
if objec.name != "":
130+
name = objec.name
131+
elif mesh.name != "":
132+
name = mesh.name
133+
else:
134+
name = transform.name
135+
136+
# Create the object
137+
ob = bpy.data.objects.new(name, me)
138+
bpy.context.collection.objects.link(ob)
139+
140+
# Set transform
141+
ob.location = transform.position
142+
ob.scale = transform.scale
143+
ob.rotation_mode = "QUATERNION"
144+
x, y, z, w = transform.rotation
145+
ob.rotation_quaternion = (w, x, y, z)
146+
147+
# Apply materials (version 3)
148+
for materialID in objec.materialIDs:
149+
print(materialID)
150+
if materialID == -1:
151+
continue
152+
me.materials.append(materials[materialID])
153+
# Set the default material to the first one we added
154+
for polygon in me.polygons:
155+
polygon.material_index = 0
156+
157+
return {"FINISHED"}
158+
159+
'''
160+
Menu
161+
'''
162+
def menu_func_import_a3d(self, context):
163+
self.layout.operator(ImportA3D.bl_idname, text="Alternativa3D HTML5 (.a3d)")
164+
165+
'''
166+
Registration
167+
'''
168+
classes = [
169+
ImportA3D
170+
]
171+
172+
def register():
173+
for c in classes:
174+
bpy.utils.register_class(c)
175+
bpy.types.TOPBAR_MT_file_import.append(menu_func_import_a3d)
176+
177+
def unregister():
178+
for c in classes:
179+
bpy.utils.unregister_class(c)
180+
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_a3d)
181+
182+
if __name__ == "__main__":
183+
register()

io_scene_a3d/__main__.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)