Hello !
Thanks again for the answer !
So, here is what I did with the HLMS. It is really a small code, but I hope it will be useful to other users, if they want to understand everything.
Feel free to correct everything you wish !
First, let's see what are the shaders we will be working with. I didn't use templates for this... And this is GLSL.
Vertex shader :
Code: Select all
#version 330
// Do not forget this line first. I had problems with the parser if it wasn't before the other layout vars. Moreover, it indicates some infos to the compiler (see Dark_Sylinc's answer)
layout (std140) uniform ;
// Inputs : those are key words for Ogre, i believe. You can't mess with it
in vec4 vertex ;
in vec3 normal ;
in vec2 uv0 ;
// The drawId is some info given by you in the HLMS to track which Ogre::Item is getting its draw.
in uint drawId ;
// We will only use the binding 2 here
layout (binding = 2) uniform entityBuffer
{
// The array is here because our buffer will contain the datas for (max) 1024 Ogre::Item. The drawId is here to help you get the good index.
// As a ConstBuffer will have a fixed size, if you change this uniform's structure, the array will be shorter (again, see Dark_Sylinc's answer). Be careful with the memory padding too.
mat4 mvpMat [1024] ;
} entityVars ;
// Output
out gl_PerVertex
{
vec4 gl_Position ;
} ;
// Fragment shader's output
out block
{
vec3 normal ;
vec2 uv ;
} outPs ;
void main ()
{
// We simply compute the real position
gl_Position = entityVars.mvpMat[drawId] * vertex ;
// And some Fragment shaders infos
outPs.normal = normal ;
outPs.uv = uv0 ;
}
And then its Fragment shader :
Code: Select all
#version 330
// Again, do not forget this line
layout (std140) uniform ;
// Input
layout (binding = 0) uniform passBuffer
{
// vec3 LightDir + float (as memory padding will pack it with a vec4, even if we give a vec3 only)
vec4 lightDir ;
vec4 diffuseColour ;
} passVars ;
// Some texture
uniform sampler2D baseTex ;
// Datas from the Vertex shader
in block
{
vec3 normal ;
vec2 uv ;
} inPs ;
// Output
out vec3 fragColor ;
void main ()
{
// Diffuse component and a texture part
// This is a directional light
fragColor = max(passVars.diffuseColour.rgb * dot(-passVars.lightDir.xyz, inPs.normal), 0.0) * texture(baseTex, inPs.uv).rgb ;
}
Then, here is a simple HlmsBufferManager that will prepare only ConstBuffers (no TexBuffer I mean). It will be something shared among the HLMS (having to prepare the buffers), so every HLMS will inherit from it.
Note that you can find the complete versions within the PBS and Unlit HLMS in Ogre sources.
Here is the header (HlmsBufferManager.h)
Code: Select all
//////////////////////////////////////////////
// HlmsBufferManager.h //
// //
// //
//////////////////////////////////////////////
#ifndef __HlmsBufferManager
#define __HlmsBufferManager
// Forward declarations
namespace Ogre
{
class VaoManager ;
class SceneManager ;
class Renderable ;
class CompositorShadowNode ;
class CommandBuffer ;
struct QueuedRenderable ;
}
#include <OgrePrerequisites.h>
#include <OgreArchive.h>
#include <OgreHardwareVertexBuffer.h>
#include <OgreHlms.h>
#include <vector>
// This one will only be an HLMS
class HlmsBufferManager : public Ogre::Hlms
{
protected :
// Attributes
// The VAO Manager (useful for the buffer's creation)
Ogre::VaoManager* _vaoManager ;
// The buffers in an array
std::vector<Ogre::ConstBufferPacked*> _constBuffers ;
// The active buffer
Ogre::uint32 _currentConstBuffer ;
// Some pointers to those buffers
Ogre::uint32* _bufferStartIndex ;
Ogre::uint32* _bufferCurrentIndex ;
// A buffer size
Ogre::uint32 _bufferSize ;
public :
// Constructor, destructor
HlmsBufferManager (Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders, Ogre::VaoManager* vaoManager, std::string name, Ogre::HlmsTypes type) ;
~HlmsBufferManager () ;
// HLMS functions
virtual const Ogre::HlmsCache* createShaderCacheEntry (Ogre::uint32 renderableHash, const Ogre::HlmsCache& passCache, Ogre::uint32 finalHash, const Ogre::QueuedRenderable& queuedRenderable) ;
virtual void calculateHashFor (Ogre::Renderable* renderable, Ogre::uint32& outHash, Ogre::uint32& outCasterHash) ;
virtual Ogre::HlmsCache preparePassHash (const Ogre::CompositorShadowNode* shadowNode, bool casterPass, bool dualParaboloid, Ogre::SceneManager* sceneManager) ;
virtual Ogre::uint32 fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer) = 0 ;
virtual Ogre::uint32 fillBuffersForV1 (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer) ;
virtual Ogre::uint32 fillBuffersForV2 (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer) ;
virtual void preCommandBufferExecution (Ogre::CommandBuffer* command) ;
virtual void postCommandBufferExecution (Ogre::CommandBuffer* command) ;
virtual void frameEnded () ;
// Buffers functions
Ogre::uint32* mapNextConstBuffer (Ogre::CommandBuffer* command) ;
void unmapConstBuffer () ;
} ;
#endif
The cpp code :
Code: Select all
//////////////////////////////////////////////
// HlmsBufferManager.cpp //
// //
// //
//////////////////////////////////////////////
/// Includes ---------------------------------
// Locals
#include "HlmsBufferManager.h"
// Natives
#include <cstddef>
#include <OgreRenderQueue.h>
#include <OgreRenderable.h>
#include <CommandBuffer/OgreCbShaderBuffer.h>
#include <CommandBuffer/OgreCommandBuffer.h>
#include <Vao/OgreConstBufferPacked.h>
#include <Vao/OgreVaoManager.h>
/// Constructor, destructor ------------------
HlmsBufferManager::HlmsBufferManager (Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders, Ogre::VaoManager* vaoManager, std::string name, Ogre::HlmsTypes type)
: Ogre::Hlms (type, name, dataFolder, libraryFolders),
_vaoManager (vaoManager),
_constBuffers (),
_currentConstBuffer (0),
_bufferStartIndex (NULL),
_bufferCurrentIndex (NULL),
_bufferSize (0)
{
// We register it with the type given
// You can register it with every id available, as long as it does not overlap another registered HLMS' id (see Dark_Sylinc's answer).
// Concerning the dataFolder and the libraryFolder, the shaders in those will be parsed (this is where you have your templates).
}
HlmsBufferManager::~HlmsBufferManager ()
{
// Buffer delete
for (unsigned int i = 0 ; i < _constBuffers.size() ; i++)
{
// See if it is unmaped or not
if (_constBuffers[i]->getMappingState() != Ogre::MS_UNMAPPED)
_constBuffers[i]->unmap(Ogre::UO_UNMAP_ALL) ;
// We can destroy it
_vaoManager->destroyConstBuffer(_constBuffers[i]) ;
}
}
/// Hlms Functions ---------------------------
const Ogre::HlmsCache* HlmsBufferManager::createShaderCacheEntry (Ogre::uint32 renderableHash, const Ogre::HlmsCache& passCache, Ogre::uint32 finalHash, const Ogre::QueuedRenderable& queuedRenderable)
{
// We won't be going further than the base Hlms implementation
const Ogre::HlmsCache* retVal = Ogre::Hlms::createShaderCacheEntry(renderableHash, passCache, finalHash, queuedRenderable) ;
return retVal ;
}
void HlmsBufferManager::calculateHashFor (Ogre::Renderable* renderable, Ogre::uint32& outHash, Ogre::uint32& outCasterHash)
{
// Same here
Hlms::calculateHashFor(renderable, outHash, outCasterHash) ;
}
Ogre::HlmsCache HlmsBufferManager::preparePassHash (const Ogre::CompositorShadowNode* shadowNode, bool casterPass, bool dualParaboloid, Ogre::SceneManager* sceneManager)
{
// Again, the base function only
Ogre::HlmsCache retVal = Hlms::preparePassHash (shadowNode, casterPass, dualParaboloid, sceneManager) ;
return retVal ;
}
Ogre::uint32 HlmsBufferManager::fillBuffersForV1 (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer)
{
// For a V1 or V2 item, I didn't make any difference (but used V2 Item)
return fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer) ;
}
Ogre::uint32 HlmsBufferManager::fillBuffersForV2 (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer)
{
// See V1's comment
return fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer) ;
}
void HlmsBufferManager::preCommandBufferExecution (Ogre::CommandBuffer* command)
{
// This is called after the CommandBuffer has been populated, but before its execution. We unmap every buffer to be sure everything is fine
unmapConstBuffer() ;
}
void HlmsBufferManager::postCommandBufferExecution (Ogre::CommandBuffer* command)
{
// Nothing to do for this simple example (called after the CommandBuffer execution)
}
void HlmsBufferManager::frameEnded ()
{
// Called after the frame is done. Here we reset the active bfufer index
_currentConstBuffer = 0 ;
}
/// Buffers Functions ------------------------
Ogre::uint32* HlmsBufferManager::mapNextConstBuffer (Ogre::CommandBuffer* command)
{
// The last is to be unmaped
unmapConstBuffer() ;
// Let's see what we have to map
if (_currentConstBuffer >= _constBuffers.size())
{
// We have to create a new buffer, because we already populated the ones before it.
// A ConstBuffer size is 64kb max from HardWare, so we see what size is best (see Dark_Sylinc's answer)
size_t bufferSize = std::min<size_t>(65536, _vaoManager->getConstBufferMaxSize()) ;
// Here the VAO is put in good use
Ogre::ConstBufferPacked* newBuff = _vaoManager->createConstBuffer(bufferSize, Ogre::BT_DYNAMIC_PERSISTENT, NULL, false) ;
// Keep it in memory
_constBuffers.push_back(newBuff) ;
}
// We can get the next buffer then
Ogre::ConstBufferPacked* buffer = _constBuffers[_currentConstBuffer] ;
// Map it
_bufferStartIndex = reinterpret_cast<Ogre::uint32*>(buffer->map(0, buffer->getNumElements())) ;
// Update some vars
_bufferCurrentIndex = _bufferStartIndex ;
// Number of elements / float size in byte (4)
_bufferSize = buffer->getNumElements() >> 2 ;
// Push some commands, as the buffer is changed
// The buffer will be bound to the binding 2 ("layout (binding = 2)"), to both shaders
Ogre::CbShaderBuffer* commandBuffer = command->addCommand<Ogre::CbShaderBuffer>() ;
*commandBuffer = Ogre::CbShaderBuffer(Ogre::VertexShader, 2, buffer, 0, buffer->getTotalSizeBytes()) ;
commandBuffer = command->addCommand<Ogre::CbShaderBuffer>() ;
*commandBuffer = Ogre::CbShaderBuffer(Ogre::PixelShader, 2, buffer, 0, buffer->getTotalSizeBytes()) ;
// Can return the buffer's pointer
return _bufferCurrentIndex ;
}
void HlmsBufferManager::unmapConstBuffer ()
{
// If we had a buffer, we can unmap it
if (_bufferStartIndex)
{
// Unmap the current one
Ogre::ConstBufferPacked* buffer = _constBuffers[_currentConstBuffer] ;
buffer->unmap(Ogre::UO_KEEP_PERSISTENT, 0, (_bufferCurrentIndex - _bufferStartIndex) * sizeof(float)) ;
// Get the next index
_currentConstBuffer++ ;
// Reset
_bufferStartIndex = NULL ;
_bufferCurrentIndex = NULL ;
_bufferSize = 0 ;
}
}
Then, the simple HLMS inheriting from it. This time, it's the real one !
Header
Code: Select all
//////////////////////////////////////////////
// HlmsGround.h //
// //
// //
//////////////////////////////////////////////
#ifndef __HlmsGround
#define __HlmsGround
// Forward declarations
namespace Ogre
{
class VaoManager ;
class ConstBufferPacked ;
}
class OgreInitializer ;
#include "HlmsBufferManager.h"
#include <OgreHlmsDatablock.h>
#include <OgreMatrix4.h>
// This is a simple datablock. I am not sure what to put in there for now, but in this simple example it is not needed
class HlmsGroundDatablock : public Ogre::HlmsDatablock
{
private :
// Attributes
public :
// Constructor, destructor
HlmsGroundDatablock (Ogre::IdString name, Ogre::Hlms* creator, const Ogre::HlmsMacroblock* macroblock, const Ogre::HlmsBlendblock* blendblock, const Ogre::HlmsParamVec& params)
: HlmsDatablock(name, creator, macroblock, blendblock, params)
{
// Nothing to do
}
~HlmsGroundDatablock ()
{
// Nothing to do
}
} ;
// Here is the real class, inheriting from the BufferManager
class HlmsGround : public HlmsBufferManager
{
private :
// Attributes
// The buffers used for the passes (one for every pass, we can't map the same accross different frames)
unsigned int _currentPassBuffer ;
std::vector<Ogre::ConstBufferPacked*> _passBuffers ;
// We want to give the WorldMat. This one will be calculated only once, so keep it there
Ogre::Matrix4 _passViewProj ;
// Sampler block we will use for the texture
const Ogre::HlmsSamplerblock* _samplerBlock ;
public :
// Constructor, destructor
HlmsGround (Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders, Ogre::VaoManager* vaoManager) ;
~HlmsGround () ;
// HLMS functions
virtual const Ogre::HlmsCache* createShaderCacheEntry (Ogre::uint32 renderableHash, const Ogre::HlmsCache& passCache, Ogre::uint32 finalHash, const Ogre::QueuedRenderable& queuedRenderable) ;
virtual Ogre::HlmsCache preparePassHash (const Ogre::CompositorShadowNode* shadowNode, bool casterPass, bool dualParaboloid, Ogre::SceneManager* sceneManager) ;
virtual Ogre::uint32 fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::uint32 lastTextureHash) ;
virtual Ogre::uint32 fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer) ;
virtual void frameEnded () ;
virtual HlmsGroundDatablock* createDatablockImpl (Ogre::IdString datablockName, const Ogre::HlmsMacroblock* macroblock, const Ogre::HlmsBlendblock* blendblock, const Ogre::HlmsParamVec& paramVec) ;
} ;
#endif
And the source file attached to it
Code: Select all
//////////////////////////////////////////////
// HlmsGround.cpp //
// //
// //
//////////////////////////////////////////////
/// Defines ----------------------------------
// Buffer : Light direction + light's colour
// I put the total pass' size here, in values
#define GROUND_HLMS_PASS_BUFFER_SIZE 8
/// Includes ---------------------------------
// Locals
#include "HlmsGround.h"
// Natives
#include <cstddef>
#include <iostream>
#include <OgreHlmsCommon.h>
#include <OgreLight.h>
#include <OgreCamera.h>
#include <OgreRenderable.h>
#include <OgreRenderQueue.h>
#include <OgreTextureManager.h>
#include <CommandBuffer/OgreCbShaderBuffer.h>
#include <CommandBuffer/OgreCbTexture.h>
#include <CommandBuffer/OgreCommandBuffer.h>
#include <Vao/OgreConstBufferPacked.h>
#include <Vao/OgreVaoManager.h>
/// Constructor, destructor ------------------
// Here you can see we give the name we want it to have, and its type
HlmsGround::HlmsGround (Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders, Ogre::VaoManager* vaoManager)
: HlmsBufferManager (dataFolder, libraryFolders, vaoManager, "Ground", Ogre::HLMS_USER0),
_currentPassBuffer (0),
_passBuffers (),
_passViewProj (Ogre::Matrix4::IDENTITY),
_samplerBlock (NULL)
{
// We register this HLMS class with the Ogre::HLMS_USER0 id. For another one, you could use HLMS_USER1, and so on
// Initiailize our sampler block for the texture
// We create a reference sampler block, to ask Ogre about the one used in intern. This is done in order to cache things and avoid having multiple blocks doing the same thing
// That's less API overhead as we won't switch for nothing
Ogre::HlmsSamplerblock samplerBlockRef ;
// You can change every parameter here. This is defining the sampler block you want from Ogre
// Let's put WRAP on U and V, it will repeat the texture (you can MIRROR, CLAMP...)
samplerBlockRef.mU = Ogre::TAM_WRAP ;
samplerBlockRef.mV = Ogre::TAM_WRAP ;
// Then, let's ask Ogre about its internal buffer. We will keep it and use it !
Ogre::HlmsManager* hlmsMan = Ogre::Root::getSingleton().getHlmsManager() ;
_samplerBlock = hlmsMan->getSamplerblock(samplerBlockRef) ;
}
HlmsGround::~HlmsGround ()
{
// We have to delete our pass buffer
for (unsigned int i = 0 ; i < _passBuffers.size() ; i++)
_vaoManager->destroyConstBuffer(_passBuffers[i]) ;
}
/// HLMS functions ---------------------------
const Ogre::HlmsCache* HlmsGround::createShaderCacheEntry (Ogre::uint32 renderableHash, const Ogre::HlmsCache& passCache, Ogre::uint32 finalHash, const Ogre::QueuedRenderable& queuedRenderable)
{
// First the parent one
const Ogre::HlmsCache* retVal = HlmsBufferManager::createShaderCacheEntry(renderableHash, passCache, finalHash, queuedRenderable) ;
// Here is where you can put the default values for the shaders.
// So we set the samplerId for our texture (to 0, let's be fair)
retVal->pixelShader->getDefaultParameters()->setNamedConstant("baseTex", 0) ;
// We have to switch the values in the program
mRenderSystem->_setProgramsFromHlms(retVal) ;
// Doing it only with the pixel shader as it is the only one needing it
Ogre::GpuProgramParametersSharedPtr psParams = retVal->pixelShader->getDefaultParameters() ;
mRenderSystem->bindGpuProgramParameters(Ogre::GPT_FRAGMENT_PROGRAM, psParams, Ogre::GPV_ALL) ;
// Would be this with the vertex shader
/*Ogre::GpuProgramParametersSharedPtr vsParams = retVal->vertexShader->getDefaultParameters() ;
mRenderSystem->bindGpuProgramParameters(Ogre::GPT_VERTEX_PROGRAM, psParams, Ogre::GPV_ALL) ;*/
// Done
return retVal ;
}
Ogre::HlmsCache HlmsGround::preparePassHash (const Ogre::CompositorShadowNode* shadowNode, bool casterPass, bool dualParaboloid, Ogre::SceneManager* sceneManager)
{
// Let's call the parent again
Ogre::HlmsCache retVal = Hlms::preparePassHash (shadowNode, casterPass, dualParaboloid, sceneManager) ;
// Let's prepare our viewProj matrix (before the pass)
// You can get the camera from wherever you want, but we will take the current active camera here
Ogre::Camera* cam = sceneManager->getCameraInProgress() ;
Ogre::Matrix4 projection = cam->getProjectionMatrixWithRSDepth() ;
Ogre::RenderTarget* rt = sceneManager->getCurrentViewport()->getTarget() ;
// Maybe we need to flip it (RTT at least)
if (rt->requiresTextureFlipping())
{
projection[1][0] = -projection[1][0] ;
projection[1][1] = -projection[1][1] ;
projection[1][2] = -projection[1][2] ;
projection[1][3] = -projection[1][3] ;
}
Ogre::Matrix4 viewProj = projection * cam->getViewMatrix(true) ;
_passViewProj = viewProj ;
// Here is the system avoiding us some errors
// Every time we have a frame, we access a new buffer, stored in the array (and add it if needed)
// Then, when the frame is ended (we know it via the frameEnded() callback), we reset our counter
if (_passBuffers.size() <= _currentPassBuffer)
{
// On doit créer un nouveau buffer
_passBuffers.push_back(_vaoManager->createConstBuffer(GROUND_HLMS_PASS_BUFFER_SIZE << 2, Ogre::BT_DYNAMIC_PERSISTENT, NULL, false)) ;
}
// Get the buffer to map. We know the next time will be the next frame, so we add 1 to the current buffer index
Ogre::ConstBufferPacked* passBuffer = _passBuffers[_currentPassBuffer++] ;
// Here is where we fill the pass buffer.
// In this simple one, only the light is set
// Map the buffer
float* passBufferPtr = reinterpret_cast<float*>(passBuffer->map(0, passBuffer->getNumElements())) ;
// Let's put the light direction (directional light)
// You fill it wih the light you wish
Ogre::Vector3 lightParam = someDirectionalLight->getDirection() ;
// Fill it
*passBufferPtr++ = lightParam.x ;
*passBufferPtr++ = lightParam.y ;
*passBufferPtr++ = lightParam.z ;
// Because of the padding, we set another float to be sure
*passBufferPtr++ = 0.f ;
// Let's give its colour
const Ogre::ColourValue& colour = _ogreInit->getSunLight()->getDiffuseColour() ;
// Simply
*passBufferPtr++ = colour.r ;
*passBufferPtr++ = colour.g ;
*passBufferPtr++ = colour.b ;
*passBufferPtr++ = colour.a ;
// Done, we can unmap the buffer
passBuffer->unmap(Ogre::UO_KEEP_PERSISTENT) ;
return retVal ;
}
Ogre::uint32 HlmsGround::fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::uint32 lastTextureHash)
{
// I didn't get any call for this one, but at least the console will tell us if it is so
std::cout << "fillBuffersFor Tex" << std::endl ;
return 0 ;
}
Ogre::uint32 HlmsGround::fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer)
{
// Here we have a big thing
// First let's see if thsi HLMS was the last called
if (OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH(lastCacheHash) != Ogre::HLMS_USER0)
{
// If not, it means the textures and the pass buffer are not bound to our shader (or at least, those are not from this HLMS)
// Put the pass buffer (from the current index minus one, as we incremented it in the preparePassHash) to the slot 0 of both shaders
// Remember the "layout (binding = 0)" ? That's why the index is 0
Ogre::ConstBufferPacked* passBuffer = _passBuffers[_currentPassBuffer - 1] ;
*commandBuffer->addCommand<Ogre::CbShaderBuffer>() = Ogre::CbShaderBuffer(Ogre::VertexShader, 0, passBuffer, 0, passBuffer->getTotalSizeBytes()) ;
*commandBuffer->addCommand<Ogre::CbShaderBuffer>() = Ogre::CbShaderBuffer(Ogre::PixelShader, 0, passBuffer, 0, passBuffer->getTotalSizeBytes()) ;
// Also bind the texture
// For this example, I took this texture from the Ogre's media folder.
Ogre::TexturePtr tex = Ogre::TextureManager::getSingletonPtr()->getByName("floor_diffuse.PNG") ;
// Well, it is supposed to be loaded from somewhere else, but...
if (tex.isNull())
tex = Ogre::TextureManager::getSingleton().load("floor_diffuse.PNG", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME) ;
// We add the command for the texture. Link it to the sampler 0 (we set the baseTex index to 0 earlier) and use our sampler block
*commandBuffer->addCommand<Ogre::CbTexture>() = Ogre::CbTexture(0, true, tex.getPointer(), _samplerBlock) ;
// The commandBuffer seems to be a... Buffer containing every "state change" you wish. Before the draw, with the HLMS, we then change the textures, the buffers, and so on
}
// We have now to fill the datas for an instance (and not a pass)
// The size of what we write to keep track of where we are
// We write only a mat4, so 16 values
Ogre::uint32 valuesToWrite = 16 ;
// Let's check if the buffer is big enough
bool bufferTooSmall = (_bufferCurrentIndex - _bufferStartIndex + valuesToWrite) > _bufferSize ;
if (bufferTooSmall)
{
// We have to ask for a new buffer, from the HlmsBufferManager parent
mapNextConstBuffer(commandBuffer) ;
}
// Fill the buffer then.
// Get the World mat
const Ogre::Matrix4& worldMat = queuedRenderable.movableObject->_getParentNodeFullTransform() ;
// We can then compute the final matrix
Ogre::Matrix4 mvpStandard = _passViewProj * worldMat ;
// Here I guess I had to transpose because it is OpenGL. Not sure though, as I saw transpose in the PBS one was done every time
Ogre::Matrix4 mvpMat = mvpStandard.transpose() ;
// We can push the matrix into our buffer
memcpy(_bufferCurrentIndex, &mvpMat, 16 * sizeof(float)) ;
// We have to update the offset for the next Item calling this function before its draw
_bufferCurrentIndex += valuesToWrite ;
// This drawId is important (see Dark_Sylinc's answer)
// Keep in mind that only the first one has to be correct, as Ogre's auto instancing will increment the base value automatically.
Ogre::uint32 drawId = ((_bufferCurrentIndex - _bufferStartIndex) / valuesToWrite) - 1 ;
return drawId ;
}
void HlmsGround::frameEnded ()
{
// We have to call this one, it is resetting the constBuffer vars for us here
HlmsBufferManager::frameEnded() ;
// And as we added a pass component, we have to reset it ourself
_currentPassBuffer = 0 ;
}
HlmsGroundDatablock* HlmsGround::createDatablockImpl (Ogre::IdString datablockName, const Ogre::HlmsMacroblock* macroblock, const Ogre::HlmsBlendblock* blendblock, const Ogre::HlmsParamVec& paramVec)
{
// Only return the datablock
return OGRE_NEW HlmsGroundDatablock (datablockName, this, macroblock, blendblock, paramVec) ;
}
I think everything is here, do not hesitate to say if it is not the case or if you notice mistakes. Anyway, I hope it will be useful !
My only doubt is the matrix transpose. I know OpenGL is row major, that's why I thought it had to be, but the PBS isn't making a difference between Direct3d or OpenGL, from what I saw.
But, your killer answer really helped me, thanks Matias ! I can get going.
EDIT : After some head banging against the wall, I added some corrections. The buffers had wrong sizes (by some magic it was working), I didn't flip the projection (so if you did RTT it was messed up), and the parameters for the textures weren't really bound, so you couldn't have more than 1 texture. Corrected though, sorry !
EDIT2: Added the sampler block support, the multi pass buffer stuff and corrected the Ogre::HlmsTypes given in the HlmsBufferManager (switched it to the GroundHlms).