Skip to content

Commit 98beb90

Browse files
authored
Adapt to blender 4.0 API changes (#263)
* basic implementation of face maps for 4.0 * fixed some issues on < 4.0 * set custom property for mesh instead of object * fixed failing test in 4.0 * create fake face map property only for < 4.0 * fix for blender 2.9 * use mesh.face_maps everywhere * next attempt * use object for < 4.0 * restructuring * updated changelog
1 parent 9829304 commit 98beb90

9 files changed

Lines changed: 82 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ jobs:
1515
runs-on: [ ubuntu-latest ]
1616
strategy:
1717
matrix:
18-
blender-version: [ '2.93', '3.6' ] #, '4.0' ]
18+
blender-version: [ '2.93', '3.6', '4.0' ]
1919
include:
2020
- blender-version: '2.93'
2121
blender-version-suffix: '13'
2222
python-version: '3.9.16'
2323
- blender-version: '3.6'
2424
blender-version-suffix: '0'
2525
python-version: '3.10.9'
26-
#- blender-version: '4.0'
27-
# blender-version-suffix: '0'
28-
# python-version: '3.10.9'
26+
- blender-version: '4.0'
27+
blender-version-suffix: '0'
28+
python-version: '3.10.9'
2929

3030
steps:
3131
- uses: actions/checkout@v3

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## v0.7.0 ()
44
* delete base sphere object and mesh after hierarchy import
5+
* adapt to API changes in Blender 4.0+
56

67
## v0.6.9 (06.04.23)
78
* fixed export of materials with normal maps

io_mesh_w3d/common/utils/material_export.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# <pep8 compliant>
22
# Written by Stephan Vedder and Michael Schnabel
33

4+
import bpy
45
from mathutils import Vector
56
from io_mesh_w3d.w3d.structs.mesh_structs.shader import *
67
from io_mesh_w3d.w3d.structs.mesh_structs.vertex_material import *
@@ -118,20 +119,24 @@ def retrieve_shader_material(context, material, principled, w3x=False):
118119
header=ShaderMaterialHeader(
119120
type_name=name),
120121
properties=[])
122+
123+
color_emissive_default = Vector((0.0, 0.0, 0.0, 1.0))
124+
if bpy.app.version >= (4, 0, 0):
125+
color_emissive_default = Vector((1.0, 1.0, 1.0, 1.0))
121126

122127
if w3x:
123128
append_property(shader_mat, 2, 'Shininess', material.specular_intensity * 200.0, 100.0)
124129
append_property(shader_mat, 5, 'ColorDiffuse', to_vec(material.diffuse_color), Vector((0.8, 0.8, 0.8, 1.0)))
125130
append_property(shader_mat, 5, 'ColorSpecular', to_vec(material.specular_color), Vector((0.0, 0.0, 0.0, 1.0)))
126131
append_property(shader_mat, 5, 'ColorAmbient', to_vec(material.ambient), Vector((1.0, 1.0, 1.0, 0.0)))
127-
append_property(shader_mat, 5, 'ColorEmissive', to_vec(principled.emission_color), Vector((0.0, 0.0, 0.0, 1.0)))
132+
append_property(shader_mat, 5, 'ColorEmissive', to_vec(principled.emission_color), color_emissive_default)
128133

129134
else:
130135
append_property(shader_mat, 2, 'SpecularExponent', material.specular_intensity * 200.0, 100.0)
131136
append_property(shader_mat, 5, 'DiffuseColor', to_vec(material.diffuse_color), Vector((0.8, 0.8, 0.8, 1.0)))
132137
append_property(shader_mat, 5, 'SpecularColor', to_vec(material.specular_color), Vector((0.0, 0.0, 0.0, 1.0)))
133138
append_property(shader_mat, 5, 'AmbientColor', to_vec(material.ambient), Vector((1.0, 1.0, 1.0, 0.0)))
134-
append_property(shader_mat, 5, 'EmissiveColor', to_vec(principled.emission_color), Vector((0.0, 0.0, 0.0, 1.0)))
139+
append_property(shader_mat, 5, 'EmissiveColor', to_vec(principled.emission_color), color_emissive_default)
135140

136141
if material.texture_1:
137142
append_property(shader_mat, 1, 'Texture_0', principled.base_color_texture)

io_mesh_w3d/common/utils/mesh_export.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,26 @@ def retrieve_meshes(context, hierarchy, rig, container_name, force_vertex_materi
183183
triangle.distance = tri_pos.length
184184
mesh_struct.triangles.append(triangle)
185185

186-
if context.file_format == 'W3X' and len(mesh_object.face_maps) > 0:
186+
if bpy.app.version < (4, 0, 0):
187+
face_maps = mesh_object.face_maps
188+
else:
189+
face_maps = mesh.face_maps
190+
191+
if context.file_format == 'W3X' and len(face_maps) > 0:
187192
context.warning('triangle surface types (mesh face maps) are not supported in W3X file format!')
188193
else:
189-
face_map_names = [map.name for map in mesh_object.face_maps]
194+
face_map_names = [map.name for map in face_maps]
190195
Triangle.validate_face_map_names(context, face_map_names)
191196

192-
for map in mesh.face_maps:
193-
for i, val in enumerate(map.data):
194-
mesh_struct.triangles[i].set_surface_type(face_map_names[val.value])
197+
if bpy.app.version < (4, 0, 0):
198+
for map in mesh.face_maps:
199+
for i, val in enumerate(map.data):
200+
mesh_struct.triangles[i].set_surface_type(face_map_names[val.value])
201+
else:
202+
for map in mesh.face_maps:
203+
for val in map.value:
204+
if val.value < len(mesh_struct.triangles):
205+
mesh_struct.triangles[val.value].set_surface_type(map.name)
195206

196207
header.face_count = len(mesh_struct.triangles)
197208

io_mesh_w3d/common/utils/mesh_import.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,19 @@ def create_mesh(context, mesh_struct, coll):
5656
if context.file_format == 'W3D':
5757
for i, triangle in enumerate(mesh_struct.triangles):
5858
surface_type_name = triangle.get_surface_type_name(context, i)
59-
if surface_type_name not in mesh_ob.face_maps:
60-
mesh_ob.face_maps.new(name=surface_type_name)
59+
if bpy.app.version < (4, 0, 0):
60+
if surface_type_name not in mesh_ob.face_maps:
61+
mesh_ob.face_maps.new(name=surface_type_name)
62+
else:
63+
if surface_type_name not in mesh.face_maps:
64+
face_map = mesh.face_maps.add()
65+
face_map.name = surface_type_name
6166

62-
mesh_ob.face_maps[surface_type_name].add([i])
67+
if bpy.app.version < (4, 0, 0):
68+
mesh_ob.face_maps[surface_type_name].add([i])
69+
else:
70+
val = mesh.face_maps[surface_type_name].value.add()
71+
val.value = i
6372

6473
for i, mat_pass in enumerate(mesh_struct.material_passes):
6574
create_vertex_color_layer(mesh, mat_pass.dcg, 'DCG', i)
@@ -71,7 +80,7 @@ def create_mesh(context, mesh_struct, coll):
7180
# vertex material stuff
7281
b_mesh = bmesh.new()
7382
b_mesh.from_mesh(mesh)
74-
83+
7584
if mesh_struct.vert_materials:
7685
create_vertex_material(
7786
context, principleds, mesh_struct, mesh, b_mesh, actual_mesh_name, triangles)

io_mesh_w3d/custom_properties.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import bpy
55
from bpy.props import *
6-
from bpy.types import Material, PropertyGroup, Bone, Mesh, Object
6+
from bpy.types import Material, PropertyGroup, Bone, Mesh
77

88

99
##########################################################################
@@ -122,6 +122,19 @@
122122
('DEBRIS', 'debris', 'desc: debris contact tag')],
123123
default='DEBRIS')
124124

125+
if bpy.app.version >= (4, 0, 0):
126+
class SurfaceType(bpy.types.PropertyGroup):
127+
value: bpy.props.IntProperty(default=0)
128+
129+
bpy.utils.register_class(SurfaceType)
130+
131+
class FaceMap(bpy.types.PropertyGroup):
132+
name: bpy.props.StringProperty(name="Face Map Name", default="Unknown")
133+
value: CollectionProperty(type=SurfaceType)
134+
135+
bpy.utils.register_class(FaceMap)
136+
137+
Mesh.face_maps = CollectionProperty(type=FaceMap)
125138

126139
##########################################################################
127140
# PoseBone

tests/common/cases/test_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,9 @@ def test_mesh_roundtrip_invalid_triangles(self):
349349
create_data(self, meshes)
350350

351351
mesh.triangles = temp_tris
352-
self.compare_data([mesh])
352+
353+
if bpy.app.version < (4, 0, 0):
354+
self.compare_data([mesh])
353355

354356
def test_hlod_4_levels_roundtrip(self):
355357
hlod = get_hlod_4_levels()

tests/common/cases/utils/test_mesh_export.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -646,15 +646,28 @@ def test_mesh_export_warned_if_unsupported_constraints_applied(self):
646646
self.assertFalse(meshes[0].is_camera_aligned())
647647

648648
def test_mesh_export_invalid_surface_types(self):
649-
mesh = get_mesh('mesh')
650-
create_mesh(self, mesh, get_collection())
649+
m = get_mesh('mesh')
650+
create_mesh(self, m, get_collection())
651651

652-
mesh_ob = bpy.data.objects['mesh']
653-
mesh_ob.face_maps.clear()
652+
if bpy.app.version < (4, 0, 0):
653+
mesh = bpy.data.objects['mesh']
654+
mesh.face_maps.clear()
655+
else:
656+
mesh = bpy.data.meshes['mesh']
657+
mesh.face_maps.clear()
654658

655-
mesh_ob.face_maps.new(name='InvalidSurfaceType')
656-
for i, _ in enumerate(mesh.verts):
657-
mesh_ob.face_maps[0].add([i])
659+
if bpy.app.version < (4, 0, 0):
660+
mesh.face_maps.new(name='InvalidSurfaceType')
661+
else:
662+
face_map = mesh.face_maps.add()
663+
face_map.name = 'InvalidSurfaceType'
664+
665+
for i, _ in enumerate(m.verts):
666+
if bpy.app.version < (4, 0, 0):
667+
mesh.face_maps[0].add([i])
668+
else:
669+
val = mesh.face_maps[0].value.add()
670+
val.value = i
658671

659672
with (patch.object(self, 'warning')) as report_func:
660673
meshes, _ = retrieve_meshes(self, None, None, 'container_name')

tests/common/cases/utils/test_mesh_import.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,10 @@ def test_mesh_import_invalid_surface_types(self):
214214
create_mesh(self, mesh_struct, bpy.context.scene.collection)
215215
report_func.assert_any_call('triangle 0 has an invalid surface type \'255\'')
216216

217-
mesh = bpy.data.objects[mesh_name]
217+
if bpy.app.version < (4, 0, 0):
218+
mesh = bpy.data.objects[mesh_name]
219+
else:
220+
mesh = bpy.data.meshes[mesh_name]
218221

219222
self.assertEqual(1, len(mesh.face_maps))
220223
self.assertEqual('Default', mesh.face_maps[0].name)

0 commit comments

Comments
 (0)