2626from bpy_extras .image_utils import load_image
2727from bpy_extras .node_shader_utils import PrincipledBSDFWrapper
2828import bmesh
29+ from mathutils import Matrix
2930
3031from .A3D import A3D
3132from .A3DBlenderImporter import A3DBlenderImporter
3233from .BlenderMaterialUtils import addImageTextureToMaterial
3334
34- class PropLibrary :
35- propCache = {}
35+ class Prop :
36+ def __init__ (self ):
37+ self .objects = []
38+ self .mainObject = None
39+
40+ def loadModel (self , modelPath ):
41+ fileExtension = modelPath .split ("." )[- 1 ]
42+ if fileExtension == "a3d" :
43+ modelData = A3D ()
44+ with open (modelPath , "rb" ) as file : modelData .read (file )
45+
46+ # Import the model
47+ modelImporter = A3DBlenderImporter (modelData , None , reset_empty_transform = False , try_import_textures = False )
48+ self .objects = modelImporter .importData ()
49+ elif fileExtension == "3ds" :
50+ bpy .ops .import_scene .max3ds (filepath = modelPath , use_apply_transform = False )
51+ for ob in bpy .context .selectable_objects :
52+ # The imported objects are added to the active collection, remove them
53+ bpy .context .collection .objects .unlink (ob )
54+
55+ # Correct the origin XXX: this does not work for all cases, investigate more
56+ ob .animation_data_clear ()
57+ x , y , z = - ob .location .x , - ob .location .y , - ob .location .z
58+ objectOrigin = Matrix .Translation ((x , y , z ))
59+ ob .data .transform (objectOrigin )
60+ ob .location = (0.0 , 0.0 , 0.0 )
61+
62+ self .objects .append (ob )
63+ else :
64+ raise RuntimeError (f"Unknown model file extension: { fileExtension } " )
65+
66+ # Identify the main parent object
67+ for ob in self .objects :
68+ if ob .parent == None : self .mainObject = ob
69+ if self .mainObject == None :
70+ raise RuntimeError (f"Unable to find the parent object for: { modelPath } " )
71+
72+ def loadSprite (self , propInfo ):
73+ spriteInfo = propInfo ["sprite" ]
74+
75+ # Create a plane we can use for the sprite
76+ me = bpy .data .meshes .new (propInfo ["name" ])
77+
78+ bm = bmesh .new ()
79+ bmesh .ops .create_grid (bm , x_segments = 1 , y_segments = 1 , size = spriteInfo ["scale" ]* 100 )
80+ bm .to_mesh (me )
81+ bm .free ()
82+
83+ ob = bpy .data .objects .new (me .name , me )
84+
85+ # Assign data
86+ ob .scale = (spriteInfo ["width" ], 1.0 , spriteInfo ["height" ]) #XXX: this should involve spriteInfo["scale"] probably?
87+ spriteOrigin = Matrix .Translation ((0.0 , spriteInfo ["originY" ], 0.0 ))
88+ me .transform (spriteOrigin )
89+
90+ # Finalise
91+ self .objects .append (ob )
92+ self .mainObject = ob
3693
94+ class PropLibrary :
95+ propGroups = {}
3796 def __init__ (self , directory ):
3897 self .directory = directory
39-
40- # Load library json
4198 self .libraryInfo = {}
42- with open (f"{ self .directory } /library.json" , "r" ) as file : # XXX: Get platform agnostic way of doing this
43- self .libraryInfo = load (file )
99+
100+ # Load library info
101+ with open (f"{ self .directory } /library.json" , "r" ) as file : self .libraryInfo = load (file )
102+ print (f"Loaded prop library: " + self .libraryInfo ["name" ])
103+
104+ def getProp (self , propName , groupName ):
105+ # Create the prop group if it's not already loaded
106+ if not groupName in self .propGroups :
107+ self .propGroups [groupName ] = {}
44108
45- print (f"Loaded prop library: { self .libraryInfo ["name" ]} " )
46-
47- def getProp (self , name , groupName ):
48- # XXX: Handle group names, this code can only load from the remaster libs
49- # Check if the prop is cached
50- if not name in self .propCache :
51- # Get the prop's info
52- propGroupInfo = self .libraryInfo ["groups" ][0 ]
53- propInfo = {}
54- for propInfo in propGroupInfo ["props" ]:
55- if propInfo ["name" ] == name : break
56-
57- # Load the prop
58- modelFilePath = f"{ self .directory } /{ propInfo ['mesh' ]['file' ]} " # XXX: Get platform agnostic way of doing this
59- modelData = A3D ()
60- with open (modelFilePath , "rb" ) as file :
61- modelData .read (file )
109+ # Load the prop if it's not already loaded
110+ if not propName in self .propGroups [groupName ]:
111+ # Find the prop group
112+ groupInfo = None
113+ for group in self .libraryInfo ["groups" ]:
114+ if group ["name" ] == groupName :
115+ groupInfo = group
116+ break
117+ if groupInfo == None :
118+ raise RuntimeError (f"Unable to find prop group with name { groupName } in " + self .libraryInfo ["name" ])
62119
63- # Import into blender
64- modelImporter = A3DBlenderImporter (modelData , self .directory , try_import_textures = False )
65- ob , = modelImporter .importData ()
120+ # Find the prop
121+ propInfo = None
122+ for prop in groupInfo ["props" ]:
123+ if prop ["name" ] == propName :
124+ propInfo = prop
125+ break
126+ if propInfo == None :
127+ raise RuntimeError (f"Unable to find prop with name { propName } in { groupName } from " + self .libraryInfo ["name" ])
66128
67- self .propCache [name ] = ob
129+ # Create the prop
130+ prop = Prop ()
131+ meshInfo = propInfo ["mesh" ]
132+ spriteInfo = propInfo ["sprite" ]
133+ if meshInfo != None :
134+ modelPath = f"{ self .directory } /" + meshInfo ["file" ]
135+ prop .loadModel (modelPath )
136+ elif spriteInfo != None :
137+ prop .loadSprite (propInfo )
138+ else :
139+ #XXX: Uhhhhhh, empty prop?
140+ pass
141+ self .propGroups [groupName ][propName ] = prop
68142
69- return self .propCache [ name ]
70-
143+ return self .propGroups [ groupName ][ propName ]
144+
71145 def getTexture (self , textureName ):
72146 im = load_image (textureName , self .directory )
73147 return im
@@ -149,14 +223,22 @@ def getPropLibrary(self, libraryName):
149223
150224 return self .libraryCache [libraryName ]
151225
226+ def tryLoadTexture (self , textureName , libraryName ):
227+ if libraryName == None :
228+ libraryName = "Remaster"
229+
230+ propLibrary = self .getPropLibrary (libraryName )
231+ texture = propLibrary .getTexture (f"{ textureName } .webp" )
232+ return texture
233+
152234 '''
153235 Blender data builders
154236 '''
155237 def getBlenderProp (self , propData ):
156238 # Load prop
157239 propLibrary = self .getPropLibrary (propData .libraryName )
158- propOB = propLibrary .getProp (propData .name , propData .groupName )
159- propOB = propOB .copy () # We want to use a copy of the prop object
240+ prop = propLibrary .getProp (propData .name , propData .groupName )
241+ propOB = prop . mainObject .copy () # We want to use a copy of the prop object
160242
161243 # Assign data
162244 propOB .name = f"{ propData .name } _{ propData .ID } "
@@ -167,7 +249,8 @@ def getBlenderProp(self, propData):
167249
168250 # Material
169251 ma = self .materials [propData .materialID ]
170- propOB .data .materials [0 ] = ma
252+ if len (propOB .data .materials ) != 0 :
253+ propOB .data .materials [0 ] = ma
171254
172255 return propOB
173256
@@ -264,15 +347,14 @@ def createBlenderMaterial(self, materialData):
264347 ma = bpy .data .materials .new (f"{ materialData .ID } _{ materialData .name } " )
265348
266349 # Shader specific logic
267- if materialData .shader == "TankiOnline/SingleTextureShader" :
350+ if materialData .shader == "TankiOnline/SingleTextureShader" or materialData . shader == "TankiOnline/SingleTextureShaderWinter" :
268351 bsdf = PrincipledBSDFWrapper (ma , is_readonly = False , use_nodes = True )
269352 bsdf .roughness_set (1.0 )
270353 bsdf .ior_set (1.0 )
271354
272355 # Try load texture
273356 textureParameter = materialData .textureParameters [0 ]
274- propLibrary = self .getPropLibrary ("Remaster" )
275- texture = propLibrary .getTexture (f"{ textureParameter .textureName } .webp" )
357+ texture = self .tryLoadTexture (textureParameter .textureName , textureParameter .libraryName )
276358
277359 addImageTextureToMaterial (texture , ma .node_tree )
278360 elif materialData .shader == "TankiOnline/SpriteShader" :
@@ -282,8 +364,7 @@ def createBlenderMaterial(self, materialData):
282364
283365 # Try load texture
284366 textureParameter = materialData .textureParameters [0 ]
285- propLibrary = self .getPropLibrary ("Remaster" )
286- texture = propLibrary .getTexture (f"{ textureParameter .textureName } .webp" )
367+ texture = self .tryLoadTexture (textureParameter .textureName , textureParameter .libraryName )
287368
288369 addImageTextureToMaterial (texture , ma .node_tree , linkAlpha = True )
289370 elif materialData .shader == "TankiOnline/Terrain" :
@@ -292,5 +373,7 @@ def createBlenderMaterial(self, materialData):
292373 bsdf .roughness_set (1.0 )
293374 bsdf .ior_set (1.0 )
294375 bsdf .base_color_set ((0.0 , 0.0 , 0.0 ))
376+ else :
377+ pass # Unknown shader
295378
296379 return ma
0 commit comments