Custom quads with shaders
by Guimo · in Torque Game Engine Advanced · 09/10/2007 (10:59 pm) · 11 replies
Hi all,
I have been looking for 2 days for this problem and feel like Im still far from the solution. I hope some can send me in the right direction.
The problem
I want to render a quad but using a material. The objective is to render the Continuous Laser resource (http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=8006) but using the Glow Shader to enhance visibility.
The Laser resource uses a dml file (just a simple text file) which holds a list of textures to use. In this case the only texture used is red_beam.png which, as the name tells) is a red texture with some alpha. When a Laser is created, the engine checks if the red_beam.png file is already loaded, if it isnt then it is deleted. If already on the Resource List then the texture is used as is.
So, I thought the engine could be tricked to use a material instead of the texture. I have this basic material for glows:
And when I want to map this shader to another Material I do:
This setup works with any 3D model. We have glowing korks running everywhen just mapping the material to the appropiate entry.
So I thought that, as the LaserGlow material is being mapped to the red_beam texture name, when the DML is loaded then the engine instead of loading the material would find my shader in that position and use it. The problem is that it just doesnt work like that. When the DML is loaded the red_beam entry is not found in the resource manager even when the Material is loaded in memory (notice it has a preload state... I have checked it eing loaded in debug mode). So the red texture is loaded and the glow doent show the expected vibrant colors.
So... what I'm asking for... someone that:
a. Explains a little about how to render a quad (not a 3d model just a simple quad) using a material.
b. Explains what happens with the map_to
c. Gives some explanation about the GFX rendering logic.
Thank you very much for hearing my frustration.
Luck!
Guimo
I have been looking for 2 days for this problem and feel like Im still far from the solution. I hope some can send me in the right direction.
The problem
I want to render a quad but using a material. The objective is to render the Continuous Laser resource (http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=8006) but using the Glow Shader to enhance visibility.
The Laser resource uses a dml file (just a simple text file) which holds a list of textures to use. In this case the only texture used is red_beam.png which, as the name tells) is a red texture with some alpha. When a Laser is created, the engine checks if the red_beam.png file is already loaded, if it isnt then it is deleted. If already on the Resource List then the texture is used as is.
So, I thought the engine could be tricked to use a material instead of the texture. I have this basic material for glows:
if(!isObject(BaseGlow)) new Material(BaseGlow)
{
baseTex[0] = "~/data/basematerials/blobtex1";
emissive[0] = true;
glow[0] = true;
preload = true;
};And when I want to map this shader to another Material I do:
new Material( LaserGlow:BaseGlow )
{ mapTo = red_beam;
};This setup works with any 3D model. We have glowing korks running everywhen just mapping the material to the appropiate entry.
So I thought that, as the LaserGlow material is being mapped to the red_beam texture name, when the DML is loaded then the engine instead of loading the material would find my shader in that position and use it. The problem is that it just doesnt work like that. When the DML is loaded the red_beam entry is not found in the resource manager even when the Material is loaded in memory (notice it has a preload state... I have checked it eing loaded in debug mode). So the red texture is loaded and the glow doent show the expected vibrant colors.
So... what I'm asking for... someone that:
a. Explains a little about how to render a quad (not a 3d model just a simple quad) using a material.
b. Explains what happens with the map_to
c. Gives some explanation about the GFX rendering logic.
Thank you very much for hearing my frustration.
Luck!
Guimo
About the author
#2
Maybe my question is wrong... let me rephrase it.
a. After reading the PrimBuild class some doubts are cleared. I found that the vertex array is cleared on the begin() method and are sent to the card in the PrimBuild::end() method. Perfectly clear. But the remaining part of the question still remains... how do I enable the shader after rendering the primitive?
b. As explained, I use the mapTo in order to tell the LaserGlow material to be identified as the red_beam material with the hopes that when the Material List for the laser is loaded, the texture loading is discarded because there is already a material loaded with that name.
So when I rin the game, the LaserGlow material loads (I have made an extensive debug and Im completely sure the material is loaded in memory) and it is mapped successfully to the red_beam identifier (again I have made an step by step debug to make sure it is loaded). But when a Laser is created, the engine reads the DML file and finds it must load the red_beam material, but the red_beam identifier is not found in the dictionary so the red_beam.png texture is loaded and used.
So, I know the LaserGlow is loaded and I can use it in other models with a skin swap, but the Laser does not find it. I suspect that the Laser is looking into a local material list while I should be searching the material in a Global Material List but I just dont find this Global Material List.
c. I hope I can find this article in TDN, if someone knows where it is please tell me.
Luck!
Guimo
09/11/2007 (3:35 pm)
Hi Marc,Maybe my question is wrong... let me rephrase it.
a. After reading the PrimBuild class some doubts are cleared. I found that the vertex array is cleared on the begin() method and are sent to the card in the PrimBuild::end() method. Perfectly clear. But the remaining part of the question still remains... how do I enable the shader after rendering the primitive?
b. As explained, I use the mapTo in order to tell the LaserGlow material to be identified as the red_beam material with the hopes that when the Material List for the laser is loaded, the texture loading is discarded because there is already a material loaded with that name.
So when I rin the game, the LaserGlow material loads (I have made an extensive debug and Im completely sure the material is loaded in memory) and it is mapped successfully to the red_beam identifier (again I have made an step by step debug to make sure it is loaded). But when a Laser is created, the engine reads the DML file and finds it must load the red_beam material, but the red_beam identifier is not found in the dictionary so the red_beam.png texture is loaded and used.
So, I know the LaserGlow is loaded and I can use it in other models with a skin swap, but the Laser does not find it. I suspect that the Laser is looking into a local material list while I should be searching the material in a Global Material List but I just dont find this Global Material List.
c. I hope I can find this article in TDN, if someone knows where it is please tell me.
Luck!
Guimo
#3
I have found that when the engine needs to load the texture, it looks in the ResourceManager for that entry and, as it is not found, the texture is loaded. I dont know why the Material is not found, is the material saved in the ResourceManager?
I have isolated the problem to a single question. How does the engine knows when to use a texture and when to use a material mapped to the same name? My guesses are:
a. When the engine has to load a texture it looks for another texture or material with the same name and if found the loading is discarded and a pointer to that material is returned. So any calls to GFX->setTexture will be made using the material.
b. The texture is loaded even when a material with the same name exists, but when it is required to render, the material takes precedence.
09/11/2007 (8:28 pm)
So, I was readin the resources recommmended by Marc. Helped a lot but Im still looking for a solution to the problem.I have found that when the engine needs to load the texture, it looks in the ResourceManager for that entry and, as it is not found, the texture is loaded. I dont know why the Material is not found, is the material saved in the ResourceManager?
I have isolated the problem to a single question. How does the engine knows when to use a texture and when to use a material mapped to the same name? My guesses are:
a. When the engine has to load a texture it looks for another texture or material with the same name and if found the loading is discarded and a pointer to that material is returned. So any calls to GFX->setTexture will be made using the material.
b. The texture is loaded even when a material with the same name exists, but when it is required to render, the material takes precedence.
#4
09/11/2007 (9:57 pm)
Does anybody knows the relation between a material and a GFXTexHandle?
#5
So to recap:
-Artist creates a DIF shape with a material on it named "FooMaterial"
-Torque Material is defined with mapTo = FooMaterial
-This material will use GFXTexHandles to load and access the texture data required by that material
09/11/2007 (10:03 pm)
A material defined in a Torque script file specifies a 'mapTo' parameter. Both DTS and DIF shapes store material names for geometry data. These are the material names which get mapped to Torque Materials. GFXTextureObject/GFXTexHandle are responsible for texture data, and they get used by the Material system. So to recap:
-Artist creates a DIF shape with a material on it named "FooMaterial"
-Torque Material is defined with mapTo = FooMaterial
-This material will use GFXTexHandles to load and access the texture data required by that material
#6
Just to make my question completely concise... say I have this material which is created and loaded in the script at the start of the game:
Now I want to render a quad using this material. Given that at render time (in the renderObject method) I have the material name (MyMaterialName) and/or the mapTo reference (reference_name). How do I find the material and how do I enable this material for rendering before sending the quad?
Thank you very much.
Guimo
09/11/2007 (10:34 pm)
Ok Pat thats excellent... I got confused because I thought the MaterialList class should be a list of materials... but in fact its a list of TextureHandles. Thats confusing.Just to make my question completely concise... say I have this material which is created and loaded in the script at the start of the game:
new Material( MyMaterialName ) {
mapTo = reference_name;
...
}Now I want to render a quad using this material. Given that at render time (in the renderObject method) I have the material name (MyMaterialName) and/or the mapTo reference (reference_name). How do I find the material and how do I enable this material for rendering before sending the quad?
Thank you very much.
Guimo
#7
After reading lots of code I think Im closing to solve the mistery, but now I have more questions.
a. The Material object isnt used to render. A MatInstance object is used instead.
b. TSMaterialList is an specialized form of MaterialList but can hold Materials.
c. If you want a TSMaterialList to be aware of materials defined in a script, add the desired identifier to the MaterialList names array and call the mapMaterials method.
d. In order to use the material, you create a RenderIntance object and setup its parameters using the material and then add it to the gRenderInstManager so it locates the RenderInstace in the proper order of the rendering process.
So, Given I can find the material, I can add it to a MaterialList and on the prepRenderImage method I can sertup all my renderInstance with my material and with the renderObject method I can create and render the quads... is it right?
Luck!
Guimo
09/12/2007 (8:04 pm)
Hi,After reading lots of code I think Im closing to solve the mistery, but now I have more questions.
a. The Material object isnt used to render. A MatInstance object is used instead.
b. TSMaterialList is an specialized form of MaterialList but can hold Materials.
c. If you want a TSMaterialList to be aware of materials defined in a script, add the desired identifier to the MaterialList names array and call the mapMaterials method.
d. In order to use the material, you create a RenderIntance object and setup its parameters using the material and then add it to the gRenderInstManager so it locates the RenderInstace in the proper order of the rendering process.
So, Given I can find the material, I can add it to a MaterialList and on the prepRenderImage method I can sertup all my renderInstance with my material and with the renderObject method I can create and render the quads... is it right?
Luck!
Guimo
#8
Luck!
Guimo
09/12/2007 (10:24 pm)
No luck... I have read the code for three days but nothing... I just cant manage to render the quad using a material. I hope someone reads this message and throws me a rope...Luck!
Guimo
#9
The WarningMaterial is a SimObject, as are all materials, so you can use Sim::findObject to get to them. It creates a MatInstance from the object returned by Sim::findObject.
This code snippet won't work as presented, but it is a starting point for some hacking:
I put this in the onWake of a GUI control:
And then this in onRender:
That code compiles, but won't run w/o crashing because it's trying to invert a matrix with garbage data and it throws a fit. Anyway this is a starting point.
09/13/2007 (6:09 pm)
Check out RenderInstManager::initWarnMat()The WarningMaterial is a SimObject, as are all materials, so you can use Sim::findObject to get to them. It creates a MatInstance from the object returned by Sim::findObject.
This code snippet won't work as presented, but it is a starting point for some hacking:
I put this in the onWake of a GUI control:
Material *warnMat = static_cast<Material*>(Sim::findObject( "WarningMaterial" ) );
if( !warnMat )
{
Con::errorf( "Can't find WarningMaterial" );
}
else
{
SceneGraphData sgData;
GFXVertexPCT *vertDef = NULL; // the variable itself is the parameter to the template function
mMatInst = new MatInstance( *warnMat );
mMatInst->init( sgData, (GFXVertexFlags)getGFXVertFlags( vertDef ) );
}And then this in onRender:
RectI rect(offset, getExtent());
PrimBuild::beginToBuffer( GFXTriangleStrip, 4 );
PrimBuild::colorWhite();
PrimBuild::vertex2f( (F32)rect.point.x, (F32)rect.point.y );
PrimBuild::texCoord2f( 0.0f, 0.0f );
PrimBuild::vertex2f( (F32)rect.point.x, (F32)rect.point.y + (F32)rect.extent.y );
PrimBuild::texCoord2f( 0.0f, 1.0f );
PrimBuild::vertex2f( (F32)rect.point.x + (F32)rect.extent.x, (F32)rect.point.y + (F32)rect.extent.y );
PrimBuild::texCoord2f( 1.0f, 1.0f );
PrimBuild::vertex2f( (F32)rect.point.x + (F32)rect.extent.x, (F32)rect.point.y );
PrimBuild::texCoord2f( 1.0f, 0.0f );
U32 numPrims;
GFXVertexBuffer *vertBuf = PrimBuild::endToBuffer( numPrims );
GFXVertexBufferHandle<GFXVertexPCT> buffHandle;
buffHandle = vertBuf;
while( mMatInst->setupPass( sgData ) )
{
mMatInst->setWorldXForm( GFX->getWorldMatrix() );
mMatInst->setBuffers( &buffHandle, NULL );
}You are going to have to actually assign data to that sgData, but if you set a breakpoint on setupPass, and trace it, you will see what data gets used, and you'll have to decide if you want to hack around that stuff or what. That code compiles, but won't run w/o crashing because it's trying to invert a matrix with garbage data and it throws a fit. Anyway this is a starting point.
#10
Its a nice starting point for additional research. In my last attempts i tried sending the material instance with the RenderInstance structure in the prepareRender method along with transformation matrices but the engine crashed. Apparently it required vertex buffers.
What i understand now is that with the default rendering procedure, if you set the RenderInstace (RI) with a RIT_Mesh type, you must also provide a material and appropiate transforms. Then the RI is added to a render bin which is the engine way to group objects with the same material properties. When the bin is rendered the objects are rendered using the RI transforms and vertex and primitive (index?) buffers.
So when you try to render a custom quad, If you try to feed the RI with a material and the RIT_Mesh, then the Ri is feed to the bin and when rendering time comes, the VB is empty. if you feed the type with RIT_Object then you lose the renderbin rendering process and when the time to render the object comes the object renderObject method is called instead. This is a completely different path.
Now, I dont want to give you advice on how to develop your engine, but, wouldnt it be wiser to force the RIT_Object also into a renderbin and then the rendering process of the bin arrives, then the RI object would be tested and if the vertexbuffer is empty then you may call the renderObject so the programmer can feed its own geometry for that material?
Ill try your code and tell you how it came.
Luck!
Guimo
09/15/2007 (2:35 am)
Thanks Pat!Its a nice starting point for additional research. In my last attempts i tried sending the material instance with the RenderInstance structure in the prepareRender method along with transformation matrices but the engine crashed. Apparently it required vertex buffers.
What i understand now is that with the default rendering procedure, if you set the RenderInstace (RI) with a RIT_Mesh type, you must also provide a material and appropiate transforms. Then the RI is added to a render bin which is the engine way to group objects with the same material properties. When the bin is rendered the objects are rendered using the RI transforms and vertex and primitive (index?) buffers.
So when you try to render a custom quad, If you try to feed the RI with a material and the RIT_Mesh, then the Ri is feed to the bin and when rendering time comes, the VB is empty. if you feed the type with RIT_Object then you lose the renderbin rendering process and when the time to render the object comes the object renderObject method is called instead. This is a completely different path.
Now, I dont want to give you advice on how to develop your engine, but, wouldnt it be wiser to force the RIT_Object also into a renderbin and then the rendering process of the bin arrives, then the RI object would be tested and if the vertexbuffer is empty then you may call the renderObject so the programmer can feed its own geometry for that material?
Ill try your code and tell you how it came.
Luck!
Guimo
#11
at the top of your file add
#include "renderInstance/renderGlowMgr.h"
then at top of your quad rendering function add
Render your object as normal here (could even use a separate shader you apply to your object for say color or whatever) then at end of your render just add...
That will get your custom (non-dts) object to glow.
09/26/2007 (9:16 pm)
After looking at it, this isn't using the material system per se, but you can render your quad (or sections thereof) directly to glowBuffer (which would solve your laser glow problem). at the top of your file add
#include "renderInstance/renderGlowMgr.h"
then at top of your quad rendering function add
// Get the glow buffer GlowBuffer *glowBuffer = gClientSceneGraph->getGlowBuff(); if( !glowBuffer || glowBuffer->isDisabled()) return; // get the current viewport RectI vp = GFX->getViewport(); // push render surfaces...render to texture GFX->pushActiveRenderSurfaces(); // set glow buffer as render target glowBuffer->setAsRenderTarget(); // set render states GFX->setCullMode( GFXCullCCW ); GFX->setZWriteEnable( false ); GFX->setTextureStageMagFilter( 0, GFXTextureFilterLinear );
Render your object as normal here (could even use a separate shader you apply to your object for say color or whatever) then at end of your render just add...
// restore render states, and copy our rendered glow to screen GFX->setZWriteEnable( true ); GFX->popActiveRenderSurfaces(); glowBuffer->copyToScreen( vp );
That will get your custom (non-dts) object to glow.
Torque 3D Owner Marc Schaerer
b) the map to is only needed if you want to replace a texture with a material
c) GFX batching stuff explanation has been added to the TDN a while ago. Above that, you would need to ask your question more precise as basically: all you see is shader driven, done in a batched manner with a specific order (RIT enumeration order)