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 ()
0 commit comments