mirror of
https://github.com/MapMakersAndProgrammers/io_scene_a3d.git
synced 2025-10-26 09:59:11 -07:00
Add initial BattleMap code
This commit is contained in:
192
io_scene_a3d/AlternativaProtocol.py
Normal file
192
io_scene_a3d/AlternativaProtocol.py
Normal file
@@ -0,0 +1,192 @@
|
||||
'''
|
||||
Copyright (c) 2024 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 io import BytesIO
|
||||
from struct import unpack
|
||||
from array import array
|
||||
from zlib import decompress
|
||||
|
||||
class OptionalMask:
|
||||
def __init__(self):
|
||||
self.optionalMask = []
|
||||
|
||||
def read(self, stream):
|
||||
print("Read optional mask")
|
||||
# Read "Null-mask" field
|
||||
nullMask = b""
|
||||
nullMaskOffset = 0
|
||||
|
||||
nullMaskField = int.from_bytes(stream.read(1), "little")
|
||||
nullMaskType = nullMaskField & 0b10000000
|
||||
if nullMaskType == 0:
|
||||
# Short null-mask: 5-29 bits
|
||||
nullMaskLength = nullMaskField & 0b01100000
|
||||
|
||||
nullMask += bytes(nullMaskField & 0b00011111)
|
||||
nullMask += stream.read(nullMaskLength) # 1,2 or 3 bytes
|
||||
nullMaskOffset = 3
|
||||
else:
|
||||
# Long null-mask: 64 - 4194304 bytes
|
||||
nullMaskLengthSize = nullMaskField & 0b01000000
|
||||
nullMaskLength = nullMaskField & 0b00111111
|
||||
if nullMaskLengthSize > 0:
|
||||
# Long length: 22 bits
|
||||
nullMaskLength = nullMaskLength << 16
|
||||
nullMaskLength += int.from_bytes(stream.read(2), "big")
|
||||
else:
|
||||
# Short length: 6 bits
|
||||
pass
|
||||
|
||||
nullMask += stream.read(nullMaskLength)
|
||||
nullMaskOffset = 0
|
||||
|
||||
nullMask = BytesIO(nullMask)
|
||||
# Process first byte (the first byte is missing some bits on some nullmask configs)
|
||||
maskByte = int.from_bytes(nullMask.read(1))
|
||||
for bitI in range(7 - nullMaskOffset, -1, -1):
|
||||
self.optionalMask.append(
|
||||
not bool(maskByte & (2**bitI))
|
||||
)
|
||||
|
||||
# Process the rest of the bytes
|
||||
for maskByte in nullMask.read():
|
||||
for bitI in range(7, -1, -1):
|
||||
self.optionalMask.append(
|
||||
not bool(maskByte & (2**bitI))
|
||||
)
|
||||
|
||||
print(f"Optional mask flags: {len(self.optionalMask)}")
|
||||
|
||||
def getOptional(self):
|
||||
optional = self.optionalMask.pop(0)
|
||||
return optional
|
||||
|
||||
def getOptionals(self, count):
|
||||
optionals = ()
|
||||
for _ in range(count):
|
||||
optionals += (self.optionalMask.pop(0),)
|
||||
return optionals
|
||||
|
||||
def getLength(self):
|
||||
return len(self.optionalMask)
|
||||
|
||||
def readPacket(stream):
|
||||
print("Reading packet")
|
||||
|
||||
# Read "Package Length" field
|
||||
packageLength = 0
|
||||
packageGzip = False
|
||||
|
||||
packageLengthField = int.from_bytes(stream.read(1), "little")
|
||||
packageLengthSize = packageLengthField & 0b10000000
|
||||
if packageLengthSize == 0:
|
||||
# Short package: 14 bits
|
||||
packageLength += (packageLengthField & 0b00111111) << 8
|
||||
packageLength += int.from_bytes(stream.read(1), "little")
|
||||
|
||||
packageGzip = packageLengthField & 0b01000000
|
||||
else:
|
||||
# Long package: 31 bits
|
||||
packageLength += (packageLengthField & 0b00111111) << 24
|
||||
packageLength += int.from_bytes(stream.read(3), "little")
|
||||
|
||||
packageGzip = packageLengthField & 0b01000000
|
||||
|
||||
# Decompress gzip data
|
||||
package = stream.read()
|
||||
if packageGzip:
|
||||
print("Decompressing packet")
|
||||
package = decompress(package)
|
||||
package = BytesIO(package)
|
||||
|
||||
return package
|
||||
|
||||
'''
|
||||
Array
|
||||
'''
|
||||
def readArrayLength(package):
|
||||
arrayLength = 0
|
||||
|
||||
arrayField = int.from_bytes(package.read(1), "little")
|
||||
arrayLengthType = arrayField & 0b10000000
|
||||
# Short array length
|
||||
if arrayLengthType == 0:
|
||||
# Length of the array is contained in the last 7 bits of this byte
|
||||
arrayLength = arrayField & 0b01111111
|
||||
else: # Must be large array length
|
||||
longArrayLengthType = arrayField & 0b01000000
|
||||
# Length in last 6 bits + next byte
|
||||
if longArrayLengthType == 0:
|
||||
lengthByte = int.from_bytes(package.read(1), "little")
|
||||
arrayLength = (arrayField & 0b00111111) << 8
|
||||
arrayLength += lengthByte
|
||||
else: # Length in last 6 bits + next 2 bytes
|
||||
lengthBytes = int.from_bytes(package.read(2), "big")
|
||||
arrayLength = (arrayField & 0b00111111) << 16
|
||||
arrayLength += lengthBytes
|
||||
|
||||
return arrayLength
|
||||
|
||||
def readObjectArray(package, objReader, optionalMask):
|
||||
length = readArrayLength(package)
|
||||
objects = []
|
||||
for _ in range(length):
|
||||
obj = objReader()
|
||||
obj.read(package, optionalMask)
|
||||
objects.append(obj)
|
||||
|
||||
return objects
|
||||
|
||||
def readString(package):
|
||||
stringLength = readArrayLength(package)
|
||||
string = package.read(stringLength)
|
||||
string = string.decode("utf-8")
|
||||
|
||||
return string
|
||||
|
||||
def readInt16Array(package):
|
||||
length = readArrayLength(package)
|
||||
integers = unpack(f"{length}h", package.read(length*2))
|
||||
integers = array("h", integers)
|
||||
|
||||
return integers
|
||||
|
||||
def readIntArray(package):
|
||||
length = readArrayLength(package)
|
||||
integers = unpack(f"{length}i", package.read(length*4))
|
||||
integers = array("i", integers)
|
||||
|
||||
return integers
|
||||
|
||||
def readInt64Array(package):
|
||||
length = readArrayLength(package)
|
||||
integers = unpack(f"{length}q", package.read(length*8))
|
||||
integers = array("q", integers)
|
||||
|
||||
return integers
|
||||
|
||||
def readFloatArray(package):
|
||||
length = readArrayLength(package)
|
||||
floats = unpack(f">{length}f", package.read(length*4))
|
||||
floats = array("f", floats)
|
||||
|
||||
return floats
|
||||
328
io_scene_a3d/BattleMap.py
Normal file
328
io_scene_a3d/BattleMap.py
Normal file
@@ -0,0 +1,328 @@
|
||||
'''
|
||||
Copyright (c) 2024 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
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
class AtlasRect:
|
||||
def __init__(self):
|
||||
self.height = 0
|
||||
self.libraryName = ""
|
||||
self.name = ""
|
||||
self.width = 0
|
||||
self.x = 0
|
||||
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)
|
||||
|
||||
class CollisionBox:
|
||||
def __init__(self):
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
self.rotation = (0.0, 0.0, 0.0)
|
||||
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)
|
||||
|
||||
class CollisionPlane:
|
||||
def __init__(self):
|
||||
self.length = 0.0
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
self.rotation = (0.0, 0.0, 0.0)
|
||||
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)
|
||||
|
||||
class CollisionTriangle:
|
||||
def __init__(self):
|
||||
self.length = 0.0
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
self.rotation = (0.0, 0.0, 0.0)
|
||||
self.v0 = (0.0, 0.0, 0.0)
|
||||
self.v1 = (0.0, 0.0, 0.0)
|
||||
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)
|
||||
|
||||
class ScalarParameter:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.value = 0.0
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read ScalarParameters")
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.value, = unpackStream(">f", stream)
|
||||
|
||||
class TextureParameter:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.textureName = ""
|
||||
|
||||
# Optional
|
||||
self.libraryName = None
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read TextureParameter")
|
||||
if optionalMask.getOptional():
|
||||
self.libraryName = AlternativaProtocol.readString(stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.textureName = AlternativaProtocol.readString(stream)
|
||||
|
||||
class Vector2Parameter:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.value = (0.0, 0.0)
|
||||
|
||||
def __init__(self, stream, optionalMask):
|
||||
print("Read Vector2Parameters")
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.value = unpackStream(">2f", stream)
|
||||
|
||||
class Vector3Parameter:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
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)
|
||||
|
||||
class Vector4Parameter:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
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)
|
||||
|
||||
'''
|
||||
Main objects
|
||||
'''
|
||||
class Atlas:
|
||||
def __init__(self):
|
||||
self.height = 0
|
||||
self.name = ""
|
||||
self.padding = 0
|
||||
self.rects = []
|
||||
self.width = 0
|
||||
|
||||
# Get the rect's texture from an atlas
|
||||
# XXX: Handle padding?
|
||||
def resolveRectImage(self, rectName, atlasImage):
|
||||
rect = None
|
||||
for childRect in self.rects:
|
||||
if childRect.name == rectName:
|
||||
rect = childRect
|
||||
if rect == None:
|
||||
raise RuntimeError(f"Couldn't find rect with name: {rectName}")
|
||||
|
||||
# Cut the texture out
|
||||
rectTexture = atlasImage.crop(
|
||||
(rect.x, rect.y, rect.x+rect.width, rect.y+rect.height)
|
||||
)
|
||||
return rectTexture
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read Atlas")
|
||||
self.height, unpackStream(">i", stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.padding = unpackStream(">I", stream)
|
||||
self.rects = AlternativaProtocol.readObjectArray(stream, AtlasRect, optionalMask)
|
||||
self.width, = unpackStream(">I", stream)
|
||||
|
||||
class Batch:
|
||||
def __init__(self):
|
||||
self.materialID = 0
|
||||
self.name = ""
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
self.propIDs = ""
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read Batch")
|
||||
self.materialID, = unpackStream(">I", stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.position = unpackStream(">3f", stream)
|
||||
self.propIDs = AlternativaProtocol.readString(stream)
|
||||
|
||||
class CollisionGeometry:
|
||||
def __init__(self):
|
||||
self.boxes = []
|
||||
self.planes = []
|
||||
self.triangles = []
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print("Read CollisionGeometry")
|
||||
self.boxes = AlternativaProtocol.readObjectArray(stream, CollisionBox, optionalMask)
|
||||
self.planes = AlternativaProtocol.readObjectArray(stream, CollisionPlane, optionalMask)
|
||||
self.triangles = AlternativaProtocol.readObjectArray(stream, CollisionTriangle, optionalMask)
|
||||
|
||||
class Material:
|
||||
def __init__(self):
|
||||
self.ID = 0
|
||||
self.name = ""
|
||||
self.shader = ""
|
||||
self.textureParameters = None
|
||||
|
||||
# Optional
|
||||
self.scalarParameters = None
|
||||
self.vector2Parameters = None
|
||||
self.vector3Parameters = None
|
||||
self.vector4Parameters = None
|
||||
|
||||
def getTextureParameterByName(self, name):
|
||||
for textureParameter in self.textureParameters:
|
||||
if textureParameter.name == name: return textureParameter
|
||||
|
||||
raise RuntimeError(f"Couldn't find texture parameter with name: {name}")
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print(f"Read Material")
|
||||
self.ID, = unpackStream(">I", stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
if optionalMask.getOptional():
|
||||
self.scalarParameters = AlternativaProtocol.readObjectArray(stream, ScalarParameter, optionalMask)
|
||||
self.shader = AlternativaProtocol.readString(stream)
|
||||
self.textureParameters = AlternativaProtocol.readObjectArray(stream, TextureParameter, optionalMask)
|
||||
if optionalMask.getOptional():
|
||||
self.vector2Parameters = AlternativaProtocol.readObjectArray(stream, Vector2Parameter, optionalMask)
|
||||
if optionalMask.getOptional():
|
||||
self.vector3Parameters = AlternativaProtocol.readObjectArray(stream, Vector3Parameter, optionalMask)
|
||||
if optionalMask.getOptional():
|
||||
self.vector4Parameters = AlternativaProtocol.readObjectArray(stream, Vector4Parameter, optionalMask)
|
||||
|
||||
class SpawnPoint:
|
||||
def __init__(self):
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
self.rotation = (0.0, 0.0, 0.0)
|
||||
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)
|
||||
|
||||
class Prop:
|
||||
def __init__(self):
|
||||
self.ID = 0
|
||||
self.libraryName = ""
|
||||
self.materialID = 0
|
||||
self.name = ""
|
||||
self.position = (0.0, 0.0, 0.0)
|
||||
|
||||
# Optional
|
||||
self.groupName = ""
|
||||
self.rotation = (0.0, 0.0, 0.0)
|
||||
self.scale = (0.0, 0.0, 0.0)
|
||||
|
||||
def read(self, stream, optionalMask):
|
||||
print(f"Read Prop")
|
||||
if optionalMask.getOptional():
|
||||
self.groupName = AlternativaProtocol.readString(stream)
|
||||
self.ID, = unpackStream(">I", stream)
|
||||
self.libraryName = AlternativaProtocol.readString(stream)
|
||||
self.materialID, = unpackStream(">I", stream)
|
||||
self.name = AlternativaProtocol.readString(stream)
|
||||
self.position = unpackStream(">3f", stream)
|
||||
if optionalMask.getOptional():
|
||||
self.rotation = unpackStream(">3f", stream)
|
||||
if optionalMask.getOptional():
|
||||
self.scale = unpackStream(">3f", stream)
|
||||
|
||||
'''
|
||||
Main
|
||||
'''
|
||||
class BattleMap:
|
||||
def __init__(self):
|
||||
self.atlases = []
|
||||
self.batches = []
|
||||
self.collisionGeometry = []
|
||||
self.collisionGeometryOutsideGamingZone = []
|
||||
self.materials = []
|
||||
self.spawnPoints = []
|
||||
self.staticGeometry = []
|
||||
|
||||
'''
|
||||
Getters
|
||||
'''
|
||||
def getMaterialByID(self, materialID):
|
||||
for material in self.materials:
|
||||
if material.ID == materialID: return material
|
||||
|
||||
raise RuntimeError(f"Couldn't find material with ID: {materialID}")
|
||||
|
||||
'''
|
||||
IO
|
||||
'''
|
||||
def read(self, stream):
|
||||
print("Reading BIN map")
|
||||
|
||||
# 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)
|
||||
|
||||
# Read data
|
||||
if optionalMask.getOptional():
|
||||
self.atlases = AlternativaProtocol.readObjectArray(packet, Atlas, optionalMask)
|
||||
if optionalMask.getOptional():
|
||||
self.batches = AlternativaProtocol.readObjectArray(packet, Batch, optionalMask)
|
||||
self.collisionGeometry = CollisionGeometry()
|
||||
self.collisionGeometry.read(packet, optionalMask)
|
||||
self.collisionGeometryOutsideGamingZone = CollisionGeometry()
|
||||
self.collisionGeometryOutsideGamingZone.read(packet, optionalMask)
|
||||
self.materials = AlternativaProtocol.readObjectArray(packet, Material, optionalMask)
|
||||
if optionalMask.getOptional():
|
||||
self.spawnPoints = AlternativaProtocol.readObjectArray(packet, SpawnPoint, optionalMask)
|
||||
self.staticGeometry = AlternativaProtocol.readObjectArray(packet, Prop, optionalMask)
|
||||
@@ -27,6 +27,7 @@ from bpy_extras.io_utils import ImportHelper
|
||||
|
||||
from .A3D import A3D
|
||||
from .A3DBlenderImporter import A3DBlenderImporter
|
||||
from .BattleMap import BattleMap
|
||||
|
||||
from glob import glob
|
||||
|
||||
@@ -40,8 +41,8 @@ class ImportA3D(Operator, ImportHelper):
|
||||
bl_options = {'PRESET', 'UNDO'}
|
||||
|
||||
filter_glob: StringProperty(default="*.a3d", options={'HIDDEN'})
|
||||
directory: StringProperty(subtype='DIR_PATH', options={'HIDDEN'})
|
||||
files: CollectionProperty(type=OperatorFileListElement, options={"HIDDEN", "SKIP_SAVE"})
|
||||
directory: StringProperty(subtype="DIR_PATH", options={'HIDDEN'})
|
||||
files: CollectionProperty(type=OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'})
|
||||
|
||||
# User options
|
||||
create_collection: BoolProperty(name="Create collection", description="Create a collection to hold all the model objects", default=False)
|
||||
@@ -78,6 +79,29 @@ class ImportA3D(Operator, ImportHelper):
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class ImportBattleMap(Operator, ImportHelper):
|
||||
bl_idname = "import_scene.tanki_battlemap"
|
||||
bl_label = "Import map"
|
||||
bl_description = "Import a BIN format Tanki Online map file"
|
||||
bl_options = {'PRESET', 'UNDO'}
|
||||
|
||||
filter_glob: StringProperty(default="*.bin", options={'HIDDEN'})
|
||||
directory: StringProperty(subtype="DIR_PATH", options={'HIDDEN'})
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
def invoke(self, context, event):
|
||||
return ImportHelper.invoke(self, context, event)
|
||||
|
||||
def execute(self, context):
|
||||
print(f"Reading BattleMap data from {self.filepath}")
|
||||
mapData = BattleMap()
|
||||
with open(self.filepath, "rb") as file:
|
||||
mapData.read(file)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
'''
|
||||
Menu
|
||||
'''
|
||||
@@ -92,22 +116,28 @@ def import_panel_options(layout, operator):
|
||||
def menu_func_import_a3d(self, context):
|
||||
self.layout.operator(ImportA3D.bl_idname, text="Alternativa3D HTML5 (.a3d)")
|
||||
|
||||
def menu_func_import_battlemap(self, context):
|
||||
self.layout.operator(ImportBattleMap.bl_idname, text="Tanki Online BattleMap (.bin)")
|
||||
|
||||
'''
|
||||
Registration
|
||||
'''
|
||||
classes = [
|
||||
ImportA3D
|
||||
ImportA3D,
|
||||
ImportBattleMap
|
||||
]
|
||||
|
||||
def register():
|
||||
for c in classes:
|
||||
bpy.utils.register_class(c)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import_a3d)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import_battlemap)
|
||||
|
||||
def unregister():
|
||||
for c in classes:
|
||||
bpy.utils.unregister_class(c)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_a3d)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_battlemap)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
Reference in New Issue
Block a user