mirror of
https://github.com/MapMakersAndProgrammers/io_scene_a3d.git
synced 2025-10-26 01:49:13 -07:00
Initial working remaster map imports
This commit is contained in:
@@ -36,12 +36,13 @@ class AtlasRect:
|
||||
self.y = 0
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read AtlasRect")
|
||||
self.height, = unpackStream(">I", stream)
|
||||
self.libraryName = AlternativaProtocol.readString(stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.width, self.x, self.y = unpackStream(">3I", stream)
|
||||
|
||||
print(f"[AtlasRect height: {self.height} libraryName: {self.libraryName} name: {self.name} width: {self.width} x: {self.x} y: {self.y}]")
|
||||
|
||||
class CollisionBox:
|
||||
def __init__(self):
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
@@ -49,10 +50,11 @@ class CollisionBox:
|
||||
self.size = (0.0, 0.0, 0.0)
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read CollisionBox")
|
||||
self.position = unpackStream(">3f", stream)
|
||||
self.rotation = unpackStream(">3f", stream)
|
||||
self.size = unpackStream(">3f", stream)
|
||||
|
||||
# print(f"[CollisionBox position: {self.position} rotation: {self.rotation} size: {self.size}]")
|
||||
|
||||
class CollisionPlane:
|
||||
def __init__(self):
|
||||
@@ -62,11 +64,12 @@ class CollisionPlane:
|
||||
self.width = 0.0
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read CollisionPlane")
|
||||
self.length, = unpackStream(">d", stream)
|
||||
self.position = unpackStream(">3f", stream)
|
||||
self.rotation = unpackStream(">3f", stream)
|
||||
self.width, = unpackStream(">d", stream)
|
||||
|
||||
# print(f"[CollisionPlane lenght: {self.length} position: {self.position} rotation: {self.rotation} width: {self.width}]")
|
||||
|
||||
class CollisionTriangle:
|
||||
def __init__(self):
|
||||
@@ -78,13 +81,14 @@ class CollisionTriangle:
|
||||
self.v2 = (0.0, 0.0, 0.0)
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read CollisionTriangle")
|
||||
self.length, = unpackStream(">d", stream)
|
||||
self.position = unpackStream(">3f", stream)
|
||||
self.rotation = unpackStream(">3f", stream)
|
||||
self.v0 = unpackStream(">3f", stream)
|
||||
self.v1 = unpackStream(">3f", stream)
|
||||
self.v2 = unpackStream(">3f", stream)
|
||||
|
||||
# print(f"[CollisionTriangle length: {self.length} position: {self.position} rotation: {self.rotation} v0: {self.v0} v1: {self.v1} v2: {self.v2}]")
|
||||
|
||||
class ScalarParameter:
|
||||
def __init__(self):
|
||||
@@ -92,7 +96,6 @@ class ScalarParameter:
|
||||
self.value = 0.0
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read ScalarParameters")
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.value, = unpackStream(">f", stream)
|
||||
|
||||
@@ -105,7 +108,6 @@ class TextureParameter:
|
||||
self.libraryName = None
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read TextureParameter")
|
||||
if optionalMask.getOptional():
|
||||
self.libraryName = AlternativaProtocol.readString(stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
@@ -117,7 +119,6 @@ class Vector2Parameter:
|
||||
self.value = (0.0, 0.0)
|
||||
|
||||
def __init__(self, stream, optionalMask):
|
||||
print("Read Vector2Parameters")
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.value = unpackStream(">2f", stream)
|
||||
|
||||
@@ -127,7 +128,6 @@ class Vector3Parameter:
|
||||
self.value = (0.0, 0.0, 0.0)
|
||||
|
||||
def __init__(self, stream, optionalMask):
|
||||
print("Read Vector3Parameters")
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.value = unpackStream(">3f", stream)
|
||||
|
||||
@@ -137,7 +137,6 @@ class Vector4Parameter:
|
||||
self.value = (0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read Vector4Parameters")
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.value = unpackStream(">4f", stream)
|
||||
|
||||
@@ -243,7 +242,6 @@ class SpawnPoint:
|
||||
self.type = 0
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read SpawnPoint")
|
||||
self.position = unpackStream(">3f", stream)
|
||||
self.rotation = unpackStream(">3f", stream)
|
||||
self.type, = unpackStream(">I", stream)
|
||||
@@ -259,10 +257,9 @@ class Prop:
|
||||
# Optional
|
||||
self.groupName = ""
|
||||
self.rotation = (0.0, 0.0, 0.0)
|
||||
self.scale = (0.0, 0.0, 0.0)
|
||||
self.scale = (1.0, 1.0, 1.0)
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print(f"Read Prop")
|
||||
if optionalMask.getOptional():
|
||||
self.groupName = AlternativaProtocol.readString(stream)
|
||||
self.ID, = unpackStream(">I", stream)
|
||||
@@ -305,11 +302,6 @@ class BattleMap:
|
||||
|
||||
# Read packet
|
||||
packet = AlternativaProtocol.readPacket(stream)
|
||||
with open("packet.bin", "wb") as packetFile:
|
||||
packetFile.write(
|
||||
packet.read()
|
||||
)
|
||||
packet.seek(0)
|
||||
optionalMask = AlternativaProtocol.OptionalMask()
|
||||
optionalMask.read(packet)
|
||||
|
||||
|
||||
105
io_scene_a3d/BattleMapBlenderImporter.py
Normal file
105
io_scene_a3d/BattleMapBlenderImporter.py
Normal file
@@ -0,0 +1,105 @@
|
||||
'''
|
||||
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 json import load
|
||||
|
||||
from .A3D import A3D
|
||||
from .A3DBlenderImporter import A3DBlenderImporter
|
||||
|
||||
class PropLibrary:
|
||||
propCache = {}
|
||||
|
||||
def __init__(self, directory):
|
||||
self.directory = directory
|
||||
|
||||
# Load library json
|
||||
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"]}")
|
||||
|
||||
def getProp(self, name, groupName):
|
||||
# XXX: Handle group names, this code can only load from the remaster libs
|
||||
# Check if the prop is cached
|
||||
if not name in self.propCache:
|
||||
# Get the prop's info
|
||||
propGroupInfo = self.libraryInfo["groups"][0]
|
||||
propInfo = {}
|
||||
for propInfo in propGroupInfo["props"]:
|
||||
if propInfo["name"] == name: break
|
||||
|
||||
# Load the prop
|
||||
modelFilePath = f"{self.directory}/{propInfo['mesh']['file']}" # XXX: Get platform agnostic way of doing this
|
||||
modelData = A3D()
|
||||
with open(modelFilePath, "rb") as file:
|
||||
modelData.read(file)
|
||||
|
||||
# Import into blender
|
||||
modelImporter = A3DBlenderImporter(modelData, self.directory, try_import_textures=False)
|
||||
ob, = modelImporter.importData()
|
||||
|
||||
self.propCache[name] = ob
|
||||
|
||||
return self.propCache[name]
|
||||
|
||||
|
||||
class BattleMapBlenderImporter:
|
||||
# Allows subsequent map loads to be faster
|
||||
libraryCache = {}
|
||||
|
||||
def __init__(self, mapData, propLibrarySourcePath):
|
||||
self.mapData = mapData
|
||||
self.propLibrarySourcePath = propLibrarySourcePath
|
||||
|
||||
def importData(self):
|
||||
print("Importing BattleMap data into blender")
|
||||
|
||||
# Load props
|
||||
propObjects = []
|
||||
for propData in self.mapData.staticGeometry:
|
||||
ob = self.getBlenderProp(propData)
|
||||
propObjects.append(ob)
|
||||
|
||||
return propObjects
|
||||
|
||||
def getBlenderProp(self, propData):
|
||||
# First check if we've already loaded the required prop library
|
||||
if not propData.libraryName in self.libraryCache:
|
||||
# Load the proplib
|
||||
libraryPath = f"{self.propLibrarySourcePath}/{propData.libraryName}" # XXX: Get platform agnostic way of doing this
|
||||
library = PropLibrary(libraryPath)
|
||||
self.libraryCache[propData.libraryName] = library
|
||||
|
||||
# Load prop
|
||||
propLibrary = self.libraryCache[propData.libraryName]
|
||||
propOB = propLibrary.getProp(propData.name, propData.groupName)
|
||||
propOB = propOB.copy() # We want to use a copy of the prop object
|
||||
|
||||
# Assign data
|
||||
propOB.name = f"{propData.name}_{propData.ID}"
|
||||
propOB.location = propData.position
|
||||
propOB.rotation_mode = "XYZ"
|
||||
propOB.rotation_euler = propData.rotation
|
||||
propOB.scale = propData.scale
|
||||
|
||||
return propOB
|
||||
@@ -21,16 +21,29 @@ SOFTWARE.
|
||||
'''
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator, OperatorFileListElement
|
||||
from bpy.types import Operator, OperatorFileListElement, AddonPreferences
|
||||
from bpy.props import StringProperty, BoolProperty, CollectionProperty
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
|
||||
from .A3D import A3D
|
||||
from .A3DBlenderImporter import A3DBlenderImporter
|
||||
from .BattleMap import BattleMap
|
||||
from .BattleMapBlenderImporter import BattleMapBlenderImporter
|
||||
|
||||
from glob import glob
|
||||
|
||||
'''
|
||||
Addon preferences
|
||||
'''
|
||||
class Preferences(AddonPreferences):
|
||||
bl_idname = __package__
|
||||
|
||||
propLibrarySourcePath: StringProperty(name="Prop library source path", subtype='FILE_PATH')
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "propLibrarySourcePath")
|
||||
|
||||
'''
|
||||
Operators
|
||||
'''
|
||||
@@ -100,6 +113,16 @@ class ImportBattleMap(Operator, ImportHelper):
|
||||
with open(self.filepath, "rb") as file:
|
||||
mapData.read(file)
|
||||
|
||||
# Import data into blender
|
||||
preferences = context.preferences.addons[__package__].preferences
|
||||
mapImporter = BattleMapBlenderImporter(mapData, preferences.propLibrarySourcePath)
|
||||
objects = mapImporter.importData()
|
||||
|
||||
# Link objects
|
||||
collection = bpy.context.collection
|
||||
for ob in objects:
|
||||
collection.objects.link(ob)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
'''
|
||||
@@ -123,6 +146,7 @@ def menu_func_import_battlemap(self, context):
|
||||
Registration
|
||||
'''
|
||||
classes = [
|
||||
Preferences,
|
||||
ImportA3D,
|
||||
ImportBattleMap
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user