diff --git a/io_scene_a3d/BattleMapBlenderImporter.py b/io_scene_a3d/BattleMapBlenderImporter.py index fee7fdb..09747bb 100644 --- a/io_scene_a3d/BattleMapBlenderImporter.py +++ b/io_scene_a3d/BattleMapBlenderImporter.py @@ -30,7 +30,7 @@ from mathutils import Matrix from .A3D import A3D from .A3DBlenderImporter import A3DBlenderImporter -from .BlenderMaterialUtils import addImageTextureToMaterial +from .BlenderMaterialUtils import addImageTextureToMaterial, decodeIntColorToTuple class Prop: def __init__(self): @@ -150,8 +150,9 @@ class BattleMapBlenderImporter: # Allows subsequent map loads to be faster libraryCache = {} - def __init__(self, mapData, propLibrarySourcePath, import_static_geom=True, import_collision_geom=False, import_spawn_points=False): + def __init__(self, mapData, lightmapData, propLibrarySourcePath, import_static_geom=True, import_collision_geom=False, import_spawn_points=False): self.mapData = mapData + self.lightmapData = lightmapData self.propLibrarySourcePath = propLibrarySourcePath self.import_static_geom = import_static_geom self.import_collision_geom = import_collision_geom @@ -211,6 +212,26 @@ class BattleMapBlenderImporter: for ob in spawnPointObjects: ob.parent = groupOB + # Create a sun light object + li = bpy.data.lights.new("DirectionalLight", "SUN") + li.color = decodeIntColorToTuple(self.lightmapData.lightColour) + + ob = bpy.data.objects.new(li.name, li) + ob.location = (0.0, 0.0, 1000.0) # Just place it like 10 meters off the ground (in alternativa units) + lightAngleX, lightAngleZ = self.lightmapData.lightAngle + ob.rotation_mode = "XYZ" + ob.rotation_euler = (lightAngleX, 0.0, lightAngleZ) + objects.append(ob) + + # Set ambient world light + scene = bpy.context.scene + if scene.world == None: + wd = bpy.data.worlds.new("map") + scene.world = wd + world = scene.world + world.use_nodes = False + world.color = decodeIntColorToTuple(self.lightmapData.ambientLightColour) + return objects def getPropLibrary(self, libraryName): @@ -253,6 +274,16 @@ class BattleMapBlenderImporter: propScale = (1.0, 1.0, 1.0) propOB.scale = propScale + # Lighting info + lightingMapObject = None + for mapObject in self.lightmapData.mapObjects: + if mapObject.index == propData.ID: + lightingMapObject = mapObject + break + if lightingMapObject != None: + #XXX: do something with lightingMapObject.recieveShadows?? + propOB.visible_shadow = lightingMapObject.castShadows + # Material ma = self.materials[propData.materialID] if len(propOB.data.materials) != 0: diff --git a/io_scene_a3d/BlenderMaterialUtils.py b/io_scene_a3d/BlenderMaterialUtils.py index 8706538..f240d6b 100644 --- a/io_scene_a3d/BlenderMaterialUtils.py +++ b/io_scene_a3d/BlenderMaterialUtils.py @@ -20,8 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ''' -from bpy.types import ShaderNodeBsdfPrincipled - ''' Functions ''' @@ -41,4 +39,13 @@ def addImageTextureToMaterial(image, node_tree, linkAlpha=False): links.new(textureNode.outputs["Alpha"], bsdfNode.inputs["Alpha"]) # Apply image - if image != None: textureNode.image = image \ No newline at end of file + if image != None: textureNode.image = image + +def decodeIntColorToTuple(intColor): + # Fromat is argb + a = (intColor >> 24) & 255 + r = (intColor >> 16) & 255 + g = (intColor >> 8) & 255 + b = intColor & 255 + + return (r/255, g/255, b/255) \ No newline at end of file diff --git a/io_scene_a3d/LightmapData.py b/io_scene_a3d/LightmapData.py new file mode 100644 index 0000000..cb4dbd2 --- /dev/null +++ b/io_scene_a3d/LightmapData.py @@ -0,0 +1,113 @@ +''' +Copyright (c) 2025 Pyogenics + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +''' + +from .IOTools import unpackStream +from . import AlternativaProtocol + +class LightmapData: + def __init__(self): + self.lightColour = (0.0, 0.0, 0.0) + self.ambientLightColour = (0.0, 0.0, 0.0) + self.lightAngle = (0.0, 0.0) # (x, z) + self.lightmaps = [] + self.mapObjects = [] + + def read(self, stream): + print("Reading LightmapData") + + # There is no signature so just start reading data and hope this is actually a lightmap data file + version, = unpackStream("= 0: + self.lightmapScaleOffset = unpackStream("<4f", stream) + + # Check if we have UVs and read them + hasUVs, = unpackStream("b", stream) + if hasUVs > 0: + vertexCount, = unpackStream(" 0 + self.recieveShadows = recieveShadows > 0 + + print(f"[MapObject index: {self.index} lightmapIndex: {self.lightmapIndex} lightmapScaleOffset: {self.lightmapScaleOffset} UV1: {len(self.UV1)} UV2: {len(self.UV2)} castShadows: {self.castShadows} recieveShadows: {self.recieveShadows}]") \ No newline at end of file diff --git a/io_scene_a3d/__init__.py b/io_scene_a3d/__init__.py index bc15cca..4ca63db 100644 --- a/io_scene_a3d/__init__.py +++ b/io_scene_a3d/__init__.py @@ -29,6 +29,7 @@ from .A3D import A3D from .A3DBlenderImporter import A3DBlenderImporter from .BattleMap import BattleMap from .BattleMapBlenderImporter import BattleMapBlenderImporter +from .LightmapData import LightmapData from glob import glob from time import time @@ -122,14 +123,18 @@ class ImportBattleMap(Operator, ImportHelper): print(f"Reading BattleMap data from {self.filepath}") importStartTime = time() - + + lightmapData = LightmapData() + with open(f"{self.directory}/lightmapdata", "rb") as file: + lightmapData.read(file) + mapData = BattleMap() with open(self.filepath, "rb") as file: mapData.read(file) # Import data into blender preferences = context.preferences.addons[__package__].preferences # TODO: check if this is set before proceeding - mapImporter = BattleMapBlenderImporter(mapData, preferences.propLibrarySourcePath, self.import_static_geom, self.import_collision_geom, self.import_spawn_points) + mapImporter = BattleMapBlenderImporter(mapData, lightmapData, preferences.propLibrarySourcePath, self.import_static_geom, self.import_collision_geom, self.import_spawn_points) objects = mapImporter.importData() # Link objects