MSH scene reading draft, no blender conversion yet
This commit is contained in:
parent
1892a1cdbd
commit
152b22feb9
|
@ -53,11 +53,12 @@ if "bpy" in locals():
|
||||||
# End of stuff taken from glTF
|
# End of stuff taken from glTF
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_extras.io_utils import ExportHelper
|
from bpy_extras.io_utils import ExportHelper, ImportHelper
|
||||||
from bpy.props import BoolProperty, EnumProperty
|
from bpy.props import BoolProperty, EnumProperty
|
||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
from .msh_scene import create_scene
|
from .msh_scene import create_scene
|
||||||
from .msh_scene_save import save_scene
|
from .msh_scene_save import save_scene
|
||||||
|
from .msh_scene_read import read_scene
|
||||||
from .msh_material_properties import *
|
from .msh_material_properties import *
|
||||||
|
|
||||||
class ExportMSH(Operator, ExportHelper):
|
class ExportMSH(Operator, ExportHelper):
|
||||||
|
@ -108,16 +109,47 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
# Only needed if you want to add into a dynamic menu
|
# Only needed if you want to add into a dynamic menu
|
||||||
def menu_func_export(self, context):
|
def menu_func_export(self, context):
|
||||||
self.layout.operator(ExportMSH.bl_idname, text="SWBF msh (.msh)")
|
self.layout.operator(ExportMSH.bl_idname, text="SWBF msh (.msh)")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ImportMSH(Operator, ImportHelper):
|
||||||
|
""" Import an SWBF .msh file. """
|
||||||
|
|
||||||
|
bl_idname = "swbf_msh.import"
|
||||||
|
bl_label = "Import SWBF .msh File"
|
||||||
|
filename_ext = ".msh"
|
||||||
|
|
||||||
|
filter_glob: StringProperty(
|
||||||
|
default="*.msh",
|
||||||
|
options={'HIDDEN'},
|
||||||
|
maxlen=255, # Max internal buffer length, longer would be clamped.
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
with open(self.filepath, 'rb') as input_file:
|
||||||
|
read_scene(input_file)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def menu_func_import(self, context):
|
||||||
|
self.layout.operator(ImportMSH.bl_idname, text="SWBF msh (.msh)")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(MaterialProperties)
|
bpy.utils.register_class(MaterialProperties)
|
||||||
bpy.utils.register_class(MaterialPropertiesPanel)
|
bpy.utils.register_class(MaterialPropertiesPanel)
|
||||||
bpy.utils.register_class(ExportMSH)
|
bpy.utils.register_class(ExportMSH)
|
||||||
|
bpy.utils.register_class(ImportMSH)
|
||||||
|
|
||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||||
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||||
bpy.types.Material.swbf_msh = bpy.props.PointerProperty(type=MaterialProperties)
|
bpy.types.Material.swbf_msh = bpy.props.PointerProperty(type=MaterialProperties)
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,7 +157,10 @@ def unregister():
|
||||||
bpy.utils.unregister_class(MaterialProperties)
|
bpy.utils.unregister_class(MaterialProperties)
|
||||||
bpy.utils.unregister_class(MaterialPropertiesPanel)
|
bpy.utils.unregister_class(MaterialPropertiesPanel)
|
||||||
bpy.utils.unregister_class(ExportMSH)
|
bpy.utils.unregister_class(ExportMSH)
|
||||||
|
bpy.utils.unregister_class(ImportMSH)
|
||||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||||
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
register()
|
register()
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
|
||||||
|
import io
|
||||||
|
import struct
|
||||||
|
|
||||||
|
class Reader:
|
||||||
|
def __init__(self, file, chunk_id: str, parent=None, indent=0):
|
||||||
|
self.file = file
|
||||||
|
self.size: int = 0
|
||||||
|
self.size_pos = None
|
||||||
|
self.parent = parent
|
||||||
|
self.header = chunk_id
|
||||||
|
self.indent = " " * indent
|
||||||
|
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.size_pos = self.file.tell()
|
||||||
|
self.file.seek(4,1) #skip header, will add check later
|
||||||
|
self.size = self.read_u32()
|
||||||
|
|
||||||
|
padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0
|
||||||
|
self.end_pos = self.size_pos + padding_length + self.size + 8
|
||||||
|
|
||||||
|
print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos))
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if self.size > self.MAX_SIZE:
|
||||||
|
raise OverflowError(f".msh file overflowed max size. size = {self.size} MAX_SIZE = {self.MAX_SIZE}")
|
||||||
|
|
||||||
|
print(self.indent + "End " + self.header)
|
||||||
|
self.file.seek(self.end_pos)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def read_bytes(self,num_bytes):
|
||||||
|
return self.file.read(num_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
def read_string(self):
|
||||||
|
last_byte = self.read_bytes(1)
|
||||||
|
result = b''
|
||||||
|
while last_byte[0] != 0x0:
|
||||||
|
result += last_byte
|
||||||
|
last_byte = self.read_bytes(1)
|
||||||
|
|
||||||
|
return result.decode("utf-8")
|
||||||
|
|
||||||
|
def read_i8(self, num=1):
|
||||||
|
buf = self.read_bytes(num)
|
||||||
|
result = struct.unpack(f"<{num}b", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
def read_u8(self, num=1):
|
||||||
|
buf = self.read_bytes(num)
|
||||||
|
result = struct.unpack(f"<{num}B", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
def read_i16(self, num=1):
|
||||||
|
buf = self.read_bytes(num * 2)
|
||||||
|
result = struct.unpack(f"<{num}h", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
def read_u16(self, num=1):
|
||||||
|
buf = self.read_bytes(num * 2)
|
||||||
|
result = struct.unpack(f"<{num}H", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
def read_i32(self, num=1):
|
||||||
|
buf = self.read_bytes(num * 4)
|
||||||
|
result = struct.unpack(f"<{num}i", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
def read_u32(self, num=1):
|
||||||
|
buf = self.read_bytes(num * 4)
|
||||||
|
result = struct.unpack(f"<{num}I", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
def read_f32(self, num=1):
|
||||||
|
buf = self.read_bytes(num * 4)
|
||||||
|
result = struct.unpack(f"<{num}f", buf)
|
||||||
|
return result[0] if num == 1 else result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def read_child(self, child_id: str):
|
||||||
|
child = Reader(self.file, chunk_id=child_id, parent=self, indent=int(len(self.indent) / 2) + 1)
|
||||||
|
return child
|
||||||
|
|
||||||
|
|
||||||
|
def skip_bytes(self,num):
|
||||||
|
self.file.seek(num,1)
|
||||||
|
|
||||||
|
|
||||||
|
def peak_next_header(self):
|
||||||
|
buf = self.read_bytes(4);
|
||||||
|
self.file.seek(-4,1)
|
||||||
|
return buf.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def could_have_child(self):
|
||||||
|
return self.end_pos - self.file.tell() >= 8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MAX_SIZE: int = 2147483647 - 8
|
||||||
|
|
||||||
|
'''
|
||||||
|
with open("/Users/will/Desktop/spacedoortest/spa1_prop_impdoor.msh", "rb") as tst_stream:
|
||||||
|
with Reader(tst_stream, "HEDR") as hedr:
|
||||||
|
print(hedr.peak_next_header())
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
""" Contains functions for saving a Scene to a .msh file. """
|
||||||
|
|
||||||
|
from itertools import islice
|
||||||
|
from typing import Dict
|
||||||
|
from .msh_scene import Scene
|
||||||
|
from .msh_model import *
|
||||||
|
from .msh_material import *
|
||||||
|
from .msh_reader import Reader
|
||||||
|
from .msh_utilities import *
|
||||||
|
|
||||||
|
from .crc import *
|
||||||
|
|
||||||
|
def read_scene(input_file) -> Scene:
|
||||||
|
|
||||||
|
scene = Scene()
|
||||||
|
scene.models = []
|
||||||
|
|
||||||
|
with Reader(file=input_file, chunk_id="HEDR") as hedr:
|
||||||
|
with hedr.read_child("MSH2") as msh2:
|
||||||
|
|
||||||
|
with msh2.read_child("SINF") as sinf:
|
||||||
|
pass
|
||||||
|
|
||||||
|
materials_list: List[str] = []
|
||||||
|
|
||||||
|
with msh2.read_child("MATL") as matl:
|
||||||
|
materials_list = _read_matl_and_get_materials_list(matl)
|
||||||
|
|
||||||
|
while ("MODL" in msh2.peak_next_header()):
|
||||||
|
with msh2.read_child("MODL") as modl:
|
||||||
|
scene.models.append(_read_modl(modl, materials_list))
|
||||||
|
|
||||||
|
mats_dict = {}
|
||||||
|
for i,mat in enumerate(materials_list):
|
||||||
|
mats_dict["Material" + str(i)] = mat
|
||||||
|
|
||||||
|
scene.materials = mats_dict
|
||||||
|
|
||||||
|
#with hedr.read_child("ANM2") as anm2: #simple for now
|
||||||
|
# for anim in scene.anims:
|
||||||
|
# _write_anm2(anm2, anim)
|
||||||
|
|
||||||
|
#with hedr.read_child("CL1L"):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return scene
|
||||||
|
|
||||||
|
|
||||||
|
def _read_matl_and_get_materials_list(matl: Reader) -> List[str]:
|
||||||
|
materials_list: List[str] = []
|
||||||
|
|
||||||
|
num_mats = matl.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_mats):
|
||||||
|
with matl.read_child("MATD") as matd:
|
||||||
|
materials_list.append(_read_matd(matd))
|
||||||
|
|
||||||
|
return materials_list
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _read_matd(matd: Reader) -> Material:
|
||||||
|
|
||||||
|
mat = Material()
|
||||||
|
|
||||||
|
while matd.could_have_child():
|
||||||
|
|
||||||
|
next_header = matd.peak_next_header()
|
||||||
|
|
||||||
|
if "NAME" in next_header:
|
||||||
|
with matd.read_child("NAME") as name:
|
||||||
|
mat.name = name.read_string()
|
||||||
|
print(matd.indent + "Got a new material: " + mat.name)
|
||||||
|
|
||||||
|
elif "DATA" in next_header:
|
||||||
|
with matd.read_child("DATA") as data:
|
||||||
|
data.read_f32(4) # Diffuse Color (Seams to get ignored by modelmunge)
|
||||||
|
mat.specular_color = data.read_f32(4)
|
||||||
|
data.read_f32(4) # Ambient Color (Seams to get ignored by modelmunge and Zero(?))
|
||||||
|
data.read_f32() # Specular Exponent/Decay (Gets ignored by RedEngine in SWBFII for all known materials)
|
||||||
|
|
||||||
|
elif "ATRB" in next_header:
|
||||||
|
with matd.read_child("ATRB") as atrb:
|
||||||
|
mat.flags = atrb.read_u8()
|
||||||
|
mat.rendertype = atrb.read_u8()
|
||||||
|
mat.data = atrb.read_u8(2)
|
||||||
|
|
||||||
|
elif "TX0D" in next_header:
|
||||||
|
with matd.read_child("TX0D") as tx0d:
|
||||||
|
mat.texture0 = tx0d.read_string()
|
||||||
|
|
||||||
|
elif "TX1D" in next_header:
|
||||||
|
with matd.read_child("TX1D") as tx1d:
|
||||||
|
mat.texture1 = tx1d.read_string()
|
||||||
|
|
||||||
|
elif "TX2D" in next_header:
|
||||||
|
with matd.read_child("TX2D") as tx2d:
|
||||||
|
mat.texture2 = tx2d.read_string()
|
||||||
|
|
||||||
|
elif "TX3D" in next_header:
|
||||||
|
with matd.read_child("TX3D") as tx3d:
|
||||||
|
mat.texture3 = tx3d.read_string()
|
||||||
|
|
||||||
|
else:
|
||||||
|
matd.skip_bytes(4)
|
||||||
|
|
||||||
|
return mat
|
||||||
|
|
||||||
|
|
||||||
|
def _read_modl(modl: Reader, materials_list: List[str]) -> Model:
|
||||||
|
|
||||||
|
model = Model()
|
||||||
|
|
||||||
|
while modl.could_have_child():
|
||||||
|
|
||||||
|
next_header = modl.peak_next_header()
|
||||||
|
|
||||||
|
if "MTYP" in next_header:
|
||||||
|
with modl.read_child("MTYP") as mtyp:
|
||||||
|
model.model_type = mtyp.read_u32()
|
||||||
|
|
||||||
|
elif "MNDX" in next_header:
|
||||||
|
with modl.read_child("MNDX") as mndx:
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif "NAME" in next_header:
|
||||||
|
with modl.read_child("NAME") as name:
|
||||||
|
model.name = name.read_string()
|
||||||
|
print(modl.indent + "New model: " + model.name)
|
||||||
|
|
||||||
|
elif "PRNT" in next_header:
|
||||||
|
with modl.read_child("PRNT") as prnt:
|
||||||
|
model.parent = prnt.read_string()
|
||||||
|
|
||||||
|
elif "FLGS" in next_header:
|
||||||
|
with modl.read_child("FLGS") as flgs:
|
||||||
|
model.hidden = flgs.read_u32()
|
||||||
|
|
||||||
|
elif "TRAN" in next_header:
|
||||||
|
with modl.read_child("TRAN") as tran:
|
||||||
|
model.transform = _read_tran(tran)
|
||||||
|
|
||||||
|
elif "GEOM" in next_header:
|
||||||
|
model.geometry = []
|
||||||
|
with modl.read_child("GEOM") as geom:
|
||||||
|
|
||||||
|
next_header_modl = geom.peak_next_header()
|
||||||
|
|
||||||
|
if "SEGM" in next_header_modl:
|
||||||
|
with geom.read_child("SEGM") as segm:
|
||||||
|
model.geometry.append(_read_segm(segm, materials_list))
|
||||||
|
'''
|
||||||
|
if model.model_type == ModelType.SKIN:
|
||||||
|
with modl.read_child("ENVL") as envl:
|
||||||
|
envl.write_u32(len(scene.models))
|
||||||
|
for i in range(len(scene.models)):
|
||||||
|
envl.write_u32(i)
|
||||||
|
'''
|
||||||
|
elif "SWCI" in next_header:
|
||||||
|
prim = CollisionPrimitive()
|
||||||
|
with modl.read_child("SWCI") as swci:
|
||||||
|
prim.shape.value = swci.read_u32()
|
||||||
|
prim.radius = swci.read_f32()
|
||||||
|
prim.height = swci.read_f32()
|
||||||
|
prim.length = swci.read_f32()
|
||||||
|
model.collisionprimitive = prim
|
||||||
|
|
||||||
|
else:
|
||||||
|
with modl.read_child("NULL") as unknown:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _read_tran(tran: Reader) -> ModelTransform:
|
||||||
|
|
||||||
|
xform = ModelTransform()
|
||||||
|
|
||||||
|
tran.skip_bytes(4 * 3) #ignore scale
|
||||||
|
xform.rotation = Quaternion(tran.read_f32(4))
|
||||||
|
xform.position = Vector(tran.read_f32(3))
|
||||||
|
|
||||||
|
return xform
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment:
|
||||||
|
|
||||||
|
geometry_seg = GeometrySegment()
|
||||||
|
|
||||||
|
while segm.could_have_child():
|
||||||
|
|
||||||
|
next_header = segm.peak_next_header()
|
||||||
|
|
||||||
|
if "MATI" in next_header:
|
||||||
|
with segm.read_child("MATI") as mati:
|
||||||
|
geometry_seg.material_name = materials_list[mati.read_u32()]
|
||||||
|
|
||||||
|
elif "POSL" in next_header:
|
||||||
|
with segm.read_child("POSL") as posl:
|
||||||
|
num_positions = posl.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_positions):
|
||||||
|
geometry_seg.positions.append(Vector(posl.read_f32(3)))
|
||||||
|
|
||||||
|
elif "NRML" in next_header:
|
||||||
|
with segm.read_child("NRML") as nrml:
|
||||||
|
num_normals = nrml.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_positions):
|
||||||
|
geometry_seg.normals.append(Vector(nrml.read_f32(3)))
|
||||||
|
|
||||||
|
elif "WGHT" in next_header:
|
||||||
|
geometry_seg.weights = []
|
||||||
|
|
||||||
|
with segm.read_child("WGHT") as wght:
|
||||||
|
num_boneweights = wght.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_boneweights):
|
||||||
|
geometry_seg.weights.append((wght.read_u32(), wght.read_f32()))
|
||||||
|
|
||||||
|
elif "CLRL" in next_header:
|
||||||
|
geometry_seg.colors = []
|
||||||
|
|
||||||
|
with segm.read_child("CLRL") as clrl:
|
||||||
|
num_colors = clrl.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_colors):
|
||||||
|
geometry_seg.colors += unpack_color(clrl.read_u32())
|
||||||
|
|
||||||
|
elif "UV0L" in next_header:
|
||||||
|
with segm.read_child("UV0L") as uv0l:
|
||||||
|
num_texcoords = uv0l.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_texcoords):
|
||||||
|
geometry_seg.texcoords.append(Vector(uv0l.read_f32(2)))
|
||||||
|
|
||||||
|
elif "NDXL" in next_header:
|
||||||
|
with segm.read_child("NDXL") as ndxl:
|
||||||
|
num_polygons = ndxl.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_polygons):
|
||||||
|
polygon = ndxl.read_u16(ndxl.read_u16())
|
||||||
|
geometry_seg.polygons.append(polygon)
|
||||||
|
|
||||||
|
elif "NDXT" in next_header:
|
||||||
|
with segm.read_child("NDXT") as ndxt:
|
||||||
|
num_tris = ndxt.read_u32()
|
||||||
|
|
||||||
|
for _ in range(num_tris):
|
||||||
|
geometry_seg.triangles.append(ndxt.read_u16(3))
|
||||||
|
|
||||||
|
elif "STRP" in next_header:
|
||||||
|
with segm.read_child("STRP") as strp:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if segm.read_u16 != 0: #trailing 0 bug
|
||||||
|
segm.skip_bytes(-2)
|
||||||
|
|
||||||
|
else:
|
||||||
|
with segm.read_child("NULL") as unknown:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return geometry_seg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
|
|
||||||
|
with anm2.read_child("CYCL") as cycl:
|
||||||
|
|
||||||
|
cycl.write_u32(1)
|
||||||
|
cycl.write_string(anim.name)
|
||||||
|
|
||||||
|
for _ in range(63 - len(anim.name)):
|
||||||
|
cycl.write_u8(0)
|
||||||
|
|
||||||
|
cycl.write_f32(10.0) #test framerate
|
||||||
|
cycl.write_u32(0) #what does play style refer to?
|
||||||
|
cycl.write_u32(0, 20) #first frame indices
|
||||||
|
|
||||||
|
|
||||||
|
with anm2.read_child("KFR3") as kfr3:
|
||||||
|
|
||||||
|
kfr3.write_u32(len(anim.bone_transforms.keys()))
|
||||||
|
|
||||||
|
for boneName in anim.bone_transforms.keys():
|
||||||
|
kfr3.write_u32(crc(boneName))
|
||||||
|
kfr3.write_u32(0) #what is keyframe type?
|
||||||
|
|
||||||
|
kfr3.write_u32(21, 21) #basic testing
|
||||||
|
|
||||||
|
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
||||||
|
kfr3.write_u32(i)
|
||||||
|
kfr3.write_f32(xform.translation.x, xform.translation.y, xform.translation.z)
|
||||||
|
|
||||||
|
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
||||||
|
kfr3.write_u32(i)
|
||||||
|
kfr3.write_f32(xform.rotation.x, xform.rotation.y, xform.rotation.z, xform.rotation.w)
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
""" Misc utilities. """
|
""" Misc utilities. """
|
||||||
|
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
from typing import List
|
||||||
|
|
||||||
def add_vec(l: Vector, r: Vector) -> Vector:
|
def add_vec(l: Vector, r: Vector) -> Vector:
|
||||||
return Vector(v0 + v1 for v0, v1 in zip(l, r))
|
return Vector(v0 + v1 for v0, v1 in zip(l, r))
|
||||||
|
@ -29,3 +30,14 @@ def pack_color(color) -> int:
|
||||||
packed |= (int(color[3] * 255.0 + 0.5) << 24)
|
packed |= (int(color[3] * 255.0 + 0.5) << 24)
|
||||||
|
|
||||||
return packed
|
return packed
|
||||||
|
|
||||||
|
def unpack_color(color: int) -> List[float]:
|
||||||
|
|
||||||
|
mask = int(0x000000ff)
|
||||||
|
|
||||||
|
r = (color & (mask << 16)) / 255.0
|
||||||
|
g = (color & (mask << 8)) / 255.0
|
||||||
|
b = (color & mask) / 255.0
|
||||||
|
a = (color & (mask << 24)) / 255.0
|
||||||
|
|
||||||
|
return [r,g,b,a]
|
||||||
|
|
Loading…
Reference in New Issue