I have been pulling the spine runtime into my own rendering and after several days I cannot figure out what's going with the uv coordinates. I have all the animations playing just fine and all of the meshes are constructed properly but the texture will not line up even close to where it's supposed to be.
The spine-cpp guide page seems to simply pack the uv pairs from MeshAttachment::getUVs() function. I tried that and it got very random looking texturing.
I am computing the world vertices and then packing them into a buffer, then drawing with glDrawElements using the indices provided by MeshAttachment::getTriangles() which gives me a nice mesh which animates perfectly. I've tried all manner of using MeshAttachment::getUVs() and MeshAttachment::getRegionUVs() and various calculations using the region UV range and region width/height.
I'm just wondering if you really are supposed to be able to just pull the uvs from MeshAttachment::getUVs() and use them directly as the runtime guide suggests or if there is a trick to calculating the actual texture coordinates from the MeshAttachment data.
The texture coordinates are just getting piped into glsl texture(sampler2D, uv). The packed texture is loaded correctly because I see bits of the character but they are no where near where they should be.
I just want the basic process of pulling the uvs but here's the block in question if you want to look at it.
Render Function
void SpineSprite::render(Camera* camera)
{
SpineSpriteType* spriteType = &spineSpriteTypes[type];
Shader* shader = &shaders[spriteType->shaderIndex];
spine::AtlasPage *page = spriteType->atlas->getPages()[0];
GLuint* texture = reinterpret_cast<GLuint*>(page->getRendererObject());
shader->use();
shader->setMat4("projection", camera->getProjection());
shader->setMat4("view", camera->look());
glBindTexture(GL_TEXTURE_2D, *texture);
int numSlots = skeleton->getSlots().size();
glBindVertexArray(VAO);
for (int i = 0; i < numSlots; i++)
{
std::vector<SpineVertex> vertices;
spine::Slot* slot = skeleton->getDrawOrder()[i];
spine::Attachment* attachment = slot->getAttachment();
// to-do: retrieve slot blend mode
std::vector<unsigned short> indices;
if (attachment->getRTTI().isExactly(spine::MeshAttachment::rtti))
{
spine::MeshAttachment* mesh = static_cast<spine::MeshAttachment*>(attachment);
size_t numVertexFloats = mesh->getWorldVerticesLength();
spine::Vector<float> floatVertices;
floatVertices.setSize(numVertexFloats, 0.0f);
mesh->computeWorldVertices(*slot, floatVertices);
spine::Vector<float> uvs = mesh->getUVs();
vertices.reserve(vertices.size() + (numVertexFloats / 2));
// pack data
for (size_t j = 0; j < numVertexFloats; j += 2)
{
SpineVertex vertex;
vertex.vertUv = glm::vec4(floatVertices[j], floatVertices[j + 1], uvs[j], uvs[j+1]);
spine::Color vertexColor = slot->getColor();
vertex.color = glm::vec4(vertexColor.r, vertexColor.g, vertexColor.b, vertexColor.a);
vertices.push_back(vertex);
}
spine::Vector<unsigned short> meshIndices = mesh->getTriangles();
indices.reserve(indices.size() + meshIndices.size());
indices.insert(indices.end(), meshIndices.buffer(), meshIndices.buffer() + meshIndices.size());
}
glBindBuffer(GL_ARRAY_BUFFER, buffers[Buffers::MESH]);
glBufferData(GL_ARRAY_BUFFER, sizeof(SpineVertex) * vertices.size(), nullptr, GL_STREAM_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(SpineVertex) * vertices.size(), & vertices[0], GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[Buffers::INDICES]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * indices.size(), nullptr, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * indices.size(), & indices[0], GL_STREAM_DRAW);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0);
}
glBindVertexArray(0);
}
VertexAttrib Setup (run once before render)
buffers = new GLuint[2];
glGenVertexArrays(1, &VAO);
glGenBuffers(2, buffers);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffers[Buffers::MESH]);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(SpineVertex), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(SpineVertex), (void *)(2 * sizeof(float)));
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(SpineVertex), (void *)(4 * sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindVertexArray(0);
Vertex Shader
#version 330 core
layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inTexCoord;
layout (location = 2) in vec4 inVertexColor;
uniform mat4 view;
uniform mat4 projection;
out vec2 texCoord;
out vec4 vertexColor;
void main(void)
{
gl_Position = projection * view * vec4(inPos, 0.0, 1.0);
texCoord = inTexCoord;
vertexColor = inVertexColor;
}
Fragment Shader which gets texCoords from vertex stage
#version 330 core
uniform sampler2D textureSampler;
in vec2 texCoord;
in vec4 vertexColor;
out vec4 fragColor;
void main()
{
vec4 texColor = texture(textureSampler, texCoord);
fragColor = texColor;//vec4(0,0,1,1);
}
If I put in the color and not the texture color I see the whole mesh playing the animation as a solid color and the wireframe looks fine.
Adding something new I realized. I used a KritaToSpine export script rather than buying photoshop. Strangley the first time I exported it included massive whitespace in the layer exports. I did a whole animation like that and packed it into an atlas and that was failing to map to the given UVs. I tried exporting the same file again and this time there is no whitespace. I will try again with these images but I have to redo everything now. Spine said that it detected the change and would resize the images but that failed spectacularly. I tried exporting with stripped whitespace and without and both failed to place the texture coordinates correctly.
Either way I still don't understand why the export would fail to map the uvs to the packed atlas it outputs.