How to allocate a gradient in Python?
-
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.
@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.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.
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:
################################## ## 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.
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?
(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!
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.
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.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.
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 ) and so here it is again:
############################################################# ## 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?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 gizmosWithout 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.gradientThis 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! ) 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! ) 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.
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.
-
On 10/06/2013 at 00:12, xxxxxxxx wrote:
The symbols file is is needed because it is part of the resource associated with a plugin or more
specifically its look up table the resource parser is using to identify certain elements.In python description ids are imported properly(automatically) from the symbols file, everything
else - custom ids, dialog ids is not. The reason ? Only god knows why. You have either to copy
the values or write a little parser for the symbols file which does autoimport its members to your
project.The downside of auto importing - if you are using an IDE, it won't know your symbols constants
anymore, so will loose the autocomplete feature on these. That is why I personally went back
to simply copy pasting the symbols file. With a little macro you can make it one press of a button.edit : You could of course write an autoimporter that doesn't work at runtime, but (over)writes
a python symbols definition py(c) file, but doing that properly will be a bit more work. as you
will have to analyze existing code in the file and so on. -
On 10/06/2013 at 00:44, xxxxxxxx wrote:
Originally posted by xxxxxxxx
In python description ids are imported properly(automatically) from the symbols file, everything
else - custom ids, dialog ids is not. The reason ? Only god knows why. You have either to copy
the values or write a little parser for the symbols file which does autoimport its members to your
project.https://github.com/NiklasRosenstein/c4dtools#example-code
c4dtools does this for you, and it also allows parsing the description resources so you don't have to
fuzz with the symbolcache. Well, if you're using an IDE, you won't have the symbols in the auto-
ompletion, but you could also write a script that parses the resource symbols and creates an
importable Python file that contains the resources.