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
|
self.y = 0
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read AtlasRect")
|
|
||||||
self.height, = unpackStream(">I", stream)
|
self.height, = unpackStream(">I", stream)
|
||||||
self.libraryName = AlternativaProtocol.readString(stream)
|
self.libraryName = AlternativaProtocol.readString(stream)
|
||||||
self.name = AlternativaProtocol.readString(stream)
|
self.name = AlternativaProtocol.readString(stream)
|
||||||
self.width, self.x, self.y = unpackStream(">3I", 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:
|
class CollisionBox:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.position = (0.0, 0.0, 0.0)
|
self.position = (0.0, 0.0, 0.0)
|
||||||
@@ -49,10 +50,11 @@ class CollisionBox:
|
|||||||
self.size = (0.0, 0.0, 0.0)
|
self.size = (0.0, 0.0, 0.0)
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read CollisionBox")
|
|
||||||
self.position = unpackStream(">3f", stream)
|
self.position = unpackStream(">3f", stream)
|
||||||
self.rotation = unpackStream(">3f", stream)
|
self.rotation = unpackStream(">3f", stream)
|
||||||
self.size = unpackStream(">3f", stream)
|
self.size = unpackStream(">3f", stream)
|
||||||
|
|
||||||
|
# print(f"[CollisionBox position: {self.position} rotation: {self.rotation} size: {self.size}]")
|
||||||
|
|
||||||
class CollisionPlane:
|
class CollisionPlane:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -62,11 +64,12 @@ class CollisionPlane:
|
|||||||
self.width = 0.0
|
self.width = 0.0
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read CollisionPlane")
|
|
||||||
self.length, = unpackStream(">d", stream)
|
self.length, = unpackStream(">d", stream)
|
||||||
self.position = unpackStream(">3f", stream)
|
self.position = unpackStream(">3f", stream)
|
||||||
self.rotation = unpackStream(">3f", stream)
|
self.rotation = unpackStream(">3f", stream)
|
||||||
self.width, = unpackStream(">d", stream)
|
self.width, = unpackStream(">d", stream)
|
||||||
|
|
||||||
|
# print(f"[CollisionPlane lenght: {self.length} position: {self.position} rotation: {self.rotation} width: {self.width}]")
|
||||||
|
|
||||||
class CollisionTriangle:
|
class CollisionTriangle:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -78,13 +81,14 @@ class CollisionTriangle:
|
|||||||
self.v2 = (0.0, 0.0, 0.0)
|
self.v2 = (0.0, 0.0, 0.0)
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read CollisionTriangle")
|
|
||||||
self.length, = unpackStream(">d", stream)
|
self.length, = unpackStream(">d", stream)
|
||||||
self.position = unpackStream(">3f", stream)
|
self.position = unpackStream(">3f", stream)
|
||||||
self.rotation = unpackStream(">3f", stream)
|
self.rotation = unpackStream(">3f", stream)
|
||||||
self.v0 = unpackStream(">3f", stream)
|
self.v0 = unpackStream(">3f", stream)
|
||||||
self.v1 = unpackStream(">3f", stream)
|
self.v1 = unpackStream(">3f", stream)
|
||||||
self.v2 = 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:
|
class ScalarParameter:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -92,7 +96,6 @@ class ScalarParameter:
|
|||||||
self.value = 0.0
|
self.value = 0.0
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read ScalarParameters")
|
|
||||||
self.name = AlternativaProtocol.readString(stream)
|
self.name = AlternativaProtocol.readString(stream)
|
||||||
self.value, = unpackStream(">f", stream)
|
self.value, = unpackStream(">f", stream)
|
||||||
|
|
||||||
@@ -105,7 +108,6 @@ class TextureParameter:
|
|||||||
self.libraryName = None
|
self.libraryName = None
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read TextureParameter")
|
|
||||||
if optionalMask.getOptional():
|
if optionalMask.getOptional():
|
||||||
self.libraryName = AlternativaProtocol.readString(stream)
|
self.libraryName = AlternativaProtocol.readString(stream)
|
||||||
self.name = AlternativaProtocol.readString(stream)
|
self.name = AlternativaProtocol.readString(stream)
|
||||||
@@ -117,7 +119,6 @@ class Vector2Parameter:
|
|||||||
self.value = (0.0, 0.0)
|
self.value = (0.0, 0.0)
|
||||||
|
|
||||||
def __init__(self, stream, optionalMask):
|
def __init__(self, stream, optionalMask):
|
||||||
print("Read Vector2Parameters")
|
|
||||||
self.name = AlternativaProtocol.readString(stream)
|
self.name = AlternativaProtocol.readString(stream)
|
||||||
self.value = unpackStream(">2f", stream)
|
self.value = unpackStream(">2f", stream)
|
||||||
|
|
||||||
@@ -127,7 +128,6 @@ class Vector3Parameter:
|
|||||||
self.value = (0.0, 0.0, 0.0)
|
self.value = (0.0, 0.0, 0.0)
|
||||||
|
|
||||||
def __init__(self, stream, optionalMask):
|
def __init__(self, stream, optionalMask):
|
||||||
print("Read Vector3Parameters")
|
|
||||||
self.name = AlternativaProtocol.readString(stream)
|
self.name = AlternativaProtocol.readString(stream)
|
||||||
self.value = unpackStream(">3f", stream)
|
self.value = unpackStream(">3f", stream)
|
||||||
|
|
||||||
@@ -137,7 +137,6 @@ class Vector4Parameter:
|
|||||||
self.value = (0.0, 0.0, 0.0, 0.0)
|
self.value = (0.0, 0.0, 0.0, 0.0)
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read Vector4Parameters")
|
|
||||||
self.name = AlternativaProtocol.readString(stream)
|
self.name = AlternativaProtocol.readString(stream)
|
||||||
self.value = unpackStream(">4f", stream)
|
self.value = unpackStream(">4f", stream)
|
||||||
|
|
||||||
@@ -243,7 +242,6 @@ class SpawnPoint:
|
|||||||
self.type = 0
|
self.type = 0
|
||||||
|
|
||||||
def read(self, stream, optionalMask):
|
def read(self, stream, optionalMask):
|
||||||
print("Read SpawnPoint")
|
|
||||||
self.position = unpackStream(">3f", stream)
|
self.position = unpackStream(">3f", stream)
|
||||||
self.rotation = unpackStream(">3f", stream)
|
self.rotation = unpackStream(">3f", stream)
|
||||||
self.type, = unpackStream(">I", stream)
|
self.type, = unpackStream(">I", stream)
|
||||||
@@ -259,10 +257,9 @@ class Prop:
|
|||||||
# Optional
|
# Optional
|
||||||
self.groupName = ""
|
self.groupName = ""
|
||||||
self.rotation = (0.0, 0.0, 0.0)
|
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):
|
def read(self, stream, optionalMask):
|
||||||
print(f"Read Prop")
|
|
||||||
if optionalMask.getOptional():
|
if optionalMask.getOptional():
|
||||||
self.groupName = AlternativaProtocol.readString(stream)
|
self.groupName = AlternativaProtocol.readString(stream)
|
||||||
self.ID, = unpackStream(">I", stream)
|
self.ID, = unpackStream(">I", stream)
|
||||||
@@ -305,11 +302,6 @@ class BattleMap:
|
|||||||
|
|
||||||
# Read packet
|
# Read packet
|
||||||
packet = AlternativaProtocol.readPacket(stream)
|
packet = AlternativaProtocol.readPacket(stream)
|
||||||
with open("packet.bin", "wb") as packetFile:
|
|
||||||
packetFile.write(
|
|
||||||
packet.read()
|
|
||||||
)
|
|
||||||
packet.seek(0)
|
|
||||||
optionalMask = AlternativaProtocol.OptionalMask()
|
optionalMask = AlternativaProtocol.OptionalMask()
|
||||||
optionalMask.read(packet)
|
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
|
import bpy
|
||||||
from bpy.types import Operator, OperatorFileListElement
|
from bpy.types import Operator, OperatorFileListElement, AddonPreferences
|
||||||
from bpy.props import StringProperty, BoolProperty, CollectionProperty
|
from bpy.props import StringProperty, BoolProperty, CollectionProperty
|
||||||
from bpy_extras.io_utils import ImportHelper
|
from bpy_extras.io_utils import ImportHelper
|
||||||
|
|
||||||
from .A3D import A3D
|
from .A3D import A3D
|
||||||
from .A3DBlenderImporter import A3DBlenderImporter
|
from .A3DBlenderImporter import A3DBlenderImporter
|
||||||
from .BattleMap import BattleMap
|
from .BattleMap import BattleMap
|
||||||
|
from .BattleMapBlenderImporter import BattleMapBlenderImporter
|
||||||
|
|
||||||
from glob import glob
|
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
|
Operators
|
||||||
'''
|
'''
|
||||||
@@ -100,6 +113,16 @@ class ImportBattleMap(Operator, ImportHelper):
|
|||||||
with open(self.filepath, "rb") as file:
|
with open(self.filepath, "rb") as file:
|
||||||
mapData.read(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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -123,6 +146,7 @@ def menu_func_import_battlemap(self, context):
|
|||||||
Registration
|
Registration
|
||||||
'''
|
'''
|
||||||
classes = [
|
classes = [
|
||||||
|
Preferences,
|
||||||
ImportA3D,
|
ImportA3D,
|
||||||
ImportBattleMap
|
ImportBattleMap
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user