material support

This commit is contained in:
SleepKiller 2019-11-15 16:28:09 +13:00
parent fd0ba8720b
commit c12fc862b0
7 changed files with 510 additions and 35 deletions

View File

@ -54,17 +54,11 @@ if "bpy" in locals():
import bpy import bpy
from bpy_extras.io_utils import ExportHelper from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, 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_material_properties import *
def write_some_data(context, filepath, use_some_setting):
with open(filepath, 'wb') as output_file:
save_scene(output_file, create_scene())
return {'FINISHED'}
# ExportHelper is a helper class, defines filename and # ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector. # invoke() function which calls the file selector.
@ -101,24 +95,30 @@ class ExportSomeData(Operator, ExportHelper):
) )
def execute(self, context): def execute(self, context):
return write_some_data(context, self.filepath, self.use_setting) with open(self.filepath, 'wb') as output_file:
save_scene(output_file, create_scene())
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(ExportSomeData.bl_idname, text="SWBF msh (.msh)") self.layout.operator(ExportSomeData.bl_idname, text="SWBF msh (.msh)")
def register(): def register():
bpy.utils.register_class(MaterialProperties)
bpy.utils.register_class(MaterialPropertiesPanel)
bpy.utils.register_class(ExportSomeData) bpy.utils.register_class(ExportSomeData)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export) bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.Material.swbf_msh = bpy.props.PointerProperty(type=MaterialProperties)
def unregister(): def unregister():
bpy.utils.unregister_class(MaterialProperties)
bpy.utils.unregister_class(MaterialPropertiesPanel)
bpy.utils.unregister_class(ExportSomeData) bpy.utils.unregister_class(ExportSomeData)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
if __name__ == "__main__": if __name__ == "__main__":
register() register()

View File

@ -0,0 +1,46 @@
""" Contains Material and dependent types for representing materials easilly
saved to a .msh file. """
from dataclasses import dataclass
from typing import Tuple
from enum import Enum, Flag
from mathutils import Color
class Rendertype(Enum):
# TODO: Add SWBF1 rendertypes.
NORMAL = 0
SCROLLING = 3
ENVMAPPED = 6
ANIMATED = 7
REFRACTION = 22
BLINK = 25
NORMALMAPPED_TILED = 24
NORMALMAPPED_ENVMAPPED = 26
NORMALMAPPED = 27
NORMALMAPPED_TILED_ENVMAP = 29
class MaterialFlags(Flag):
NONE = 0
UNLIT = 1
GLOW = 2
BLENDED_TRANSPARENCY = 4
DOUBLESIDED = 8
HARDEDGED_TRANSPARENCY = 16
PERPIXEL = 32
ADDITIVE_TRANSPARENCY = 64
SPECULAR = 128
@dataclass
class Material:
""" Data class representing a .msh material.
Intended to be stored in a dictionary so name is missing. """
specular_color: Color = Color((1.0, 1.0, 1.0))
rendertype: Rendertype = Rendertype.NORMAL
flags: MaterialFlags = MaterialFlags.NONE
data: Tuple[int, int] = (0, 0)
texture0: str = "white.tga"
texture1: str = ""
texture2: str = ""
texture3: str = ""

View File

@ -0,0 +1,110 @@
""" Gathers the Blender materials and returns them as a dictionary of
strings and Material objects. """
import bpy
from typing import Dict
from .msh_material import *
def gather_materials() -> Dict[str, Material]:
""" Gathers the Blender materials and returns them as
a dictionary of strings and Material objects. """
materials: Dict[str, Material] = {}
for blender_material in bpy.data.materials:
materials[blender_material.name] = read_material(blender_material)
return materials
def read_material(blender_material: bpy.types.Material) -> Material:
""" Reads a the swbf_msh properties from a Blender material and
returns a Material object. """
result = Material()
if blender_material.swbf_msh is None:
return result
props = blender_material.swbf_msh
result.specular_color = props.specular_color.copy()
result.rendertype = _read_material_props_rendertype(props)
result.flags = _read_material_props_flags(props)
result.data = _read_material_props_data(props)
result.texture0 = props.diffuse_map
result.texture1 = _read_normal_map_or_distortion_map_texture(props)
result.texture2 = _read_detail_texture(props)
result.texture3 = _read_envmap_texture(props)
return result
_RENDERTYPES_MAPPING = {
"NORMAL_BF2": Rendertype.NORMAL,
"SCROLLING_BF2": Rendertype.SCROLLING,
"ENVMAPPED_BF2": Rendertype.ENVMAPPED,
"ANIMATED_BF2": Rendertype.ANIMATED,
"REFRACTION_BF2": Rendertype.REFRACTION,
"BLINK_BF2": Rendertype.BLINK,
"NORMALMAPPED_TILED_BF2": Rendertype.NORMALMAPPED_TILED,
"NORMALMAPPED_ENVMAPPED_BF2": Rendertype.NORMALMAPPED_ENVMAPPED,
"NORMALMAPPED_BF2": Rendertype.NORMALMAPPED,
"NORMALMAPPED_TILED_ENVMAPPED_BF2": Rendertype.NORMALMAPPED_TILED_ENVMAP}
def _read_material_props_rendertype(props) -> Rendertype:
return _RENDERTYPES_MAPPING[props.rendertype]
def _read_material_props_flags(props) -> MaterialFlags:
flags = MaterialFlags.NONE
if props.blended_transparency:
flags |= MaterialFlags.BLENDED_TRANSPARENCY
if props.additive_transparency:
flags |= MaterialFlags.ADDITIVE_TRANSPARENCY
if props.hardedged_transparency:
flags |= MaterialFlags.HARDEDGED_TRANSPARENCY
if props.unlit:
flags |= MaterialFlags.UNLIT
if props.glow:
flags |= MaterialFlags.GLOW
if props.perpixel:
flags |= MaterialFlags.PERPIXEL
if props.specular:
flags |= MaterialFlags.SPECULAR
if props.doublesided:
flags |= MaterialFlags.DOUBLESIDED
return flags
def _read_material_props_data(props) -> Tuple[int, int]:
if "SCROLLING" in props.rendertype:
return (props.scroll_speed_u, props.scroll_speed_v)
if "BLINK" in props.rendertype:
return (props.blink_min_brightness, props.blink_speed)
if "NORMALMAPPED_TILED" in props.rendertype:
return (props.normal_map_tiling_u, props.normal_map_tiling_v)
if "REFRACTION" in props.rendertype:
return (0, 0)
if "ANIMATED" in props.rendertype:
return (int(str(props.animation_length).split("_")[1]), props.animation_speed)
return (props.detail_map_tiling_u, props.detail_map_tiling_v)
def _read_normal_map_or_distortion_map_texture(props) -> str:
if "REFRACTION" in props.rendertype:
return props.distortion_map
if "NORMALMAPPED" in props.rendertype:
return props.normal_map
return ""
def _read_detail_texture(props) -> str:
if "REFRACTION" in props.rendertype:
return ""
return props.detail_map
def _read_envmap_texture(props) -> str:
if "ENVMAPPED" not in props.rendertype:
return ""
return props.environment_map

View File

@ -0,0 +1,257 @@
""" Contains Blender properties and UI for .msh materials. """
import bpy
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatVectorProperty, IntProperty
from bpy.types import PropertyGroup
UI_MATERIAL_RENDERTYPES = (
('NORMAL_BF2', "00 Normal (SWBF2)", "Normal Material. Unlike you there is nothing inherently awesome "
"about this material. By default it has per-vertex diffuse "
"lighting and can also have a detail map."),
('SCROLLING_BF2', "03 Scrolling (SWBF2)", "Scrolling Material"),
('ENVMAPPED_BF2', "06 Envmapped (SWBF2)", "Envmapped Material"),
('ANIMATED_BF2', "07 Animated (SWBF2)", "Animated Material"),
('REFRACTION_BF2', "22 Refractive (SWBF2)", "Refractive Material"),
('BLINK_BF2', "25 Blink (SWBF2)", "Blinking Material\n\n"
"Note: If you see any statues while using this material you "
"are advised **not** to blink (or take your eyes off the statue under any circumstances) "
"and immediately make your way to a crowded public space."),
('NORMALMAPPED_TILED_BF2', "24 Normalmapped Tiled (SWBF2)", "Normalmapped Tiled Material"),
('NORMALMAPPED_ENVMAPPED_BF2', "26 Normalmapped Envmapped (SWBF2)", "Normalmapped Envmapped Material"),
('NORMALMAPPED_BF2', "27 Normalmapped (SWBF2)", "Normalmapped Material"),
('NORMALMAPPED_TILED_ENVMAPPED_BF2', "26 Normalmapped Tiled Envmapped (SWBF2)", "Normalmapped Tiled Envmapped Material"))
def _make_anim_length_entry(length):
from math import sqrt
len_sqrt = int(sqrt(length))
return (
f'FRAMES_{length}',
f"{length} ({len_sqrt}x{len_sqrt})",
f"Input texture should be laid out as {len_sqrt}x{len_sqrt} frames.")
UI_MATERIAL_ANIMATION_LENGTHS = (
('FRAMES_1', "1 (1x1)", "Why do you have an animated texture with one frame?"),
_make_anim_length_entry(4),
_make_anim_length_entry(9),
_make_anim_length_entry(16),
_make_anim_length_entry(25),
_make_anim_length_entry(36),
_make_anim_length_entry(49),
_make_anim_length_entry(64),
_make_anim_length_entry(81),
_make_anim_length_entry(100),
_make_anim_length_entry(121),
_make_anim_length_entry(144),
_make_anim_length_entry(169),
_make_anim_length_entry(196),
_make_anim_length_entry(225))
class MaterialProperties(PropertyGroup):
rendertype: EnumProperty(name="Rendertype",
description="Rendertype for the material.",
items=UI_MATERIAL_RENDERTYPES,
default='NORMAL_BF2')
specular_color: FloatVectorProperty(name="Specular Colour",
description="Specular colour of the material. "
"Can be used to tint specular highlights "
"or reduce their strength.",
default=(1.0, 1.0, 1.0),
min=0.0, max=1.0,
soft_min=0.0, soft_max=1.0,
subtype="COLOR")
blended_transparency: BoolProperty(name="Blended",
description="Enable blended transparency.",
default=False)
additive_transparency: BoolProperty(name="Additive",
description="Enable additive transparency.",
default=False)
hardedged_transparency: BoolProperty(name="Hardedged",
description="Enable hardedged (alpha cutout/clip) transparency "
"with a treshold of 0.5/0x80/128.",
default=False)
unlit: BoolProperty(name="Unlit",
description="Makes the material unlit/emissive.",
default=False)
glow: BoolProperty(name="Glow",
description="Same as 'Unlit' but also enables the use of a glowmap "
"in the diffuse texture's alpha channel. The material will be significantly "
"significantly brightened based on how opaque the glowmap is.",
default=False)
perpixel: BoolProperty(name="Per-Pixel Lighting",
description="Use per-pixel lighting instead of per-vertex for diffuse lighting. "
"Be warned due to the way SWBFII handles per-pixel lighting this "
"adds an extra draw call for each segment using the material.",
default=False)
specular: BoolProperty(name="Specular Lighting",
description="Use specular lighting as well as diffuse lighting. A gloss map "
"In the diffuse map's and normal map's alpha channel can be used "
"to attenuate the specular lighting's strength. (More transparent = less strong).\n\n"
"Be warned due to the way SWBFII handles specular lighting this "
"adds an extra draw call for each segment using the material.",
default=False)
doublesided: BoolProperty(name="Doublesided",
description="Disable backface culling, "
"causing both sides of the material to be rasterized.",
default=False)
detail_map_tiling_u: IntProperty(name="Detail Map Tiling U",
description="Tiling of the detail map in the U direction. (0 = no tiling).",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
detail_map_tiling_v: IntProperty(name="Detail Map Tiling V",
description="Tiling of the detail map in the V direction. (0 = no tiling).",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
normal_map_tiling_u: IntProperty(name="Normal Map Tiling U",
description="Tiling of the normal map in the U direction. (0 = no tiling).",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
normal_map_tiling_v: IntProperty(name="Normal Map Tiling V",
description="Tiling of the normal map in the V direction. (0 = no tiling).",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
scroll_speed_u: IntProperty(name="Scroll Speed U",
description="Texture scroll speed in the U direction.",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
scroll_speed_v: IntProperty(name="Scroll Speed V",
description="Texture scroll speed in the V direction.",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
animation_length: EnumProperty(name="Animation Length",
description="Number of frames in the texture animation.",
items=UI_MATERIAL_ANIMATION_LENGTHS,
default='FRAMES_4')
animation_speed: IntProperty(name="Animation Speed",
description="Animation speed in frames per second.",
default=1,
min=0, max=255,
soft_min=0, soft_max=255)
blink_min_brightness: IntProperty(name="Blink Minimum Brightness",
description="Minimum brightness to blink between.",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
blink_speed: IntProperty(name="Blink Speed",
description="Speed of blinking, higher is faster.",
default=0,
min=0, max=255,
soft_min=0, soft_max=255)
diffuse_map: StringProperty(name="Diffuse Map",
description="The basic diffuse map for the material. The alpha channel "
"is either the transparency map, glow map or gloss map, "
"depending on the selected rendertype and flags.",
default="white.tga")
detail_map: StringProperty(name="Detail Map",
description="Detail maps allow you to add in 'detail' to the diffuse "
"map at runtime. Or they can be used as fake ambient occlusion "
"maps or even wacky emissive maps. See docs for more details.")
normal_map: StringProperty(name="Normal Map",
description="Normal maps can provide added detail from lighting. "
"If Specular is enabled the alpha channel will be "
"the gloss map.")
environment_map: StringProperty(name="Environment Map",
description="Environment map for the material. Provides static "
"reflections around. Must be a cubemap.")
distortion_map: StringProperty(name="Distortion Map",
description="Distortion maps control how Refractive materials "
"distort the scene behind them. Should be a normal map "
"with '-forceformat v8u8' in it's '.tga.option' file.")
class MaterialPropertiesPanel(bpy.types.Panel):
""" Creates a Panel in the Object properties window """
bl_label = "SWBF .msh Properties"
bl_idname = "MATERIAL_PT_swbf_msh"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "material"
def draw(self, context):
layout = self.layout
material_props = context.material.swbf_msh
layout.prop(material_props, "rendertype")
layout.prop(material_props, "specular_color")
layout.label(text="Transparency Flags: ")
row = layout.row()
row.prop(material_props, "blended_transparency")
row.prop(material_props, "additive_transparency")
row.prop(material_props, "hardedged_transparency")
layout.label(text="Material Flags: ")
row = layout.row()
row.prop(material_props, "unlit")
row.prop(material_props, "glow")
row = layout.row()
row.prop(material_props, "perpixel")
row.prop(material_props, "specular")
layout.prop(material_props, "doublesided")
if "REFRACTION" not in material_props.rendertype:
layout.label(text="Material Data: ")
row = layout.row()
if "SCROLLING" in material_props.rendertype:
row.prop(material_props, "scroll_speed_u")
row.prop(material_props, "scroll_speed_v")
elif "ANIMATED" in material_props.rendertype:
row.prop(material_props, "animation_length")
row = layout.row()
row.prop(material_props, "animation_speed")
elif "BLINK" in material_props.rendertype:
row.prop(material_props, "blink_min_brightness")
row.prop(material_props, "blink_speed")
elif "NORMALMAPPED_TILED" in material_props.rendertype:
row.prop(material_props, "normal_map_tiling_u")
row.prop(material_props, "normal_map_tiling_v")
elif "REFRACTION" not in material_props.rendertype:
row.prop(material_props, "detail_map_tiling_u")
row.prop(material_props, "detail_map_tiling_v")
layout.label(text="Texture Maps: ")
layout.prop(material_props, "diffuse_map")
if "REFRACTION" not in material_props.rendertype:
layout.prop(material_props, "detail_map")
if "NORMALMAPPED" in material_props.rendertype:
layout.prop(material_props, "normal_map")
if "ENVMAPPED" in material_props.rendertype:
layout.prop(material_props, "environment_map")
if "REFRACTION" in material_props.rendertype:
layout.prop(material_props, "distortion_map")

View File

@ -0,0 +1,24 @@
""" Utilities for operating on Material objects. """
from typing import Dict, List
from .msh_material import *
from .msh_model import *
def remove_unused_materials(materials: Dict[str, Material],
models: List[Model]) -> Dict[str, Material]:
""" Given a dictionary of materials and a list of models
returns a dictionary containing only the materials that are used. """
filtered_materials: Dict[str, Material] = {}
for model in models:
if model.geometry is None:
continue
for segment in model.geometry:
if not segment.material_name:
continue
filtered_materials[segment.material_name] = materials[segment.material_name]
return filtered_materials

View File

@ -2,7 +2,7 @@
from a Blender scene. """ from a Blender scene. """
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List from typing import List, Dict
from copy import copy from copy import copy
import bpy import bpy
from mathutils import Vector from mathutils import Vector
@ -10,6 +10,9 @@ from .msh_model import Model
from .msh_model_gather import gather_models from .msh_model_gather import gather_models
from .msh_model_utilities import sort_by_parent, has_multiple_root_models, reparent_model_roots, get_model_world_matrix from .msh_model_utilities import sort_by_parent, has_multiple_root_models, reparent_model_roots, get_model_world_matrix
from .msh_model_triangle_strips import create_models_triangle_strips from .msh_model_triangle_strips import create_models_triangle_strips
from .msh_material import *
from .msh_material_gather import gather_materials
from .msh_material_utilities import remove_unused_materials
from .msh_utilities import * from .msh_utilities import *
@dataclass @dataclass
@ -38,6 +41,7 @@ class SceneAABB:
class Scene: class Scene:
""" Class containing the scene data for a .msh """ """ Class containing the scene data for a .msh """
name: str = "Scene" name: str = "Scene"
materials: Dict[str, Material] = field(default_factory=dict)
models: List[Model] = field(default_factory=list) models: List[Model] = field(default_factory=list)
def create_scene() -> Scene: def create_scene() -> Scene:
@ -47,6 +51,8 @@ def create_scene() -> Scene:
scene.name = bpy.context.scene.name scene.name = bpy.context.scene.name
scene.materials = gather_materials()
scene.models = gather_models() scene.models = gather_models()
scene.models = sort_by_parent(scene.models) scene.models = sort_by_parent(scene.models)
scene.models = create_models_triangle_strips(scene.models) scene.models = create_models_triangle_strips(scene.models)
@ -54,6 +60,8 @@ def create_scene() -> Scene:
if has_multiple_root_models(scene.models): if has_multiple_root_models(scene.models):
scene.models = reparent_model_roots(scene.models) scene.models = reparent_model_roots(scene.models)
scene.materials = remove_unused_materials(scene.materials, scene.models)
return scene return scene
def create_scene_aabb(scene: Scene) -> SceneAABB: def create_scene_aabb(scene: Scene) -> SceneAABB:

View File

@ -1,8 +1,10 @@
""" Contains functions for saving a Scene to a .msh file. """ """ Contains functions for saving a Scene to a .msh file. """
from itertools import islice from itertools import islice
from typing import Dict
from .msh_scene import Scene, create_scene_aabb from .msh_scene import Scene, create_scene_aabb
from .msh_model import * from .msh_model import *
from .msh_material import *
from .msh_writer import Writer from .msh_writer import Writer
from .msh_utilities import * from .msh_utilities import *
@ -15,12 +17,14 @@ def save_scene(output_file, scene: Scene):
with msh2.create_child("SINF") as sinf: with msh2.create_child("SINF") as sinf:
_write_sinf(sinf, scene) _write_sinf(sinf, scene)
material_index: Dict[str, int] = {}
with msh2.create_child("MATL") as matl: with msh2.create_child("MATL") as matl:
_write_matl(matl, scene) material_index = _write_matl_and_get_material_index(matl, scene)
for index, model in enumerate(scene.models): for index, model in enumerate(scene.models):
with msh2.create_child("MODL") as modl: with msh2.create_child("MODL") as modl:
_write_modl(modl, model, index) _write_modl(modl, model, index, material_index)
with hedr.create_child("CL1L"): with hedr.create_child("CL1L"):
pass pass
@ -44,30 +48,56 @@ def _write_sinf(sinf: Writer, scene: Scene):
bbox.write_f32(bbox_position.x, bbox_position.y, bbox_position.z) bbox.write_f32(bbox_position.x, bbox_position.y, bbox_position.z)
bbox.write_f32(bbox_size.x, bbox_size.y, bbox_size.z, bbox_length) bbox.write_f32(bbox_size.x, bbox_size.y, bbox_size.z, bbox_length)
def _write_matl(matl: Writer, scene: Scene): def _write_matl_and_get_material_index(matl: Writer, scene: Scene):
# TODO: Material support. material_index: Dict[str, int] = {}
matl.write_u32(1) # Material count. if len(scene.materials) > 0:
matl.write_u32(len(scene.materials)) # Material count.
with matl.create_child("MATD") as matd: for index, name_material in enumerate(scene.materials.items()):
with matd.create_child("NAME") as name: with matl.create_child("MATD") as matd:
name.write_string(f"{scene.name}Material") # TODO: Proper name with material support. material_index[name_material[0]] = index
_write_matd(matd, name_material[0], name_material[1])
else:
matl.write_u32(1) # Material count.
with matd.create_child("DATA") as data: default_material_name = f"{scene.name}Material"
data.write_f32(1.0, 1.0, 1.0, 1.0) # Diffuse Color (Seams to get ignored by modelmunge) material_index[default_material_name] = 0
data.write_f32(1.0, 1.0, 1.0, 1.0) # Specular Color
data.write_f32(0.0, 0.0, 0.0, 1.0) # Ambient Color (Seams to get ignored by modelmunge and Zero(?))
data.write_f32(50.0) # Specular Exponent/Decay (Gets ignored by RedEngine in SWBFII for all known materials)
with matd.create_child("ATRB") as atrb: with matl.create_child("MATD") as matd:
atrb.write_u8(0) # Material Flags _write_matd(matd, default_material_name, Material())
atrb.write_u8(0) # Rendertype
atrb.write_u8(0, 0) # Rendertype Params (Scroll rate, animation divisors, etc)
with matd.create_child("TX0D") as tx0d: return material_index
tx0d.write_string("null_detailmap.tga")
def _write_modl(modl: Writer, model: Model, index: int): def _write_matd(matd: Writer, material_name: str, material: Material):
with matd.create_child("NAME") as name:
name.write_string(material_name)
with matd.create_child("DATA") as data:
data.write_f32(1.0, 1.0, 1.0, 1.0) # Diffuse Color (Seams to get ignored by modelmunge)
data.write_f32(material.specular_color[0], material.specular_color[1],
material.specular_color[2], 1.0)
data.write_f32(0.0, 0.0, 0.0, 1.0) # Ambient Color (Seams to get ignored by modelmunge and Zero(?))
data.write_f32(50.0) # Specular Exponent/Decay (Gets ignored by RedEngine in SWBFII for all known materials)
with matd.create_child("ATRB") as atrb:
atrb.write_u8(material.flags.value)
atrb.write_u8(material.rendertype.value)
atrb.write_u8(material.data[0], material.data[1])
with matd.create_child("TX0D") as tx0d:
tx0d.write_string(material.texture0)
if material.texture1 or material.texture2 or material.texture3:
with matd.create_child("TX1D") as tx1d:
tx1d.write_string(material.texture1)
if material.texture2 or material.texture3:
with matd.create_child("TX2D") as tx2d:
tx2d.write_string(material.texture2)
if material.texture3:
with matd.create_child("TX3D") as tx3d:
tx3d.write_string(material.texture3)
def _write_modl(modl: Writer, model: Model, index: int, material_index: Dict[str, int]):
with modl.create_child("MTYP") as mtyp: with modl.create_child("MTYP") as mtyp:
mtyp.write_u32(model.model_type.value) mtyp.write_u32(model.model_type.value)
@ -92,7 +122,7 @@ def _write_modl(modl: Writer, model: Model, index: int):
with modl.create_child("GEOM") as geom: with modl.create_child("GEOM") as geom:
for segment in model.geometry: for segment in model.geometry:
with geom.create_child("SEGM") as segm: with geom.create_child("SEGM") as segm:
_write_segm(segm, segment) _write_segm(segm, segment, material_index)
# TODO: Collision Primitive # TODO: Collision Primitive
@ -101,10 +131,10 @@ def _write_tran(tran: Writer, transform: ModelTransform):
tran.write_f32(transform.rotation.x, transform.rotation.y, transform.rotation.z, transform.rotation.w) tran.write_f32(transform.rotation.x, transform.rotation.y, transform.rotation.z, transform.rotation.w)
tran.write_f32(transform.translation.x, transform.translation.y, transform.translation.z) tran.write_f32(transform.translation.x, transform.translation.y, transform.translation.z)
def _write_segm(segm: Writer, segment: GeometrySegment): def _write_segm(segm: Writer, segment: GeometrySegment, material_index: Dict[str, int]):
with segm.create_child("MATI") as mati: with segm.create_child("MATI") as mati:
mati.write_u32(0) mati.write_u32(material_index.get(segment.material_name, 0))
with segm.create_child("POSL") as posl: with segm.create_child("POSL") as posl:
posl.write_u32(len(segment.positions)) posl.write_u32(len(segment.positions))