Initial lightmapdata import

This commit is contained in:
Pyogenics
2025-04-09 11:30:57 +01:00
parent 4b2ba7eba1
commit 4866a3ff8a
4 changed files with 163 additions and 7 deletions

View File

@@ -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:

View File

@@ -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
'''
@@ -42,3 +40,12 @@ def addImageTextureToMaterial(image, node_tree, linkAlpha=False):
# Apply image
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)

View File

@@ -0,0 +1,113 @@
'''
Copyright (c) 2025 Pyogenics <https://github.com/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("<I", stream)
print(f"Reading LightmapData version {version}")
if version == 1:
self.read1(stream)
elif version == 2:
self.read2(stream)
else:
raise RuntimeError(f"Unknown LightmapData version: {version}")
'''
Version specific readers
'''
def read1(self, stream):
raise RuntimeError("Version 1 LightmapData is not implemented yet")
def read2(self, stream):
# Light info
self.lightColour, self.ambientLightColour = unpackStream("<2I", stream)
self.lightAngle = unpackStream("<2f", stream)
# Lightmaps
lightmapCount, = unpackStream("<I", stream)
print(f"Reading {lightmapCount} lightmaps")
for _ in range(lightmapCount):
lightmap = AlternativaProtocol.readString(stream)
self.lightmaps.append(lightmap)
# Map objects
mapObjectCount, = unpackStream("<I", stream)
print(f"Reading {mapObjectCount} map objects")
for _ in range(mapObjectCount):
mapObject = MapObject()
mapObject.read(stream)
self.mapObjects.append(mapObject)
#XXX: there is more data but do we actually care about it?
print(f"[LightmapData2 lightColour: {hex(self.lightColour)} ambientLightColour: {hex(self.ambientLightColour)} lightAngle: {self.lightAngle}]")
'''
Objects
'''
class MapObject:
def __init__(self):
self.index = 0
self.lightmapIndex = 0
self.lightmapScaleOffset = (0.0, 0.0, 0.0, 0.0)
self.UV1 = []
self.UV2 = []
self.castShadows = False
self.recieveShadows = False
def read(self, stream):
self.index, self.lightmapIndex = unpackStream("<2i", stream)
# Read lightmap data
if self.lightmapIndex >= 0:
self.lightmapScaleOffset = unpackStream("<4f", stream)
# Check if we have UVs and read them
hasUVs, = unpackStream("b", stream)
if hasUVs > 0:
vertexCount, = unpackStream("<I", stream)
for _ in range(vertexCount//2):
UV1 = unpackStream("<2f", stream)
self.UV1.append(UV1)
UV2 = unpackStream("<2f", stream)
self.UV2.append(UV2)
# Light settings
castShadows, recieveShadows = unpackStream("2b", stream)
self.castShadows = castShadows > 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}]")

View File

@@ -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
@@ -123,13 +124,17 @@ class ImportBattleMap(Operator, ImportHelper):
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