A few Python Generator Spline Primitives - WIP


#1

Very much a part time project.

Using the Python Generator to produce to splines that are not in the C4D Toolset.

Tried to find a method of creating splines that would allow the same code/approach (essentially) to be used for various shapes. It starts as set of point coordinates, these are then controlled by users data and switches. Chamfers and Outlines then applied based on click boxes.

Would appreciate feedback from programmers if there are better ways to do this - or indeed if there are any obvious mistakes.

All have a rounding/chamfer amount control
All have switch to turn chamfering from round to flat
All have switches to turn on and off chamfering on various parts
All have an outline option, with variable offset
As its WIP - some limit checks, not foolproof

Arrow Spline - Square arrow tail can be taken into negative
Chevron - Can lock internal chevron point distance
Square - Individual corner radius

As I said - work in progress - if you find anything useful - needs correcting - or would like a specific shape, let me know.

Second image - all the same square spline generator


#2

Still - WIP

Another one - J spline


#3

hi,

they look nice, but the python generator sucks :smiley: transfer the code into an objectdata
plugin would be my first advise :slight_smile:


#4

As a part time project - taking these to a full plugin is beyond me.

It would be great to have a more experienced programmer show how to take one of these to a full plugin

i.e. without changing the workings - what is needed to turn say the J spline these into a plugin in its most basic form - so everyone can see the differences between the two methods.


#5

Hi Paul,

I just wanted to ask you a question.
What is it specifically about making a plugin that you find so hard to understand?
Is it by any chance the class structure?

-ScottA


#6

Hi Scott,

The whole structure of the class and reasoning behind the various files required - symbols, descriptions, strings (and their content) is a mystery.

self.xxxxx
appears to be static members of the class

main appears to have some path descriptions

why are there some declarations as @staticmethod

without taking a lot of these apart and comparing - can’t see the obvious (min) structure
sorry if its a long way of saying - yes its a mystery

If I could see what is specific to the structure and where the actual code drops in, what is required in the support files - that would be most enlightening.


#7

it is quite easy, you have just to overwrite the init method and the getvirtualobjects
method to get a working plugin. all other methods are just optional.

this is a working template.

Omyobjectplugin.pyp


import c4d
import math
import sys
import os
from c4d import plugins, utils, bitmaps, gui

PLUGIN_ID = 1000000 # get a propper id from plugincafe

class Omyobjectplugin(plugins.ObjectData):
	
	def Init(self, op):
		# init the values you defined in your res file
		self.InitAttr(op, float, [c4d.MYVALUE])
		# set a default value
		op[c4d.MYVALUE] = 10.0

		return True

	def Draw(self, op, type, bd, bh):
		return c4d.DRAWRESULT_OK

	def Message(self, node, type, data):
		return True	
		
	def GetVirtualObjects(self, op, hierarchyhelp):
		dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTY_DATA)
		if dirty is False: return op.GetCache(hierarchyhelp)
		
                #your code begins here

		return myResultBaseObject
    
if __name__ == "__main__":
    path, file = os.path.split(__file__)
    icon = bitmaps.BaseBitmap()
    icon.InitWith(os.path.join(path, "res", "Omyobjectplugin.tif"))
    plugins.RegisterObjectPlugin(id          = PLUGIN_ID,
								 str         = "MyObjectPlugin",
								 g           = Omyobjectplugin,
								 description = "Omyobjectplugin",
								 icon        = icon,
								 info        = c4d.OBJECT_GENERATOR)

the description system is also quite easy to understand. think of it as userdata split into
three files.

1.the .res file - here you define the interface elements itself, their layout and their flags (sliders, min / max values …). in the c++ documentation is a whole pages with examples
for each token and its flags.

  1. the header (.h) file. here you assign the ids to your descption elements defined in the res file

  2. the .str file(s), here you define the the strings displayed next to your description elements.

to complete my fake example :

the folder structure


[B]+Omyobjectplugin
    +res
        +descriptions[/B]
               Omyobjectplugin.res
               Omyobjectplugin.h
        [B]+strings_us[/B]
                Omyobjectplugin.str (english)
        [B]+strings_de[/B]
                 Omyobjectplugin.str (optional language)
          c4d_symbols.h (just copy&paste from one of the example plugins)
          Omyobjectplugin.tif                  
     Omyobjin.pyp

Omyobjectplugin.res


// the container for your plugin
CONTAINER Omyobjectplugin 
{
    NAME Omyobjectplugin;
    // include the standard tabs every object has - coordinates , editor color...
    INCLUDE Obase;
	
    // exclude (hide) some elements form our previous include statement
    HIDE ID_BASEOBJECT_USECOLOR;
    HIDE ID_BASEOBJECT_COLOR;
    SHOW ID_BASEOBJECT_GENERATOR_FLAG;
	
    // group token awithin the container are tabs and should be named
    GROUP SOME_GRP  
    {
      // the tab is selected when the object has been inserted into the document	
      DEFAULT 1; 
	  // in this group we have 2 elements in each row
	  COLUMNS 2 ;
	  
	  // our float value, it is quite the same as for userdata , just without a gui	
          REAL  MYVALUE   { UNIT PERCENT; MIN 0; MAX 1000000.0; MINSLIDER 0.0; MAXSLIDER 1000.0; STEP 5; CUSTOMGUI REALSLIDER; }
	  
	  // an unnamed subgroup, doesn't need to appear in the .h and .str files. doesn't have a visual representation in c4d, but you can create layout grups with it
	  GROUP
	  {
	  
	  }
	  // a named subgroup appears as foldable group in a tab like the userdata groups
	  GROUP NAMEDSUB_GRP
	  {
	  
	  }

    }
}  

Omyobjectplugin.h

#ifndef _OMYOBJECTPLUGIN_H_
#define _OMYOBJECTPLUGIN_H_

// well, your ids, should start somewhere from 1000, but you could also start from 3485364856438
enum
{
    Omyobjectplugin                               = 10000,
    // our tab
    OME_GRP                                       = 1000 ,
    // our value
    MYVALUE                                       = 1001 ,
    // our named sub group
    NAMEDSUB_GRP                                  = 1002 
};
#endif

Omyobjectplugin.str

STRINGTABLE Omyobjectplugin 
{
    Omyobjectplugin                               "myobjectplugin";
    // our tab
    OME_GRP                                       "Tab Title";
    // our value
    MYVALUE                                       "Meaningfull stuff";
    // our named sub group
    NAMEDSUB_GRP                                  "SubGroup Title" ;
}

#8

some functions are static that they can maintain their value(s) from call to call.
self is a pointer to the current instance of a class. you have got a class myclass
and a method myfunction which uses self. self points to the instance of the class
myclass form which myfunction has been called, so that you can use some data
stored within this class.
in python you have the special case that each method of a class has to pass a
pointer to the instance of its class from which is has been called as its first argument.


#9

Here’s an example of static & non static methods written as a script.
Sometimes it helps to see them next to each other to see the differences.

#A static method is called from the parent class it was defined in
 
 import c4d
 
 class MyClass1():
 	def myMethod(self,arg):   #This is a standard method...Note how it needs the word 'self' in it
 		print arg
 
 class MyClass2():			 #This is a static method...Note there is no word 'self' used in it
 	@staticmethod
 	def myStaticMethod(x):
 		print x
 
 class Math():
 	@staticmethod			 #This is a static method...Note there is no word 'self' used in it
 	def add(arg1, arg2):
 		x = arg1
 		y = arg2
 		return x+y
 
 def main():
 
   #Calling the MyClass1() class
 	s = "Hello"
 	mc = MyClass1()				  #Note: We first create a copy of the class
 	mc.myMethod(s)				   #Then: We use that copy to call to a method
 
 
   #Calling the MyClass2() class
 	s = "Hello Again"
 	MyClass2.myStaticMethod(s)	   #Note: With static. We don't need to create a class instance 
 
 
   #Calling the Math() class
 	first = 0
 	second = 5
 	print Math.add(first,second)	 #Note: With static. We don't need to create a class instance   
 
 if __name__=='__main__':
 	main()

-ScottA


#10

Hey Guys,

Really appreciate the help

I need a little while to work though

many thanks

Paul


#11

I’ve tried copying the code and running that as a new plugin but it throws various errors.

I think line 19 Omyobjectplugin.res should be commented out?
Otherwise it doest load

after that I get repeating errors

Symbol ‘Omyobjectplugin’ not found
File ‘/Applications/MAXON/CINEMA 4D R13/plugins/Omyobjectplugin/res/description/omyobjectplugin.res’
Line 4

Symbol ‘Omyobjectplugin’ not found
File ‘/Applications/MAXON/CINEMA 4D R13/plugins/Omyobjectplugin/res/description/omyobjectplugin.res’
Line 14

?

I know its a long shot …
but if someone could take the J spline Generator code and put it into a plugin,
having that to refer to would be most educational


#12

Here’s a little starter plugin for you to get up and running.
I set up a few GUI controls. But you’ll have to create the rest of them as you like.
There’s more than one way to create these plugins. So try not to get confused when you see some people doing it differently. This is just the way I tend to make them.

Note: Be aware that when working with descriptions in .res files. If you make a mistake editing it. You’ll sometimes need to delete the coffeesymbolcache file. Or the plugin might not load the res file. Even if you’ve fixed the code and the code in it is proper.
This is a big issue in R12 that catches many people…I’m not sure if it still happens in R13.

-ScottA


#13

As ever - thanks Scott

Had to start work early this morning. So I’ve only had a quick look. But it’s obvious already that this is exactly what I needed. Seeing where the various parts exist in a plugin helps so much - a great reference file and another leg up the learning ladder.

Last Q - (The SDK doesnt explain - just has example text)

In the plugins.RegisterObjectPlugin(…
What is/which file, is the ‘g’ referring to, as there are many files starting with MySplineObject
g=MySplineObject

And
info=c4d.OBJECT_GENERATOR
Is there a list of ‘info’ types?


#14

hi,

sorry for the errros, just took the pyp file from an existing plugin and cropped it and wrote the
res, h and str files from scratch in the cgtalk editor to match the pyp file. i guess there is a
typo somewhere.

to your last question - g is your plugins class. for more complex plugins your class name and
the description file names might differ. the c4d.OBJECT_GENERATOR flag states that the plugin
is a generator plugin. it could also be a modifier plugin or more. for an extensive list read the
respective part of the sdk documentation.

http://www.thirdpartyplugins.com/python/modules/c4d.plugins/index.html#c4d.plugins.RegisterObjectPlugin


#15

Hi LD

Its all been a great help. Hopefully others will benefit as well.

Appreciate the time and effort you guys put in to help others.

atb

Paul


#16

Since you are making a spline object that
you control from the Plugin AM, using GetContour()
instead of GetVirtualObjects() would be a better way
as it then is a “real” spline seen by HairShader, Cloner etc

Check example:
JSplineContour

Note: it uses the same Plugin ID as the scott example.
You also might need to trash the symbolcache file in user prefs folder
before re-starting Cinema.

Cheers
Lennart


#17

Cheers Lennart

I’ll be taking a look at this as soon as I finish work

thanks a bunch

Paul


#18

Getting there

Where can I find the info for setting up the res file info

ie

REAL OUTLINE_OFFSET { UNIT METER; MIN 0.0; CUSTOMGUI REALSLIDER; STEP 0.01; MINSLIDER 0.0; MAXSLIDER 100.0;}

For a checkbox I guessed
BOOL CHAMFER_BASE { }

where is the info on gadget parameter settings / syntax /options for python?


#19

res, str and h files are the same for COFFEE, C++ and Python plugins.
Open the corresponding res/str/h file for any object/tag you want to mimic
I’d say it’s the best way to see how to format them.
Otherwise I think the description of them is best in the old COFFEE 9.5 SDK
but again seeing them from functional object are the better way to understanding.

Cheers
Lennart


#20

Rui Mac wrote a COFFEE book that explains this pretty well.
I’d check out his book on his site.