Skip to content

Commit

Permalink
Merge pull request #79 from monkeyman192/development
Browse files Browse the repository at this point in the history
New features and latest NMS version compatibility
  • Loading branch information
monkeyman192 authored Oct 8, 2020
2 parents 676b972 + 7f6b665 commit 7c304b7
Show file tree
Hide file tree
Showing 102 changed files with 1,469 additions and 369 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ textures/*

# pytest cache files
.pytest_cache/

# Ignore any logs produced by pytest
std.err
std.out
287 changes: 287 additions & 0 deletions BlenderExtensions/ContextMenu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
# Add some right click options

# stdlib imports
from copy import copy

# Local imports
from ..utils.misc import get_root_node, clone_node

# Blender imports
import bmesh
import bpy
from bpy.types import Menu, Operator
from mathutils import Matrix


class AddReferenceNode(Operator):
"""Add a new NMS reference node"""
bl_idname = "nmsdk.add_reference_node"
bl_label = "Reference Node"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
selected_objs = getattr(context, "selected_objects", None)
if not selected_objs:
self.report({'ERROR_INVALID_INPUT'},
'Please select an object to add a child node to')
return {'FINISHED'}
# Create a new empty reference node.
for obj in selected_objs:
empty_mesh = bpy.data.meshes.new('ref')
empty_obj = bpy.data.objects.new('ref', empty_mesh)
empty_obj.NMSNode_props.node_types = 'Reference'
empty_obj.matrix_world = Matrix()
empty_obj.parent = obj
empty_obj.rotation_mode = 'QUATERNION'
bpy.context.scene.collection.objects.link(empty_obj)

return {'FINISHED'}


class AddLocatorNode(Operator):
"""Add a new NMS reference node"""
bl_idname = "nmsdk.add_locator_node"
bl_label = "Locator Node"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
selected_objs = getattr(context, "selected_objects", None)
if not selected_objs:
self.report({'ERROR_INVALID_INPUT'},
'Please select an object to add a child node to')
return {'FINISHED'}
# Create a new empty reference node.
for obj in selected_objs:
empty_mesh = bpy.data.meshes.new('loc')
empty_obj = bpy.data.objects.new('loc', empty_mesh)
empty_obj.NMSNode_props.node_types = 'Locator'
empty_obj.matrix_world = Matrix()
empty_obj.parent = obj
empty_obj.rotation_mode = 'QUATERNION'
bpy.context.scene.collection.objects.link(empty_obj)

return {'FINISHED'}


class AddBoxCollisionNode(Operator):
"""Add a new NMS box collision node"""
bl_idname = "nmsdk.add_box_collision_node"
bl_label = "Box Collision node"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
selected_objs = getattr(context, "selected_objects", None)
if not selected_objs:
self.report({'ERROR_INVALID_INPUT'},
'Please select an object to add a child node to')
return {'FINISHED'}
# Create a new empty reference node.
for obj in selected_objs:
# Create a new cube for collisions
mesh = bpy.data.meshes.new('cube')
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=1.0)
bm.to_mesh(mesh)
bm.free()
cube = bpy.data.objects.new('cube', mesh)
cube.NMSNode_props.node_types = 'Collision'
cube.NMSCollision_props.collision_types = 'Box'
cube.matrix_world = Matrix()
cube.parent = obj
bpy.context.scene.collection.objects.link(cube)
cube.rotation_mode = 'QUATERNION'

return {'FINISHED'}


class CloneNodes(Operator):
"""Clone the selcted node(s)"""
bl_idname = "nmsdk.clone_nodes"
bl_label = "Clone node(s)"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
selected_objs = getattr(context, "selected_objects", None)
if not selected_objs:
self.report({'ERROR_INVALID_INPUT'},
'Please select an object to clone')
return {'FINISHED'}
# Make a copy of each of the selected nodes.
for obj in selected_objs:
clone_node(obj)
return {'FINISHED'}


class CloneNodesRecursively(Operator):
"""Clone the selcted node(s) and all attached children"""
bl_idname = "nmsdk.clone_nodes_recursively"
bl_label = "Clone node(s) recursively"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
selected_objs = getattr(context, "selected_objects", None)
if not selected_objs:
self.report({'ERROR_INVALID_INPUT'},
'Please select an object to clone')
return {'FINISHED'}
# Make a copy of each of the selected nodes.
for obj in selected_objs:
clone_node(obj, True)
return {'FINISHED'}


class NMSDK_OT_move_to_parent(Operator):
"""Set the selected object(s) to be a child node of the selected parent"""
bl_idname = "nmsdk.add_to_parent"
bl_label = "Set object(s) as child of selected parent"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
selected_objs = getattr(context, "selected_objects", [])
parent_obj = getattr(context, "active_object", None)

# First, make sure we have, a) enough objects selected, and b) the
# correct set of objects to iterate over so that it doesn't include the
# parent object.
if len(selected_objs) < 2:
self.report({'ERROR_INVALID_INPUT'},
'Please select 2 or more objects')
return {'FINISHED'}
child_objs = set(selected_objs) - {parent_obj}
root_obj = get_root_node(parent_obj)
for obj in child_objs:
# There are two possibilities for moving an object under a parent.
# 1. It can already be inside the imported scene, in which case we
# don't want to apply any kind of transforms, but simply set the
# parent and local transform.
# 2. The object exists outside of the scene, so we need to do a few
# extra things detailed within the code block.
if get_root_node(obj) == root_obj:
# Copy the local transform.
trans = copy(obj.matrix_local)
# set the parent.
obj.parent = parent_obj
# Set the local transform as before
obj.matrix_local = trans
del trans
else:
# 1. copy the world matrix of the object.
trans = copy(obj.matrix_world)
# 2. Set the world matrix of the object to be the Identity
# matrix.
obj.matrix_world = Matrix()
# 3. Change the object to be in the coordinate system of the
# root node. Apply this change the object.
obj.data.transform(root_obj.matrix_world)
obj.matrix_world = Matrix()
# 4. Parent the object to the required object.
obj.parent = parent_obj
# 5. Set the objects transform from before.
obj.matrix_local = trans
del trans

return {'FINISHED'}


class NMSDK_MT_add_NMS_Scenenodes(Menu):
bl_label = "Add new Scene Node objects"
bl_idname = "NMSDK_MT_add_NMS_Scenenodes"

def draw(self, context):
layout = self.layout
row = layout.row()
row.operator('nmsdk.add_reference_node')
row = layout.row()
row.operator('nmsdk.add_locator_node')
row = layout.row()
row.operator('nmsdk.add_box_collision_node')


class NMSDK_MT_clone_NMS_Scenenodes(Menu):
bl_label = "Clone NMS Scenenode objects"
bl_idname = "NMSDK_MT_clone_NMS_Scenenodes"

def draw(self, context):
layout = self.layout
row = layout.row()
row.operator('nmsdk.clone_nodes')
row = layout.row()
row.operator('nmsdk.clone_nodes_recursively')
row = layout.row()


# Functions to modify blenders' menu items


def add_empty_root_node(self, context):
""" Add an option to the 'add' menu in the 3D view to add an empty root
NMS SceneNode reference for exporting. """
layout = self.layout
layout.separator()
layout.operator('nmsdk.create_root_scene')


def parent_menu_func(self, context):
layout = self.layout
layout.separator()
layout.operator('nmsdk.add_to_parent')


def add_obj_menu_func(self, context):
""" Add the sub-menu to the context menu. """
layout = self.layout
layout.separator()
row = layout.row()
row.menu("NMSDK_MT_add_NMS_Scenenodes")
row = layout.row()
row.menu("NMSDK_MT_clone_NMS_Scenenodes")


classes = (NMSDK_OT_move_to_parent,
AddReferenceNode,
AddBoxCollisionNode,
AddLocatorNode,
NMSDK_MT_add_NMS_Scenenodes,
NMSDK_MT_clone_NMS_Scenenodes,
CloneNodes,
CloneNodesRecursively)


class ContextMenus():
@staticmethod
def register():
# Register classes to be used.
for cls_ in classes:
bpy.utils.register_class(cls_)
# Add the functions to the menus they need to be in.
bpy.types.VIEW3D_MT_add.append(add_empty_root_node)
bpy.types.VIEW3D_MT_object_parent.append(parent_menu_func)
bpy.types.VIEW3D_MT_object_context_menu.append(add_obj_menu_func)

@staticmethod
def unregister():
# Unregister classes used.
for cls_ in classes:
bpy.utils.unregister_class(cls_)
# Remove the functions from the menus they were in.
bpy.types.VIEW3D_MT_add.remove(add_empty_root_node)
bpy.types.VIEW3D_MT_object_parent.remove(parent_menu_func)
bpy.types.VIEW3D_MT_object_context_menu.remove(add_obj_menu_func)
33 changes: 23 additions & 10 deletions BlenderExtensions/NMSObjectsPanels.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ class NMSRotationProperties(bpy.types.PropertyGroup):
class NMSReferenceProperties(bpy.types.PropertyGroup):
reference_path: StringProperty(
name="Reference Path",
description="Path to scene to be referenced at this location.")
description="Path to scene to be referenced at this location.",
subtype='FILE_PATH')
ref_path: StringProperty(
name="Reference Path (internal)",
description="Internal use only reference path variable.")
Expand Down Expand Up @@ -174,6 +175,15 @@ def draw(self, context):
row.prop(obj.NMSNode_props, "node_types", expand=True)


class test_OT_op(bpy.types.Operator):
bl_label = "test"
bl_idname = "test.op"

def execute(self, context):
print(context.object)
return {'FINISHED'}


class NMSDK_PT_ReferencePropertyPanel(bpy.types.Panel):
"""Creates a Panel in the scene context of the properties editor"""
bl_label = "NMS Reference Properties"
Expand All @@ -198,6 +208,8 @@ def draw(self, context):
row.prop(obj.NMSReference_props, "scene_name")
row = layout.row()
row.prop(obj.NMSReference_props, "is_proc")
row = layout.row()
row.operator("nmsdk._import_ref_scene")


class NMSDK_PT_MaterialPropertyPanel(bpy.types.Panel):
Expand Down Expand Up @@ -410,7 +422,8 @@ def draw(self, context):
NMSRotationProperties,
NMSAnimationProperties,
NMSCollisionProperties,
NMSDescriptorProperties)
NMSDescriptorProperties,
test_OT_op)
panel_classes = (NMSDK_PT_NodePropertyPanel,
NMSDK_PT_MeshPropertyPanel,
NMSDK_PT_MaterialPropertyPanel,
Expand All @@ -427,8 +440,8 @@ class NMSPanels():
@staticmethod
def register():
# register the properties
for cls in classes:
register_class(cls)
for cls_ in classes:
register_class(cls_)
# link the properties with the objects' internal variables
bpy.types.Object.NMSNode_props = bpy.props.PointerProperty(
type=NMSNodeProperties)
Expand All @@ -451,14 +464,14 @@ def register():
bpy.types.Object.NMSDescriptor_props = bpy.props.PointerProperty(
type=NMSDescriptorProperties)
# register the panels
for cls in panel_classes:
register_class(cls)
for cls_ in panel_classes:
register_class(cls_)

@staticmethod
def unregister():
# unregister the property classes
for cls in reversed(classes):
unregister_class(cls)
for cls_ in reversed(classes):
unregister_class(cls_)
# delete the properties from the objects
del bpy.types.Object.NMSNode_props
del bpy.types.Object.NMSMesh_props
Expand All @@ -471,5 +484,5 @@ def unregister():
del bpy.types.Object.NMSCollision_props
del bpy.types.Object.NMSDescriptor_props
# unregister the panels
for cls in reversed(panel_classes):
unregister_class(cls)
for cls_ in reversed(panel_classes):
unregister_class(cls_)
4 changes: 2 additions & 2 deletions BlenderExtensions/SettingsPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def draw(self, context):
text='PCBANKS location')
row.separator()
row.operator('nmsdk._remove_pcbanks',
icon='X', emboss=False, text=" ")
icon='X', emboss=False, text="Remove PCBANKS directory")
_dir = context.scene.nmsdk_default_settings.PCBANKS_directory
if _dir != "":
layout.label(text=_dir)
Expand All @@ -79,7 +79,7 @@ def draw(self, context):
text='MBINCompiler location')
row.separator()
row.operator('nmsdk._remove_mbincompiler',
icon='X', emboss=False, text=" ")
icon='X', emboss=False, text="Remove MBINCompiler path")
_dir = context.scene.nmsdk_default_settings.MBINCompiler_path
if _dir != "":
layout.label(text=_dir)
Expand Down
Loading

0 comments on commit 7c304b7

Please sign in to comment.