Initial, broken, support for legacy maps

This commit is contained in:
Pyogenics
2025-04-07 13:17:50 +01:00
parent 5b35a26c6e
commit 8a96286bae
2 changed files with 123 additions and 40 deletions

View File

@@ -274,7 +274,7 @@ class Prop:
self.position = (0.0, 0.0, 0.0) self.position = (0.0, 0.0, 0.0)
# Optional # Optional
self.groupName = "" self.groupName = None
self.rotation = (0.0, 0.0, 0.0) self.rotation = (0.0, 0.0, 0.0)
self.scale = (1.0, 1.0, 1.0) self.scale = (1.0, 1.0, 1.0)

View File

@@ -26,47 +26,121 @@ import bpy
from bpy_extras.image_utils import load_image from bpy_extras.image_utils import load_image
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
import bmesh import bmesh
from mathutils import Matrix
from .A3D import A3D from .A3D import A3D
from .A3DBlenderImporter import A3DBlenderImporter from .A3DBlenderImporter import A3DBlenderImporter
from .BlenderMaterialUtils import addImageTextureToMaterial from .BlenderMaterialUtils import addImageTextureToMaterial
class PropLibrary: class Prop:
propCache = {} def __init__(self):
self.objects = []
self.mainObject = None
def loadModel(self, modelPath):
fileExtension = modelPath.split(".")[-1]
if fileExtension == "a3d":
modelData = A3D()
with open(modelPath, "rb") as file: modelData.read(file)
# Import the model
modelImporter = A3DBlenderImporter(modelData, None, reset_empty_transform=False, try_import_textures=False)
self.objects = modelImporter.importData()
elif fileExtension == "3ds":
bpy.ops.import_scene.max3ds(filepath=modelPath, use_apply_transform=False)
for ob in bpy.context.selectable_objects:
# The imported objects are added to the active collection, remove them
bpy.context.collection.objects.unlink(ob)
# Correct the origin XXX: this does not work for all cases, investigate more
ob.animation_data_clear()
x, y, z = -ob.location.x, -ob.location.y, -ob.location.z
objectOrigin = Matrix.Translation((x, y, z))
ob.data.transform(objectOrigin)
ob.location = (0.0, 0.0, 0.0)
self.objects.append(ob)
else:
raise RuntimeError(f"Unknown model file extension: {fileExtension}")
# Identify the main parent object
for ob in self.objects:
if ob.parent == None: self.mainObject = ob
if self.mainObject == None:
raise RuntimeError(f"Unable to find the parent object for: {modelPath}")
def loadSprite(self, propInfo):
spriteInfo = propInfo["sprite"]
# Create a plane we can use for the sprite
me = bpy.data.meshes.new(propInfo["name"])
bm = bmesh.new()
bmesh.ops.create_grid(bm, x_segments=1, y_segments=1, size=spriteInfo["scale"]*100)
bm.to_mesh(me)
bm.free()
ob = bpy.data.objects.new(me.name, me)
# Assign data
ob.scale = (spriteInfo["width"], 1.0, spriteInfo["height"]) #XXX: this should involve spriteInfo["scale"] probably?
spriteOrigin = Matrix.Translation((0.0, spriteInfo["originY"], 0.0))
me.transform(spriteOrigin)
# Finalise
self.objects.append(ob)
self.mainObject = ob
class PropLibrary:
propGroups = {}
def __init__(self, directory): def __init__(self, directory):
self.directory = directory self.directory = directory
# Load library json
self.libraryInfo = {} self.libraryInfo = {}
with open(f"{self.directory}/library.json", "r") as file: # XXX: Get platform agnostic way of doing this
self.libraryInfo = load(file)
print(f"Loaded prop library: {self.libraryInfo["name"]}") # Load library info
with open(f"{self.directory}/library.json", "r") as file: self.libraryInfo = load(file)
print(f"Loaded prop library: " + self.libraryInfo["name"])
def getProp(self, name, groupName): def getProp(self, propName, groupName):
# XXX: Handle group names, this code can only load from the remaster libs # Create the prop group if it's not already loaded
# Check if the prop is cached if not groupName in self.propGroups:
if not name in self.propCache: self.propGroups[groupName] = {}
# Get the prop's info
propGroupInfo = self.libraryInfo["groups"][0]
propInfo = {}
for propInfo in propGroupInfo["props"]:
if propInfo["name"] == name: break
# Load the prop # Load the prop if it's not already loaded
modelFilePath = f"{self.directory}/{propInfo['mesh']['file']}" # XXX: Get platform agnostic way of doing this if not propName in self.propGroups[groupName]:
modelData = A3D() # Find the prop group
with open(modelFilePath, "rb") as file: groupInfo = None
modelData.read(file) for group in self.libraryInfo["groups"]:
if group["name"] == groupName:
groupInfo = group
break
if groupInfo == None:
raise RuntimeError(f"Unable to find prop group with name {groupName} in " + self.libraryInfo["name"])
# Import into blender # Find the prop
modelImporter = A3DBlenderImporter(modelData, self.directory, try_import_textures=False) propInfo = None
ob, = modelImporter.importData() for prop in groupInfo["props"]:
if prop["name"] == propName:
propInfo = prop
break
if propInfo == None:
raise RuntimeError(f"Unable to find prop with name {propName} in {groupName} from " + self.libraryInfo["name"])
self.propCache[name] = ob # Create the prop
prop = Prop()
meshInfo = propInfo["mesh"]
spriteInfo = propInfo["sprite"]
if meshInfo != None:
modelPath = f"{self.directory}/" + meshInfo["file"]
prop.loadModel(modelPath)
elif spriteInfo != None:
prop.loadSprite(propInfo)
else:
#XXX: Uhhhhhh, empty prop?
pass
self.propGroups[groupName][propName] = prop
return self.propCache[name] return self.propGroups[groupName][propName]
def getTexture(self, textureName): def getTexture(self, textureName):
im = load_image(textureName, self.directory) im = load_image(textureName, self.directory)
@@ -149,14 +223,22 @@ class BattleMapBlenderImporter:
return self.libraryCache[libraryName] return self.libraryCache[libraryName]
def tryLoadTexture(self, textureName, libraryName):
if libraryName == None:
libraryName = "Remaster"
propLibrary = self.getPropLibrary(libraryName)
texture = propLibrary.getTexture(f"{textureName}.webp")
return texture
''' '''
Blender data builders Blender data builders
''' '''
def getBlenderProp(self, propData): def getBlenderProp(self, propData):
# Load prop # Load prop
propLibrary = self.getPropLibrary(propData.libraryName) propLibrary = self.getPropLibrary(propData.libraryName)
propOB = propLibrary.getProp(propData.name, propData.groupName) prop = propLibrary.getProp(propData.name, propData.groupName)
propOB = propOB.copy() # We want to use a copy of the prop object propOB = prop.mainObject.copy() # We want to use a copy of the prop object
# Assign data # Assign data
propOB.name = f"{propData.name}_{propData.ID}" propOB.name = f"{propData.name}_{propData.ID}"
@@ -167,6 +249,7 @@ class BattleMapBlenderImporter:
# Material # Material
ma = self.materials[propData.materialID] ma = self.materials[propData.materialID]
if len(propOB.data.materials) != 0:
propOB.data.materials[0] = ma propOB.data.materials[0] = ma
return propOB return propOB
@@ -264,15 +347,14 @@ class BattleMapBlenderImporter:
ma = bpy.data.materials.new(f"{materialData.ID}_{materialData.name}") ma = bpy.data.materials.new(f"{materialData.ID}_{materialData.name}")
# Shader specific logic # Shader specific logic
if materialData.shader == "TankiOnline/SingleTextureShader": if materialData.shader == "TankiOnline/SingleTextureShader" or materialData.shader == "TankiOnline/SingleTextureShaderWinter":
bsdf = PrincipledBSDFWrapper(ma, is_readonly=False, use_nodes=True) bsdf = PrincipledBSDFWrapper(ma, is_readonly=False, use_nodes=True)
bsdf.roughness_set(1.0) bsdf.roughness_set(1.0)
bsdf.ior_set(1.0) bsdf.ior_set(1.0)
# Try load texture # Try load texture
textureParameter = materialData.textureParameters[0] textureParameter = materialData.textureParameters[0]
propLibrary = self.getPropLibrary("Remaster") texture = self.tryLoadTexture(textureParameter.textureName, textureParameter.libraryName)
texture = propLibrary.getTexture(f"{textureParameter.textureName}.webp")
addImageTextureToMaterial(texture, ma.node_tree) addImageTextureToMaterial(texture, ma.node_tree)
elif materialData.shader == "TankiOnline/SpriteShader": elif materialData.shader == "TankiOnline/SpriteShader":
@@ -282,8 +364,7 @@ class BattleMapBlenderImporter:
# Try load texture # Try load texture
textureParameter = materialData.textureParameters[0] textureParameter = materialData.textureParameters[0]
propLibrary = self.getPropLibrary("Remaster") texture = self.tryLoadTexture(textureParameter.textureName, textureParameter.libraryName)
texture = propLibrary.getTexture(f"{textureParameter.textureName}.webp")
addImageTextureToMaterial(texture, ma.node_tree, linkAlpha=True) addImageTextureToMaterial(texture, ma.node_tree, linkAlpha=True)
elif materialData.shader == "TankiOnline/Terrain": elif materialData.shader == "TankiOnline/Terrain":
@@ -292,5 +373,7 @@ class BattleMapBlenderImporter:
bsdf.roughness_set(1.0) bsdf.roughness_set(1.0)
bsdf.ior_set(1.0) bsdf.ior_set(1.0)
bsdf.base_color_set((0.0, 0.0, 0.0)) bsdf.base_color_set((0.0, 0.0, 0.0))
else:
pass # Unknown shader
return ma return ma