Skip to content

Commit

Permalink
Adapt to blender 4.0 API changes (#263)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Tarcontar authored Jan 27, 2024
1 parent 9829304 commit 98beb90
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 25 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ jobs:
runs-on: [ ubuntu-latest ]
strategy:
matrix:
blender-version: [ '2.93', '3.6' ] #, '4.0' ]
blender-version: [ '2.93', '3.6', '4.0' ]
include:
- blender-version: '2.93'
blender-version-suffix: '13'
python-version: '3.9.16'
- blender-version: '3.6'
blender-version-suffix: '0'
python-version: '3.10.9'
#- blender-version: '4.0'
# blender-version-suffix: '0'
# python-version: '3.10.9'
- blender-version: '4.0'
blender-version-suffix: '0'
python-version: '3.10.9'

steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

## v0.6.9 (06.04.23)
* fixed export of materials with normal maps
Expand Down
9 changes: 7 additions & 2 deletions io_mesh_w3d/common/utils/material_export.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# <pep8 compliant>
# Written by Stephan Vedder and Michael Schnabel

import bpy
from mathutils import Vector
from io_mesh_w3d.w3d.structs.mesh_structs.shader import *
from io_mesh_w3d.w3d.structs.mesh_structs.vertex_material import *
Expand Down Expand Up @@ -118,20 +119,24 @@ def retrieve_shader_material(context, material, principled, w3x=False):
header=ShaderMaterialHeader(
type_name=name),
properties=[])

color_emissive_default = Vector((0.0, 0.0, 0.0, 1.0))
if bpy.app.version >= (4, 0, 0):
color_emissive_default = Vector((1.0, 1.0, 1.0, 1.0))

if w3x:
append_property(shader_mat, 2, 'Shininess', material.specular_intensity * 200.0, 100.0)
append_property(shader_mat, 5, 'ColorDiffuse', to_vec(material.diffuse_color), Vector((0.8, 0.8, 0.8, 1.0)))
append_property(shader_mat, 5, 'ColorSpecular', to_vec(material.specular_color), Vector((0.0, 0.0, 0.0, 1.0)))
append_property(shader_mat, 5, 'ColorAmbient', to_vec(material.ambient), Vector((1.0, 1.0, 1.0, 0.0)))
append_property(shader_mat, 5, 'ColorEmissive', to_vec(principled.emission_color), Vector((0.0, 0.0, 0.0, 1.0)))
append_property(shader_mat, 5, 'ColorEmissive', to_vec(principled.emission_color), color_emissive_default)

else:
append_property(shader_mat, 2, 'SpecularExponent', material.specular_intensity * 200.0, 100.0)
append_property(shader_mat, 5, 'DiffuseColor', to_vec(material.diffuse_color), Vector((0.8, 0.8, 0.8, 1.0)))
append_property(shader_mat, 5, 'SpecularColor', to_vec(material.specular_color), Vector((0.0, 0.0, 0.0, 1.0)))
append_property(shader_mat, 5, 'AmbientColor', to_vec(material.ambient), Vector((1.0, 1.0, 1.0, 0.0)))
append_property(shader_mat, 5, 'EmissiveColor', to_vec(principled.emission_color), Vector((0.0, 0.0, 0.0, 1.0)))
append_property(shader_mat, 5, 'EmissiveColor', to_vec(principled.emission_color), color_emissive_default)

if material.texture_1:
append_property(shader_mat, 1, 'Texture_0', principled.base_color_texture)
Expand Down
21 changes: 16 additions & 5 deletions io_mesh_w3d/common/utils/mesh_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,26 @@ def retrieve_meshes(context, hierarchy, rig, container_name, force_vertex_materi
triangle.distance = tri_pos.length
mesh_struct.triangles.append(triangle)

if context.file_format == 'W3X' and len(mesh_object.face_maps) > 0:
if bpy.app.version < (4, 0, 0):
face_maps = mesh_object.face_maps
else:
face_maps = mesh.face_maps

if context.file_format == 'W3X' and len(face_maps) > 0:
context.warning('triangle surface types (mesh face maps) are not supported in W3X file format!')
else:
face_map_names = [map.name for map in mesh_object.face_maps]
face_map_names = [map.name for map in face_maps]
Triangle.validate_face_map_names(context, face_map_names)

for map in mesh.face_maps:
for i, val in enumerate(map.data):
mesh_struct.triangles[i].set_surface_type(face_map_names[val.value])
if bpy.app.version < (4, 0, 0):
for map in mesh.face_maps:
for i, val in enumerate(map.data):
mesh_struct.triangles[i].set_surface_type(face_map_names[val.value])
else:
for map in mesh.face_maps:
for val in map.value:
if val.value < len(mesh_struct.triangles):
mesh_struct.triangles[val.value].set_surface_type(map.name)

header.face_count = len(mesh_struct.triangles)

Expand Down
17 changes: 13 additions & 4 deletions io_mesh_w3d/common/utils/mesh_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,19 @@ def create_mesh(context, mesh_struct, coll):
if context.file_format == 'W3D':
for i, triangle in enumerate(mesh_struct.triangles):
surface_type_name = triangle.get_surface_type_name(context, i)
if surface_type_name not in mesh_ob.face_maps:
mesh_ob.face_maps.new(name=surface_type_name)
if bpy.app.version < (4, 0, 0):
if surface_type_name not in mesh_ob.face_maps:
mesh_ob.face_maps.new(name=surface_type_name)
else:
if surface_type_name not in mesh.face_maps:
face_map = mesh.face_maps.add()
face_map.name = surface_type_name

mesh_ob.face_maps[surface_type_name].add([i])
if bpy.app.version < (4, 0, 0):
mesh_ob.face_maps[surface_type_name].add([i])
else:
val = mesh.face_maps[surface_type_name].value.add()
val.value = i

for i, mat_pass in enumerate(mesh_struct.material_passes):
create_vertex_color_layer(mesh, mat_pass.dcg, 'DCG', i)
Expand All @@ -71,7 +80,7 @@ def create_mesh(context, mesh_struct, coll):
# vertex material stuff
b_mesh = bmesh.new()
b_mesh.from_mesh(mesh)

if mesh_struct.vert_materials:
create_vertex_material(
context, principleds, mesh_struct, mesh, b_mesh, actual_mesh_name, triangles)
Expand Down
15 changes: 14 additions & 1 deletion io_mesh_w3d/custom_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import bpy
from bpy.props import *
from bpy.types import Material, PropertyGroup, Bone, Mesh, Object
from bpy.types import Material, PropertyGroup, Bone, Mesh


##########################################################################
Expand Down Expand Up @@ -122,6 +122,19 @@
('DEBRIS', 'debris', 'desc: debris contact tag')],
default='DEBRIS')

if bpy.app.version >= (4, 0, 0):
class SurfaceType(bpy.types.PropertyGroup):
value: bpy.props.IntProperty(default=0)

bpy.utils.register_class(SurfaceType)

class FaceMap(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(name="Face Map Name", default="Unknown")
value: CollectionProperty(type=SurfaceType)

bpy.utils.register_class(FaceMap)

Mesh.face_maps = CollectionProperty(type=FaceMap)

##########################################################################
# PoseBone
Expand Down
4 changes: 3 additions & 1 deletion tests/common/cases/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,9 @@ def test_mesh_roundtrip_invalid_triangles(self):
create_data(self, meshes)

mesh.triangles = temp_tris
self.compare_data([mesh])

if bpy.app.version < (4, 0, 0):
self.compare_data([mesh])

def test_hlod_4_levels_roundtrip(self):
hlod = get_hlod_4_levels()
Expand Down
27 changes: 20 additions & 7 deletions tests/common/cases/utils/test_mesh_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,15 +646,28 @@ def test_mesh_export_warned_if_unsupported_constraints_applied(self):
self.assertFalse(meshes[0].is_camera_aligned())

def test_mesh_export_invalid_surface_types(self):
mesh = get_mesh('mesh')
create_mesh(self, mesh, get_collection())
m = get_mesh('mesh')
create_mesh(self, m, get_collection())

mesh_ob = bpy.data.objects['mesh']
mesh_ob.face_maps.clear()
if bpy.app.version < (4, 0, 0):
mesh = bpy.data.objects['mesh']
mesh.face_maps.clear()
else:
mesh = bpy.data.meshes['mesh']
mesh.face_maps.clear()

mesh_ob.face_maps.new(name='InvalidSurfaceType')
for i, _ in enumerate(mesh.verts):
mesh_ob.face_maps[0].add([i])
if bpy.app.version < (4, 0, 0):
mesh.face_maps.new(name='InvalidSurfaceType')
else:
face_map = mesh.face_maps.add()
face_map.name = 'InvalidSurfaceType'

for i, _ in enumerate(m.verts):
if bpy.app.version < (4, 0, 0):
mesh.face_maps[0].add([i])
else:
val = mesh.face_maps[0].value.add()
val.value = i

with (patch.object(self, 'warning')) as report_func:
meshes, _ = retrieve_meshes(self, None, None, 'container_name')
Expand Down
5 changes: 4 additions & 1 deletion tests/common/cases/utils/test_mesh_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,10 @@ def test_mesh_import_invalid_surface_types(self):
create_mesh(self, mesh_struct, bpy.context.scene.collection)
report_func.assert_any_call('triangle 0 has an invalid surface type \'255\'')

mesh = bpy.data.objects[mesh_name]
if bpy.app.version < (4, 0, 0):
mesh = bpy.data.objects[mesh_name]
else:
mesh = bpy.data.meshes[mesh_name]

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

0 comments on commit 98beb90

Please sign in to comment.