Rendering/Drawing to a Texture
by Demolishun · in Torque 3D Professional · 01/14/2013 (8:42 am) · 7 replies
This has got me stumped. I cannot figure out the right combination to make this work. I found this article at MSDN: msdn.microsoft.com/en-us/library/windows/desktop/bb147263%28v=vs.85%29.aspx#Usin.... This helped me understand what needs to happen regarding dynamic textures. However, I am struggling to translate this to code in T3D. I have been using the resource: www.garagegames.com/community/forums/viewthread/109335/5. I am failing to extract what I actually need to make it work.
Here is my attempt.
.H
Here is my attempt.
.H
class AudioTextureMap;
// Object that can display sampled data on a texture
class AudioTextureObject : public SceneObject
{
private:
typedef SceneObject Parent;
enum MaskBits
{
TransformMask = Parent::NextFreeMask << 0,
UpdateMask = Parent::NextFreeMask << 1,
NextFreeMask = Parent::NextFreeMask << 2
};
// buffers and pointers to texture
// texture writing is double buffered
GBitmap* mBitmap;
GFXTexHandle mTextureBuffer1;
GFXTexHandle mTextureBuffer2;
GFXTexHandle mWarningTexture;
GFXTextureObject* mTexture; //
// The size in pixels of the backing texture
S32 mTexSize;
// texture reference name for use in materials
// eg: diffuseMap[0] = "#MyWebTexture";
String mTextureName;
// texture target reg object
NamedTexTarget* mTextureTarget;
// enable rendering texture onto an object in the scene
// this will default to false to prevent rendering when used as a texture source for materials
// the texture will be mapped to a simple quad
bool mEnableRender;
// The name of the Material we will use for rendering
//String mMaterialName;
// The actual Material instance
//aseMatInstance* mMaterialInst;
// render variables
//typedef GFXVertexPCN VertexType;
typedef GFXVertexPNT VertexType;
// The handles for our StateBlocks
GFXStateBlockRef mNormalSB;
GFXStateBlockRef mReflectSB;
// The GFX vertex and primitive buffers
GFXVertexBufferHandle< VertexType > mVertexBuffer;
//GFXPrimitiveBufferHandle mPrimitiveBuffer;
public:
AudioTextureObject();
virtual ~AudioTextureObject();
// get the current texture handle
GFXTextureObject* getTexture(){
return mTexture;
};
// Allows the object to update its editable settings
// from the server object to the client
virtual void inspectPostApply();
// Set up any fields that we want to be editable (like position or render enable)
static void initPersistFields();
// Handle when we are added to the scene and removed from the scene
bool onAdd();
void onRemove();
// Override this so that we can dirty the network flag when it is called
void setTransform( const MatrixF &mat );
// networking
U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
void unpackUpdate( NetConnection *conn, BitStream *stream );
// Create the geometry for rendering
// would used for debug only
inline void createGeometry();
void updateMaterial();
// This is the function that allows this object to submit itself for rendering
void prepRenderImage( SceneRenderState *state );
// render the bitmap
void render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
DECLARE_CONOBJECT(AudioTextureObject);
};
// displaying audio data
class AudioTextureMap : public NamedTexTarget
{
public:
AudioTextureMap(AudioTextureObject* tobj) {mAudioTextureObject = tobj;}
~AudioTextureMap() { }
// MatTextureTarget
virtual GFXTextureObject* getTexture( U32 mrtIndex ) const { return mAudioTextureObject->getTexture(); }
virtual const RectI& getTargetViewport() const { return RectI::One; }
virtual void setupSamplerState( GFXSamplerStateDesc *desc ) const { TORQUE_UNUSED( desc ); }
virtual ConditionerFeature* getTargetConditioner() const { return NULL; }
private:
AudioTextureObject* mAudioTextureObject;
};.CPPIMPLEMENT_CO_NETOBJECT_V1(AudioTextureObject);
AudioTextureObject::AudioTextureObject(){
// Flag this object so that it will always
// be sent across the network to clients
mNetFlags.set( Ghostable | ScopeAlways );
mTypeMask |= StaticObjectType | StaticShapeObjectType;
mTextureTarget = NULL;
mTexSize = 512;
mBitmap = NULL;
mTexture = NULL;
}
AudioTextureObject::~AudioTextureObject(){
}
void AudioTextureObject::initPersistFields(){
addGroup( "Rendering" );
/*addField( "material", TypeMaterialName, Offset( mMaterialName, AudioTextureObject ),
"" );*/
addField( "texture", TypeRealString, Offset( mTextureName, AudioTextureObject ),
"The target render texture." );
endGroup( "Rendering" );
// SceneObject already handles exposing the transform
Parent::initPersistFields();
}
void AudioTextureObject::inspectPostApply()
{
Parent::inspectPostApply();
// Flag the network mask to send the updates
// to the client object
setMaskBits( UpdateMask );
}
bool AudioTextureObject::onAdd(){
if ( !Parent::onAdd() )
return false;
// setup material
if( isClientObject() ){
updateMaterial();
}
// Set up a 1x1x1 bounding box
mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ),
Point3F( 0.5f, 0.5f, 0.5f ) );
resetWorldBox();
// Add this object to the scene
addToScene();
return true;
}
void AudioTextureObject::onRemove(){
// Remove this object from the scene
removeFromScene();
if(mWarningTexture)
SAFE_DELETE(mWarningTexture);
if(mTextureTarget){
SAFE_DELETE(mTextureTarget);
}
if(mBitmap){
SAFE_DELETE(mBitmap);
}
Parent::onRemove();
}
void AudioTextureObject::setTransform( const MatrixF &mat ){
// Let SceneObject handle all of the matrix manipulation
Parent::setTransform( mat );
// Dirty our network mask so that the new transform gets
// transmitted to the client object
setMaskBits( TransformMask );
}
U32 AudioTextureObject::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ){
// Allow the Parent to get a crack at writing its info
U32 retMask = Parent::packUpdate( conn, mask, stream );
// Write our transform information
if ( stream->writeFlag( mask & TransformMask ) )
{
mathWrite(*stream, getTransform());
mathWrite(*stream, getScale());
}
// Write out any of the updated editable properties
if ( stream->writeFlag( mask & UpdateMask ) )
stream->write( mTextureName );
return retMask;
}
void AudioTextureObject::unpackUpdate( NetConnection *conn, BitStream *stream ){
// Let the Parent read any info it sent
Parent::unpackUpdate(conn, stream);
if ( stream->readFlag() ) // TransformMask
{
mathRead(*stream, &mObjToWorld);
mathRead(*stream, &mObjScale);
setTransform( mObjToWorld );
}
if ( stream->readFlag() ) // UpdateMask
{
stream->read( &mTextureName );
if ( isProperlyAdded() )
updateMaterial();
}
}
// search for TSLastDetail::render to find info on possible engine support of billboards
inline void AudioTextureObject::createGeometry(){
U32 numPoints = 12;
static const Point3F cubePoints[4] =
{
Point3F( -1.0f, 0.0f, -1.0f), Point3F( -1.0f, 0.0f, 1.0f),
Point3F( 1.0f, 0.0f, 1.0f), Point3F( 1.0f, 0.0f, -1.0f)
};
static const Point3F cubeNormals[2] =
{
Point3F( 0.0f, 1.0f, 0.0f), Point3F(0.0f, -1.0f, 0.0f)
};
static const ColorI cubeColors[4] =
{
ColorI( 128, 128, 128, 255 ),
ColorI( 255, 128, 128, 255 ),
ColorI( 128, 255, 128, 255 ),
ColorI( 128, 128, 255, 255 ),
};
static const Point2F cubeTexCoords[4] =
{
Point2F( 0, 0), Point2F( 0, 1),
Point2F( 1, 0), Point2F( 1, 1)
};
// for color
/*
static const U32 cubeFaces[12][3] =
{
{ 0, 0, 0 }, { 1, 0, 0 }, { 2, 0, 0 },
{ 2, 0, 1 }, { 3, 0, 1 }, { 0, 0, 1 },
{ 2, 1, 2 }, { 1, 1, 2 }, { 0, 1, 2 },
{ 2, 1, 3 }, { 0, 1, 3 }, { 3, 1, 3 },
};
*/
// for texture
static const U32 cubeFaces[12][3] =
{
{ 0, 0, 1 }, { 1, 0, 0 }, { 2, 0, 2 },
{ 2, 0, 2 }, { 3, 0, 3 }, { 0, 0, 1 },
{ 3, 1, 1 }, { 2, 1, 0 }, { 1, 1, 2 },
{ 1, 1, 2 }, { 0, 1, 3 }, { 3, 1, 1 },
};
// Fill the vertex buffer
VertexType *pVert = NULL;
//mVertexBuffer.set( GFX, numPoints, GFXBufferTypeStatic );
mVertexBuffer.set( GFX, numPoints, GFXBufferTypeDynamic );
pVert = mVertexBuffer.lock();
Point3F halfSize = getObjBox().getExtents() * 0.5f;
for (U32 i = 0; i < numPoints; i++)
{
const U32& vdx = cubeFaces[i][0];
const U32& ndx = cubeFaces[i][1];
//const U32& cdx = cubeFaces[i][2];
const U32& tdx = cubeFaces[i][2];
pVert[i].point = cubePoints[vdx] * halfSize;
pVert[i].normal = cubeNormals[ndx];
//pVert[i].color = cubeColors[cdx];
pVert[i].texCoord = cubeTexCoords[tdx];
}
mVertexBuffer.unlock();
// Set up our normal and reflection StateBlocks
GFXStateBlockDesc desc;
/*
desc.setCullMode( GFXCullCCW );
desc.setBlend( true );
desc.setZReadWrite( false, false );
desc.samplersDefined = true;
desc.samplers[0].addressModeU = GFXAddressWrap;
desc.samplers[0].addressModeV = GFXAddressWrap;
desc.samplers[0].addressModeW = GFXAddressWrap;
desc.samplers[0].magFilter = GFXTextureFilterLinear;
desc.samplers[0].minFilter = GFXTextureFilterLinear;
desc.samplers[0].mipFilter = GFXTextureFilterLinear;
desc.samplers[0].textureColorOp = GFXTOPModulate;
//desc.samplers[0].textureColorOp = GFXTOPAdd;
*/
//desc.samplers[0].textureColorOp = GFXTOPModulate;
// DrawBitmapStretchSR
desc.setCullMode(GFXCullCCW);
desc.setZReadWrite(false);
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.samplersDefined = true;
// Linear: Create wrap SB
desc.samplers[0] = GFXSamplerStateDesc::getWrapLinear();
//mBitmapStretchWrapLinearSB = mDevice->createStateBlock(bitmapStretchSR);
// Linear: Create clamp SB
desc.samplers[0] = GFXSamplerStateDesc::getClampLinear();
//mBitmapStretchLinearSB = mDevice->createStateBlock(bitmapStretchSR);
// Point:
desc.samplers[0].minFilter = GFXTextureFilterPoint;
desc.samplers[0].mipFilter = GFXTextureFilterPoint;
desc.samplers[0].magFilter = GFXTextureFilterPoint;
// Point: Create clamp SB, last created clamped so no work required here
//mBitmapStretchSB = mDevice->createStateBlock(bitmapStretchSR);
// The normal StateBlock only needs a default StateBlock
mNormalSB = GFX->createStateBlock( desc );
// The reflection needs its culling reversed
desc.cullDefined = true;
desc.cullMode = GFXCullCW;
mReflectSB = GFX->createStateBlock( desc );
}
void AudioTextureObject::updateMaterial()
{
//BaseMatInstance* tmpMat = MATMGR->createMatInstance( tmpMatName, getGFXVertexFormat< VertexType >() );
if(mWarningTexture.isNull()){
BaseMatInstance* tmpMat = MATMGR->createWarningMatInstance();
if(tmpMat){
const char* tmpTexName = tmpMat->getMaterial()->getDataField(StringTable->insert("diffuseMap"),"0");
if(tmpTexName){
mWarningTexture.set(String(tmpTexName),&GFXDefaultStaticDiffuseProfile,"");
//Con::warnf("AudioTextureObject::updateMaterial - setting WarningMaterial texture: %s",tmpTexName);
}
// get rid of temp material instance
SAFE_DELETE( tmpMat );
}
}
if(mTextureName.isNotEmpty()){
if(!mTextureTarget){
// create texture target object with the pointer to this object
mTextureTarget = new NamedTexTarget();
if(!mTextureTarget)
Con::errorf("AudioTextureObject::updateMaterial - could not allocate memory for new texture target.");
}
// if we have a texture target and the name is not the same as what is already set on the target
if(mTextureTarget && !mTextureName.equal( mTextureTarget->getName(), String::NoCase )){
// check to see if the name is already registered
if(!mTextureTarget->registerWithName(mTextureName)){
Con::errorf("AudioTextureObject::updateMaterial - could not register texture target name (%s). The target name may already be in use.",mTextureName.c_str());
SAFE_DELETE(mTextureTarget);
}else{
// allocate space for texture
//mTextureBuffer.set(mTexSize, mTexSize, GFXFormatR8G8B8X8, &GFXDefaultStaticDiffuseProfile, "", 0);
//mTextureBuffer.set(mTexSize, mTexSize, GFXFormatR8G8B8X8, &GFXDefaultPersistentProfile, "", 0);
if(mBitmap){
SAFE_DELETE(mBitmap);
}
mBitmap = new GBitmap(mTexSize, mTexSize, false, GFXFormatR8G8B8X8);
mBitmap->fill(ColorI(0,0,255));
mTextureBuffer1.set(mBitmap, &GFXDefaultPersistentProfile, false, String(""));
mTextureBuffer2.set(mBitmap, &GFXDefaultPersistentProfile, false, String(""));
// set the texture
mTextureTarget->setTexture(mTextureBuffer1.getPointer());
Con::warnf("AudioTextureObject::updateMaterial - setting texture to texture target: %s",mTextureName.c_str());
//
mTexture = mTextureBuffer1.getPointer();
}
}
}else{
if(mTextureTarget)
SAFE_DELETE(mTextureTarget);
}
/*
if( mMaterialName.isEmpty() )
return;
// If the material name matches then don't bother updating it.
if ( mMaterialInst && mMaterialName.equal( mMaterialInst->getMaterial()->getName(), String::NoCase ) )
return;
SAFE_DELETE( mMaterialInst );
mMaterialInst = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat< VertexType >() );
if ( !mMaterialInst )
Con::errorf( "AudioTextureObject::updateMaterial - no Material called '%s'", mMaterialName.c_str() );
mTextureName = mMaterialInst->getMaterial()->getDataField(StringTable->insert("diffuseMap"),"0");
//Con::warnf("AudioTextureObject::updateMaterial - Texture name '%s'",mTextureName.c_str());
*/
}
void AudioTextureObject::prepRenderImage( SceneRenderState *state ){
//Con::warnf("AudioTextureObject::prepRenderImage - called");
// Do a little prep work if needed
//if ( mVertexBuffer.isNull() )
createGeometry();
// Perform drawing on texture here
if(mTextureTarget){
/*
GFXTextureObject* tmpTexture = mTextureTarget->getTexture();
tmpTexture->lock();
GBitmap* tmpBitmap = tmpTexture->getBitmap();
U32 w = tmpBitmap->getWidth();
U32 h = tmpBitmap->getHeight();
tmpBitmap->fill(ColorI(0,255,0));
tmpBitmap->setColor(w/2, h/2, ColorI(255,128,128));
tmpTexture->unlock();
*/
mTexture = mTextureTarget->getTexture();
mTexture->lock();
mBitmap->fill(ColorI(0,255,0));
mTexture->unlock();
//mTexture->
// swap textures
/*
if(mTextureTarget->getTexture() == mTextureBuffer1.getPointer()){
mTextureTarget->setTexture(mTextureBuffer2.getPointer());
}else{
mTextureTarget->setTexture(mTextureBuffer1.getPointer());
}
*/
}
// only display textured object when in the editor
// if the render request is not submitted to the render pass then ::render is not called
if(!gEditingMission)
return;
// Allocate an ObjectRenderInst so that we can submit it to the RenderPassManager
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
// Now bind our rendering function so that it will get called
ri->renderDelegate.bind( this, &AudioTextureObject::render );
// Set our RenderInst as a standard object render
ri->type = RenderPassManager::RIT_Object;
// Set our sorting keys to a default value
ri->defaultKey = 0;
ri->defaultKey2 = 0;
// Submit our RenderInst to the RenderPassManager
state->getRenderPass()->addInst( ri );
}
void AudioTextureObject::render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ){
//Con::warnf("AudioTextureObject::render - called");
if ( overrideMat )
return;
if ( mVertexBuffer.isNull() )
return;
PROFILE_SCOPE(AudioTextureObject_Render);
// Set up a GFX debug event (this helps with debugging rendering events in external tools)
GFXDEBUGEVENT_SCOPE( AudioTextureObject_Render, ColorI::RED );
// GFXTransformSaver is a handy helper class that restores
// the current GFX matrices to their original values when
// it goes out of scope at the end of the function
GFXTransformSaver saver;
// Calculate our object to world transform matrix
MatrixF objectToWorld = getRenderTransform();
objectToWorld.scale( getScale() );
// Apply our object transform
GFX->multWorld( objectToWorld );
// Deal with reflect pass otherwise
// set the normal StateBlock
if ( state->isReflectPass() )
GFX->setStateBlock( mReflectSB );
else
GFX->setStateBlock( mNormalSB );
// Set up the "generic" shaders
// These handle rendering on GFX layers that don't support
// fixed function. Otherwise they disable shaders.
//GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
//GFX->setupGenericShaders( GFXDevice::GSTexture );
// Set the vertex buffer
GFX->setVertexBuffer( mVertexBuffer );
// set texture
if (!mTextureTarget){
GFX->setTexture(0, mWarningTexture);
}else{
GFX->setTexture(0, mTexture);
}
// define shader type
GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
// Draw our triangles
GFX->drawPrimitive( GFXTriangleList, 0, 4 );
}
DefineEngineMethod( AudioTextureObject, postApply, void, (),,
"A utility method for forcing a network update.n")
{
object->inspectPostApply();
}Note: I am not actually using the AudioTextureMap class so ignore. I am using the NamedTexTarget instead.About the author
I love programming, I love programming things that go click, whirr, boom. For organized T3D Links visit: http://demolishun.com/?page_id=67
#2
01/14/2013 (8:35 pm)
This is a feature I could make alot of use out of, many thanks to you if you figure out how to make it work.
#3
Take a look at imposterCapture.cpp. It does a render to texture. I will post more later.
01/15/2013 (7:56 am)
I have successfully rendered to a texture:// render to texture
if(mTextureTarget){
mGFXTextureTarget = GFX->allocRenderToTextureTarget();
mGFXTextureTarget->attachTexture(GFXTextureTarget::RenderSlot::Color0,mTextureTarget->getTexture());
GFX->pushActiveRenderTarget();
GFX->setActiveRenderTarget(mGFXTextureTarget);
GFX->clear(GFXClearTarget,ColorI(0,0,0),1.0f,0);
GFX->setStateBlock( mNormalSB );
GFX->setVertexBuffer( mVertexBuffer );
GFX->setTexture(0, mWarningTexture);
GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
GFX->drawPrimitive( GFXTriangleList, 0, 4 );
mGFXTextureTarget->resolve();
GFX->popActiveRenderTarget();
}Obviously there is a lot of detail missing, but it gives you an idea of how simple this is to do. Take a look at imposterCapture.cpp. It does a render to texture. I will post more later.
#4
01/15/2013 (8:30 am)
Sorry Frank - I should have thought of imposters when you first posted this, but I'm a little slow. I knew they did this but for some reason never put the two together.... duh.
#5
I needed to learn how the rendering pipeline worked a little better. When I started I had 0 (zero) knowledge about DirectX. Now I know I don't like DirectX, but hey I figured out how to mess with some things. I really like the abstraction layer T3D has. I mostly am not directly exposed to DirectX. This is a good thing.
I do need to try and understand how to render memory to texture as well. That is what the webctrl is doing. I sort of can do this, but it is not working right.
As for going forward I will gin up some simple render to texture examples that people can use to understand this process. The only reason I am even using a rendered object is to get the preprender function call to render to the texture.
01/15/2013 (8:38 am)
@Richard,I needed to learn how the rendering pipeline worked a little better. When I started I had 0 (zero) knowledge about DirectX. Now I know I don't like DirectX, but hey I figured out how to mess with some things. I really like the abstraction layer T3D has. I mostly am not directly exposed to DirectX. This is a good thing.
I do need to try and understand how to render memory to texture as well. That is what the webctrl is doing. I sort of can do this, but it is not working right.
As for going forward I will gin up some simple render to texture examples that people can use to understand this process. The only reason I am even using a rendered object is to get the preprender function call to render to the texture.
#6
go to www.gameinstitute.com and register an account ( NO COST)
you can then access the "Community Downloads" section. There
you will find a wide range of demonstration projects showing
different aspects of using DirectX. They are written for the
most part in C++, and cover items such as Point sprites,render to
texture, visual effects, etc. Bear in mind that not all are
in production quality code, rather for learning tools. As well
you will find some tutorials covering various related topics.
I have always found that it is easier for me to create a small
proof of concept project when trying to add features to an engine
or project.If you do not find what you seek, post here as I may
have something in archive that is not listed in the downloads.
01/15/2013 (9:12 am)
@Frank: You may find this info helpful in some way. If yougo to www.gameinstitute.com and register an account ( NO COST)
you can then access the "Community Downloads" section. There
you will find a wide range of demonstration projects showing
different aspects of using DirectX. They are written for the
most part in C++, and cover items such as Point sprites,render to
texture, visual effects, etc. Bear in mind that not all are
in production quality code, rather for learning tools. As well
you will find some tutorials covering various related topics.
I have always found that it is easier for me to create a small
proof of concept project when trying to add features to an engine
or project.If you do not find what you seek, post here as I may
have something in archive that is not listed in the downloads.
#7
Thanks. The problem has not so much been finding examples in DirectX, as much as it has been understanding which calls in T3D perform which DirectX calls.
I did find some simple examples:
www.two-kings.de/tutorials/dxgraphics/dxgraphics16.html
or
www.codesampler.com/dx9src/dx9src_7.htm#dx9_offscreen_rendering
01/15/2013 (1:37 pm)
@Dale,Thanks. The problem has not so much been finding examples in DirectX, as much as it has been understanding which calls in T3D perform which DirectX calls.
I did find some simple examples:
www.two-kings.de/tutorials/dxgraphics/dxgraphics16.html
or
www.codesampler.com/dx9src/dx9src_7.htm#dx9_offscreen_rendering
Torque Owner Demolishun
DemolishunConsulting Rocks!
www.two-kings.de/tutorials/dxgraphics/dxgraphics16.html
I will explore this later today. I don't think I can update a bitmap and get what I want. So I will do the real RTT methods described in the new link. It also talks about point sprites. This sounds very interesting especially when rendering a lot of points to a texture. This might make what I am doing a lot more efficient.