diff --git a/README.md b/README.md
index 19cd2ad..c2a4ab7 100644
--- a/README.md
+++ b/README.md
@@ -13,18 +13,22 @@
## For Mod Authors
-The 1.1 update has introduced huge changes into the rendering pipeline and 3d assets, to update mods that were made for 1.0 please follow the steps below:
-1. Update your [WWMI Tools Blender plugin to 0.8.1](https://github.com/SpectrumQT/WWMI-TOOLS/releases/tag/v0.8.1).
-2. Make a new frame dump of modded object (with mod disabled!) and extract it again.
-3. Import newly extracted object into Blender (use default Merged Skeleton setting)
-4. Make changed Vertex Groups ids in your custom mesh match ones of new import (you may use [Weight Match Blender Addon](https://gamebanana.com/tools/15699) to speed up the process)
-5. Export your updated custom model as new mod into new folder (use default Merged Skeleton setting).
-6. Check textures one by one and move the ones you've edited from old to new mod folder.
+**WWMI 0.6.1** and **WWMI Tools 0.8.2** updates resolved issues with Shape Keys (face and shoulder animations). To fix existing mods:
+1. Update **WWMI** to the [latest version](https://github.com/SpectrumQT/WWMI/releases/latest).
+2. Update **WWMI Tools** Blender plugin to the [latest version](https://github.com/SpectrumQT/WWMI-TOOLS/releases/latest).
+3. Restart Bledner.
+4. If you created WWMI mod before Wuthering Waves 1.0 update, follow [Modder Guide](https://github.com/SpectrumQT/WWMI-TOOLS/blob/main/guides/modder_guide.md) instead.
+5. Export mod to the new folder (or backup and use existing one).
+6. Apply any desired manual tweaks to the new mod.ini and move textures.
## Known Issues
- Blurry edges on modded model during fast movement (fix: disable DLSS or FSR)
- Glitching see-through 'white shadow' when character is behind large boss (fix: reduce Shadow Quality in Graphics Settings)
- Shape Keys aren't (fully) loading for Jinshi and other characters (research required)
+
+- Blurry edges on modded model during fast movement (fix: disable DLSS or FSR)
+- Glitch with duplicate modded objects on screen (merged skeleton limitation)
+
+## Disclaimers
+
+- **Alpha-1 Warning** — WWMI is in early alpha testing phase, so you can expect all kinds of issues. Also, please keep in mind that WWMI feature set and formats are not set in stone and may be subject to change.
## Features
@@ -35,10 +39,6 @@ The 1.1 update has introduced huge changes into the rendering pipeline and 3d as
- **Shape Keys Support** — Automatically handles original shape keys and supports custom ones
- **Customizable Export** — Fast mod export engine with per-buffer export support
-## Disclaimers
-
-- **Alpha-1 Warning** — WWMI is in early alpha testing phase, so you can expect all kinds of issues. Also, please keep in mind that WWMI feature set and formats are not set in stone and may be subject to change.
-
## How To Use
All fields and actions of the plugin have basic tooltips. Refer to [Modder Guide](https://github.com/SpectrumQT/WWMI-TOOLS/blob/main/guides/modder_guide.md) for more details.
diff --git a/guides/modder_guide.md b/guides/modder_guide.md
index ddb00af..cc89d0e 100644
--- a/guides/modder_guide.md
+++ b/guides/modder_guide.md
@@ -1,5 +1,14 @@
WWMI Tools Modder Guide
+How To Update WWMI 1.0 Mod to 1.1
+
+The WuWa 1.1 update has introduced huge changes into the rendering pipeline and 3d assets, to update mods that were made for 1.0 please follow the steps below:
+1. Make a new frame dump of modded object (with mod disabled!) and extract it again.
+2. Import newly extracted object into Blender (use default Merged Skeleton setting)
+3. Make changed Vertex Groups ids in your custom mesh match ones of new import (you may use [Weight Match Blender Addon](https://gamebanana.com/tools/15699) to speed up the process)
+4. Export your updated custom model as new mod into new folder (use default Merged Skeleton setting).
+5. Check textures one by one and move the ones you've edited from old to new mod folder.
+
Frame Dump Objects Export
1. Start the game with **WWMI Loader.exe**
diff --git a/wwmi-tools/__init__.py b/wwmi-tools/__init__.py
index b58269c..2746671 100644
--- a/wwmi-tools/__init__.py
+++ b/wwmi-tools/__init__.py
@@ -4,8 +4,8 @@
bl_info = {
"name": "WWMI Tools",
- "version": (0, 8, 1),
- "wwmi_version": (0, 6, 0),
+ "version": (0, 8, 3),
+ "wwmi_version": (0, 6, 1),
"blender": (2, 80, 0),
"author": "SpectrumQT, DarkStarSword",
"location": "View3D > Sidebar > Tool Tab",
diff --git a/wwmi-tools/blender_export/blender_export.py b/wwmi-tools/blender_export/blender_export.py
index da5a7c1..cab9577 100644
--- a/wwmi-tools/blender_export/blender_export.py
+++ b/wwmi-tools/blender_export/blender_export.py
@@ -133,8 +133,7 @@ def get_default_data_map():
BufferSemantic(AbstractSemantic(Semantic.RawData), DXGIFormat.R32_UINT),
],
'ShapeKeyVertexOffset': [
- BufferSemantic(AbstractSemantic(Semantic.RawData, 0), DXGIFormat.R16_FLOAT, stride=6),
- BufferSemantic(AbstractSemantic(Semantic.RawData, 1), DXGIFormat.R16_FLOAT, stride=6),
+ BufferSemantic(AbstractSemantic(Semantic.RawData, 0), DXGIFormat.R16_FLOAT),
],
},
)
@@ -233,7 +232,7 @@ def extract_shapekey_data(loop_data, shapekeys, shapekey_data):
for vertex_id, vertex_offsets in shapekey.items():
shapekey_vertex_ids.extend([vertex_id])
- shapekey_vertex_offsets.extend(vertex_offsets)
+ shapekey_vertex_offsets.extend(vertex_offsets + [0, 0, 0])
shapekey_verts_count += 1
return shapekey_offsets, shapekey_vertex_ids, shapekey_vertex_offsets
@@ -307,7 +306,8 @@ def blender_export(operator, context, cfg, data_map):
object_merger = ObjectMerger(
extracted_object=extracted_object,
- ignore_hidden=cfg.ignore_hidden,
+ ignore_hidden_objects=cfg.ignore_hidden_objects,
+ ignore_muted_shape_keys=cfg.ignore_muted_shape_keys,
apply_modifiers=cfg.apply_all_modifiers,
context=context,
collection=cfg.component_collection,
diff --git a/wwmi-tools/blender_export/ini_maker.py b/wwmi-tools/blender_export/ini_maker.py
index 8a08ac5..7c151ca 100644
--- a/wwmi-tools/blender_export/ini_maker.py
+++ b/wwmi-tools/blender_export/ini_maker.py
@@ -9,12 +9,13 @@
from ..migoto_io.buffers.byte_buffer import ByteBuffer
+from ..migoto_io.ini_builder.IniBuilder import IniBuilder, IniSection, SectionType, IniSectionConditional
+
from ..extract_frame_data.metadata_format import ExtractedObject
from .object_merger import MergedObject, SkeletonType
from .metadata_collector import Version, ModInfo
from .texture_collector import Texture
-from .ini_builder.IniBuilder import IniBuilder, IniSection, SectionType, IniSectionConditional
def is_ini_edited(ini_path):
@@ -109,7 +110,7 @@ def make_mod_state_group(self):
self.ini.add_section(constants, 0)
constants.body.add_comment(r'Allows WWMI to safely disable incompatible mod and notify user about it')
- constants.body.add_command(r'global $required_wwmi_version = %.1f' % self.mod_info.required_wwmi_version.as_float())
+ constants.body.add_command(r'global $required_wwmi_version = %.2f' % self.mod_info.required_wwmi_version.as_float())
constants.body.add_comment(r'Number of indices in original model')
constants.body.add_command(r'global $object_guid = %d' % self.extracted_object.index_count)
@@ -176,12 +177,12 @@ def make_mod_state_group(self):
last_draw_count_if_body = last_draw_count_condition.add_if_clause('$last_draw_count == 0 && $\WWMIv1\in_character_menu')
last_draw_count_if_body.add_command(r'$delay_load = time + 0.5')
- draw_count_body.add_comment(r'Delays load of custom model for 0.5s if there were more than 1.5x time of expected draw calls')
- draw_count_body.add_comment(r'Avoids skeleton glitch when there are more than one object with modded model on screen')
- component_draw_count_condition = draw_count_body.add_command(IniSectionConditional())
+ # draw_count_body.add_comment(r'Delays load of custom model for 0.5s if there were more than 1.5x time of expected draw calls')
+ # draw_count_body.add_comment(r'Avoids skeleton glitch when there are more than one object with modded model on screen')
+ # component_draw_count_condition = draw_count_body.add_command(IniSectionConditional())
- last_draw_count_if_body = component_draw_count_condition.add_if_clause('$draw_counter > $\WWMIv1\component_draw_calls_count')
- last_draw_count_if_body.add_command(r'$delay_load = time + 0.5')
+ # last_draw_count_if_body = component_draw_count_condition.add_if_clause('$draw_counter > $\WWMIv1\component_draw_calls_count')
+ # last_draw_count_if_body.add_command(r'$delay_load = time + 0.5')
draw_count_body.add_comment(r'Copy completed merged skeleton from previous frame to override original skeleton with it in current frame')
copy_skeleton_condition = draw_count_body.add_command(IniSectionConditional())
@@ -443,19 +444,27 @@ def make_draw_calls_group(self):
mod_enabled_body.add_comment(r'Skip original draw call')
mod_enabled_body.add_command(r'handling = skip')
-
- mod_enabled_body.add_comment(r'Override shared resources')
- mod_enabled_body.add_command(f'run = {replace_shared_resources.get_section_title()}')
-
- mod_enabled_body.add_comment(r'Override textures')
- mod_enabled_body.add_command(f'run = {replace_textures.get_section_title()}')
custom_component = self.merged_object.components[component_id]
if len(custom_component.objects) > 0:
+
+ mod_enabled_body.add_comment(r'Override shared resources')
+ mod_enabled_body.add_command(f'run = {replace_shared_resources.get_section_title()}')
+
+ mod_enabled_body.add_comment(r'Override textures')
+ mod_enabled_body.add_command(f'run = {replace_textures.get_section_title()}')
+
for obj in custom_component.objects:
mod_enabled_body.add_persistent_comment(f'Draw {obj.name}')
mod_enabled_body.add_command(f'drawindexed = {obj.index_count}, {obj.index_offset}, 0')
else:
+
+ mod_enabled_body.add_comment(r'Override shared resources')
+ mod_enabled_body.add_persistent_comment(f'run = {replace_shared_resources.get_section_title()}')
+
+ mod_enabled_body.add_comment(r'Override textures')
+ mod_enabled_body.add_persistent_comment(f'run = {replace_textures.get_section_title()}')
+
mod_enabled_body.add_persistent_comment(f'Draw skipped: No matching custom components found')
def make_texture_resources_group(self):
@@ -592,7 +601,7 @@ def make_shape_keys_override_group(self):
)
self.ini.add_section(multiply_shapekeys, 4)
multiply_shapekeys.body.add_comment(r'Pass number of shapekeyed vertices to adjust required threads count via dipatch_y')
- multiply_shapekeys.body.add_command(r'$\WWMIv1\shapekey_vertex_count = $shapekey_vertex_count')
+ multiply_shapekeys.body.add_command(r'$\WWMIv1\custom_vertex_count = $mesh_vertex_count')
multiply_shapekeys.body.add_comment(r'Run custom Shape Key Multiplier CS to set deformation intensity')
multiply_shapekeys.body.add_command(r'run = CustomShader\WWMIv1\ShapeKeyMultiplier')
diff --git a/wwmi-tools/blender_export/object_merger.py b/wwmi-tools/blender_export/object_merger.py
index 68c8ad2..5767828 100644
--- a/wwmi-tools/blender_export/object_merger.py
+++ b/wwmi-tools/blender_export/object_merger.py
@@ -55,7 +55,8 @@ class ObjectMerger:
# Input
context: bpy.context
extracted_object: ExtractedObject
- ignore_hidden: bool
+ ignore_hidden_objects: bool
+ ignore_muted_shape_keys: bool
apply_modifiers: bool
collection: str
skeleton_type: SkeletonType
@@ -90,7 +91,7 @@ def import_objects_from_collection(self):
for obj in get_collection_objects(self.collection):
- if self.ignore_hidden and object_is_hidden(obj):
+ if self.ignore_hidden_objects and object_is_hidden(obj):
continue
if obj.name.startswith('TEMP_'):
@@ -118,14 +119,12 @@ def prepare_temp_objects(self):
for temp_object in component.objects:
temp_obj = temp_object.object
- # Remove ignored or unexpected vertex groups
- if self.skeleton_type == SkeletonType.Merged:
- total_vg_count = sum([component.vg_count for component in self.extracted_object.components])
- ignore_list = [vg for vg in get_vertex_groups(temp_obj) if 'ignore' in vg.name.lower() or vg.index >= total_vg_count]
- elif self.skeleton_type == SkeletonType.PerComponent:
- extracted_component = self.extracted_object.components[component_id]
- total_vg_count = len(extracted_component.vg_map)
- ignore_list = [vg for vg in get_vertex_groups(temp_obj) if 'ignore' in vg.name.lower() or vg.index >= total_vg_count]
+ # Remove muted shape keys
+ if self.ignore_muted_shape_keys and temp_obj.data.shape_keys:
+ for shapekey_id in range(len(temp_obj.data.shape_keys.key_blocks)):
+ sheape_key = temp_obj.data.shape_keys.key_blocks[shapekey_id]
+ if sheape_key.mute:
+ temp_obj.shape_key_remove(sheape_key)
# Apply all modifiers to temporary object
if self.apply_modifiers:
with OpenObject(self.context, temp_obj) as obj:
@@ -133,6 +132,21 @@ def prepare_temp_objects(self):
apply_modifiers_for_object_with_shape_keys(self.context, selected_modifiers, None)
# Triangulate temporary object, this step is crucial as export supports only triangles
triangulate_object(self.context, temp_obj)
+ # Handle Vertex Groups
+ vertex_groups = get_vertex_groups(temp_obj)
+ if self.skeleton_type == SkeletonType.Merged:
+ # Exclude VGs with 'ignore' tag or with higher id VG count from Metadata.ini for current component
+ total_vg_count = sum([component.vg_count for component in self.extracted_object.components])
+ ignore_list = [vg for vg in vertex_groups if 'ignore' in vg.name.lower() or vg.index >= total_vg_count]
+ elif self.skeleton_type == SkeletonType.PerComponent:
+ # Exclude VGs with 'ignore' tag or with higher id VG count from Metadata.ini for current component
+ extracted_component = self.extracted_object.components[component_id]
+ total_vg_count = len(extracted_component.vg_map)
+ ignore_list = [vg for vg in vertex_groups if 'ignore' in vg.name.lower() or vg.index >= total_vg_count]
+ # Rename VGs to their indicies to merge ones of different components together
+ for vg in vertex_groups:
+ vg.name = str(vg.index)
+ # Remove ignored or unexpected vertex groups
remove_vertex_groups(temp_obj, ignore_list)
# Calculate vertex count of temporary object
temp_object.vertex_count = len(temp_obj.data.vertices)
diff --git a/wwmi-tools/blender_export/ini_builder/IniBuilder.Test.py b/wwmi-tools/migoto_io/ini_builder/IniBuilder.Test.py
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/IniBuilder.Test.py
rename to wwmi-tools/migoto_io/ini_builder/IniBuilder.Test.py
diff --git a/wwmi-tools/blender_export/ini_builder/IniBuilder.py b/wwmi-tools/migoto_io/ini_builder/IniBuilder.py
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/IniBuilder.py
rename to wwmi-tools/migoto_io/ini_builder/IniBuilder.py
diff --git a/wwmi-tools/blender_export/ini_builder/Test.Conditional.ini b/wwmi-tools/migoto_io/ini_builder/Test.Conditional.ini
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/Test.Conditional.ini
rename to wwmi-tools/migoto_io/ini_builder/Test.Conditional.ini
diff --git a/wwmi-tools/blender_export/ini_builder/Test.Full.ini b/wwmi-tools/migoto_io/ini_builder/Test.Full.ini
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/Test.Full.ini
rename to wwmi-tools/migoto_io/ini_builder/Test.Full.ini
diff --git a/wwmi-tools/blender_export/ini_builder/Test.HeaderFooter.ini b/wwmi-tools/migoto_io/ini_builder/Test.HeaderFooter.ini
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/Test.HeaderFooter.ini
rename to wwmi-tools/migoto_io/ini_builder/Test.HeaderFooter.ini
diff --git a/wwmi-tools/blender_export/ini_builder/Test.SectionPosition.ini b/wwmi-tools/migoto_io/ini_builder/Test.SectionPosition.ini
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/Test.SectionPosition.ini
rename to wwmi-tools/migoto_io/ini_builder/Test.SectionPosition.ini
diff --git a/wwmi-tools/blender_export/ini_builder/__init__.py b/wwmi-tools/migoto_io/ini_builder/__init__.py
similarity index 100%
rename from wwmi-tools/blender_export/ini_builder/__init__.py
rename to wwmi-tools/migoto_io/ini_builder/__init__.py
diff --git a/wwmi-tools/wwmi_tools.py b/wwmi-tools/wwmi_tools.py
index f8d9a3e..4f58030 100644
--- a/wwmi-tools/wwmi_tools.py
+++ b/wwmi-tools/wwmi_tools.py
@@ -122,8 +122,8 @@ class WWMI_Settings(bpy.types.PropertyGroup):
mirror_mesh: BoolProperty(
name="Mirror Mesh",
- description="Automatically mirror mesh to match actual in-game left-right",
- default=True,
+ description="Automatically mirror mesh to match actual in-game left-right. May cause issues with some Blender tools. Can be me manually set via adding or removing '-' sign for Scale X in Transform section of Object Properties",
+ default=False,
) # type: ignore
########################################
@@ -136,12 +136,6 @@ class WWMI_Settings(bpy.types.PropertyGroup):
type=bpy.types.Collection,
# default=False
) # type: ignore
-
- ignore_hidden: BoolProperty(
- name="Ignore Hidden Objects",
- description="If enabled, hidden objects inside Components collection won't be exported",
- default=False,
- ) # type: ignore
mod_output_folder: StringProperty(
name="Mod Folder",
@@ -244,6 +238,18 @@ class WWMI_Settings(bpy.types.PropertyGroup):
description="Contains shape keys data",
default=True,
) # type: ignore
+
+ ignore_hidden_objects: BoolProperty(
+ name="Ignore Hidden Objects",
+ description="If enabled, hidden objects inside Components collection won't be exported",
+ default=False,
+ ) # type: ignore
+
+ ignore_muted_shape_keys: BoolProperty(
+ name="Ignore Muted Shape Keys",
+ description="If enabled, muted (unchecked) shape keys won't be exported",
+ default=True,
+ ) # type: ignore
apply_all_modifiers: BoolProperty(
name="Apply All Modifiers",
@@ -580,13 +586,15 @@ def draw_menu_export_mod(self, context):
layout.row()
layout.row().prop(cfg, 'component_collection')
- layout.row().prop(cfg, 'ignore_hidden')
layout.row().prop(cfg, 'object_source_folder')
layout.row().prop(cfg, 'mod_output_folder')
layout.row().prop(cfg, 'mod_skeleton_type')
layout.row()
+ layout.row().prop(cfg, 'ignore_hidden_objects')
+ layout.row().prop(cfg, 'ignore_muted_shape_keys')
+
layout.row().prop(cfg, 'partial_export')
if cfg.partial_export: