material support
This commit is contained in:
parent
fd0ba8720b
commit
c12fc862b0
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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 = ""
|
|
@ -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
|
|
@ -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")
|
|
@ -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
|
|
@ -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:
|
||||||
|
|
|
@ -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] = {}
|
||||||
|
|
||||||
|
if len(scene.materials) > 0:
|
||||||
|
matl.write_u32(len(scene.materials)) # Material count.
|
||||||
|
|
||||||
|
for index, name_material in enumerate(scene.materials.items()):
|
||||||
|
with matl.create_child("MATD") as matd:
|
||||||
|
material_index[name_material[0]] = index
|
||||||
|
_write_matd(matd, name_material[0], name_material[1])
|
||||||
|
else:
|
||||||
matl.write_u32(1) # Material count.
|
matl.write_u32(1) # Material count.
|
||||||
|
|
||||||
with matl.create_child("MATD") as matd:
|
default_material_name = f"{scene.name}Material"
|
||||||
with matd.create_child("NAME") as name:
|
material_index[default_material_name] = 0
|
||||||
name.write_string(f"{scene.name}Material") # TODO: Proper name with material support.
|
|
||||||
|
|
||||||
|
with matl.create_child("MATD") as matd:
|
||||||
|
_write_matd(matd, default_material_name, Material())
|
||||||
|
|
||||||
|
return material_index
|
||||||
|
|
||||||
|
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:
|
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(1.0, 1.0, 1.0, 1.0) # Diffuse Color (Seams to get ignored by modelmunge)
|
||||||
data.write_f32(1.0, 1.0, 1.0, 1.0) # Specular Color
|
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(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)
|
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 matd.create_child("ATRB") as atrb:
|
||||||
atrb.write_u8(0) # Material Flags
|
atrb.write_u8(material.flags.value)
|
||||||
atrb.write_u8(0) # Rendertype
|
atrb.write_u8(material.rendertype.value)
|
||||||
atrb.write_u8(0, 0) # Rendertype Params (Scroll rate, animation divisors, etc)
|
atrb.write_u8(material.data[0], material.data[1])
|
||||||
|
|
||||||
with matd.create_child("TX0D") as tx0d:
|
with matd.create_child("TX0D") as tx0d:
|
||||||
tx0d.write_string("null_detailmap.tga")
|
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)
|
||||||
|
|
||||||
def _write_modl(modl: Writer, model: Model, index: int):
|
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))
|
||||||
|
|
Loading…
Reference in New Issue