Import spawnpoints and some collision geometry

This commit is contained in:
Pyogenics
2025-03-30 21:09:38 +01:00
parent 8141194dc1
commit f07e9a58ee
3 changed files with 149 additions and 15 deletions

View File

@@ -235,6 +235,25 @@ class Material:
if optionalMask.getOptional(): if optionalMask.getOptional():
self.vector4Parameters = AlternativaProtocol.readObjectArray(stream, Vector4Parameter, optionalMask) self.vector4Parameters = AlternativaProtocol.readObjectArray(stream, Vector4Parameter, optionalMask)
#TODO: tanki has more than this number of spawn types now, investigate it
BATTLEMAP_SPAWNPOINTTYPE_DM = 0
BATTLEMAP_SPAWNPOINTTYPE_DOM_TEAMA = 1
BATTLEMAP_SPAWNPOINTTYPE_DOM_TEAMB = 2
BATTLEMAP_SPAWNPOINTTYPE_RUGBY_TEAMA = 3
BATTLEMAP_SPAWNPOINTTYPE_RUGBY_TEAMB = 4
BATTLEMAP_SPAWNPOINTTYPE_TEAMA = 5
BATTLEMAP_SPAWNPOINTTYPE_TEAMB = 6
BATTLEMAP_SPAWNPOINTTYPE_UNKNOWN = 7
BattleMapSpawnPointTypeName = {
BATTLEMAP_SPAWNPOINTTYPE_DM: "Deathmatch",
BATTLEMAP_SPAWNPOINTTYPE_DOM_TEAMA: "DominationTeamA",
BATTLEMAP_SPAWNPOINTTYPE_DOM_TEAMB: "DominationTeamB",
BATTLEMAP_SPAWNPOINTTYPE_RUGBY_TEAMA: "RugbyTeamA",
BATTLEMAP_SPAWNPOINTTYPE_RUGBY_TEAMB: "RugbyTeamB",
BATTLEMAP_SPAWNPOINTTYPE_TEAMA: "TeamA",
BATTLEMAP_SPAWNPOINTTYPE_TEAMB: "TeamB",
BATTLEMAP_SPAWNPOINTTYPE_UNKNOWN: "Unknown"
}
class SpawnPoint: class SpawnPoint:
def __init__(self): def __init__(self):
self.position = (0.0, 0.0, 0.0) self.position = (0.0, 0.0, 0.0)
@@ -279,8 +298,8 @@ class BattleMap:
def __init__(self): def __init__(self):
self.atlases = [] self.atlases = []
self.batches = [] self.batches = []
self.collisionGeometry = [] self.collisionGeometry = None
self.collisionGeometryOutsideGamingZone = [] self.collisionGeometryOutsideGamingZone = None
self.materials = [] self.materials = []
self.spawnPoints = [] self.spawnPoints = []
self.staticGeometry = [] self.staticGeometry = []

View File

@@ -22,6 +22,8 @@ SOFTWARE.
from json import load from json import load
import bpy
from .A3D import A3D from .A3D import A3D
from .A3DBlenderImporter import A3DBlenderImporter from .A3DBlenderImporter import A3DBlenderImporter
@@ -67,20 +69,61 @@ class BattleMapBlenderImporter:
# Allows subsequent map loads to be faster # Allows subsequent map loads to be faster
libraryCache = {} libraryCache = {}
def __init__(self, mapData, propLibrarySourcePath): def __init__(self, mapData, propLibrarySourcePath, import_static_geom=True, import_collision_geom=False, import_spawn_points=False):
self.mapData = mapData self.mapData = mapData
self.propLibrarySourcePath = propLibrarySourcePath self.propLibrarySourcePath = propLibrarySourcePath
self.import_static_geom = import_static_geom
self.import_collision_geom = import_collision_geom
self.import_spawn_points = import_spawn_points
def importData(self): def importData(self):
print("Importing BattleMap data into blender") print("Importing BattleMap data into blender")
# Load props
propObjects = [] propObjects = []
for propData in self.mapData.staticGeometry: if self.import_static_geom:
ob = self.getBlenderProp(propData) # Load props
propObjects.append(ob) for propData in self.mapData.staticGeometry:
ob = self.getBlenderProp(propData)
propObjects.append(ob)
collisionObjects = []
if self.import_collision_geom:
# Load collision meshes
collisionTriangles = self.mapData.collisionGeometry.triangles + self.mapData.collisionGeometryOutsideGamingZone.triangles
collisionTriangleObjects = self.createBlenderCollisionTriangles(collisionTriangles)
collisionPlanes = self.mapData.collisionGeometry.planes + self.mapData.collisionGeometryOutsideGamingZone.planes
collisionPlaneObjects = self.createBlenderCollisionPlanes(collisionPlanes)
collisionBoxes = self.mapData.collisionGeometry.boxes + self.mapData.collisionGeometryOutsideGamingZone.boxes
collisionBoxObjects = self.createBlenderCollisionBoxes(collisionBoxes)
return propObjects collisionObjects += collisionTriangleObjects
collisionObjects += collisionPlaneObjects
collisionObjects += collisionBoxObjects
spawnPointObjects = []
if self.import_spawn_points:
# Create spawn points
for spawnPointData in self.mapData.spawnPoints:
ob = self.createBlenderSpawnPoint(spawnPointData)
spawnPointObjects.append(ob)
# Create empty objects to house each type of object
objects = propObjects + collisionObjects + spawnPointObjects
if self.import_static_geom:
groupOB = bpy.data.objects.new("StaticGeometry", None)
objects.append(groupOB)
for ob in propObjects:
ob.parent = groupOB
if self.import_collision_geom:
groupOB = bpy.data.objects.new("CollisionGeometry", None)
objects.append(groupOB)
for ob in collisionObjects:
ob.parent = groupOB
if self.import_spawn_points:
groupOB = bpy.data.objects.new("SpawnPoints", None)
objects.append(groupOB)
for ob in spawnPointObjects:
ob.parent = groupOB
return objects
def getBlenderProp(self, propData): def getBlenderProp(self, propData):
# First check if we've already loaded the required prop library # First check if we've already loaded the required prop library
@@ -103,3 +146,62 @@ class BattleMapBlenderImporter:
propOB.scale = propData.scale propOB.scale = propData.scale
return propOB return propOB
def createBlenderCollisionTriangles(self, collisionTriangles):
objects = []
for collisionTriangle in collisionTriangles:
# Create the mesh
me = bpy.data.meshes.new("collisionTriangle")
# Create array for coordinate data, blender doesn't like tuples
vertices = []
vertices += collisionTriangle.v0
vertices += collisionTriangle.v1
vertices += collisionTriangle.v2
# Assign coordinates
me.vertices.add(3)
me.vertices.foreach_set("co", vertices)
me.loops.add(3)
me.loops.foreach_set("vertex_index", [0, 1, 2])
me.polygons.add(1)
me.polygons.foreach_set("loop_start", [0])
me.validate()
me.update()
# Create object
ob = bpy.data.objects.new("collisionTriangle", me)
ob.location = collisionTriangle.position
ob.rotation_mode = "XYZ"
ob.rotation_euler = collisionTriangle.rotation
#print(collisionTriangle.length) # XXX: how to handle collisionTriangle.length?
objects.append(ob)
return objects
def createBlenderCollisionPlanes(self, collisionPlanes):
objects = []
for collisionPlane in collisionPlanes:
pass
return objects
def createBlenderCollisionBoxes(self, collisionBoxes):
objects = []
for collisionBox in collisionBoxes:
pass
return objects
def createBlenderSpawnPoint(self, spawnPointData):
#TODO: implement spawn type name lookup
ob = bpy.data.objects.new(f"SpawnPoint_{spawnPointData.type}", None)
ob.empty_display_type = "ARROWS"
ob.empty_display_size = 100
ob.location = spawnPointData.position
ob.rotation_mode = "XYZ"
ob.rotation_euler = spawnPointData.rotation
return ob

View File

@@ -63,7 +63,7 @@ class ImportA3D(Operator, ImportHelper):
reset_empty_transform: BoolProperty(name="Reset empty transforms", description="Reset rotation and scale if it is set to 0, more useful for version 2 models like props", default=True) reset_empty_transform: BoolProperty(name="Reset empty transforms", description="Reset rotation and scale if it is set to 0, more useful for version 2 models like props", default=True)
def draw(self, context): def draw(self, context):
import_panel_options(self.layout, self) import_panel_options_a3d(self.layout, self)
def invoke(self, context, event): def invoke(self, context, event):
return ImportHelper.invoke(self, context, event) return ImportHelper.invoke(self, context, event)
@@ -101,8 +101,13 @@ class ImportBattleMap(Operator, ImportHelper):
filter_glob: StringProperty(default="*.bin", options={'HIDDEN'}) filter_glob: StringProperty(default="*.bin", options={'HIDDEN'})
directory: StringProperty(subtype="DIR_PATH", options={'HIDDEN'}) directory: StringProperty(subtype="DIR_PATH", options={'HIDDEN'})
# User options
import_static_geom: BoolProperty(name="Import static geometry", description="Static geometry includes all the visual aspects of the map", default=True)
import_collision_geom: BoolProperty(name="Import collision geometry", description="Collision geometry defines the geometry used for collision checks and cannot normally be seen by players", default=False)
import_spawn_points: BoolProperty(name="Import spawn points", description="Places a marker at locations where tanks can spawn", default=False)
def draw(self, context): def draw(self, context):
pass import_panel_options_battlemap(self.layout, self)
def invoke(self, context, event): def invoke(self, context, event):
return ImportHelper.invoke(self, context, event) return ImportHelper.invoke(self, context, event)
@@ -114,8 +119,8 @@ class ImportBattleMap(Operator, ImportHelper):
mapData.read(file) mapData.read(file)
# Import data into blender # Import data into blender
preferences = context.preferences.addons[__package__].preferences preferences = context.preferences.addons[__package__].preferences # TODO: check if this is set before proceeding
mapImporter = BattleMapBlenderImporter(mapData, preferences.propLibrarySourcePath) mapImporter = BattleMapBlenderImporter(mapData, preferences.propLibrarySourcePath, self.import_static_geom, self.import_collision_geom, self.import_spawn_points)
objects = mapImporter.importData() objects = mapImporter.importData()
# Link objects # Link objects
@@ -128,7 +133,7 @@ class ImportBattleMap(Operator, ImportHelper):
''' '''
Menu Menu
''' '''
def import_panel_options(layout, operator): def import_panel_options_a3d(layout, operator):
header, body = layout.panel("alternativa_import_options", default_closed=False) header, body = layout.panel("alternativa_import_options", default_closed=False)
header.label(text="Options") header.label(text="Options")
if body: if body:
@@ -136,6 +141,14 @@ def import_panel_options(layout, operator):
body.prop(operator, "try_import_textures") body.prop(operator, "try_import_textures")
body.prop(operator, "reset_empty_transform") body.prop(operator, "reset_empty_transform")
def import_panel_options_battlemap(layout, operator):
header, body = layout.panel("tanki_battlemap_import_options", default_closed=False)
header.label(text="Options")
if body:
body.prop(operator, "import_static_geom")
body.prop(operator, "import_collision_geom")
body.prop(operator, "import_spawn_points")
def menu_func_import_a3d(self, context): def menu_func_import_a3d(self, context):
self.layout.operator(ImportA3D.bl_idname, text="Alternativa3D HTML5 (.a3d)") self.layout.operator(ImportA3D.bl_idname, text="Alternativa3D HTML5 (.a3d)")