Peter's Grid System        

Introduction

After some digging around about grid systems and trying Jeroen Dierckx's EditorGridSystem, we decided to write our own grid system with different feature set and release it to the community, because Ogre is si cool.

Screenshots

Achieved with the default settings but with material that respects alpha blending. XY3D.jpg
XYXZ3D.jpg ALL3D.jpg

Features


Following features are supported by Peter's Grid System:

  • This grid have three planes support - XY, XZ, YZ that can be visible/hidden
  • Grid is generated/updated by request, uses ManualObject
  • Limitations in each dimension
  • Cell size per dimension
  • Grid Offset
  • Colors for offset, master and division axis,
  • Does NOT depend from the camera type used,
  • Not limited to one instance (no named items used!)
  • Not built in, the Snap to grid feature's code is in this wiki page

Usage


First, get the source from below and just add OgreGrid.cpp in your project. Include OgreGrid.h and enjoy.

To create a grid, just create an instance of the OgreGrid class and set the properties you want to, then call update()

Note that you MUST call update() after construction, as the grid is not generated, it's only setup to some maybe-reasonable defaults.

View C++ code - Minimal, gives the first screenshot's grid
/*OgreGrid*/ grid = new OgreGrid(/*Ogre::SceneManager*/scene, /*Material name*/"BaseWhiteAlphaBlended");

    //Attach it to some SceneNode
    grid->attachToNode(scene->getRootSceneNode());

    //Generate the grid
    grid->update();

The next example gives the grid from the last screenshot:

View C++ code - Some customizations
/*OgreGrid*/ grid = new OgreGrid(/*Ogre::SceneManager*/scene, /*Material name*/"BaseWhiteAlphaBlended");

    //Where to draw the grid?
    grid->drawQueue = Ogre::RENDER_QUEUE_OVERLAY;

    //Attach it to some SceneNode
    grid->attachToNode(scene->getRootSceneNode());

    //Some grid settings
    grid->setCellSize(0.5);
    grid->ymin = -9;
    grid->ymax = 2;
    grid->ofY = 2;
    grid->zmin = -2;
    grid->zmax = 1;
    grid->showXZ = true;
    grid->showXY = true;
    grid->showYZ = true;
    grid->setDivisions(10);
    grid->colorDivision = Ogre::ColourValue(0,1,0,0.3);
    grid->colorOffset = Ogre::ColourValue(0,0,1,1);
    grid->colorMaster = Ogre::ColourValue(1,0,0,0.7);

    //Re/generate the grid
    grid->update();



Options


After creating an instance to the grid class, you can set the options of the grid you want to. After you change option, you must call update() to update the grid manually. All options are public data members with the exception of some convenience setters.

Cell sizes

float csX, csY, csZ are used to control the master grid axes distance, per dimention. You may have a grid with cell size 2x1 in the X/Y dimension. You can use grid sizes such as 0.5x0.5. You can also use setCellSize(float size) and setCellSize(float x, float y, float z) for this purpose.

Zero offset

float ofX, ofY, ofZ are used to move the center of the grid in each dimension. You can also use setOffset(float x, float y, float z)

Division count

int divX, divY, divZ are used to set the count of subdivisions in each dimension. You can also use setDivisions(int n) and setDivisions(int x,int y,int z) for this purpose. To show or hide the divisions and use only the master axes, set bool showDivisions

Grid range limitations

Our grid is limited to specified range in each dimension. If axis is limited from -5 to +5, only these cells that are in this range will be drawn for the given dimension. Limitations are set via int xmin, xmax, ymin, ymax, zmin, zmax. Note that the default limitations are from -5 to +5, and the system does not support infinite grids still. In the future, I may decide to implement infinite planes. Also, setting huge limitations will generate too much lines and may affect performance, especially if there are too much divisions, as well. If you want infinite 2D grid, you should consider Jeroen Dierckx's EditorGridSystem instead.

Shown planes

You can set which grid plane is drawn by setting bool showXY, showXZ, showYZ as you like. By default, only the XY plane is shown. You can also use showPlanes(bool xy, bool xz, bool yz) for this purpose.

Colors

  • colorMaster - The color of master lines. Master lines are drawn each cell size units.
  • colorOffset - The color of the offset lines, e.g. these that pass thru (0,0,0)+Grid's offset
  • colorDivision - The color of the division lines. You usually want to set lighter color for these.

Please note that the default material, "BaseWhiteNoLighting", does not support alpha from the vertex colors. Peter's Grid System uses vertex colors for grid lines color, and all this is within 1 section in 1 ManualObject. If you want alpha, you can use the following material:

View material
material "BaseWhiteAlphaBlended"
{
	receive_shadows on
	transparency_casts_shadows off
	technique
	{
		lod_index 0
		scheme Default
		pass
		{
			lighting off
			max_lights 8
			start_light 0
			iteration once
			point_size 4
			point_sprites off
			point_size_attenuation off
			point_size_min 0
			point_size_max 0
			scene_blend alpha_blend
			depth_check on
			alpha_rejection always_pass 0
			alpha_to_coverage off
			transparent_sorting on
			depth_write on
			depth_func less_equal
			depth_bias 0 0
			iteration_depth_bias 0
			light_scissor off
			light_clip_planes off
			cull_hardware clockwise
			cull_software back
			shading gouraud
			polygon_mode solid
			polygon_mode_overrideable on
			normalise_normals off
			fog_override false
		}
	}
}

Other notes


If you want just to set the grid's offset, you may directly set the position of the mOwnNode. If you call update(), this will be lost, however. This way, it's faster that setting the offset and updating, as it didn't regenerate the grid. You may also need the global transform of mOwnNode.

If you want to snap something to the grid, you can use the following code:

Snap this C++ code to the grid...
Ogre::Vector3 r = /*the point you need, in world space*/;

    //should we snap to divisions or just to master lines?
    bool snapToDivs = true;
    Ogre::Vector3 divs = snapToDivs ? Ogre::Vector3(grid->divX, grid->divY, grid->divZ) : Ogre::Vector3(1,1,1);

    //undo the scale, translation and rotation of the Grid Plane and multiply to division count
    r = grid->mOwnNode->_getFullTransform().inverse() * r;
    r *= divs;

    //snap the point! Just a round!
    r.x = round(r.x);
    r.y = round(r.y);
    r.z = round(r.z);

    //divide the divisions, redo the scale, translation and rotation of the Grid Plane
    r /= divs;
    r = grid->mOwnNode->_getFullTransform() * r;

    //and use r as you like. It's now snapped to the nearest grid point.
    //Note that it does not mean grid line, as the third coordinate is also respected!
    //If you draw the XY plane at z=0, but the point is in z=9,87, the snapped point's z will be 10

So? Where can I get this?


Copy-paste from here:

The header file that you usually want to #include

OgreGrid.h
/*
-----------------------------------------------------------------------------
This source file is supposed to be used with OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

And this grid is 3D-enabled, but limited with min/max, and
not a FrameListener... you can make it a frameListener if you want to!

Copyright (c) 2013 Peter Petrov, Pi-Dev, Bulgaria
Inspired by Jeroen Dierckx's EditorGridSystem, but rewritten from scratch.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#ifndef OGRE_GRID_SYSTEM_H
#define OGRE_GRID_SYSTEM_H
 
// Includes
#include <Ogre.h>

/*
    Ogre Grid by PeterSvP

    • This grid have three planes support - XY, XZ, YZ,
    • limitations in each dimension (required)
    • cell size per dimension
    • offset
    • colors for offset, master and division axis,
    • does NOT depend from the camera used,
    • not limited to one instance (NO NAMED items used!)

    If you need infinite 2D grid, write code that alters the min and max on the fly,
    OR use the Jeroen Dierckx's grid system
*/

class OgreGrid //: public RenderTargetListener - if you want to, I didn't needed this
{
private:
    OgreGrid(); //Do NOT copy and create directly...
public:

    explicit OgreGrid(Ogre::SceneManager* scene, Ogre::String materialName="BaseWhiteNoLighting");
    ~OgreGrid();

    //The grid's ManualObject
    Ogre::ManualObject* mGrid;

    /*
        mOwnNode - under this node is the manual object.
        The Cell size is achieved by scaling this node.
        The Offset is achieved by translating this node.
        use attachToNode() function to attach the grid to your own node.
        mAttachedNode is the node that the grid is attached.
        mOwnNode is created by this grid, you should not destroy it.
        mAttachedNode is node from your own application.
        To transform and rotate the grid, you should manipulate your own node.
        The Grid's Zero offset and Cell Size are grid features despite the fact that their imlementation
        is done by just translation and scaling mOwnNode.
        You may need to access the mOwnNode itself to get its transfrmation if you need to, but altering it
        will be lost on next call to update().
        An example snapping function is provided in the Ogre's wiki and forum post
     */
    Ogre::SceneNode *mOwnNode, *mAttachedNode;

    //The Scene Manager.
    Ogre::SceneManager* mScene;

    //The material to use for the ManualObject.
    //Note that BaseWhiteNoLighting does not support alpha blending
    Ogre::String material;

    //These are the grid settings.
    // After you complete them, call update() to regenerate the grid.
    // These are public data members as intended, don't blame me for not using setters/getters

    //Cell sizes
    float csX, csY, csZ;

    //Grid's (0,0,0) offset
    float ofX, ofY, ofZ;

    //Number of divisions in each dimension
    int divX, divY, divZ;

    //Will the divisions be shown?
    bool showDivisions;

    //The grid is limited in each dimension, and these are its min and max limiters
    int xmin, xmax,
        ymin, ymax,
        zmin, zmax;

    // Which planes to show? You can show only planes you need
    bool showXY, showXZ, showYZ;

    //offset color is the color of (0,0,0) axis
    //master color is the usual master color
    //division color is the color of the subdivisions
    Ogre::ColourValue colorMaster, colorOffset, colorDivision;

    //The Ogre RenderQueue where to draw the grid - RENDER_QUEUE_MAIN/OVERLAY/BACKGROUND/ any int
    int drawQueue;

    // These setters are for convenience only, you MUST call update() after use!
    inline void setCellSize(float size){csX=csY=csZ=size;}
    inline void setCellSize(float x, float y, float z){csX=x;csY=y,csZ=z;}
    inline void setOffset(float x, float y, float z){ofX=x,ofY=y,ofZ=z;}
    inline void setDivisions(int n){divX=divY=divZ=n;}
    inline void setDivisions(int x,int y,int z){divX=x;divY=y;divZ=z;}
    inline void showPlanes(bool xy, bool xz, bool yz){showXY=xy;showXZ=xz;showYZ=yz;}

    //Attach the grid to your provided sceneNode.
    void attachToNode(Ogre::SceneNode* node);

    //After setup, call this to regenerate the Grid.
    void update();

    //Grid's visibility
    void hide();
    void show();
};
 
#endif // OGRE_GRID_SYSTEM_H

And the implementation file that you must ADD to your project.

OgreGrid.cpp
/*
-----------------------------------------------------------------------------
This source file is supposed to be used with OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

And this grid is 3D-enabled, but limited with min/max, and
not a FrameListener... you can make it a frameListener if you want to!

Copyright (c) 2013 Peter Petrov, Pi-Dev, Bulgaria
Inspired by Jeroen Dierckx's EditorGridSystem, but rewritten from scratch.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
// Includes
#include "OgreGrid.h"
#include <Ogre.h>
using namespace Ogre;
 
OgreGrid::OgreGrid(SceneManager *scene, String name):
    mScene(scene),
    mAttachedNode(0),
    material(name)
{
    assert(scene);
    mOwnNode = scene->createSceneNode();
    mGrid = scene->createManualObject();
    mGrid->setDynamic(true);
    mOwnNode->attachObject(mGrid);

    //now set the default settings, the default grid will be xy plane, -5 to +5 with 10 divisions
    drawQueue = 1;    //assuming that Background is not always the best place
    csX = csY = csZ = 1;    //cell sizes
    ofX = ofY = ofZ = 0;    //offsets
    xmin = ymin = zmin = -5;
    xmax = ymax = zmax = 5;
    showXY = true;
    showXZ = showYZ = false;
    colorOffset = ColourValue(0,0,0,1);
    colorMaster = ColourValue(0,0,0,0.4);
    colorDivision = ColourValue(0,0,0,0.1);
    showDivisions = true;
    divX = divY = divZ = 10;
    //unfortunately, you MUST call update() when you complete the set-up! No mesh id generated still, just these default settings
}

void OgreGrid::attachToNode(SceneNode *node)
{
    //if already attached, deattach it first
    if(mAttachedNode)mAttachedNode->removeChild(mOwnNode);
    //attach to the new node
    mAttachedNode = node;
    node->addChild(mOwnNode);
}

OgreGrid::~OgreGrid()
{
    //deattach
    if(mAttachedNode)
        mAttachedNode->removeChild(mOwnNode);
    //remove the manualobject and the own node
    mScene->destroyManualObject(mGrid);
    mScene->destroySceneNode(mOwnNode);
}

void OgreGrid::hide()
{
    mOwnNode->setVisible(false);
}
void OgreGrid::show()
{
    mOwnNode->setVisible(true);
}

void OgreGrid::update()
{
    //Generate the grid mesh
    ManualObject* o = mGrid;

    //set the render queue
    o->setRenderQueueGroup(drawQueue);

    //offset and cell size
    mOwnNode->setPosition(ofX,ofY,ofZ);
    mOwnNode->setScale(csX,csY,csZ);

    Ogre::ColourValue* cl;
    Real r;

    //Clear the manual object to completely regenerate it
    o->clear();
    o->begin(material, RenderOperation::OT_LINE_LIST);
    if(showXY)
    {
        //major lines
        //X
        for(int x=xmin;x<=xmax;++x)
        {
            if(x==0) cl = &colorOffset;
            else cl = &colorMaster;
            o->position(x,ymin,0);
            o->colour(*cl);
            o->position(x,ymax,0);
            o->colour(*cl);
            //divisions
            if(x<xmax && showDivisions && divX)
            {
                for(int d=1;d<divX;++d)
                {
                    r = x+ (1.0/Real(divX))*Real(d);
                    o->position(r,ymin,0);
                    o->colour(colorDivision);
                    o->position(r,ymax,0);
                    o->colour(colorDivision);
                }
            }
        }

        //Y
        for(int y=ymin;y<=ymax;++y)
        {
            if(y==0) cl = &colorOffset;
            else cl = &colorMaster;
            o->position(xmin,y,0);
            o->colour(*cl);
            o->position(xmax,y,0);
            o->colour(*cl);
            //divisions
            if(y<ymax && showDivisions && divY)
            {
                for(int d=1;d<divY;++d)
                {
                    r = y+ (1.0/Real(divY))*Real(d);
                    o->position(xmin,r,0);
                    o->colour(colorDivision);
                    o->position(xmax,r,0);
                    o->colour(colorDivision);
                }
            }
        }
    }


    if(showXZ)
    {
        //major lines
        //X
        for(int x=xmin;x<=xmax;++x)
        {
            if(x==0) cl = &colorOffset;
            else cl = &colorMaster;
            o->position(x,0,zmin);
            o->colour(*cl);
            o->position(x,0,zmax);
            o->colour(*cl);
            //divisions
            if(x<xmax && showDivisions)
            {
                for(int d=0;d<divX;++d)
                {
                    r = x+ (1.0/Real(divX))*Real(d);
                    o->position(r,0,zmin);
                    o->colour(colorDivision);
                    o->position(r,0,zmax);
                    o->colour(colorDivision);
                }
            }
        }

        //Z
        for(int z=zmin;z<=zmax;++z)
        {
            if(z==0) cl = &colorOffset;
            else cl = &colorMaster;
            o->position(xmin,0,z);
            o->colour(*cl);
            o->position(xmax,0,z);
            o->colour(*cl);
            if(z<zmax && showDivisions)
            {
                //divisions
                for(int d=0;d<divZ;++d)
                {
                    r = z+ (1.0/Real(divZ))*Real(d);
                    o->position(xmin,0,r);
                    o->colour(colorDivision);
                    o->position(xmax,0,r);
                    o->colour(colorDivision);
                }
            }
        }
    }

    //YZ
    if(showYZ)
    {
        //major lines
        //Y
        for(int y=ymin;y<=ymax;++y)
        {
            if(y==0) cl = &colorOffset;
            else cl = &colorMaster;
            o->position(0,y,zmin);
            o->colour(*cl);
            o->position(0,y,zmax);
            o->colour(*cl);
            //divisions
            if(y<ymax && showDivisions)
            {
                for(int d=0;d<divY;++d)
                {
                    r = y+ (1.0/Real(divY))*Real(d);
                    o->position(0,r,zmin);
                    o->colour(colorDivision);
                    o->position(0,r,zmax);
                    o->colour(colorDivision);
                }
            }
        }

        //Z
        for(int z=zmin;z<=zmax;++z)
        {
            if(z==0) cl = &colorOffset;
            else cl = &colorMaster;
            o->position(0,ymin,z);
            o->colour(*cl);
            o->position(0,ymax,z);
            o->colour(*cl);
            //divisions
            if(z<zmax && showDivisions)
            {
                for(int d=0;d<divZ;++d)
                {
                    r = z+ (1.0/Real(divZ))*Real(d);
                    o->position(0,ymin,r);
                    o->colour(colorDivision);
                    o->position(0,ymax,r);
                    o->colour(colorDivision);
                }
            }
        }
    }

    o->end();
}