In-Layer Y-Axis Render Sort
by Paolo · in Torque Game Builder · 03/16/2006 (3:12 pm) · 37 replies
I was wondering if you guys could add a in-layer y-axis sort.
Basically, you can set Layer 0 to a default sort (where you can manipulate the in-layer order by moving things FORWARD and BACKWARD), but on Layer 1 you can set all sceneobjects within Layer 1 to sort on the y-axis for rendering.
Something like:
mySceneGraph.IsoMetricSort(Layer0, false);
mySceneGraph.IsoMetricSort(Layer1, false);
Some of the compiled code changes script suggestions on the forum don't really work out too well. The code changes to make everything sort on the y-axis affects EVERYTHING, not just within a layer. While other algorithms using TorqueScript involve double-linked list and other complex logic to create a sort order.
Since there are many games that use y-axis sorting such as RPGS, 2.5D top-down view, and Isometric games, I thought it would be good to have this functionality available for the full release of TGB.
Thanks guys for a wonderful product! I've loved T2D since it's Alpha release and can't wait for the full release of TGB!
Basically, you can set Layer 0 to a default sort (where you can manipulate the in-layer order by moving things FORWARD and BACKWARD), but on Layer 1 you can set all sceneobjects within Layer 1 to sort on the y-axis for rendering.
Something like:
mySceneGraph.IsoMetricSort(Layer0, false);
mySceneGraph.IsoMetricSort(Layer1, false);
Some of the compiled code changes script suggestions on the forum don't really work out too well. The code changes to make everything sort on the y-axis affects EVERYTHING, not just within a layer. While other algorithms using TorqueScript involve double-linked list and other complex logic to create a sort order.
Since there are many games that use y-axis sorting such as RPGS, 2.5D top-down view, and Isometric games, I thought it would be good to have this functionality available for the full release of TGB.
Thanks guys for a wonderful product! I've loved T2D since it's Alpha release and can't wait for the full release of TGB!
About the author
#2
Otherwise, you can always do it my way ;)
03/17/2006 (9:15 pm)
If you super desperatly need this ASAP, it should be simple to do with that code hack michael posted.Otherwise, you can always do it my way ;)
#3
Maybe I will code something clean enough over the next days and send the diff to Melv.
Maybe...
We will see if it gets included in one of the upcoming releases. But I am not in the position to make any promises.
03/18/2006 (12:45 am)
As I started out with RPG making too, I think this would be a nice. And this is a small feature where I would say "Why not?". Adding some of the most common layer sorting orders to the stock is a good idea.Maybe I will code something clean enough over the next days and send the diff to Melv.
Maybe...
We will see if it gets included in one of the upcoming releases. But I am not in the position to make any promises.
#4
Here are the pieces of code to add/modify:
In t2dSceneGraph.h add the top at about line 80:
The same file at line about line 220, change this
Same file, about line 310, change the setRenderSortFunction() method to this:
In file t2dSceneGraph.cc at about line 60 modify the function findLayeredObjectsCallback to this:
In t2dSceneGraph.cc at about line 3140 change the t2dSceneGraph::renderView() method to this:
Now you should be able to use different sorting methods in different layers. This code compiles fine but is not really tested.
-Michael
EDIT:
In t2dSceneGraph.cc you have to modify the scenegraph's constructor so it initializes the sorting function pointers. Go to t2dSceneGraph::t2dSceneGraph() and replace this line:
You can also delete every occurance of the mRenderSortFn member-variable (its declaration in t2dSceneGraph.h and its initialization in the constructor). It is not needed anymore.
03/18/2006 (2:04 pm)
Ok, this is quite a modification to enable different sorting per layer:Here are the pieces of code to add/modify:
In t2dSceneGraph.h add the top at about line 80:
// Add this under "Structures".
struct tRenderListLayer
{
Vector<t2dSceneObject*> mSceneObjectVector;
S32 (QSORT_CALLBACK *mRenderSortFn)(const void *, const void *);
};The same file at line about line 220, change this
typeSceneObjectVector mLayeredRenderList[t2dSceneGraph::maxLayersSupported]; ///< Layered Render-List.to this:
tRenderListLayer mLayeredRenderList[t2dSceneGraph::maxLayersSupported]; ///< Layered Render-List.
Same file, about line 310, change the setRenderSortFunction() method to this:
void setRenderSortFunction( S32 (QSORT_CALLBACK *sortFn)(const void *, const void *), const U32 layer ) { if(layer < t2dSceneGraph::maxLayersSupported) mLayeredRenderList[layer].mRenderSortFn = sortFn; };In file t2dSceneGraph.cc at about line 60 modify the function findLayeredObjectsCallback to this:
void findLayeredObjectsCallback(t2dSceneObject* pSceneObject, void* storage)
{
// Cast Callback List.
tRenderListLayer* callbackList = (tRenderListLayer*)storage;
// Add Object to List.
callbackList[pSceneObject->getLayer()].mSceneObjectVector.push_back(pSceneObject);
}In t2dSceneGraph.cc at about line 3140 change the t2dSceneGraph::renderView() method to this:
void t2dSceneGraph::renderView( const RectF viewWindow, const U32 layerMask, const U32 groupMask, CDebugStats* pDebugStats )
{
#ifdef T2D_DEBUG_PROFILING
PROFILE_START(T2D_t2dSceneGraph_renderView);
#endif
// Reset Render Stats.
pDebugStats->objectsLayerSorted = 0;
pDebugStats->objectsPotentialRender = 0;
pDebugStats->objectsActualRendered = 0;
// Clear Layered Render-List.
for ( U32 n = 0; n < t2dSceneGraph::maxLayersSupported; n++ )
mLayeredRenderList[n].mSceneObjectVector.clear();
// Find Objects in View Window.
if ( mSceneContainer.findObjects( viewWindow, layerMask, groupMask, false, false, findLayeredObjectsCallback, mLayeredRenderList, NULL ) > 0 )
{
// Step through layers.
for ( S32 layer = t2dSceneGraph::maxLayersSupported-1; layer >= 0 ; layer-- )
{
// Fetch Layer Size.
const U32 layerSize = mLayeredRenderList[layer].mSceneObjectVector.size();
// Are there any objects to render in this layer?
if ( layerSize > 0 )
{
// Are we using layer sorting?
if ( mUseLayerSorting && layerSize > 1 )
{
#ifdef T2D_DEBUG_PROFILING
PROFILE_START(T2D_t2dSceneGraph_layerSorting);
#endif
// Yes, so Quick-Sort the layer.
dQsort(mLayeredRenderList[layer].mSceneObjectVector.address(), layerSize, sizeof(t2dSceneObject*), mLayeredRenderList[layer].mRenderSortFn);
#ifdef T2D_DEBUG_PROFILING
PROFILE_END(); // T2D_t2dSceneGraph_layerSorting
#endif
// Increase Layer Sorts.
pDebugStats->objectsLayerSorted += layerSize;
}
// Viewport/Object View Intersection.
RectF viewIntersection;
// Yes, so step through objects.
for ( typeSceneObjectVector::iterator itr = mLayeredRenderList[layer].mSceneObjectVector.begin();
itr < mLayeredRenderList[layer].mSceneObjectVector.end();
itr++ )
{
// Increase Potential Renders.
pDebugStats->objectsPotentialRender++;
// Fetch more direct reference.
t2dSceneObject* pSceneObject2D = (*itr);
// Is the Object being Deleted?
if ( !pSceneObject2D->isBeingDeleted() )
{
// No, so is the object in view or always scoped?
//
// NOTE:- Object must have been initially updated.
if ( pSceneObject2D->getInitialUpdate() && (pSceneObject2D->getIsAlwaysScope() || pSceneObject2D->getIsInViewport(viewWindow, viewIntersection)) )
{
#ifdef T2D_DEBUG_PROFILING
PROFILE_START(T2D_t2dSceneGraph_renderObjects);
#endif
// Yes, so render Object.
pSceneObject2D->renderObject( viewWindow, viewIntersection );
#ifdef T2D_DEBUG_PROFILING
PROFILE_END(); // T2D_t2dSceneGraph_renderObjects
#endif
// Increase Actual Renders.
pDebugStats->objectsActualRendered++;
}
}
}
}
}
// Reset Blend Options.
t2dSceneObject::resetBlendOptions();
}
// Calculate Render Hit Percentage ( if any ).
if ( pDebugStats->objectsPotentialRender != 0 )
pDebugStats->objectsHitPercRendered = pDebugStats->objectsActualRendered * ( 100.0f / pDebugStats->objectsPotentialRender );
else
pDebugStats->objectsHitPercRendered = 100.0f;
#ifdef T2D_DEBUG_PROFILING
PROFILE_END(); // T2D_t2dSceneGraph_renderView
#endif
}Now you should be able to use different sorting methods in different layers. This code compiles fine but is not really tested.
-Michael
EDIT:
In t2dSceneGraph.cc you have to modify the scenegraph's constructor so it initializes the sorting function pointers. Go to t2dSceneGraph::t2dSceneGraph() and replace this line:
VECTOR_SET_ASSOCIATION( mLayeredRenderList[n] );with these two lines:
VECTOR_SET_ASSOCIATION( mLayeredRenderList[n].mSceneObjectVector );
mLayeredRenderList[n].mRenderSortFn = layeredRenderSort;You can also delete every occurance of the mRenderSortFn member-variable (its declaration in t2dSceneGraph.h and its initialization in the constructor). It is not needed anymore.
#5
03/18/2006 (2:33 pm)
Wow Michael! THANKS! I'll have to test it thoroughly because this would be a great standard addition.
#6
this is just the c++ modification. Script-side is not in there yet. I'll add this later but right now the sun is shining to beautifully to sit in front of the computer ;)
03/18/2006 (2:36 pm)
Paolo,this is just the c++ modification. Script-side is not in there yet. I'll add this later but right now the sun is shining to beautifully to sit in front of the computer ;)
#7
I thought programmers never get to see the sun.... Or have girlfriends.... Or have a life.... :)
Question, are there any good resources to start deciphering the actual Torque source code? The more I look at it, the more I am awed by the genius of the programmers who created this engine.
03/18/2006 (2:39 pm)
Michael,I thought programmers never get to see the sun.... Or have girlfriends.... Or have a life.... :)
Question, are there any good resources to start deciphering the actual Torque source code? The more I look at it, the more I am awed by the genius of the programmers who created this engine.
#8
03/18/2006 (7:22 pm)
@Paolo, check the C++ tutorials on TGB's TDN page. Though I wish there were more too
#9
after spending an hour without sun, here is the code to tie the above to the console:
t2dCustomLayerSorting.h
t2dCustomLayerSorting.cc
Again, this code compiles fine, does not crash but is not really tested.
You can add every sorting function you want like the ones in the Callback section.
Switching sorting functions is done with:
I hope this turns out to be useful. :)
-Michael
03/20/2006 (12:02 pm)
Ok,after spending an hour without sun, here is the code to tie the above to the console:
t2dCustomLayerSorting.h
#ifndef _T2DCUSTOMLAYERSORTING_H_
#define _T2DCUSTOMLAYERSORTING_H_
typedef S32 (QSORT_CALLBACK *RenderSortFunction)(const void *, const void *);
class RenderSortCallback {
public:
RenderSortCallback(const char* name, RenderSortFunction sortFn);
static RenderSortFunction getFunction(const char* name) { return getFunctionList() ? getFunctionList()->find(name) : 0; }
static void printAvailableSortFunctions();
private:
struct Entry {
Entry(const char* name, RenderSortFunction sortFn, Entry* next);
const char* mName;
RenderSortFunction mSortFn;
Entry* mNext;
RenderSortFunction find(const char* name) const;
};
static Entry*& getFunctionList();
};
#endift2dCustomLayerSorting.cc
#include "platform/platform.h"
#include "console/console.h"
#include "T2D/t2dSceneObject.h"
#include "T2D/t2dSceneGraph.h"
#include "./t2dCustomLayerSorting.h"
//-----------------------------------------------------------------------------
RenderSortCallback::Entry::Entry(const char *name, RenderSortFunction sortFn, RenderSortCallback::Entry *next):
mName(name),
mSortFn(sortFn),
mNext(next)
{
}
//-----------------------------------------------------------------------------
RenderSortFunction RenderSortCallback::Entry::find(const char *name) const
{
if( dStricmp(mName,name) == 0 )
{
return mSortFn;
}
return mNext ? mNext->find(name) : 0;
}
//-----------------------------------------------------------------------------
RenderSortCallback::RenderSortCallback(const char* name, RenderSortFunction sortFn)
{
if(getFunctionList() && getFunctionList()->find(name))
{
Con::errorf("RenderSortFunctions::RenderSortFunctions() - Error! Name %s registered twice!", name);
AssertFatal(false,"");
}
else
{
int len = dStrlen(name);
char* nameBuffer = new char[len+1];
dStrcpy(nameBuffer, name);
getFunctionList() = new Entry(nameBuffer, sortFn, getFunctionList());
}
}
//-----------------------------------------------------------------------------
void RenderSortCallback::printAvailableSortFunctions()
{
Con::printf("Available Layer Render sorting functions");
for(RenderSortCallback::Entry* walk = getFunctionList(); walk; walk = walk->mNext)
Con::printf("-%s", walk->mName);
}
//-----------------------------------------------------------------------------
RenderSortCallback::Entry*& RenderSortCallback::getFunctionList()
{
static RenderSortCallback::Entry* functionList = 0;
return functionList;
}
//-----------------------------------------------------------------------------
// CONSOLE
//-----------------------------------------------------------------------------
ConsoleMethod(t2dSceneGraph, setRenderSortFunction, void, 4, 4, "t2dSceneGraph::setRenderSortFunction(%name,%layer)")
{
argc;
const U32 layer = dAtoi(argv[3]);
if( layer > t2dSceneGraph::maxLayersSupported )
{
Con::warnf("t2dSceneGraph::setRenderSortFunction() - Warning! %u is no valid layer.", layer);
return;
}
RenderSortFunction sortFn = RenderSortCallback::getFunction( argv[2] );
if( !sortFn )
{
Con::warnf("t2dSceneGraph::setRenderSortFunction() - Warning! %s is no valid render sort function.", argv[2]);
Con::warnf("t2dSceneGraph::setRenderSortFunction() - Call \"printAvailableRenderSortFunctions()\" for a list of available functions.");
return;
}
object->setRenderSortFunction( sortFn, layer );
}
//-----------------------------------------------------------------------------
ConsoleFunction(printAvailableRenderSortFunctions,void,1,1,"Prints available functions for sorting scene-objects within a layer.")
{
RenderSortCallback::printAvailableSortFunctions();
}
//-----------------------------------------------------------------------------
// Callbacks
//-----------------------------------------------------------------------------
static RenderSortCallback standardLayeredSort("standardLayeredSort", t2dSceneGraph::layeredRenderSort);
//-----------------------------------------------------------------------------
static S32 QSORT_CALLBACK isoRenderSortFn(const void* a, const void* b)
{
// Sort by Y - coordinate
const t2dSceneObject& objA = **((t2dSceneObject**)a);
const t2dSceneObject& objB = **((t2dSceneObject**)b);
return (objA.getPosition().mY + objA.getHalfSize().mY) - (objB.getPosition().mY + objB.getHalfSize().mY);
}
static RenderSortCallback isoRenderSortCallback("isoSortFunction",isoRenderSortFn);
//-----------------------------------------------------------------------------
static S32 QSORT_CALLBACK sortByY(const void* a, const void* b)
{
// Sort by Y - coordinate
const t2dSceneObject& objA = **((t2dSceneObject**)a);
const t2dSceneObject& objB = **((t2dSceneObject**)b);
return (objA.getPosition().mY) - (objB.getPosition().mY);
}
static RenderSortCallback sortByYCallback("sortByY", sortByY);
//-----------------------------------------------------------------------------
static S32 QSORT_CALLBACK sortByX(const void* a, const void* b)
{
// Sort by X - coordinate
const t2dSceneObject& objA = **((t2dSceneObject**)a);
const t2dSceneObject& objB = **((t2dSceneObject**)b);
return (objA.getPosition().mX) - (objB.getPosition().mX);
}
static RenderSortCallback sortByXCallback("sortByX", sortByX);
//-----------------------------------------------------------------------------
static S32 QSORT_CALLBACK isoRenderSortMod(const void* a, const void* b)
{
// Sort by Y - coordinate
t2dSceneObject& objA = **((t2dSceneObject**)a);
t2dSceneObject& objB = **((t2dSceneObject**)b);
return (objA.getPosition().mY + dAtof(objA.getDataField(StringTable->insert("renderSortMod"),0)) ) - (objB.getPosition().mY + dAtof(objA.getDataField(StringTable->insert("renderSortMod"),0)));
}
static RenderSortCallback isoSortModCallback("isoRenderSortMod",isoRenderSortMod);
//-----------------------------------------------------------------------------Again, this code compiles fine, does not crash but is not really tested.
You can add every sorting function you want like the ones in the Callback section.
Switching sorting functions is done with:
$someSceneGraph.setRenderSortFunction( %functionName, %layer );
I hope this turns out to be useful. :)
-Michael
#10
03/20/2006 (2:36 pm)
Michael: what would be the advantage of this code over the code you posted on the other forum? from what i understand is that the other code Y sorts everything, and this is per layer?? thanks!
#11
sorting always is applied within one layer. Layer 0 will always be above layer 1 - regardless of any sorting.
This code gives you the ability to sort different layers with different sorting methods. Normally all layers would be sorted by the same way. Now you can have layer 3 sorted by Y-values and layer 9 sorted by X-values.
And this code gives you the ability to switch between different sorting method from script in a cleaner way than the other.
The code in the other thread was just a quick hack. But it may well be sufficient for your needs.
03/20/2006 (5:26 pm)
James,sorting always is applied within one layer. Layer 0 will always be above layer 1 - regardless of any sorting.
This code gives you the ability to sort different layers with different sorting methods. Normally all layers would be sorted by the same way. Now you can have layer 3 sorted by Y-values and layer 9 sorted by X-values.
And this code gives you the ability to switch between different sorting method from script in a cleaner way than the other.
The code in the other thread was just a quick hack. But it may well be sufficient for your needs.
#12
03/20/2006 (8:53 pm)
Sweet michael, you should try to get melv et. all to put this in the next release! i think the concept offers great utility to tgb devs.
#13
I'll probably do some testing and then post this as a resource. But Melv said this may get included some time in the future.
03/21/2006 (1:34 pm)
Jason,I'll probably do some testing and then post this as a resource. But Melv said this may get included some time in the future.
#14
I will try this one tonight!
03/21/2006 (3:49 pm)
Now that I think about it, I knew that........ i'm a bit slow sometimes :)I will try this one tonight!
#15
Error from Executable Runner: Torque2D-MacCarb-Release has exited due to signal 10 (SIGBUS).
Any idea what is going on??? I triple checked what you posted above, and everything is exactly the same. thanks for all your help :)
03/23/2006 (2:50 am)
@Michael: OK i finally got a chance to try that out. The code compiles without errors, and TGB starts fine, but the problem starts when you try to run a game. As soon as I click fish demo or run game it immediately freezes. the only thing i can see that Xcode is saying is:Error from Executable Runner: Torque2D-MacCarb-Release has exited due to signal 10 (SIGBUS).
Any idea what is going on??? I triple checked what you posted above, and everything is exactly the same. thanks for all your help :)
#16
I just have looked into this and noticed that I forgot to post one modification. I'll add it in the above post under "EDIT".
03/23/2006 (8:16 am)
Sorry James,I just have looked into this and noticed that I forgot to post one modification. I'll add it in the above post under "EDIT".
#17
i'll try it when i get home tonight!
03/23/2006 (1:43 pm)
Ah awesome. thats a ton michael, you have been ridiculously helpful :)i'll try it when i get home tonight!
#18
and this is coming up in the console:
T2D/gameScripts/game.cs (22): Unable to find object: ' ' attempting to call function 'setRenderSortFunction'
sorry for all the trouble ;)
03/23/2006 (11:17 pm)
@Michael: haha ok its always something, i swear. its no longer freezing, but when i call the render function i'm getting an error message. i think i'm just calling it wrong. i have:$t2dScene.setRenderSortFunction( %sortByY, 0 );
and this is coming up in the console:
T2D/gameScripts/game.cs (22): Unable to find object: ' ' attempting to call function 'setRenderSortFunction'
sorry for all the trouble ;)
#19
03/23/2006 (11:22 pm)
It has to bet2dScene.setRenderSortFunction( "sortByY", 0 );given your scenegraph is called 't2dScene'. Then it should work :)
#20
however... this one seems to render a bit differently than the last one. the hack seems to have better handling of different sized sprites. in specific, my character would stay behind a larger sprite until the bottom his sprite was below the bottom of the large one. now, its maybe 1/2 an inch up when he comes to the front. i'm going to look at the code on both and try to understand why. thanks for everything, again :)
haha nevermind... now i understand halfsize :-)
michael, you rock my friend.
03/23/2006 (11:49 pm)
Oops, haha that was obvious. sorry about that.however... this one seems to render a bit differently than the last one. the hack seems to have better handling of different sized sprites. in specific, my character would stay behind a larger sprite until the bottom his sprite was below the bottom of the large one. now, its maybe 1/2 an inch up when he comes to the front. i'm going to look at the code on both and try to understand why. thanks for everything, again :)
haha nevermind... now i understand halfsize :-)
michael, you rock my friend.
Torque Owner Kneekick