Skip to content

Commit

Permalink
feat: material.blend_method migration for EEVEE-Next
Browse files Browse the repository at this point in the history
  • Loading branch information
saturday06 committed Jun 12, 2024
1 parent 11830c4 commit 41dca77
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
91 changes: 91 additions & 0 deletions src/io_scene_vrm/common/native.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import ctypes
import sysconfig
from collections.abc import Mapping
from platform import python_implementation
from typing import Optional

import bpy
from bpy.types import Material

from .logging import get_logger

logger = get_logger(__name__)


def read_blend_method_from_memory_address(material: Material) -> Optional[str]:
"""material.blend_methodの値を直接メモリから読み取る.
Blender 4.2からmaterial.blend_methodの値が廃止されて読めなくなった。
しかし、マテリアルのマイグレーションのためにはどうしてもその値を知る必要がある。
仕方がないので既知のプラットフォームに限定してメモリから直接値を読む。
ログは多めに出す。
"""
if bpy.app.version < (4, 2):
return material.blend_method

if bpy.app.build_type != b"Release":
logger.warning(
f"Does not read the Blend Mode of {material.name}. "
+ f'"{bpy.app.build_type!r}" builds are not supported.'
)
return None

if python_implementation() != "CPython":
logger.warning(
f"Does not read the Blend Mode of {material.name}. "
+ f"{python_implementation()} is not supported."
)
return None

native_struct_offsets_by_major_minor: Mapping[
tuple[int, int],
Mapping[str, int],
] = {
(4, 2): {
"win-amd64": 324,
"macosx-11.00-arm64": 324,
},
}

major_minor = ( # Use [x] to make type checkers happy
bpy.app.version[0],
bpy.app.version[1],
)
native_struct_offsets = native_struct_offsets_by_major_minor.get(major_minor)
if native_struct_offsets is None:
logger.warning(
f"Does not read the Blend Mode of {material.name}. "
+ f"Blender {major_minor[0]}.{major_minor[1]} is not supported."
)
return None

platform = sysconfig.get_platform()
native_struct_offset = native_struct_offsets.get(platform)
if native_struct_offset is None:
logger.warning(
f"Does not read the Blend Mode of {material.name}. "
+ f"{platform} is not supported."
)
return None

logger.warning(
f"Starts reading the Blend Mode of {material.name} from its memory address."
)
native_char = ctypes.c_char.from_address(
material.as_pointer() + native_struct_offset
)
logger.warning(
f"Finished reading the Blend Mode of {material.name} from its memory address."
)

if native_char.value == bytes([0]):
return "OPAQUE"
if native_char.value == bytes([3]):
return "CLIP"
if native_char.value == bytes([4]):
return "HASHED"
if native_char.value == bytes([5]):
return "BLEND"

return None
7 changes: 4 additions & 3 deletions src/io_scene_vrm/editor/mtoon1/migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)
from idprop.types import IDPropertyGroup

from ...common import convert
from ...common import convert, native
from ...common.gl import GL_LINEAR, GL_NEAREST
from .property_group import (
GL_LINEAR_IMAGE_INTERPOLATIONS,
Expand Down Expand Up @@ -92,9 +92,10 @@ def migrate(context: Context) -> None:
alpha_cutoff: Optional[float] = None
if addon_version < (2, 20, 55):
alpha_cutoff = material.alpha_threshold
if material.blend_method in ["BLEND", "HASHED"]:
blend_method = native.read_blend_method_from_memory_address(material)
if blend_method in ["BLEND", "HASHED"]:
alpha_mode = Mtoon1MaterialPropertyGroup.ALPHA_MODE_BLEND
if material.blend_method == "CLIP":
if blend_method == "CLIP":
alpha_mode = Mtoon1MaterialPropertyGroup.ALPHA_MODE_MASK
else:
alpha_mode = Mtoon1MaterialPropertyGroup.ALPHA_MODE_OPAQUE
Expand Down
Binary file added tests/blend_mode.blend
Binary file not shown.
27 changes: 27 additions & 0 deletions tests/blender_test_read_blend_method_from_memory_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from pathlib import Path

import bpy
from bpy.types import Context

from io_scene_vrm.common.native import read_blend_method_from_memory_address


def test(context: Context) -> None:
bpy.ops.wm.open_mainfile(filepath=str(Path(__file__).parent / "blend_mode.blend"))

for material_name, expected_blend_method in {
"Opaque": "OPAQUE",
"AlphaClip": "CLIP",
"AlphaHashed": "HASHED",
"AlphaBlend": "BLEND",
}.items():
material = context.blend_data.materials[material_name]
actual_blend_method = read_blend_method_from_memory_address(material)
assert expected_blend_method == actual_blend_method, (
f'"{material_name}" material is not "{expected_blend_method}"'
+ f" but {actual_blend_method}"
)


if __name__ == "__main__":
test(bpy.context)

0 comments on commit 41dca77

Please sign in to comment.