How to allocate a gradient in Python?

On 07/06/2013 at 03:24, xxxxxxxx wrote:

Hi,

I try to transform the gradient shader example from the C++ SDK to Python.
I have the basic template, the plugin starts, the gui elements are there and I can set and read most of the parameters except of the gradient itself. Cause I can't figure out how to allocate a gradient to use and manipulate the corresponding gui element with Gradient.SetKnot() and Gradient.GetKnot() and so on.

So please, what is the Python equivalent to this C++ method?

  
    Gradient*     gradient;   
    AutoAlloc<Gradient> gradient;   

I guess it will be something with:

c4d.gui.GradientCustomGui

By the way, my gui element for the gradient is:

  
    GRADIENT PY_GRADIENTSHADER_GRADIENT { COLOR; ICC_BASEDOCUMENT; }   

If this helps for a code example to show how to access it.

It can't be that hard, can it? ;)

Kind regards,
Tom

On 07/06/2013 at 08:26, xxxxxxxx wrote:

Hi Phoenix,

well, there's the c4d.Gradient class. :)

-Niklas

On 07/06/2013 at 10:01, xxxxxxxx wrote:

Originally posted by xxxxxxxx

Hi Phoenix,

well, there's the c4d.Gradient class. :)

-Niklas

Ahm yeah, so what?
I know that but unfortunately it doesn't really help me.

How should it be used in practice?

There is also a   Gradient.\__init\_\_()   function in it, but I don't know how to use it either.
It is not exactly described in detail in the Python SDK documentation and also without an example how to use it.

I'm at the very beginning and have only some basic Python coding knowledge and even less C4D Python plugin coding knowledge, you know?

That's the problem.

I try to learn it but it's not so easy with the lack of information out there how to do things for c4d with it.

And the python sdk docu is also not so easy to understand.
At least for a beginner like me!

Of course I also searched for informations in the internet and learned and accomplished a lot but without remarkable success for this specific problem.
The most results therefore are mainly just mirrors of the SDK docu. :angry:

And the best results I found about this topic here at the forum are these two:

Editing Material Gradients in R13

SetKnot to a gradient does not work properly

But also not exactly what I'm looking for cause they are both just about the manipulation of still existing shaders in a material channel.
But I focus on creating the shader itself in an arbitrary channel of a material.
So I have to allocate it at first myself inside the plugin, if you know what I mean.

What I know is, it doesn't work like this:

gradient = c4d.Gradient()

or this:

gradient = c4d.Gradient(c4d.PY_GRADIENTSHADER_GRADIENT) 

Maybe it's something more complicated like:

  
    
    
    class Gradient(c4d.Gradient):  
      
        def __init__() :  
           _some code_  
            return _gradient_  
      
        def GetKnot() :  
            _some code_  
            return _?_  
      
        def SetKnot() :  
            _some code_  
            return _?_  
    

But this is just a riddle game for me and I don't know how to do and use it!

So please show me some working example code to learn from and understand the deeper principles.

Kind regards,
Tom

On 07/06/2013 at 11:45, xxxxxxxx wrote:

Hi,
I haven't created any Python shader plugins yet. Only in C++. So I don't know if this will work.
But I think you can create the gradient using the Xgradient constant.

This is a script. I don't know if it also works in plugin form:

import c4d  
def main() :  
  mat = c4d.BaseMaterial(c4d.Mmaterial)  
  sha = c4d.BaseList2D(c4d.Xgradient)  
  mat[c4d.MATERIAL_COLOR_SHADER] = sha  
  mat.InsertShader( sha )  
    
  mat.Message( c4d.MSG_UPDATE ) #Update the material  
  mat.Update( True, True )              
  doc.InsertMaterial( mat )     #Insert the material into the document      
    
  c4d.EventAdd()  
  
if __name__=='__main__':  
  main()

-ScottA

On 07/06/2013 at 12:04, xxxxxxxx wrote:

he is talking about the customdatatype and not the xshader type. x = c4d.gradient() is the 
correct way to instantiate a gradient.

On 07/06/2013 at 15:25, xxxxxxxx wrote:

If you manage to get it working. Could you please post an example?
I can't even get the stupid gradient gizmo to show up. :/

-ScottA

On 07/06/2013 at 16:19, xxxxxxxx wrote:

Originally posted by xxxxxxxx

Originally posted by xxxxxxxx

Hi Phoenix,

well, there's the c4d.Gradient class. :)

-Niklas

Ahm yeah, so what?
I know that but unfortunately it doesn't really help me.

How should it be used in practice?

There is also a   Gradient.\__init\_\_()   function in it, but I don't know how to use it either.
It is not exactly described in detail in the Python SDK documentation and also without an example how to use it.

I'm at the very beginning and have only some basic Python coding knowledge and even less C4D Python plugin coding knowledge, you know?

That's the problem.

I try to learn it but it's not so easy with the lack of information out there how to do things for c4d with it.

And the python sdk docu is also not so easy to understand.
At least for a beginner like me!

Of course I also searched for informations in the internet and learned and accomplished a lot but without remarkable success for this specific problem.
The most results therefore are mainly just mirrors of the SDK docu. :angry:

And the best results I found about this topic here at the forum are these two:

Editing Material Gradients in R13

SetKnot to a gradient does not work properly

But also not exactly what I'm looking for cause they are both just about the manipulation of still existing shaders in a material channel.
But I focus on creating the shader itself in an arbitrary channel of a material.
So I have to allocate it at first myself inside the plugin, if you know what I mean.

What I know is, it doesn't work like this:

gradient = c4d.Gradient()

or this:

gradient = c4d.Gradient(c4d.PY_GRADIENTSHADER_GRADIENT) 

Maybe it's something more complicated like:

 
   
   
   class Gradient(c4d.Gradient):  
     
       def __init__() :  
          _some code_  
           return _gradient_  
     
       def GetKnot() :  
           _some code_  
           return _?_  
     
       def SetKnot() :  
           _some code_  
           return _?_  
   

But this is just a riddle game for me and I don't know how to do and use it!

So please show me some working example code to learn from and understand the deeper principles.

Kind regards,
Tom

Hi Phoenix,

did you actually try the first snippet you have shown? Because it is correct:

> gradient = c4d.Gradient()

Sure, it's not easy for a beginner for to understand things quickly. I didn't know you where at a
very beginning state with Python. Even though, I think you understand the principle of creating
an instance of a class, therefore a simply google on "how do I create an instance of a class in
Python" would help you understand. Also checking the official Python tutorial is a must for every
beginner (which is the least place you find all the information you need, packed into one tutorial
that is easy to follow).

Things like __init__() etc. are also worth a shot for Google.

-Niklas

On 08/06/2013 at 02:39, xxxxxxxx wrote:

Originally posted by xxxxxxxx

If you manage to get it working. Could you please post an example?
I can't even get the stupid gradient gizmo to show up. :/

-ScottA

not sure where the problem is, it is nothing special, maybe i am missing something here.
the whole thing is meant to be used in a python tag which has a gradient as its first user 
data. the gradient is overwritten on each pass for the standard 3 sec document, resulting
in a kind of a black to white animation for that span.

import c4d
#Welcome to the world of Python
  
def main() :
    t = doc.GetTime().Get() * (1/3)
    grad = c4d.Gradient()
    grad.InsertKnot(col = c4d.Vector(t),
                    pos = 0.5)
    op[c4d.ID_USERDATA,1] = grad

On 08/06/2013 at 05:23, xxxxxxxx wrote:

@littledevil:
Thank you, so my first idea was correct indeed.

So it is:

gradient = c4d.Gradient()

But by the way, be careful, of course you have to respect the case sensitivity! Wink

The problem was in fact introduced in another part of the source code and I only thought my allocation failed. :smile:

@ScottA:
Here it is (it's nearly complete but there are some problems with the Output() left) :

  
#############################################################  
## CINEMA 4D Python Gradient Shader                        ##  
#############################################################  
## (c) 2013 Thomas Chen, all rights reserved               ##  
#############################################################  
## Python-Gradient.pyp                                     ##  
#############################################################  
  
import os  
import math  
  
import c4d  
from c4d import plugins, bitmaps, utils, storage, gui  
from c4d.utils.noise import Turbulence  
  
#warning Please obtain your own plugin ID from http://www.plugincafe.com  
PLUGIN_ID = 1000001  
  
  
class SDKGradientClass(plugins.ShaderData) :  
  
  cycle = False  
  mode = 0  
  angle = 0.0  
  c = [0.0, 0.0, 0.0, 0.0]        # maybe not optimal?  
  sa = 0.0  
  ca = 0.0      
  
  turbulence = 0.0  
  octaves = 5.0  
  scale = 1.0  
  freq = 1.0  
  absolute = False  
  
  gradient = c4d.Gradient()  
  
  
  def Init(self, node) :  
      #Called when a new instance of the node plugin has been allocated.  
  
      k1_col = c4d.Vector(0.0, 0.0, 1.0)  
      k1_pos = 0.0  
  
      k2_col = c4d.Vector(1.0, 1.0, 1.0)  
      k2_pos = 1.0  
  
      self.gradient.InsertKnot(col = k1_col, pos = k1_pos)  
      self.gradient.InsertKnot(col = k2_col, pos = k2_pos)  
  
      node[c4d.SDKGRADIENTSHADER_COLOR] = self.gradient  
      node[c4d.SDKGRADIENTSHADER_CYCLE] = self.cycle  
      node[c4d.SDKGRADIENTSHADER_MODE] = self.mode  
      node[c4d.SDKGRADIENTSHADER_ANGLE] = self.angle  
  
      node[c4d.SDKGRADIENTSHADER_TURBULENCE] = self.turbulence  
      node[c4d.SDKGRADIENTSHADER_OCTAVES] = self.octaves  
      node[c4d.SDKGRADIENTSHADER_SCALE] = self.scale  
      node[c4d.SDKGRADIENTSHADER_FREQ] = self.freq  
      node[c4d.SDKGRADIENTSHADER_ABSOLUTE] = self.absolute  
  
      return True  
  
  
  def InitRender(self, sh, irs) :  
      #Precalculate any data for rendering.  
  
      self.mode = sh[c4d.SDKGRADIENTSHADER_MODE]  
      self.angle = sh[c4d.SDKGRADIENTSHADER_ANGLE]  
      self.cycle = sh[c4d.SDKGRADIENTSHADER_CYCLE]  
      self.turbulence = sh[c4d.SDKGRADIENTSHADER_TURBULENCE]  
      self.octaves = sh[c4d.SDKGRADIENTSHADER_OCTAVES]  
      self.scale = sh[c4d.SDKGRADIENTSHADER_SCALE]  
      self.freq = sh[c4d.SDKGRADIENTSHADER_FREQ]  
      self.absolute = sh[c4d.SDKGRADIENTSHADER_ABSOLUTE]  
      self.gradient = sh[c4d.SDKGRADIENTSHADER_COLOR]  
#        if !self.gradient or !self.gradient.InitRender(irs) : return c4d.INITRENDERRESULT_OUTOFMEMORY            # SYNTAX ERROR! "if !var" doesn't exist in Python! But it's "just" a safety check and it should work without it.  
  
      self.sa, self.ca = utils.SinCos(self.angle)  
  
      for i in range (0, 4) :  
          self.c[i] = 0.0  
          k = self.gradient.GetKnot(i)        # maybe Gradient.GetKnot(index) is wrong here, but Gradient.GetRenderKnot(index) seem not to exist in the Python SDK.  
          if (k) : self.c[i] = k['col']  
  
      return 0  
  
  
  def FreeRender(self, sh) :  
      #Free any resources used for the precalculated data from InitRender()  
      if (self.gradient) : self.gradient.FreeRender()  
      self.gradient = None  
  
  
  def Output(self, sh, cd) :  
      #Called for each point of the visible surface of a shaded object. Here you should calculate and return the channel color for the point cd.p.  
  
      p = cd.p  
      r = 0.0  
  
      if (self.turbulence > 0.0) :  
          scl = 5.0 * self.scale  
          tt = cd.t * self.freq * 0.3  
  
          res = c4d.Vector(Turbulence(p * scl, tt, self.octaves, True), Turbulence((p+c4d.Vector(0.34, 13.0, 2.43)) * scl, tt, self.octaves, True), 0.0)  
  
          if self.absolute:  
              p.x = utils.MixNum(p.x, res.x, self.turbulence)  
              p.y = utils.MixNum(p.y, res.y, self.turbulence)  
          else:  
              p.x += (res.x - 0.5) * self.turbulence  
              p.y += (res.y - 0.5) * self.turbulence  
  
      #rotation  
      p.x -= 0.5  
      p.y -= 0.5  
  
      xx = self.ca * p.x - self.sa * p.y + 0.5  
      yy = self.sa * p.x + self.ca * p.y + 0.5  
  
      p.x = xx  
      p.y = yy  
  
      if (self.mode <= c4d.SDKGRADIENTSHADER_MODE_CORNER) and self.cycle and (cd.texflag & c4d.TEX_TILE) :  
          if cd.texflag is c4d.TEX_MIRROR:  
              p.x = p.x % 2.0  
              if p.x >= 1.0: p.x = 2.0 - p.x  
  
              p.y = p.y % 2.0  
              if p.y >= 1.0: p.y = 2.0 - p.y  
  
          else:  
              p.x = p.x % 1.0  
              p.y = p.y % 1.0  
  
      if self.mode is c4d.SDKGRADIENTSHADER_MODE_U:  
          r = p.x  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_V:  
          r = 1.0 - p.y  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_DIAGONAL:  
          r  = (p.x + p.y) * 0.5  
   
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_RADIAL:  
          p.x -= 0.5  
          p.y -= 0.5  
          if p.x == 0.0: p.x = 0.00001  
  
          angle = math.atan(p.y / p.x)  
          if p.x < 0.0: angle += math.pi  
          if angle < 0.0: angle += (math.pi * 2.0)  
          r  = angle / (math.pi * 2.0)  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_CIRCULAR:  
          p.x -= 0.5  
          p.y -= 0.5  
          r = math.sqrt(p.x * p.x + p.y * p.y) * 2.0  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_BOX:  
          p.x = abs(p.x - 0.5)  
          p.y = abs(p.y - 0.5)  
          r = max(p.x, p.y) * 2.0  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_STAR:  
          p.x = abs(p.x - 0.5) - 0.5  
          p.y = abs(p.y - 0.5) - 0.5  
          r = math.sqrt(p.x * p.x + p.y * p.y) * 1.4142  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_CORNER:  
          cx = utils.FCut(p.x, 0.0, 1.0)  
          ca = utils.MixVec(self.c[0], self.c[1], cx)  
          cb = utils.MixVec(self.c[2], self.c[3], cx)  
            
          return utils.MixVec(ca, cb, utils.FCut(p.y, 0.0, 1.0))  
  
      return self.gradient.CalcGradientPixel(utils.FCut(r, 0.0, 1.0))         # something is wrong here, because "self.gradient" seems black end empty and so is of course the output  
  
  
def RegisterSDKGradientClass() :  
    
  fn = os.path.join(os.path.dirname(__file__), "res", "gradienttypes.tif")  
  bmp = bitmaps.BaseBitmap()  
  if c4d.IMAGERESULT_OK != bmp.InitWith(fn)[0]: return false  
  
  gui.RegisterIcon(200000135, bmp, 0*32, 0, 32, 32)  
  gui.RegisterIcon(200000136, bmp, 1*32, 0, 32, 32)  
  gui.RegisterIcon(200000137, bmp, 2*32, 0, 32, 32)  
  gui.RegisterIcon(200000138, bmp, 3*32, 0, 32, 32)  
  gui.RegisterIcon(200000139, bmp, 4*32, 0, 32, 32)  
  gui.RegisterIcon(200000140, bmp, 5*32, 0, 32, 32)  
  gui.RegisterIcon(200000141, bmp, 6*32, 0, 32, 32)  
  gui.RegisterIcon(200000142, bmp, 7*32, 0, 32, 32)  
    
  return plugins.RegisterShaderPlugin(PLUGIN_ID, "Python Gradient", 0, SDKGradientClass, "xsdkgradient", 0)  
  
if __name__ == '__main__':  
  RegisterSDKGradientClass()  
  

It is (should be) as close as I was able to do it on the C++ gradient shader example.
And also the resources are the same, so you could take it directly from the C++ SDK example.

The problems, as far as I could locate them are marked in the source code.
Maybe one of you could figure them out and tell me how to fix them completely! ;)

Also if you see something what could/should have been done in a other/better way, please tell me.
As I told before, I'm just learning the whole Python stuff. ;)

Kind regards,
Tom

On 08/06/2013 at 05:31, xxxxxxxx wrote:

Python doesn't have a ! operator, it's "not".

On 08/06/2013 at 08:33, xxxxxxxx wrote:

The problem I'm having isn't the code. It's with the layout structure of a shader plugin.
For some reason. I cannot get the plugin to load the Gradient gizmo from the .res file.

I've got the correct .res file name in the registration. Everything has an ID#. And I have all the text constant assigned to them in the .str file.
I don't usually have much trouble setting up a plugin's framework. But this one is being real P.I.A. for some reason.

Also. None of the shader plugin examples in the python SDK use a .res file for me to see what I'm doing wrong. I hate it when they do that and don't provide a .res based example.
That's really annoying.

-ScottA

Edit:
Are you guys using R14?
The gradient gimo refuses to show up in my plugin. And I'm starting to wonder if the reason I can't get it to show up is because it's not supported in R13?

Here is my .res file:

CONTAINER myshader  
{  
  NAME myshader;  
  INCLUDE Mpreview;  //The preview window gizmo... Works fine  
  INCLUDE Xbase;     //The Blurr sliders gizmos... works fine  
  
  
  GRADIENT MY_GRADIENT { ICC_BASEDOCUMENT; }  //<--does not work!!!!  
  
}

I'm banging my head trying to figure out why this stupid thing will not show up.

-ScottA

On 08/06/2013 at 11:24, xxxxxxxx wrote:

Doh!
I figured out the problem.
This has to in the .res file for the gradient gizmo to show up:   INCLUDE xsdkgradient;

-ScottA

On 08/06/2013 at 11:51, xxxxxxxx wrote:

Originally posted by xxxxxxxx

Python doesn't have a ! operator, it's "not".

Or you can do it with exception handling (with try: and except ) as far as I know.
I'm not just yet sure what would be the best solution for this situation. :smile:

Originally posted by xxxxxxxx

The problem I'm having isn't the code. It's with the layout structure of a shader plugin.
For some reason. I cannot get the plugin to load the Gradient gizmo from the .res file.

I've got the correct .res file name in the registration. Everything has an ID#. And I have all the text constant assigned to them in the .str file.
I don't usually have much trouble setting up a plugin's framework. But this one is being real P.I.A. for some reason.

Also. None of the shader plugin examples in the python SDK use a .res file for me to see what I'm doing wrong. I hate it when they do that and don't provide a .res based example.
That's really annoying.

-ScottA

Edit:
Are you guys using R14?
The gradient gimo refuses to show up in my plugin. And I'm starting to wonder if the reason I can't get it to show up is because it's not supported in R13?

Here is my .res file:

CONTAINER myshader  
{  
  NAME myshader;  
  INCLUDE Mpreview;  //The preview window gizmo... Works fine  
  INCLUDE Xbase;     //The Blurr sliders gizmos... works fine  
 
 
  GRADIENT MY_GRADIENT { ICC_BASEDOCUMENT; }  //<--does not work!!!!  
 
}

I'm banging my head trying to figure out why this stupid thing will not show up.

-ScottA

Might be a problem with the symbol cache file(s) from C4D where it stores all the keywords from C.O.F.F.E.E. and Python plugins if they had been recogniced by C4D earlier.

And that can cause serious trouble when you are under development and make changes to the resource files. And yes, it's quite tricky to find out. I know what I'm talking about. :wink:

For this reason I made me a little script for my development c4d installation(s) (yes, I use seperate copies of my C4D Releases configured for development purposes only) that delete this symbolcache file at every startup (it will become automatically recreated by C4D itself when it doesn't exist anymore but now with the fresh and current valid values ).

The script have to be named "python_init.py" and it belongs to "{USER_FOLDER}/prefs/python/" to be executed on startup of CINEMA 4D.

And here is mine: :wink:

  
##################################  
## python_init.py               ##  
##################################  
## created 2013 by Thomas Chen  ##  
##################################  
  
import c4d  
import os  
  
#================================================================================  
# Removes the symbol cache file in case it exist.                           #====  
# (THIS IS FOR DEVELOPMENT PURPOSES ONLY, NOT FOR REGULAR USE OF C4D!!!)    #====  
#================================================================================  
path = c4d.storage.GeGetStartupWritePath() + "\prefs\symbolcache"           #====  
if os.path.exists(path) :                                                    #====  
  print path                                                              #====  
  os.remove(path)                                                         #====  
  print "symbolcache removed!"                                            #====  
else: print "there is no symbolcache!"                                      #====  
#================================================================================  
#================================================================================  

Till now I just handle the file "symbolcache" that way.

There is also a "directorycache" file, that could also be deleted.
But I hadn't recognize problems related to it yet.

And I can remeber a "coffeesymbolcache" file but I guess that was just the name in earlier releases before c4d had Python support too.

Feel free to use it and I hope it helps. :slightly_smiling_face:

Kind regards,
Tom

On 08/06/2013 at 13:02, xxxxxxxx wrote:

Thanks Tom.
But I figured it out. And got it working.

I looked at your code.
You did a good job converting it to Python. But the problem you're having with the turbulence looks like a conversion problem from C++ Python.
I guess you can't use commas with multiple variables the same way in Python as it's written in C++.

Try this code out and you should get a result from the turbulence gizmo value:

      
Comment out this sucker  
#res = c4d.Vector(Turbulence(p * scl, tt, self.octaves, True), Turbulence((p+c4d.Vector(0.34, 13.0, 2.43)) * scl, tt, self.octaves, True), 0.0)  
  
And use this instead:  
  turb = Turbulence(p * scl, self.octaves, True)  
  res = c4d.Vector(turb, 0, 0 )

That should give you a similar kind of turbulence result as the C++ plugin. But not exactly though.
What I would do is write those three turbulence's out as three separate variables. Doing them one at a time. And watching the python console for errors.

Then when you have them all working without any errors.
Plug them into your res variable. Like this: res = c4d.Vector(turb1, turb2, turb3)

-ScottA

On 08/06/2013 at 16:27, xxxxxxxx wrote:

Originally posted by xxxxxxxx

Thanks Tom.
But I figured it out. And got it working.

Originally posted by xxxxxxxx

This has to in the .res file for the gradient gizmo to show up:   INCLUDE xsdkgradient;

Sorry, but I think there you are mistaken.

Okay I indeed used the xsdkgradient.h, xsdkgradient.res and xsdkgradient.str from the original C++ SDK gradient example, but very well-considered and only to stay as close as possible to that certain example.
So that it will be as comparable as it gets in the end.

But it is absolutely not required for that task.
You could also build up the whole resources stuff by yourself.

It really sounds to me like the symbol cache problem I mentioned above!

So please do me a favor, search for the "symbolcache" file in the C4D "{USER_FOLDER}/prefs/", delete it and then try your old method without the extra include again.

At least try it and then tell me about it, okay? :wink:
(Of course there could also be another problem, but I'm quite sure that's it in your case, really!)

Originally posted by xxxxxxxx

I looked at your code.
You did a good job converting it to Python.

Oh, thank you very much! :blush:

Originally posted by xxxxxxxx

But the problem you're having with the turbulence looks like a conversion problem from C++ Python.
I guess you can't use commas with multiple variables the same way in Python as it's written in C++.

Hey, great find! You're absolutely right. :clap:
As a matter of fact I hadn't checked and compared the parameter list of this function implementation in both coding languages but merely changed the syntax to Python.
That was a bit careless from me and indeed there is a little difference. :joy:

That's the original C++ code line:

  
res = Vector(Turbulence(p*scl,tt,gdata.octaves,TRUE),Turbulence((p+Vector(0.34,13.0,2.43))*scl,tt,gdata.octaves,TRUE),0.0);  

And so it have to look like in Python:

  
res = c4d.Vector(Turbulence(p * scl, self.octaves, True, tt), Turbulence((p+c4d.Vector(0.34, 13.0, 2.43)) * scl, self.octaves, True, tt), 0.0)  

Or to make it short: the time parameter of the C4DNoise.Turbulence() function simply has to jump from the middle of the list to the end. ;)
 
But of course that wasn't the reason for the main problem.
The real trouble came from the one line that I temporarily commented out and dismissed overhasty as "just" a safety check.

So yes, the code is complete now and works, as it should. :slightly_smiling_face:

Unfortunately I have to admit, that I still couldn't figure out what exactly this line is for and why it's so essential?
Obviously the important element here have to be the "gradient.InitRender(irs)" because the gradient itself should normally always be true.

But as I said, it's so far complete now and should be working as expected (or at least I hope so :wink:) and so here it is again: :slightly_smiling_face:

  
#############################################################  
## CINEMA 4D Python Gradient Shader                        ##  
## [1:1 conversion from the analogical C++ SDK example]    ##  
## [originally done by the MAXON Computer GmbH]            ##  
#############################################################  
## (c) 2013 Thomas Chen, all rights reserved               ##  
#############################################################  
## Python-Gradient.pyp                                     ##  
#############################################################  
  
import os  
import math  
  
import c4d  
from c4d import plugins, bitmaps, utils, storage, gui  
from c4d.utils.noise import Turbulence  
  
#warning Please obtain your own plugin ID from http://www.plugincafe.com  
PLUGIN_ID = 1000001  
  
  
class SDKGradientClass(plugins.ShaderData) :  
  
  cycle = False  
  mode = 0  
  angle = 0.0  
  c = [0.0, 0.0, 0.0, 0.0]        # maybe not optimal?  
  sa = 0.0  
  ca = 0.0      
  
  turbulence = 0.0  
  octaves = 5.0  
  scale = 1.0  
  freq = 1.0  
  absolute = False  
  
  gradient = c4d.Gradient()  
  
  
  def Init(self, node) :  
      #Called when a new instance of the node plugin has been allocated.  
  
      k1_col = c4d.Vector(0.0, 0.0, 1.0)  
      k1_pos = 0.0  
  
      k2_col = c4d.Vector(1.0, 1.0, 1.0)  
      k2_pos = 1.0  
  
      self.gradient.InsertKnot(col = k1_col, pos = k1_pos)  
      self.gradient.InsertKnot(col = k2_col, pos = k2_pos)  
  
      node[c4d.SDKGRADIENTSHADER_COLOR] = self.gradient  
      node[c4d.SDKGRADIENTSHADER_CYCLE] = self.cycle  
      node[c4d.SDKGRADIENTSHADER_MODE] = self.mode  
      node[c4d.SDKGRADIENTSHADER_ANGLE] = self.angle  
  
      node[c4d.SDKGRADIENTSHADER_TURBULENCE] = self.turbulence  
      node[c4d.SDKGRADIENTSHADER_OCTAVES] = self.octaves  
      node[c4d.SDKGRADIENTSHADER_SCALE] = self.scale  
      node[c4d.SDKGRADIENTSHADER_FREQ] = self.freq  
      node[c4d.SDKGRADIENTSHADER_ABSOLUTE] = self.absolute  
  
      return True  
  
  
  def InitRender(self, sh, irs) :  
      #Precalculate any data for rendering.  
  
      self.mode = sh[c4d.SDKGRADIENTSHADER_MODE]  
      self.angle = sh[c4d.SDKGRADIENTSHADER_ANGLE]  
      self.cycle = sh[c4d.SDKGRADIENTSHADER_CYCLE]  
      self.turbulence = sh[c4d.SDKGRADIENTSHADER_TURBULENCE]  
      self.octaves = sh[c4d.SDKGRADIENTSHADER_OCTAVES]  
      self.scale = sh[c4d.SDKGRADIENTSHADER_SCALE]  
      self.freq = sh[c4d.SDKGRADIENTSHADER_FREQ]  
      self.absolute = sh[c4d.SDKGRADIENTSHADER_ABSOLUTE]  
      self.gradient = sh[c4d.SDKGRADIENTSHADER_COLOR]  
      if (not self.gradient) or (not self.gradient.InitRender(irs)) : return c4d.INITRENDERRESULT_OUTOFMEMORY  
        
      self.sa, self.ca = utils.SinCos(self.angle)  
  
      for i in range (0, 4) :  
          self.c[i] = 0.0  
          k = self.gradient.GetKnot(i)        # here I had to use "Gradient.GetKnot(index)" because there is no "Gradient.GetRenderKnot(index)" in the Python SDK.  
          if (k) : self.c[i] = k['col']  
  
      return 0  
  
  
  def FreeRender(self, sh) :  
      #Free any resources used for the precalculated data from InitRender()  
      if (self.gradient) : self.gradient.FreeRender()  
      self.gradient = None  
  
  
  def Output(self, sh, cd) :  
      #Called for each point of the visible surface of a shaded object. Here you should calculate and return the channel color for the point cd.p.  
  
      p = cd.p  
      r = 0.0  
  
      if (self.turbulence > 0.0) :  
          scl = 5.0 * self.scale  
          tt = cd.t * self.freq * 0.3  
  
          res = c4d.Vector(Turbulence(p * scl, self.octaves, True, tt), Turbulence((p+c4d.Vector(0.34, 13.0, 2.43)) * scl, self.octaves, True, tt), 0.0)  
  
          if self.absolute:  
              p.x = utils.MixNum(p.x, res.x, self.turbulence)  
              p.y = utils.MixNum(p.y, res.y, self.turbulence)  
          else:  
              p.x += (res.x - 0.5) * self.turbulence  
              p.y += (res.y - 0.5) * self.turbulence  
  
      #rotation  
      p.x -= 0.5  
      p.y -= 0.5  
  
      xx = self.ca * p.x - self.sa * p.y + 0.5  
      yy = self.sa * p.x + self.ca * p.y + 0.5  
  
      p.x = xx  
      p.y = yy  
  
      if (self.mode <= c4d.SDKGRADIENTSHADER_MODE_CORNER) and self.cycle and (cd.texflag & c4d.TEX_TILE) :  
          if cd.texflag is c4d.TEX_MIRROR:  
              p.x = p.x % 2.0  
              if p.x >= 1.0: p.x = 2.0 - p.x  
  
              p.y = p.y % 2.0  
              if p.y >= 1.0: p.y = 2.0 - p.y  
  
          else:  
              p.x = p.x % 1.0  
              p.y = p.y % 1.0  
  
      if self.mode is c4d.SDKGRADIENTSHADER_MODE_U:  
          r = p.x  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_V:  
          r = 1.0 - p.y  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_DIAGONAL:  
          r = (p.x + p.y) * 0.5  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_RADIAL:  
          p.x -= 0.5  
          p.y -= 0.5  
          if p.x == 0.0: p.x = 0.00001  
  
          angle = math.atan(p.y / p.x)  
          if p.x < 0.0: angle += math.pi  
          if angle < 0.0: angle += (math.pi * 2.0)  
          r = angle / (math.pi * 2.0)  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_CIRCULAR:  
          p.x -= 0.5  
          p.y -= 0.5  
          r = math.sqrt(p.x * p.x + p.y * p.y) * 2.0  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_BOX:  
          p.x = abs(p.x - 0.5)  
          p.y = abs(p.y - 0.5)  
          r = max(p.x, p.y) * 2.0  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_STAR:  
          p.x = abs(p.x - 0.5) - 0.5  
          p.y = abs(p.y - 0.5) - 0.5  
          r = math.sqrt(p.x * p.x + p.y * p.y) * 1.4142  
  
      elif self.mode is c4d.SDKGRADIENTSHADER_MODE_CORNER:  
          cx = utils.FCut(p.x, 0.0, 1.0)  
          ca = utils.MixVec(self.c[0], self.c[1], cx)  
          cb = utils.MixVec(self.c[2], self.c[3], cx)  
            
          return utils.MixVec(ca, cb, utils.FCut(p.y, 0.0, 1.0))  
  
      return self.gradient.CalcGradientPixel(utils.FCut(r, 0.0, 1.0))  
  
  
def RegisterSDKGradientClass() :  
    
  fn = os.path.join(os.path.dirname(__file__), "res", "gradienttypes.tif")  
  bmp = bitmaps.BaseBitmap()  
  if c4d.IMAGERESULT_OK != bmp.InitWith(fn)[0]: return False  
  
  gui.RegisterIcon(200000135, bmp, 0*32, 0, 32, 32)  
  gui.RegisterIcon(200000136, bmp, 1*32, 0, 32, 32)  
  gui.RegisterIcon(200000137, bmp, 2*32, 0, 32, 32)  
  gui.RegisterIcon(200000138, bmp, 3*32, 0, 32, 32)  
  gui.RegisterIcon(200000139, bmp, 4*32, 0, 32, 32)  
  gui.RegisterIcon(200000140, bmp, 5*32, 0, 32, 32)  
  gui.RegisterIcon(200000141, bmp, 6*32, 0, 32, 32)  
  gui.RegisterIcon(200000142, bmp, 7*32, 0, 32, 32)  
  
  return plugins.RegisterShaderPlugin(PLUGIN_ID, "Python Gradient", 0, SDKGradientClass, "xsdkgradient", 0)  
  
if __name__ == '__main__':  
  RegisterSDKGradientClass()  
  

Kind regards,
Tom

On 08/06/2013 at 17:39, xxxxxxxx wrote:

Originally posted by xxxxxxxx

It really sounds to me like the symbol cache problem I mentioned above!
So please do me a favor, search for the "symbolcache" file in the C4D "{USER_FOLDER}/prefs/", delete it and then try your old method without the extra include again.
At least try it and then tell me about it, okay? :wink:

Nope. Not a symbol cache problem.
I deleted it and tested it. And the Gradient gizmo still does not show up without this: INCLUDE xsdkgradient;

I honestly think that's the correct way it's supposed to work though.
For example. Look at the other entries in my .res file:
  INCLUDE Mpreview;         //The preview window gizmo
  INCLUDE Xbase;            //The Blurr sliders gizmos

Without those in my .res file. Those two gizmos do not show up either.
I have no idea how you are able to get your gizmos to show up without #Including them in your .res file. But I suspect that you are doing that in some other place in your plugin.

I guess it doesn't really matter though. It works and that's all that really matters.
Thanks again for posting your code. It saved me the trouble of making one from scratch.

Vielen Dank,
-ScottA

On 08/06/2013 at 22:15, xxxxxxxx wrote:

I just figured out why I had to use INCLUDE xsdkgradient; in my .res file.

Your code is not creating a truly unique instance of the gradient class. And you are still using the same IDs from the instance created by the the example in the SDK.
That's why it works for me when I use the Include in my .res file.

Just writing this code isn't enough to create unique instance: gradient = c4d.Gradient()
We also have to set the type of gui as: CUSTOMDATATYPE_GRADIENT.
And we also have to somehow assign our own personal ID for the gradient instance in the .res file to this specific instance.
Doing this does not work: node[c4d.MY_GRADIENT] = self.gradient

This is the  C++ code that does that. And needs to be translated into Python to create a truly unique gradient instance:

    BaseContainer *data = ((BaseShader* )node)->GetDataInstance();  
  
  AutoAlloc<Gradient> gradient;     //Creates a new gradient instance  
  if (!gradient) return FALSE;  
  
  //This is where we assign the gradient instance with our own  personal ID in the .res file  
  //This is not in your python version..You're still using the gradient instance the C++ SDK created  
  data->SetData(c4d.SDKGRADIENTSHADER_COLOR, GeData(CUSTOMDATATYPE_GRADIENT,gradient));

Your first question was never really answered.
We still need to know how to create a unique instance of a gradient gizmo.

-ScottA

On 09/06/2013 at 09:52, xxxxxxxx wrote:

Hi Scott,

as the whole xsdkgradient thing is also just a plugin and not a part of C4D itself, I think it's not a very safe idea to make includes from it, as they have to fail for sure, if you don't have this sdk examples installed in C4D!

With "Mpreview" and "Xbase" it is different.
That are elements that all shaders (normally should) have!
So it wouldn't be very reasonable (and also against the OOP idea! :wink:) to write your own stuff for it instead to include these basic elements, that are available from the C4D API.

Of course this might be also true for a gradient, if you need (and want to use) a c4d standard gradient anywhere for a plugin.
But therefore you'd have to include "x sla gradient" and never ever "x sdk gradient"!

And in this case the keyword for the gradient itself would be:

c4d.SLA_GRADIENT_GRADIENT

By the way, here are the elements for it from the "xslagradient.h":

  
#ifndef _Xslagradient_H_  
#define _Xslagradient_H_  
  
enum  
{  
  XSLAGradient                          = 1000,  
  
  SLA_GRADIENT_TYPE                     = 1001,   // LONG  
  SLA_GRADIENT_TYPE_2D_U              = 2000,  
  SLA_GRADIENT_TYPE_2D_V,  
  SLA_GRADIENT_TYPE_2D_DIAG,  
  SLA_GRADIENT_TYPE_2D_RAD,  
  SLA_GRADIENT_TYPE_2D_CIRC,  
  SLA_GRADIENT_TYPE_2D_BOX,  
  SLA_GRADIENT_TYPE_2D_STAR,  
  SLA_GRADIENT_TYPE_2D_FOUR_CORNER,  
  SLA_GRADIENT_TYPE_3D_LINEAR,  
  SLA_GRADIENT_TYPE_3D_CYLINDRICAL,  
  SLA_GRADIENT_TYPE_3D_SPHERICAL,  
  
  SLA_GRADIENT_CYCLE                    = 1002,   // Bool  
  SLA_GRADIENT_START                    = 1003,   // Vector  
  SLA_GRADIENT_END                      = 1004,   // Vector  
  SLA_GRADIENT_RADIUS                   = 1005,   // Real  
  SLA_GRADIENT_SPACE                    = 1006,   // LONG  
  SLA_GRADIENT_SPACE_TEXTURE          = 2020,  
  SLA_GRADIENT_SPACE_OBJECT,  
  SLA_GRADIENT_SPACE_WORLD,  
  SLA_GRADIENT_SPACE_CAMERA,  
  SLA_GRADIENT_SPACE_SCREEN,  
  SLA_GRADIENT_SPACE_RASTER,  
  
  SLA_GRADIENT_TURBULENCE               = 1011, // real  
  SLA_GRADIENT_OCTAVES                  = 1012, // real  
  SLA_GRADIENT_SCALE                    = 1013, // real  
  SLA_GRADIENT_FREQ                     = 1014, // real  
  SLA_GRADIENT_SEED                                            = 1017, // long  
  SLA_GRADIENT_ABSOLUTE                 = 1015, // bool  
  SLA_GRADIENT_ANGLE                    = 1016, // real  
  
  SLA_GRADIENT_GRADIENT                 = 1007,   // Gradient  
    
  SLA_GRADIENT_DUMMY_  
};  
  
#endif // _Xslagradient_H_  

You can find it here: {CINEMA 4D main directory}\resource\modules\sla\res\description\

Okay, now here is a very basic gradient shader example by me, reduced to the minimum without any dependencies to the xsdkgradient (or the xslagradient! :wink:) and with all the resource files for it:

py-gradient.pyp

  
import c4d  
from c4d import plugins, utils  
  
  
class PyGradient(plugins.ShaderData) :  
  
  gradient = c4d.Gradient()  
  
  
  def Init(self, node) :  
      self.gradient.InsertKnot(col = c4d.Vector(0.0, 0.0, 0.0), pos = 0.0)  
      self.gradient.InsertKnot(col = c4d.Vector(1.0, 1.0, 1.0), pos = 1.0)  
      node[c4d.PY_GRADIENTSHADER_GRADIENT] = self.gradient  
      return True  
  
  
  def InitRender(self, sh, irs) :  
      self.gradient = sh[c4d.PY_GRADIENTSHADER_GRADIENT]  
      if (not self.gradient) or (not self.gradient.InitRender(irs)) : return c4d.INITRENDERRESULT_OUTOFMEMORY  
      return 0  
  
  
  def Output(self, sh, cd) :  
      return self.gradient.CalcGradientPixel(cd.p.x)  
   
  
  def FreeRender(self, sh) :  
      if (self.gradient) : self.gradient.FreeRender()  
      self.gradient = None  
  
  
def RegisterPyGradient() :  
  
  PLUGIN_ID = 1000010    #warning Please obtain your own plugin ID from http://www.plugincafe.com  
  IDS_PY_GRADIENT = 10000    #string resource, must be manually defined  
  
  return plugins.RegisterShaderPlugin(PLUGIN_ID, plugins.GeLoadString(IDS_PY_GRADIENT), 0, PyGradient, "xpygradient", 0)  
  
if __name__ == '__main__':  
  RegisterPyGradient()  
  

xpygradient.h

  
#ifndef _Xpygradient_H_  
#define _Xpygradient_H_  
  
enum  
{  
  PY_GRADIENTSHADER_GRADIENT            = 2000, // GRADIENT  
};  
  
#endif  
  

** xpygradient.res**

  
CONTAINER Xpygradient  
{  
  NAME Xpygradient;  
  
  INCLUDE Mpreview;  
  INCLUDE Xbase;  
  
  GROUP ID_SHADERPROPERTIES  
  {  
      GRADIENT PY_GRADIENTSHADER_GRADIENT { }  
  }  
}  
  

** xpygradient.str**

  
STRINGTABLE Xpygradient  
{  
  Xpygradient "Python Gradient Shader";  
  
  PY_GRADIENTSHADER_GRADIENT                "Gradient";  
}  
  

** c4d_symbols.h**

  
enum  
{  
  // string table definitions  
  IDS_PY_GRADIENT = 10000,        // this entry is actually redundant, because C4D Python doesn't recognize it from here so far! That's why it's also defined in "py-gradient.pyp" itself.  
  
// End of symbol definition  
  _DUMMY_ELEMENT_  
};  
  

** c4d_strings.str**

  
// C4D-StringResource  
// Identifier    Text  
  
STRINGTABLE  
{  
  IDS_PY_GRADIENT        "Py-Gradient";  
}  
  

I guess, the plugin structure should be clear, but just for completeness:

  
    
    
    Py-Gradient/
        py-gradient.pyp
        res/
            c4d_symbols.h
            description/
                xpygradient.h
                xpygradient.res
            strings_us/
                c4d_strings.str
                description/
                    xpygradient.str  
    

Maybe that helps to find out what's the problem for you. :slightly_smiling_face:
I hope so!

Kind regards,
Tom

On 09/06/2013 at 11:37, xxxxxxxx wrote:

Yes that does help Tom.

For some strange reason that I have yet to figure out. I couldn't create my own unique instance of the gradient gui. It just refused to work.
I could not replace:

node[c4d.SDKGRADIENTSHADER_COLOR] = self.gradient  
with  
node[c4d.MY_GRADIENT] = self.gradient

But your small example works for me. So I will use that rebuild my plugin with unique ID's.

Vielen Dank,
-ScottA

On 09/06/2013 at 13:10, xxxxxxxx wrote:

By the way, can anyone tell me why C4D doesn't use the string table definitions from c4d_symbols.h for Python  plugins?
But it wants this resource file nevertheless. But what for, then?

** c4d_symbols.h**
_<_t_>_

enum
{
  // string table definitions
  IDS_PY_GRADIENT = 10000,        // this entry is actually redundant, because C4D Python doesn't recognize it from here so far! That's why it's also defined in "py-gradient.pyp" itself.

// End of symbol definition
  _DUMMY_ELEMENT_
};

<_<_t_>_

Would be good if this get "fixed" some day. ;)