cocos2d::CustomCommand and multiple textures

Is it possible to use several different textures in one CustomCommand? Below is a piece of my code for illustration purposes. I’m using cocos2d-x v4.0. With one texture I’m able to draw a 2d terrain nicely but is it possible somehow to perform the drawing so that different triangles have different texture in drawing?

Or do I just have to create a separate CustomCommand for each texture and associated set of vertices?

void MyClass::init()
{
auto& pipelineDescriptor = m_customCommand.getPipelineDescriptor();
auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR);
_programState = new (std::nothrow) backend::ProgramState(program);
pipelineDescriptor.programState = _programState;
setVertexLayout(m_customCommand);
m_customCommand.setDrawType(CustomCommand::DrawType::ARRAY);
m_customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
m_customCommand.createVertexBuffer(sizeof(V2F_C4B_T2F), m_vertexDataCount, CustomCommand::BufferUsage::DYNAMIC);
m_mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation(“u_MVPMatrix”);
m_textureLocation = pipelineDescriptor.programState->getUniformLocation(“u_texture”);
}

void MyClass::setVertexLayout(CustomCommand& cmd)
{
auto* programState = cmd.getPipelineDescriptor().programState;
auto vertexLayout = programState->getVertexLayout();
const auto& attributeInfo = programState->getProgram()->getActiveAttributes();
auto iter = attributeInfo.find(“a_position”);
if (iter != attributeInfo.end())
{
vertexLayout->setAttribute(“a_position”, iter->second.location, backend::VertexFormat::FLOAT2, 0, false);
}
iter = attributeInfo.find(“a_texCoord”);
if (iter != attributeInfo.end())
{
vertexLayout->setAttribute(“a_texCoord”, iter->second.location, backend::VertexFormat::FLOAT2, 2 * sizeof(float), false);
}
iter = attributeInfo.find(“a_color”);
if (iter != attributeInfo.end())
{
vertexLayout->setAttribute(“a_color”, iter->second.location, backend::VertexFormat::UBYTE4, 4 * sizeof(float), true);
}
vertexLayout->setLayout(4 * sizeof(float) + 4 * sizeof(uint8_t));
}

void MyClass::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
m_customCommand.init(_globalZOrder);
m_customCommand.setVertexDrawInfo(0, m_vertexDataCount);
renderer->addCommand(&m_customCommand);

auto programState = m_customCommand.getPipelineDescriptor().programState;
programState->setTexture(m_textureLocation, 0, getTexture()->getBackendTexture());

const auto& projectionMat = 
   Director::getInstance()>getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Mat4 finalMat = projectionMat * transform;
programState->setUniform(m_mvpMatrixLocation, finalMat.m, sizeof(Mat4));

int offset = 0;

for (unsigned int i = 0; i < m_vertexDataCount; ++i)
{
  m_customCommand.updateVertexBuffer(&m_vertexData[i].vertices, offset, sizeof(m_vertexData[0].vertices));
  m_customCommand.updateVertexBuffer(&m_vertexData[i].texCoords, offset + sizeof(m_vertexData[0].vertices), sizeof(m_vertexData[0].texCoords));
  m_customCommand.updateVertexBuffer(&m_vertexData[i].colors, offset + sizeof(m_vertexData[0].vertices) + sizeof(m_vertexData[0].texCoords), 4 * sizeof(uint8_t));
  offset += vertexSize;
}

}

Hi @tpheikki

your texture is being fed to the shader in this code

so it all depends on what is being returned from getTexture()

I’m just not that clear on what do you really mean or trying to achieve .

Thank you for your response.

I try to explain my problem more in code-wise.

I have the draw() function where the programState->setTexture() sets the texture for the vertice data that is updated as follows

for (unsigned int i = 0; i < m_vertexDataCount; ++i)
{
m_customCommand.updateVertexBuffer(&m_vertexData[i].vertices, offset, sizeof(m_vertexData[0].vertices));
m_customCommand.updateVertexBuffer(&m_vertexData[i].texCoords, offset + sizeof(m_vertexData[0].vertices), sizeof(m_vertexData[0].texCoords));
m_customCommand.updateVertexBuffer(&m_vertexData[i].colors, offset + sizeof(m_vertexData[0].vertices) + sizeof(m_vertexData[0].texCoords), 4 * sizeof(uint8_t));
offset += vertexSize;
}

So is it possible to use more than one texture for one vertex buffer (and basically also for one CustomCommand)?

Or the same problem in other words, is it possible to bind a different texture for different parts of the vertex buffer?

Yes, you can use more than 1 texture for the same vertex buffer.
Your fragment shader can have multiple texture uniforms.
As to which texture you want to read from for each vertex, that you will have to form your own algorithm in your shader.

Here’s an example code for feeding a second texture to the shader:

auto loc = programState->getUniformLocation("u_texture1");
programState->setTexture(loc, 1, secondTexture->getBackendTexture());

First, get the uniform location based on the name you are using in the shader.
And then set the texture with a different index number for every additional texture you want to use.

Thanks for the valuable information. It is good to know that the trick needs to be done using shaders.

I have been thinking about my case. I typically have just a few textures used for one level so it could be easier to simply create dynamically the needed CustomCommand instances for each texture and related vertices. Other content is created using cocos2d-x sprites.

If I understood correctly, in order to change the texture using a shader would involve some sort of logic implemented in the shader code. This logic would be aware of the vertex indices when to set a different texture while drawing the vertices of the vertex buffer.

Was this the idea you were also thinking about?

Or what is your opinion, would it be easier just to create the few CustomCommand instances?

If you happen to know a good tutorial about using shader to set different textures for a single vertex buffer, please let me know. I have rather long experience of cocos2d-x and C++, but at the moment I’m bit lacking shader-related experience.

The background for this my whole question is that I had a smooth 60fps level rendering implementation in cocos2d-x 3.x environment but as OpenGL will be deprecated in iOS environment, I had to move to cocos2d-x v4.0 version that handles the Metal API compatibility.

Hmm… i am still not really clear what you are trying to achieve.
I was under the impression that you wanted to render multiple textures onto a single quad, say each corner with different texture and blended together?

But when you say “simply create dynamically the needed CustomCommand instances for each texture”
then, this is different from what i was thinking? This idea is akin to creating multiple sprites each having a different texture.
Are are you just trying to the change the texture on a single quad and only 1 texture will be used at any one time?

Sorry for not being clear enough.

My purpose is not to blend several textures together. May be it is closer to having multiple sprites each having a different texture.

For example, my level might have a solid ground that is rendered using just one same texture. This would be easy so achieve using one custom command and vertex buffer.

But, in addition, I would like to add some coating on top of the ground, e.g. grass. In a simple case I could use just one additional custom command.

But then the bit more complex part. If I want to use a different texture (e.g. tarmac or ice) for coating in some particular places, how to achieve that using the same custom command and vertex buffer.

Is this more understandable explanation?

With OpenGL this was very easy. I just changed the texture when I wanted to draw with a different coating but in this current cocos2d-x rendering framework it is not possible to use several textures in one custom command within one draw call. Or have I understood this correctly?

Ok, if i understand correctly, the quad is the coating that you would like to change between grass, tarmac or ice.

So, basically you can apply what i mentioned in the first post.

programState->setTexture(m_textureLocation, 0, getTexture()->getBackendTexture());

the 3rd argument in the function call will be the texture that you would want to swap out depending on which coating you want (eg, grassTexture, tarmacTexture, iceTexture).