Custom Tile Shader problem with previews [SOLVED]
-
On 04/12/2014 at 07:49, xxxxxxxx wrote:
User Information:
Cinema 4D Version: R12+
Platform: Windows ;
Language(s) : C++ ;---------
Hello,currently I'm working on a channel shader plugin that will do some fancy tiling stuff internally.
And that even works so far, at least regarding to the final rendering.
But unfortunately it's dependent on the right tiling settings in the texture tag.
And that have to be fixed.The shader output itself should be treated like a single texture without touching the shader internal tiling at all and the texture tag tiling settings should only affect the texture tag tiling alone if the "Tiles U/V" and "Repetitions U/V" settings are used for some additional tiling.
But first of all I mainly have some trouble with the different previews (editor preview, shader preview, channel preview).
To illustrate my problems I made a very simplified demo shader and did some screenshots.
First the code:
////////////////////////////////////////////////////////////// // CINEMA 4D Tile Shader Test // ////////////////////////////////////////////////////////////// // (c) 2014 Thomas Chen, all rights reserved // ////////////////////////////////////////////////////////////// // tileshadertest.cpp // ////////////////////////////////////////////////////////////// // example for a channel shader with access to basechannel // using standard GUI elements #include "c4d.h" #include "c4d_symbols.h" #include "xtileshadertest.h" // be sure to use a unique ID obtained from www.plugincafe.com #define ID_TILESHADERTEST 1000010 //Plugin IDs 1000001-1000010 are reserved for development! class BitmapData : public ShaderData { public: // NodeData virtual Bool Init(GeListNode *node); virtual Bool Read(GeListNode *node, HyperFile *hf, LONG level); virtual Bool Message(GeListNode *node, LONG type, void *data); // ShaderData virtual INITRENDERRESULT InitRender(BaseShader *chn, const InitRenderStruct &irs); virtual void FreeRender(BaseShader *chn); virtual Vector Output(BaseShader *chn, ChannelData *cd); static NodeData *Alloc(void) { return gNew BitmapData; } private: LONG tiles; BaseShader *texture; }; Bool BitmapData::Init(GeListNode *node) { BaseContainer *data = ((BaseShader* )node)->GetDataInstance(); data->SetLong(TILESHADERTEST_TILES, 1); data->SetLink(TILESHADERTEST_TEXTURE, NULL); return TRUE; } Bool BitmapData::Read(GeListNode *node, HyperFile *hf, LONG level) { // GePrint("HyperFile Version: " + RealToString(hf->GetFileVersion())); if (hf->GetFileVersion() < 8300) { if (!hf->ReadChannelConvert(node, TILESHADERTEST_TEXTURE)) return FALSE; // convert old basechannel } return TRUE; } Bool BitmapData::Message(GeListNode *node, LONG type, void *msgdat) { BaseContainer *data = ((BaseShader* )node)->GetDataInstance(); HandleInitialChannel(node, TILESHADERTEST_TEXTURE, type, msgdat); HandleShaderMessage(node, (BaseShader* )data->GetLink(TILESHADERTEST_TEXTURE, node->GetDocument(), Xbase), type, msgdat); return TRUE; } INITRENDERRESULT BitmapData::InitRender(BaseShader *chn, const InitRenderStruct &irs) { BaseContainer *data = chn->GetDataInstance(); // cache values for fast access tiles = data->GetLong(TILESHADERTEST_TILES); texture = (BaseShader* )data->GetLink(TILESHADERTEST_TEXTURE, irs.doc, Xbase); if (texture) { texture->InitRender(irs); } return INITRENDERRESULT_OK; } void BitmapData::FreeRender(BaseShader *chn) { if (texture) { texture->FreeRender(); } texture = NULL; } Vector BitmapData::Output(BaseShader *chn, ChannelData *cd) { cd->p.x = cd->p.x * tiles; cd->p.y = cd->p.y * tiles; Vector col = 0.0; if (texture) { col = texture->Sample(cd); } return col; } Bool RegisterTileShaderTest(void) { return RegisterShaderPlugin(ID_TILESHADERTEST, GeLoadString(IDS_TILESHADERTEST), 0, BitmapData::Alloc, "Xtileshadertest", 0); }
And now the screenshots:
(What I termed here as small shader preview I would also call the channel preview.)And some more pictures to compare the previews and the render with different settings for the tiling and seamless mirroring in the texture tag attributes.
Klick on the small pictures to show them in full size.1. (default)
Tile = on
Seamless = off
Shader internal tiling = 2 x 2
Teture Tag tiling = 1 x 1
Here the rendering is correct but the bigger shader preview and the editor preview have a mirrored tiling and the smaller channel preview has no tiling at all.2. (here "Seamless" is also switched on)
Tile = on
Seamless = on
Shader internal tiling = 2 x 2
Teture Tag tiling = 1 x 1
With seamless on my shader output also get a mirrored tiling (but it shouldn't).3. (here "Tile" is switched off)
Tile = off
Seamless = NA (it is switched off here but the state is not important at all without "Tile")
Shader internal tiling = 2 x 2
Teture Tag tiling = 1 x 1
Now the rendert shader lacks in tiling (it is not supposed to do so by what I want to achieve either) but the previews are still the same like always.4. (now with some additional tiling from the texture tag settings)
Tile = on
Seamless = off
Shader internal tiling = 2 x 2
Teture Tag tiling = 2 x 2
5. (same as before but "Seamless" is activated too)
Tile = on
Seamless = on
Shader internal tiling = 2 x 2
Teture Tag tiling = 2 x 2
And here is how I'd want it to be in this case:
Tile = off
Seamless = NA (same as at 3. )
Shader internal tiling = 2 x 2
Teture Tag tiling = 2 x 2
This is what I get now and here comes what I want to have in this case:
(only shader output related here, the previews are of course also not what they should be here)
Ignore the "Tile" and "Repetitions U/V" settings here in the texture tag attributes.
They are just a quick workaround to force the desired result for the settings above!I guess all of this have to do with the TexData and/or ChannelData texflags:
TEX_TILE and ``TEX_MIRROR
.
And I probably have to use the CALC_TEXINFO function somehow to make it work.But I couldn't figure out how exactly that have to be done.
And the available informations about that toppic are very rare (or very hard to find).
What I found in the SDK docu and the "c4d_shader.h" doesn't help me too much.And the best finds I had in this forum are on the one hand:
https://developers.maxon.net/forum/topic/8270/10779_custom-materialdata-uv--parameter-bugs-solved&PID=42600#42600And on the other a very old post related to the C4D R8.5 SDK:
https://developers.maxon.net/forum/topic/1582/924_85-sdkSo any concrete help here is highly appreciated!
Kind regards,
Tom -
On 04/12/2014 at 19:13, xxxxxxxx wrote:
Hi Phoenix,
Your situation seems to be a bit complex, and I'm afraid you might be expecting interactions across the various parts of Cinema 4D (shader, texture tag, channel, etc.) that can't occur. I think we have to focus on the end result, which I assume is related to the rendered output. If instead they are purely modeling related, I hope my suggestions are still helpful.
Lets cover the limitations across the various parts first. The settings in the texture tag cannot influence the outcome of the material and shader preview in the material. That's because you can use a material on different objects with different texture tags. When that's the case, which texture tag would Cinema 4D use for the preview? You wouldn't be able to use all, choosing one in particular doesn't make sense either, so the answer is none.
In terms of shader programming, the Output() method can return different results depending on what it is being called for. If it's called for the 2D shader preview, there are no objects to retrieve any data from, so you'll likely return the ChannelData surface point x and y coordinates. If there are objects, such as for rendering, you can use the object colour. As for the display in the viewport, you can implement InitGLImage() to fill in the bitmap, but that will also be based off of different data.
Therefore, I believe what you want is shown just above 6 as the tile layout you'd want on all faces?
Part I
Either do 1 or 2, depending on which is best for your situation:
1. load the tile, create a bitmap that is twice as wide and twice as high, and copy the tile into each quadrant, so your code will be a bit more complicated but you'll keep disk space use efficient.
2. Create a tile that is twice the size and copy / paste the tile into each quadrant, so you'll have a larger bitmap but simpler code.
Part II
use the following settings, as you stated for situation 2:
Tile = on
Seamless = on
Shader internal tiling = 2 x 2
Texture Tag tiling = 1 x 1If the above is not the end result you wanted, or you wanted different results based off of interactions that aren't possible, all I can say is that you'll have to accept these realities and focus on coding your shader to get the end results you want with your tiling. If it means you're stuck with a workflow issue, you may have to think of a different approach altogether.
I hope that helps!
Joey Gaspe
SDK Support Engineer -
On 09/12/2014 at 14:23, xxxxxxxx wrote:
Hello Mr. Gaspe,
first thanks for your reply.
But maybe I haven't expressed myself as clearly as I thought.
Originally posted by xxxxxxxx
Your situation seems to be a bit complex, and I'm afraid you might be expecting interactions across the various parts of Cinema 4D (shader, texture tag, channel, etc.) that can't occur.
No not really I think.
I only want the shader and channel preview images to represent the result of my shader calculations. Just like nearly any other C4D shader does.And I want the editor preview to represent the render result as good as it gets.
And that's also related to the tiling settings in the texture tag and common too for most of the C4D shaders.
But that's of course independent from the shader/channel preview inside the material.Originally posted by xxxxxxxx
I think we have to focus on the end result, which I assume is related to the rendered output.
Yes mainly.
Originally posted by xxxxxxxx
If instead they are purely modeling related, I hope my suggestions are still helpful.
I don't understand what you mean by "modeling related" in this context?
Originally posted by xxxxxxxx
Lets cover the limitations across the various parts first. The settings in the texture tag cannot influence the outcome of the material and shader preview in the material. That's because you can use a material on different objects with different texture tags. When that's the case, which texture tag would Cinema 4D use for the preview? You wouldn't be able to use all, choosing one in particular doesn't make sense either, so the answer is none.
Yes, that is absolutely clear and it's also not my goal anyway.
The problem is, that the tiling settings in the texture tag should not influence my shader internal tiling but instead shoul be completely independent from that.
But it does!
That is what I tried to demonstrate at point [5.] & [6.].
When the tiling settings in the texture tag are used they also mess up what my shader create.
I guess you can see it like I want to have the control of the TEX_TILE and TEX_MIRROR flags of my own ShaderData and it shouldn't be influenced by the TEX_TILE and TEX_MIRROR flags settings of the MaterialData (a C4D standard material) that use my shader (as a subshader?) in one of it's channels. I hope it makes sence what I try to explain here and you understand what I mean.
One way maybe could be to read out cd->texflag and calculate my tiles according to the actual conditions of the TEX_TILE and TEX_MIRROR flags of the texture tag (or wherever they get their current conditions from).
But somehow I think this would be an unnecessary effort.
It would have to be easier to set the TEX_TILE and TEX_MIRROR flags for my shader directly if I understand the principe correctly and channel shader plugins have there own independent flags.
Originally posted by xxxxxxxx
In terms of shader programming, the Output() method can return different results depending on what it is being called for. If it's called for the 2D shader preview, there are no objects to retrieve any data from, so you'll likely return the ChannelData surface point x and y coordinates. If there are objects, such as for rendering, you can use the object colour. As for the display in the viewport, you can implement InitGLImage() to fill in the bitmap, but that will also be based off of different data.
I hoped and actually really think it should work without InitGLImage() too.
(At least as long as the input (the subshader) for my shader is a simple bitmap texture and not a procedural shader itself and even then I'd suppose it to work in most cases.)But if not I'm not sure what would be the best way to achieve that.
Do I have to write a bitmap from my shader result in Output() in a global bitmap variable
that'll be used in InitGLImage then?And have that bitmap than also to be used for the Output() method itself instead of returning the calculated data directly to avoid the unwanted additional internal mirroring if a user activates seamless tiling in a texture tag that's associated to a material that use my shader?
And how to decide about the resolution of such a bitmap?
Especially when it should be used as final shader output too?Okay if the input is also a bitmap, it could be calculated based on the source resolution multiplied with the shader tiling but I'm afraid that could easily become a big waste of resources (in RAM usage as well as speed related) in many cases.
Originally posted by xxxxxxxx
Therefore, I believe what you want is shown just above 6 as the tile layout you'd want on all faces?
No not really in general at all.
That would only be the case if a user would explicitly decide to apply an additional seamless tiling (in the texture tag settings) to my shader.I prepared some more images to compare what I get now versus how I expect it to be.
Maybe it becomes more clear then.Okay, here they are:
Lets say, this is the input subshader for my shader (in this case a simple bitmap texture) :
Than this would be the result of my shader with an (internal) 2x2 tiling:
(And that's also what I'd like to get for the shader and channel previews and what I actually get as a render result, as long as tiling is on and seamless is off in the texture tag settings.)But here is what I get for the channel preview and what is used for the editor preview too.
And that's wrong in any case!
Because there should never be a mirroring for my shader internal tiling.
At least as long as I don't offer this as an extra option for my shader of course.
But even than it always have to be independent from any texture tag settings!
(But that's not the problem I guess as it probably is always indepent to that anyway.)Here are now the comparison screenshots for how it is now (but wrong) versus how it should be (correct) with an shader internal 2x2 tiling and also related to the main texture tag tiling settings with an additional 2x2 tiling there. I still think that demonstrate it the best
(The screenshots on the right are photoshoped to show the expected behaviour).Texture Tag settings: Tile = off & Seamless = off:
now but wrong: expected and correct:Texture Tag settings: Tile = on & Seamless = off:
now but wrong: expected and correct:Texture Tag settings: Tile = on & Seamless = on:
now but wrong: expected and correct:I hope it's clear now and unambiguous where my problems are and what I'd like to achieve.
Kind regards,
Tom -
On 12/12/2014 at 04:06, xxxxxxxx wrote:
Hello?
-
On 12/12/2014 at 10:59, xxxxxxxx wrote:
Hi Phoenix,
I've read your response and working on giving you a precise answer. I understood the practical results you want are to mirror the 2x2 image as though one tile, not the original tile within the 2x2 image. I've asked the others on the support team for help, and we believe you want to write a shader that ignores the tile Settings of the texture tag. I'm looking into whether this is possible or not.
Joey Gaspe
SDK Support Engineer -
On 12/12/2014 at 16:41, xxxxxxxx wrote:
Hi Joey,
thank you for your response so far.
Originally posted by xxxxxxxx
I understood the practical results you want are to mirror the 2x2 image as though one tile, not the original tile within the 2x2 image.
Yes, in conclusion to my example and supposed to the user activates seamless tiling at all that'd be the case. Or at least my favourite solution.
Originally posted by xxxxxxxx
I've asked the others on the support team for help, and we believe you want to write a shader that ignores the tile Settings of the texture tag. I'm looking into whether this is possible or not.
At least it have to be possible to ignore the mirroring (the "Seamless" setting) completely.
Because it works for the native c4d checkerboardshader too.To demonstrate you that I built a shader setup with a layer shader, several checkerboard shaders and a tile shader (as subshaders to the layer shader) to recreate my example test texture procedurally and here is the outcome for the different tiling settings as reviewed before with my own shader example.
Texture Tag settings: Tile = off:
Texture Tag settings: Tile = on & Seamless = off:
Texture Tag settings: Tile = on & Seamless = on:
First thing to notice here is that the shader and channel previews are correct!
Second thing is that the editor preview is always like I would expect it dependent on those settings!
But it's also striking to note that there never happens a tiling at all for the final rendered output.
Independent from the "Seamless" setting of the used texture tag.And that means that in fact it happens here that the "Seamless" option from the tile settings of the texture tag is ignored for the final output (the rendering).
(And I would even call that a bug after all in this case too, think about it!)
But it would be at least the second best solution for my own shader.
And I'm also curious to know how that can be achieved at all.So I think the real question is not if it's possible to ignore the tiling settings, but if it's also possible to use it as expected (and descibed above and also the editor preview shows).
And most important: how?And if not, then why does it work for the editor preview?
But that's just for further clarification.
At any rate I'm looking forward to what you'll come up with and thank you very much again for helping me with my problem.
Kind regards,
Tom -
On 17/12/2014 at 15:05, xxxxxxxx wrote:
Hi Tom,
This really was a team effort to figure out. I have to thank Sebastian and Andreas for their help!
After doing a deep analysis of the C4D code through the debugger, with a re-creation of your plugin and using your texture on a cube, my answers to your questions are the following:
First I'll go back to your main question in your second post:
Originally posted by xxxxxxxx
"I guess you can see it like I want to have the control of the TEX_TILE and TEX_MIRROR flags of my own ShaderData and it shouldn't be influenced by the TEX_TILE and TEX_MIRROR flags settings of the MaterialData (a C4D standard material) that use my shader (as a subshader?) in one of it's channels. I hope it makes sence what I try to explain here and you understand what I mean.
One way maybe could be to read out cd->texflag and calculate my tiles according to the actual conditions of the TEX_TILE and TEX_MIRROR flags of the texture tag (or wherever they get their current conditions from)."
Unfortunately, this is the effort you'll likely have to make, please read on to find out why.
Originally posted by xxxxxxxx
"So I think the real question is not if it's possible to ignore the tiling settings, but if it's also possible to use it as expected (and descibed above and also the editor preview shows)."
It's definitely not possible to ignore the tiling settings. There is nothing that gives you overriding control for them to be set or not, nor to stop them from being read, when passed to the code that determines where a pixel is drawn, at least not without intervention within your plugin code. This is true for the small preview, big preview, and the material sampling drawing calls.
As for using the tiling settings as expected, the source of the settings varies according to what is being drawn. For instance, when you change the texture tile amount, the material sampling code is called, and then BaseShader::InitGLImage(). Since you aren't implementing the latter, it uses the default implementation inside Cinema 4D. More on this below.
Originally posted by xxxxxxxx
"And most important: how?"
One thing I can suggest, at least to analyze how this all works without access to C4D's full source code, is that you force them on and off in your plugin code. My suggestion is based off of the fact BitmapData::Output() is called with the ChannelData object containing the tiling and seamless settings you're passing on to texture->Sample(cd).
To force tile and mirroring flags:
cd->texflag = TEX_TILE+TEX_MIRROR;
To eliminate all texture flags (including those unrelated to TEX_TILE / TEX_MIRROR) :
cd->texflag = 0;
I can confirm that TEX_MIRROR will do nothing if TEX_TILE is not defined for all cases I found in Cinema 4D's code.
Originally posted by xxxxxxxx
"And if not, then why does it work for the editor preview?
But that's just for further clarification."When the editor preview calls BaseShader::InitGLImage(), a case I only found triggered when changing the amount of tiles in the shader, the following steps occur:
1. TEX_TILE and TEX_MIRROR are set on within texflag in the channel data object InitGLImage() creates and passes on to BaseShader::Sample()
2. BaseShader::Sample() calls your plugin's BitmapData::Output() call, which is a chance to read the flags and potentially change them
3. The channel data is passed through more code when you call texture->Sample(cd)
4. Finally, C4D applies the tiling / mirroring changes when calculating the mipmap value, along with other calculations to get the final calculated color, and then returned up
Therefore, the only way to override this code path is to implement your own version of InitGLImage() in your plugin.
I believe I answered the questions related to your main issue, and so you'll have to take it from here.
Joey Gaspe
SDK Support Engineer -
On 18/12/2014 at 03:32, xxxxxxxx wrote:
Hi Joey,
thank you very much for all your effort (also to Sebastian and Andreas)!
Seems like this whole stuff actually isn't really that trivial as I thought.There are some valuable informations in your post.
So maybe it would be an idea to mention this stuff in the SDK docu too?Kind regards,
Tom -
On 18/12/2014 at 07:03, xxxxxxxx wrote:
Hi Tom,
Thank you for letting us know! Unfortunately, it certainly isn't trivial, but was a great investigative deep dive for me (I'm still new at MAXON). I'll definitely find a place for this information in the SDK docs.
Regards,
Joey Gaspe
SDK Support Engineer