mirror of
				https://github.com/MapMakersAndProgrammers/io_scene_a3d.git
				synced 2025-10-27 02:19:06 -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
	 Pyogenics
					Pyogenics