Skip to content

Commit

Permalink
add feature - export particle objects as multimeshinstance to godot (#…
Browse files Browse the repository at this point in the history
…354)

* add feature particle to multimeshinstance

* fix exporting single object particle will cause error

* fix wrong scale that will scale postion before

* fix for reveiwing

* fix for reveiwing

* pep8

* pep8 line length

* pep8 indent

* mat4 changes and change export particle from active to all

* fix active

* fix wrong

* fix pep8 error

* fix pep8 error ..

* fix pep8 error ...

* Simplify MultiMeshResource key.

* Wrap too-long lines.

Co-authored-by: U-APPLE\celpec <[email protected]>
Co-authored-by: Lu Jiacheng <[email protected]>
  • Loading branch information
3 people committed Nov 13, 2020
1 parent 61e55f2 commit a01b0e9
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 3 deletions.
3 changes: 2 additions & 1 deletion io_scene_godot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
("CAMERA", "Camera", ""),
("LIGHT", "Light", ""),
("ARMATURE", "Armature", ""),
("GEOMETRY", "Geometry", "")
("GEOMETRY", "Geometry", ""),
("PARTICLE", "Particle", "")
),
default={
"EMPTY",
Expand Down
3 changes: 3 additions & 0 deletions io_scene_godot/converters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .physics import export_physics_properties
from .armature import export_armature_node, export_bone_attachment
from .animation import export_animation_data
from .multimesh import export_multimesh_node, has_particle

# TODO: What about Empties which refer to group instances? (in 2.8 Collection
# Instances)
Expand All @@ -38,3 +39,5 @@
BONE_ATTACHMENT_EXPORTER = export_bone_attachment

ANIMATION_DATA_EXPORTER = export_animation_data

MULTIMESH_EXPORTER = export_multimesh_node
167 changes: 167 additions & 0 deletions io_scene_godot/converters/multimesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"""Exports particles as multimesh to Godot"""
import math
import bpy
import mathutils

from ..structures import (
NodeTemplate, InternalResource, mat4_to_string)
from .mesh import ArrayMeshResourceExporter


def export_multimesh_node(escn_file, export_settings,
obj, parent_gd_node):
"""Export blender particle to a MultiMeshInstance"""
context = bpy.context
dg_eval = context.evaluated_depsgraph_get()
obj_eval = context.object.evaluated_get(dg_eval)

multimeshid_active = None
for _ps in obj_eval.particle_systems:
# In Blender's particle system params, If "Render - Render As" are
# switched to "Collection", there maybe several objects instanced to
# one particle, but in Godot one MultiMeshInstance just have one
# object to instance, so choose the first object in Blender to display
# as the only one object in Godot's MultiMeshInstance's resource.
if (_ps.settings.instance_collection and
_ps.settings.instance_collection.all_objects[0]):
instance_object = _ps.settings.instance_collection.all_objects[0]
elif _ps.settings.instance_object:
instance_object = _ps.settings.instance_object

multimeshnode = NodeTemplate(
_ps.name, 'MultiMeshInstance', parent_gd_node
)

# Export instance mesh resource first
instance_mesh_exporter = ArrayMeshResourceExporter(instance_object)

mesh_id = instance_mesh_exporter.export_mesh(
escn_file, export_settings
)

multimesh_exporter = MultiMeshResourceExporter(obj, mesh_id, _ps)

multimeshid = multimesh_exporter.export_multimesh(
escn_file, export_settings, _ps.name)

if _ps == obj_eval.particle_systems.active:
multimeshid_active = multimeshid

multimeshnode['multimesh'] = 'SubResource({})'.format(multimeshid_active)
multimeshnode['visible'] = obj.visible_get()

escn_file.add_node(multimeshnode)

return multimeshnode


def has_particle(node):
"""Returns True if the object has particles"""
context = bpy.context
dg_eval = context.evaluated_depsgraph_get()
obj_eval = context.object.evaluated_get(dg_eval)

return len(obj_eval.particle_systems) > 0


class MultiMeshResourceExporter:
"""Export a multimesh resource from a blender mesh object"""

def __init__(self, mesh_object, instance_mesh_id, particle_system):
# blender multimesh object
self.object = mesh_object
self.instance_mesh_id = instance_mesh_id
self.particle_system = particle_system

self.mesh_resource = None

def export_multimesh(self, escn_file, export_settings, particle_name):
"""Saves a mesh into the escn file"""
converter = MultiMeshConverter(self.particle_system)
# Due the missing instance particle support in Godot,
# we export one MultiMeshResource from each ParticleSystem.
# For now it is safe to use bpy ParticleSystem object as
# the hash key.
key = self.particle_system
# Check if multi-mesh resource exists so we don't bother to export
# it twice,
multimesh_id = escn_file.get_internal_resource(key)
if multimesh_id is not None:
return multimesh_id

multimesh = converter.to_multimesh()
if multimesh is not None:
self.mesh_resource = MultiMeshResource(particle_name)
self.mesh_resource['instance_count'] = '{}'.format(
len(self.particle_system.particles))
self.mesh_resource['mesh'] = 'SubResource({})'.format(
self.instance_mesh_id)
self.mesh_resource['transform_array'] = (
'PoolVector3Array({})'.format(
converter.to_multimesh())
)

multimesh_id = escn_file.add_internal_resource(
self.mesh_resource, key)
assert multimesh_id is not None

return multimesh_id


class MultiMeshResource(InternalResource):
"""Godot MultiMesh resource"""

def __init__(self, name):
super().__init__('MultiMesh', name)
self['transform_format'] = 1

# Change above value in MultiMeshResourceExporter
self['instance_count'] = 0
self['mesh'] = None
self['transform_array'] = None


class MultiMeshConverter:
"""Blender Particles' mat4x4 to
Godot MultiMesh resource PoolVector3Array"""

def __init__(self, particle_system):
self.particle_system = particle_system

def to_multimesh(self):
"""Evaluates object & converts to final multimesh, ready for export.
The multimesh is only temporary."""
transform_array = []
float32array = ''
for _particle in self.particle_system.particles:
quat_x = mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
quat_y = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0))
quat_z = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(90.0))
quat_a = _particle.rotation.copy()
quat_a.rotate(quat_x)
quat_a.rotate(quat_y)
quat_a.rotate(quat_z)
quat_a.normalize()
rot_tmp = quat_a[1]
quat_a[1] = quat_a[3]
quat_a[3] = rot_tmp

rot = quat_a
loc = _particle.location - mathutils.Vector((0, 0, 1))
scl = _particle.size

mat_sca_x = mathutils.Matrix.Scale(scl, 4, (1.0, 0.0, 0.0))
mat_sca_y = mathutils.Matrix.Scale(scl, 4, (0.0, 1.0, 0.0))
mat_sca_z = mathutils.Matrix.Scale(scl, 4, (0.0, 0.0, 1.0))

mat_rot = rot.to_matrix()
mat_trs = mathutils.Matrix.Translation(loc)

mat = (
mat_trs @ mat_rot.to_4x4() @ mat_sca_x @ mat_sca_y @ mat_sca_z
)

mat4 = mat.to_4x4()

transform_array.append(mat4_to_string(mat4, prefix='', suffix=''))
return ','.join(transform_array)
8 changes: 8 additions & 0 deletions io_scene_godot/export_godot.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ def export_object(self, obj, parent_gd_node):
obj,
parent_gd_node
)
if ("PARTICLE" in self.config['object_types'] and
converters.has_particle(obj)):
converters.MULTIMESH_EXPORTER(
self.escn_file,
self.config,
obj,
parent_gd_node
)

# Perform the export, note that `exported_node.parent` not
# always the same as `parent_gd_node`, as sometimes, one
Expand Down
4 changes: 2 additions & 2 deletions io_scene_godot/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,10 +419,10 @@ def gamma_correct(color):


# ------------------ Implicit Conversions of Blender Types --------------------
def mat4_to_string(mtx):
def mat4_to_string(mtx, prefix='Transform(', suffix=')'):
"""Converts a matrix to a "Transform" string that can be parsed by Godot"""
mtx = fix_matrix(mtx)
array = Array('Transform(')
array = Array(prefix, suffix=suffix)
for row in range(3):
for col in range(3):
array.append(mtx[row][col])
Expand Down

0 comments on commit a01b0e9

Please sign in to comment.