Created
December 16, 2016 01:45
-
-
Save NiklasRosenstein/9801b159183ede6947d285efd29a3bc7 to your computer and use it in GitHub Desktop.
Sample plugin created by knickknack -- http://www.plugincafe.com/forum/forum_posts.asp?TID=7157
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum { | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Offset-Y Spline | |
Takes a child-spline as input and offset all its points on the y-axis by a specific value. Tangents are unaffected. | |
Usage Instructions | |
------------------ | |
1. Save in a file called OffsetYSpline.pyp | |
2. Locate it in the plugin folder and create the resources needed to have the offset value displayed as a parameter | |
with PY_OFFSETYSPLINE_OFFSET (ID 1000) in the Attribute Manager | |
3. Start Cinema | |
4. Create a generating spline | |
5. From the Plugin menu, select OffsetYSpline | |
6. Set the generating spline as input child of the OffsetySpline | |
""" | |
# =====================================================================================================================# | |
# Imports | |
# =====================================================================================================================# | |
import os | |
import c4d | |
PLUGIN_ID = 12345678 # change it with one obtained from PluginCafe | |
# =====================================================================================================================# | |
# Global Functions Definitions | |
# =====================================================================================================================# | |
# Global function responsible to set the close status of a spline | |
def SetClosed(spline, value): | |
if spline is not None: | |
spline.SetParameter(c4d.DescID(c4d.DescLevel(c4d.SPLINEOBJECT_CLOSED)), value, c4d.DESCFLAGS_SET_FORCESET) | |
spline.GetDataInstance().SetBool(c4d.SPLINEOBJECT_CLOSED, value) | |
return True | |
return False | |
# Global function responsible to check the close status of a spline | |
def IsClosed(spline): | |
if spline is None: | |
return False | |
if spline.GetCacheParent() is not None: | |
return spline.GetCacheParent().IsClosed() | |
else: | |
return spline.IsClosed() | |
# Global function responsible to copy the spline parameters across a source and a destination | |
def CopySplineParamsValue(sourceSpline, destSpline): | |
if sourceSpline is None or destSpline is None: | |
return False | |
sourceRealSpline = sourceSpline.GetRealSpline() | |
if sourceRealSpline is not None: | |
sourceRealSplineBC = sourceRealSpline.GetDataInstance() | |
if sourceRealSplineBC is not None: | |
destSpline.SetParameter(c4d.SPLINEOBJECT_INTERPOLATION, sourceRealSplineBC.GetInt32(c4d.SPLINEOBJECT_INTERPOLATION), c4d.DESCFLAGS_SET_FORCESET) | |
destSpline.SetParameter(c4d.SPLINEOBJECT_MAXIMUMLENGTH, sourceRealSplineBC.GetFloat(c4d.SPLINEOBJECT_MAXIMUMLENGTH), c4d.DESCFLAGS_SET_FORCESET) | |
destSpline.SetParameter(c4d.SPLINEOBJECT_SUB, sourceRealSplineBC.GetInt32(c4d.SPLINEOBJECT_SUB), c4d.DESCFLAGS_SET_FORCESET) | |
destSpline.SetParameter(c4d.SPLINEOBJECT_ANGLE, sourceRealSplineBC.GetFloat(c4d.SPLINEOBJECT_ANGLE), c4d.DESCFLAGS_SET_FORCESET) | |
return True | |
return False | |
# Global function responsible to return the final representation of the spline | |
def FinalSpline(source): | |
if source is None: | |
return None | |
# check is source can be threated as a spline | |
if (not source.IsInstanceOf(c4d.Oline)) and (not(source.GetInfo()&c4d.OBJECT_SPLINE)): | |
return None | |
if source.GetDeformCache() is not None: | |
# it seems it's never hit | |
source = source.GetDeformCache() | |
# return the spline is a procedural curve | |
if (not source.IsInstanceOf(c4d.Ospline)) and (source.GetRealSpline() is not None): | |
return source.GetRealSpline() | |
return source | |
# Global function responsible for modifying the spline | |
def OffsetSpline(inputSpline, offsetValue): | |
if inputSpline is None: | |
return None | |
inputML = inputSpline.GetMl() | |
# retrieve child points count and type | |
pointsCnt = inputSpline.GetPointCount() | |
tangentsCnt = 0 | |
splineType = 0 | |
if (inputSpline.GetType() == c4d.Ospline): | |
tangentsCnt = inputSpline.GetTangentCount() | |
splineType = inputSpline.GetInterpolationType() | |
# allocate the resulting spline | |
resSpline = c4d.SplineObject(pointsCnt, splineType) | |
if resSpline is None: | |
return None | |
# set the points position and tangency data | |
for i in range(pointsCnt): | |
#currPos = inputSpline.GetPoint(i) | |
currPos = inputML * inputSpline.GetPoint(i) | |
resSpline.SetPoint(i, c4d.Vector(currPos.x,currPos.y+offsetValue, currPos.z)) | |
# set in case the tangency data | |
if tangentsCnt != 0: | |
currTan = inputSpline.GetTangent(i) | |
resSpline.SetTangent(i, currTan["vl"], currTan["vr"]) | |
# return the computed spline | |
return resSpline | |
# Global function responsible to return the first enabled object in a hierarchy | |
def RecurseOnChild(op): | |
if op is None: | |
return None | |
child = op.GetDown() | |
if child is None: | |
return None | |
#skip deformers | |
isModifier = child.GetInfo()&c4d.OBJECT_MODIFIER | |
if isModifier: | |
return None | |
# check and return the first active child | |
if child.GetDeformMode(): | |
return child | |
else: | |
return RecurseOnChild(child) | |
# Global function responsible to recursively check the dirty flag in a hierarchy | |
def RecursiveCheckDirty(op): | |
res = 0 | |
if op is None: | |
return res | |
current = op | |
next = op.GetNext() | |
child = op.GetDown() | |
res += current.GetDirty(c4d.DIRTYFLAGS_DATA | c4d.DIRTYFLAGS_MATRIX) | |
if child is not None: | |
res += RecursiveCheckDirty(child) | |
if next is not None: | |
res += RecursiveCheckDirty(next) | |
return res | |
# =====================================================================================================================# | |
# Class Definitions | |
# =====================================================================================================================# | |
class OffsetYSpline(c4d.plugins.ObjectData): | |
def Init(self, op): | |
if op is None: | |
return False | |
bc = op.GetDataInstance() | |
if bc is None: | |
return False | |
bc.SetInt32(c4d.PY_OFFSETYSPLINE_OFFSET, 100) | |
self._countourChildDirty = -1 | |
self._childDirty = -1 | |
return True | |
def GetDimension(self, op, mp, rad): | |
mp.x = 0 | |
mp.y = 0 | |
mp.z = 0 | |
rad.x = 0 | |
rad.y = 0 | |
rad.z = 0 | |
if op is None: | |
return | |
bc = op.GetDataInstance() | |
if op.GetDown() is not None: | |
rad.x = op.GetDown().GetRad().x | |
rad.y = op.GetDown().GetRad().y | |
rad.z = op.GetDown().GetRad().z | |
mp.x = op.GetMg().off.x | |
mp.y = op.GetMg().off.y + bc.GetFloat(c4d.PY_OFFSETYSPLINE_OFFSET) | |
mp.z = op.GetMg().off.z | |
def CheckDirty(self, op, doc): | |
if op is None or doc is None: | |
return | |
childDirty = 0 | |
child = op.GetDown() | |
if child is not None: | |
childDirty = RecursiveCheckDirty(child) | |
if (childDirty != self._countourChildDirty): | |
op.SetDirty(c4d.DIRTYFLAGS_DATA) | |
self._countourChildDirty = childDirty | |
def GetVirtualObjects(self, op, hh): | |
if op is None or hh is None: | |
return | |
child = None | |
childSpline = None | |
resSpline = None | |
dirty = False | |
cloneDirty = False | |
temp = None | |
childDirty = -1 | |
cache = op.GetCache() | |
bc = op.GetDataInstance() | |
if bc is None: | |
return c4d.BaseObject(c4d.Onull) | |
offsetValue = bc.GetFloat(c4d.PY_OFFSETYSPLINE_OFFSET) | |
# look for the first enabled child in order to support hierarchical | |
child = RecurseOnChild(op) | |
if child is None: | |
self._childDirty = -1 | |
self._countourChildDirty = -1 | |
return c4d.BaseObject(c4d.Onull) | |
# Store now the closure state of the child cause child will be later on overwritten | |
isChildClosed = IsClosed(child.GetRealSpline()) | |
# Use the GetHierarchyClone and the GetAndCheckHierarchyClone to operate as a two-step | |
# GetHierarchyClone operates when passing a bool reference in the first step to check | |
# the dirtyness and a nullptr on a second step to operate the real clone | |
resGHC = op.GetHierarchyClone(hh, child, c4d.HIERARCHYCLONEFLAGS_ASSPLINE) | |
if resGHC is None: | |
resGHC = op.GetAndCheckHierarchyClone(hh, child, c4d.HIERARCHYCLONEFLAGS_ASSPLINE, False) | |
if resGHC is not None: | |
cloneDirty = resGHC["dirty"] | |
temp = resGHC["clone"] | |
dirty |= cloneDirty | |
# recursively check the dirty flag for the children (deformers or other generators) | |
if temp is not None and (temp.IsInstanceOf(c4d.Ospline) or (temp.GetInfo()&c4d.OBJECT_ISSPLINE) or temp.IsInstanceOf(c4d.Oline)): | |
childDirty = RecursiveCheckDirty(child) | |
if childSpline is None: | |
childSpline = temp | |
child.Touch() | |
dirty |= op.IsDirty(c4d.DIRTYFLAGS_DATA) | |
# compare the dirtyness of local and member variable and accordingly update the generator's | |
# dirty status and the member variable value | |
dirty |= self._childDirty != childDirty | |
self._childDirty = childDirty | |
if not dirty and cache is not None: | |
return cache | |
if childSpline is None: | |
return c4d.BaseObject(c4d.Onull) | |
#operate the spline modification | |
resSpline = OffsetSpline(FinalSpline(childSpline), offsetValue) | |
if resSpline is None: | |
return c4d.BaseObject(c4d.Onull) | |
# restore the closing status of the spline | |
SetClosed(resSpline, isChildClosed) | |
# copy the spline tags | |
childSpline.CopyTagsTo(resSpline, True, c4d.NOTOK, c4d.NOTOK) | |
# copy the spline parameters value | |
CopySplineParamsValue(childSpline, resSpline) | |
# notify about the generator update | |
resSpline.Message(c4d.MSG_UPDATE) | |
return resSpline | |
def GetContour(self, op, doc, lod, bt): | |
if op is None: | |
return None | |
if doc is None: | |
doc = op.GetDocument() | |
if doc is None: | |
return None | |
child = None | |
childSpline = None | |
resSpline = None | |
bc = op.GetDataInstance() | |
if bc is None: | |
return None | |
offsetValue = bc.GetFloat(c4d.PY_OFFSETYSPLINE_OFFSET) | |
child = RecurseOnChild(op) | |
if child is None: | |
self._childDirty = 0 | |
self._countourChildDirty = 0 | |
return None | |
# Store now the closure state of the child cause child will be later on overwritten | |
isChildClosed = IsClosed(child.GetRealSpline()) | |
# emulate the GetHierarchyClone in the GetContour by using the SendModelingCommand | |
temp = child | |
if temp is not None: | |
temp = temp.GetClone(c4d.COPYFLAGS_NO_ANIMATION) | |
if temp is None: | |
return None | |
result = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT, list = [temp], doc = doc) | |
if result[0] is not None and temp is not result[0]: | |
temp = result[0] | |
if (temp.GetType() == c4d.Onull): | |
result2 = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN, list = [temp], doc = doc) | |
if result2[0] is not None and temp is not result2[0]: | |
temp = result2[0] | |
if (temp is not None and (temp.IsInstanceOf(c4d.Ospline) or (temp.GetInfo()&c4d.OBJECT_SPLINE) or temp.IsInstanceOf(c4d.Oline))): | |
if childSpline is None: | |
childSpline = temp | |
if childSpline is None: | |
return None | |
#operate the spline modification | |
resSpline = OffsetSpline(FinalSpline(childSpline), offsetValue) | |
if resSpline is None: | |
return None | |
# restore the closing status of the spline | |
SetClosed(resSpline, isChildClosed) | |
# copy the spline parameters value | |
CopySplineParamsValue(childSpline, resSpline) | |
# notify about the generator update | |
resSpline.Message(c4d.MSG_UPDATE) | |
return resSpline | |
# =====================================================================================================================# | |
# Plugin registration | |
# =====================================================================================================================# | |
if __name__ == "__main__": | |
c4d.plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="OffsetYSpline", | |
g=OffsetYSpline, | |
description="OoffsetYSpline", icon=None, | |
info=c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT | c4d.OBJECT_ISSPLINE) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum { | |
PY_OFFSETYSPLINE_OFFSET = 1000 | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CONTAINER OoffsetYSpline { | |
INCLUDE Obase; | |
GROUP ID_OBJECTPROPERTIES { | |
REAL PY_OFFSETYSPLINE_OFFSET { UNIT METER; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
STRINGTABLE OoffsetYSpline { | |
PY_OFFSETYSPLINE_OFFSET "Offset"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The .py file should be renamed to .pyp so that C4D will load it as a plugin.
Here's a recommended directory structure from the C++ SDK.