Compare commits

..

2 Commits

Author SHA1 Message Date
Adam Johnson 86ebb069ed
Fix release publishing failure. 10 months ago
Adam Johnson 8ab208de27
Bump to Korman v0.16. 10 months ago
  1. 2
      CMakeLists.txt
  2. 1
      korman/__init__.py
  3. 52
      korman/exporter/utils.py
  4. 32
      korman/properties/modifiers/base.py
  5. 90
      korman/properties/modifiers/logic.py
  6. 19
      korman/ui/modifiers/logic.py
  7. 1
      release.ps1

2
CMakeLists.txt

@ -17,7 +17,7 @@ cmake_minimum_required(VERSION 3.12)
set(_PROJECT_INFO set(_PROJECT_INFO
korman korman
VERSION 0.15 VERSION 0.16
DESCRIPTION "Blender plugin for creating ages for Cyan Worlds' proprietary Plasma engine and its open source variant, CyanWorlds.com Engine." DESCRIPTION "Blender plugin for creating ages for Cyan Worlds' proprietary Plasma engine and its open source variant, CyanWorlds.com Engine."
LANGUAGES CXX # This should probably be NONE, but we need the compiler for string_theory tests LANGUAGES CXX # This should probably be NONE, but we need the compiler for string_theory tests
) )

1
korman/__init__.py

@ -23,6 +23,7 @@ from . import operators
bl_info = { bl_info = {
"name": "Korman", "name": "Korman",
"author": "Guild of Writers", "author": "Guild of Writers",
"version": (0, 16),
"blender": (2, 79, 0), "blender": (2, 79, 0),
"location": "File > Import-Export", "location": "File > Import-Export",
"description": "Exporter for Cyan Worlds' Plasma Engine", "description": "Exporter for Cyan Worlds' Plasma Engine",

52
korman/exporter/utils.py

@ -20,7 +20,6 @@ import bpy
import mathutils import mathutils
from contextlib import contextmanager from contextlib import contextmanager
import enum
from typing import * from typing import *
from PyHSPlasma import * from PyHSPlasma import *
@ -123,54 +122,21 @@ def create_camera_object(name: str) -> bpy.types.Object:
bpy.context.scene.objects.link(cam_obj) bpy.context.scene.objects.link(cam_obj)
return cam_obj return cam_obj
class RegionOrigin(enum.Enum): def create_cube_region(name: str, size: float, owner_object: bpy.types.Object) -> bpy.types.Object:
center = enum.auto()
bottom = enum.auto()
def create_cube_region(name: str, size: float, owner_object: bpy.types.Object, origin: RegionOrigin = RegionOrigin.center) -> bpy.types.Object:
"""Create a cube shaped region object""" """Create a cube shaped region object"""
# Proxy over to the generic rectangular prism implementation to
# ensure that it is well tested.
return create_box_region(name, (size, size, size), owner_object, origin)
def create_box_region(
name: str, size: Union[Tuple[float, float, float], mathutils.Vector],
owner_object: bpy.types.Object,
origin: RegionOrigin = RegionOrigin.center
) -> bpy.types.Object:
"""Create a rectangular prism region object"""
if not isinstance(size, mathutils.Vector):
size = mathutils.Vector(size)
region_object = BMeshObject(name) region_object = BMeshObject(name)
region_object.plasma_object.enabled = True region_object.plasma_object.enabled = True
region_object.plasma_object.page = owner_object.plasma_object.page region_object.plasma_object.page = owner_object.plasma_object.page
region_object.hide_render = True region_object.hide_render = True
with region_object as bm: with region_object as bm:
center = owner_object.matrix_world.translation bmesh.ops.create_cube(bm, size=(size))
if origin == RegionOrigin.bottom: bmesh.ops.transform(
center = center.copy() bm,
center.z += size.z * 0.5 matrix=mathutils.Matrix.Translation(
vert_src = [ owner_object.matrix_world.translation - region_object.matrix_world.translation
(center.x + size.x * 0.5, center.y + size.y * 0.5, center.z + size.z * 0.5), ),
(center.x + size.x * 0.5, center.y + size.y * 0.5, center.z - size.z * 0.5), space=region_object.matrix_world, verts=bm.verts
(center.x + size.x * 0.5, center.y - size.y * 0.5, center.z + size.z * 0.5), )
(center.x + size.x * 0.5, center.y - size.y * 0.5, center.z - size.z * 0.5),
(center.x - size.x * 0.5, center.y + size.y * 0.5, center.z + size.z * 0.5),
(center.x - size.x * 0.5, center.y + size.y * 0.5, center.z - size.z * 0.5),
(center.x - size.x * 0.5, center.y - size.y * 0.5, center.z + size.z * 0.5),
(center.x - size.x * 0.5, center.y - size.y * 0.5, center.z - size.z * 0.5),
]
verts = [bm.verts.new(i) for i in vert_src]
new_face = bm.faces.new
new_face((verts[0], verts[1], verts[3], verts[2])) # X+
new_face((verts[4], verts[5], verts[7], verts[6])) # X-
new_face((verts[0], verts[1], verts[5], verts[4])) # Y+
new_face((verts[2], verts[3], verts[7], verts[6])) # Y-
new_face((verts[0], verts[2], verts[6], verts[4])) # Z+
new_face((verts[1], verts[3], verts[7], verts[5])) # Z-
return region_object.release() return region_object.release()
@contextmanager @contextmanager

32
korman/properties/modifiers/base.py

@ -13,17 +13,12 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
from bpy.props import * from bpy.props import *
import abc import abc
import itertools import itertools
from typing import * from typing import Any, Dict, FrozenSet, Optional
if TYPE_CHECKING:
from ...nodes.node_python import *
from ... import helpers from ... import helpers
@ -207,23 +202,24 @@ class PlasmaModifierLogicWiz:
pfm_node.update() pfm_node.update()
return pfm_node return pfm_node
def _create_python_attribute(self, pfm_node: PlasmaPythonFileNode, attribute_name: str, **kwargs): def _create_python_attribute(self, pfm_node, attribute_name: str, attribute_type: Optional[str] = None, **kwargs):
"""Creates and links a Python Attribute Node to the Python File Node given by `pfm_node`. """Creates and links a Python Attribute Node to the Python File Node given by `pfm_node`.
For attribute nodes that require multiple values, the `value` may be set to None and This will automatically handle simple attribute types such as numbers and strings, however,
handled in your code.""" for object linkage, you should specify the optional `attribute_type` to ensure the proper
attribute_defn = next((i for i in pfm_node.attributes if i.attribute_name == attribute_name), None) attribute type is found. For attribute nodes that require multiple values, the `value` may
if attribute_defn is None: be set to None and handled in your code."""
raise KeyError(attribute_name) from ...nodes.node_python import PlasmaAttribute, PlasmaAttribNodeBase
if attribute_type is None:
from ...nodes.node_python import PlasmaAttribNodeBase assert len(kwargs) == 1 and "value" in kwargs, \
attribute_type = attribute_defn.attribute_type "In order to deduce the attribute_type, exactly one attribute value must be passed as a kw named `value`"
attribute_type = PlasmaAttribute.type_LUT.get(kwargs["value"].__class__)
node_cls = next((i for i in PlasmaAttribNodeBase.__subclasses__() if attribute_type in i.pl_attrib), None) node_cls = next((i for i in PlasmaAttribNodeBase.__subclasses__() if attribute_type in i.pl_attrib), None)
assert node_cls is not None, \ assert node_cls is not None, "'{}': Unable to find attribute node type for '{}' ('{}')".format(
f"'{self.id_data.name}': Unable to find attribute node type for '{attribute_name}' ('{attribute_type}')" self.id_data.name, attribute_name, attribute_type
)
node = pfm_node.id_data.nodes.new(node_cls.bl_idname) node = pfm_node.id_data.nodes.new(node_cls.bl_idname)
node.link_output(pfm_node, "pfm", attribute_name) node.link_output(pfm_node, "pfm", attribute_name)
assert kwargs
for i, j in kwargs.items(): for i, j in kwargs.items():
setattr(node, i, j) setattr(node, i, j)
return node return node

90
korman/properties/modifiers/logic.py

@ -13,35 +13,16 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bmesh import bmesh
import bpy import bpy
from bpy.props import * from bpy.props import *
import mathutils import mathutils
from PyHSPlasma import * from PyHSPlasma import *
from typing import *
if TYPE_CHECKING:
from ...exporter import Exporter
from ...nodes.node_conditions import *
from ...nodes.node_messages import *
from ...nodes.node_responder import *
from ...addon_prefs import game_versions from ...addon_prefs import game_versions
from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz
from ...exporter import ExportError, utils from ...exporter import ExportError, utils
from ... import idprops from ... import idprops
from .physics import bounds_type_index, bounds_type_str, bounds_types
entry_cam_pfm = {
"filename": "xEntryCam.py",
"attribs": (
{ 'id': 1, 'type': "ptAttribActivator", 'name': "actRegionSensor" },
{ 'id': 2, 'type': "ptAttribSceneobject", 'name': "camera" },
{ 'id': 3, 'type': "ptAttribBoolean", 'name': "undoFirstPerson" },
)
}
class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup): class PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup):
version = EnumProperty(name="Version", version = EnumProperty(name="Version",
@ -95,7 +76,7 @@ class PlasmaAdvancedLogic(PlasmaModifierProperties):
return any((i.node_tree.requires_actor for i in self.logic_groups if i.node_tree)) return any((i.node_tree.requires_actor for i in self.logic_groups if i.node_tree))
class PlasmaSpawnPoint(PlasmaModifierProperties, PlasmaModifierLogicWiz): class PlasmaSpawnPoint(PlasmaModifierProperties):
pl_id = "spawnpoint" pl_id = "spawnpoint"
bl_category = "Logic" bl_category = "Logic"
@ -103,73 +84,10 @@ class PlasmaSpawnPoint(PlasmaModifierProperties, PlasmaModifierLogicWiz):
bl_description = "Point at which avatars link into the Age" bl_description = "Point at which avatars link into the Age"
bl_object_types = {"EMPTY"} bl_object_types = {"EMPTY"}
def _get_bounds(self) -> int:
if self.exit_region is not None:
return bounds_type_index(self.exit_region.plasma_modifiers.collision.bounds_type)
return bounds_type_index("hull")
def _set_bounds(self, value: int) -> None:
if self.exit_region is not None:
self.exit_region.plasma_modifiers.collision.bounds_type = bounds_type_str(value)
entry_camera = PointerProperty(
name="Entry Camera",
description="Camera to use when the player spawns at this location",
type=bpy.types.Object,
poll=idprops.poll_camera_objects
)
exit_region = PointerProperty(
name="Exit Region",
description="Pop the camera when the player exits this region",
type=bpy.types.Object,
poll=idprops.poll_mesh_objects
)
bounds_type = EnumProperty(
name="Bounds",
description="",
items=bounds_types,
get=_get_bounds,
set=_set_bounds,
default="hull"
)
def pre_export(self, exporter: Exporter, bo: bpy.types.Object) -> None:
if self.entry_camera is None:
return
if self.exit_region is None:
self.exit_region = yield utils.create_box_region(
f"{self.key_name}_ExitRgn", (2.0, 2.0, 6.0),
bo, utils.RegionOrigin.bottom
)
yield self.convert_logic(bo)
def logicwiz(self, bo, tree):
pfm_node = self._create_python_file_node(
tree,
entry_cam_pfm["filename"],
entry_cam_pfm["attribs"]
)
volume_sensor: PlasmaVolumeSensorNode = tree.nodes.new("PlasmaVolumeSensorNode")
volume_sensor.find_input_socket("enter").allow = True
volume_sensor.find_input_socket("exit").allow = True
volume_sensor.region_object = self.exit_region
volume_sensor.bounds = self.bounds_type
volume_sensor.link_output(pfm_node, "satisfies", "actRegionSensor")
self._create_python_attribute(
pfm_node,
"camera",
target_object=self.entry_camera
)
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
exporter.mgr.add_object(pl=plSpawnModifier, so=so, name=self.key_name) # Not much to this modifier... It's basically a flag that tells the engine, "hey, this is a
# place the avatar can show up." Nice to have a simple one to get started with.
spawn = exporter.mgr.add_object(pl=plSpawnModifier, so=so, name=self.key_name)
@property @property
def requires_actor(self): def requires_actor(self):

19
korman/ui/modifiers/logic.py

@ -13,15 +13,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
from typing import *
if TYPE_CHECKING:
from ...properties.modifiers.logic import *
from .. import ui_list from .. import ui_list
class LogicListUI(bpy.types.UIList): class LogicListUI(bpy.types.UIList):
@ -43,18 +36,8 @@ def advanced_logic(modifier, layout, context):
layout.row().prop_menu_enum(logic, "version") layout.row().prop_menu_enum(logic, "version")
layout.prop(logic, "node_tree", icon="NODETREE") layout.prop(logic, "node_tree", icon="NODETREE")
def spawnpoint(modifier: PlasmaSpawnPoint, layout, context): def spawnpoint(modifier, layout, context):
layout.label(text="Avatar faces negative Y.") layout.label(text="Avatar faces negative Y.")
layout.separator()
col = layout.column()
col.prop(modifier, "entry_camera", icon="CAMERA_DATA")
sub = col.row()
sub.active = modifier.entry_camera is not None
sub.prop(modifier, "exit_region", icon="MESH_DATA")
sub = col.row()
sub.active = modifier.entry_camera is not None and modifier.exit_region is not None
sub.prop(modifier, "bounds_type")
def maintainersmarker(modifier, layout, context): def maintainersmarker(modifier, layout, context):
layout.label(text="Positive Y is North, positive Z is up.") layout.label(text="Positive Y is North, positive Z is up.")

1
release.ps1

@ -42,6 +42,7 @@ function Invoke-GitHubRequest($Method, $Uri, $Body, $ValidResults) {
-Uri "$Uri" ` -Uri "$Uri" `
-Method "$Method" ` -Method "$Method" `
-Headers $RequestHeaders ` -Headers $RequestHeaders `
-ContentType "application/json" `
-Body @(ConvertTo-JSON $Body) ` -Body @(ConvertTo-JSON $Body) `
-SkipHttpErrorCheck -SkipHttpErrorCheck
Test-GitHubResult ` Test-GitHubResult `

Loading…
Cancel
Save