Formatting

This commit is contained in:
Mario Zechner 2024-07-02 12:32:59 +02:00
parent e9aab1c94e
commit bf0a33876a
10 changed files with 665 additions and 658 deletions

View File

@ -21,12 +21,20 @@ spotless {
'spine-cocos2dx/src/**/*.h', 'spine-cocos2dx/src/**/*.h',
'spine-cocos2dx/example/Classes/**/*.cpp', 'spine-cocos2dx/example/Classes/**/*.cpp',
'spine-cocos2dx/example/Classes/**/*.h', 'spine-cocos2dx/example/Classes/**/*.h',
'spine-sdl/**/*.c', 'spine-glfw/src/**/*.cpp',
'spine-sdl/**/*.cpp', 'spine-glfw/src/**/*.h',
'spine-sdl/**/*.h', 'spine-glfw/example/**/*.cpp',
'spine-sfml/**/*.c', 'spine-glfw/example/**/*.h',
'spine-sfml/**/*.cpp', 'spine-sdl/src/**/*.c',
'spine-sfml/**/*.h', 'spine-sdl/src/**/*.cpp',
'spine-sdl/src/**/*.h',
'spine-sdl/example/**/*.c',
'spine-sdl/example/**/*.cpp',
'spine-sdl/example/**/*.h',
'spine-sfml/c/src/**/*.c',
'spine-sfml/c/src/**/*.h',
'spine-sfml/cpp/src/**/*.cpp',
'spine-sfml/cpp/src/**/*.h',
'spine-ue/**/*.cpp', 'spine-ue/**/*.cpp',
'spine-ue/**/*.h', 'spine-ue/**/*.h',
'spine-godot/spine_godot/*.cpp', 'spine-godot/spine_godot/*.cpp',

View File

@ -81,7 +81,7 @@ public:
} }
void compress() { void compress() {
if (blocks.size() == 1) return; if (blocks.size() == 1) return;
int totalSize = 0; int totalSize = 0;
for (int i = 0, n = blocks.size(); i < n; i++) { for (int i = 0, n = blocks.size(); i < n; i++) {
totalSize += blocks[i].size; totalSize += blocks[i].size;

View File

@ -39,210 +39,210 @@
using namespace spine; using namespace spine;
SkeletonRenderer::SkeletonRenderer(): _allocator(4096), _worldVertices(), _quadIndices(), _clipping(), _renderCommands() { SkeletonRenderer::SkeletonRenderer() : _allocator(4096), _worldVertices(), _quadIndices(), _clipping(), _renderCommands() {
_quadIndices.add(0); _quadIndices.add(0);
_quadIndices.add(1); _quadIndices.add(1);
_quadIndices.add(2); _quadIndices.add(2);
_quadIndices.add(2); _quadIndices.add(2);
_quadIndices.add(3); _quadIndices.add(3);
_quadIndices.add(0); _quadIndices.add(0);
} }
SkeletonRenderer::~SkeletonRenderer() { SkeletonRenderer::~SkeletonRenderer() {
} }
static RenderCommand *createRenderCommand(BlockAllocator &allocator, int numVertices, int32_t numIndices, BlendMode blendMode, void *texture) { static RenderCommand *createRenderCommand(BlockAllocator &allocator, int numVertices, int32_t numIndices, BlendMode blendMode, void *texture) {
RenderCommand *cmd = allocator.allocate<RenderCommand>(1); RenderCommand *cmd = allocator.allocate<RenderCommand>(1);
cmd->positions = allocator.allocate<float>(numVertices << 1); cmd->positions = allocator.allocate<float>(numVertices << 1);
cmd->uvs = allocator.allocate<float>(numVertices << 1); cmd->uvs = allocator.allocate<float>(numVertices << 1);
cmd->colors = allocator.allocate<uint32_t>(numVertices); cmd->colors = allocator.allocate<uint32_t>(numVertices);
cmd->darkColors = allocator.allocate<uint32_t>(numVertices); cmd->darkColors = allocator.allocate<uint32_t>(numVertices);
cmd->numVertices = numVertices; cmd->numVertices = numVertices;
cmd->indices = allocator.allocate<uint16_t>(numIndices); cmd->indices = allocator.allocate<uint16_t>(numIndices);
cmd->numIndices = numIndices; cmd->numIndices = numIndices;
cmd->blendMode = blendMode; cmd->blendMode = blendMode;
cmd->texture = texture; cmd->texture = texture;
cmd->next = nullptr; cmd->next = nullptr;
return cmd; return cmd;
} }
static RenderCommand *batchSubCommands(BlockAllocator &allocator, Vector<RenderCommand *> &commands, int first, int last, int numVertices, int numIndices) { static RenderCommand *batchSubCommands(BlockAllocator &allocator, Vector<RenderCommand *> &commands, int first, int last, int numVertices, int numIndices) {
RenderCommand *batched = createRenderCommand(allocator, numVertices, numIndices, commands[first]->blendMode, commands[first]->texture); RenderCommand *batched = createRenderCommand(allocator, numVertices, numIndices, commands[first]->blendMode, commands[first]->texture);
float *positions = batched->positions; float *positions = batched->positions;
float *uvs = batched->uvs; float *uvs = batched->uvs;
uint32_t *colors = batched->colors; uint32_t *colors = batched->colors;
uint32_t *darkColors = batched->darkColors; uint32_t *darkColors = batched->darkColors;
uint16_t *indices = batched->indices; uint16_t *indices = batched->indices;
int indicesOffset = 0; int indicesOffset = 0;
for (int i = first; i <= last; i++) { for (int i = first; i <= last; i++) {
RenderCommand *cmd = commands[i]; RenderCommand *cmd = commands[i];
memcpy(positions, cmd->positions, sizeof(float) * 2 * cmd->numVertices); memcpy(positions, cmd->positions, sizeof(float) * 2 * cmd->numVertices);
memcpy(uvs, cmd->uvs, sizeof(float) * 2 * cmd->numVertices); memcpy(uvs, cmd->uvs, sizeof(float) * 2 * cmd->numVertices);
memcpy(colors, cmd->colors, sizeof(int32_t) * cmd->numVertices); memcpy(colors, cmd->colors, sizeof(int32_t) * cmd->numVertices);
memcpy(darkColors, cmd->darkColors, sizeof(int32_t) * cmd->numVertices); memcpy(darkColors, cmd->darkColors, sizeof(int32_t) * cmd->numVertices);
for (int ii = 0; ii < cmd->numIndices; ii++) for (int ii = 0; ii < cmd->numIndices; ii++)
indices[ii] = cmd->indices[ii] + indicesOffset; indices[ii] = cmd->indices[ii] + indicesOffset;
indicesOffset += cmd->numVertices; indicesOffset += cmd->numVertices;
positions += 2 * cmd->numVertices; positions += 2 * cmd->numVertices;
uvs += 2 * cmd->numVertices; uvs += 2 * cmd->numVertices;
colors += cmd->numVertices; colors += cmd->numVertices;
darkColors += cmd->numVertices; darkColors += cmd->numVertices;
indices += cmd->numIndices; indices += cmd->numIndices;
} }
return batched; return batched;
} }
static RenderCommand *batchCommands(BlockAllocator &allocator, Vector<RenderCommand *> &commands) { static RenderCommand *batchCommands(BlockAllocator &allocator, Vector<RenderCommand *> &commands) {
if (commands.size() == 0) return nullptr; if (commands.size() == 0) return nullptr;
RenderCommand *root = nullptr; RenderCommand *root = nullptr;
RenderCommand *last = nullptr; RenderCommand *last = nullptr;
RenderCommand *first = commands[0]; RenderCommand *first = commands[0];
int startIndex = 0; int startIndex = 0;
int i = 1; int i = 1;
int numVertices = first->numVertices; int numVertices = first->numVertices;
int numIndices = first->numIndices; int numIndices = first->numIndices;
while (i <= (int) commands.size()) { while (i <= (int) commands.size()) {
RenderCommand *cmd = i < (int) commands.size() ? commands[i] : nullptr; RenderCommand *cmd = i < (int) commands.size() ? commands[i] : nullptr;
if (cmd && cmd->numVertices == 0 && cmd->numIndices == 0) { if (cmd && cmd->numVertices == 0 && cmd->numIndices == 0) {
i++; i++;
continue; continue;
} }
if (cmd != nullptr && cmd->texture == first->texture && if (cmd != nullptr && cmd->texture == first->texture &&
cmd->blendMode == first->blendMode && cmd->blendMode == first->blendMode &&
cmd->colors[0] == first->colors[0] && cmd->colors[0] == first->colors[0] &&
cmd->darkColors[0] == first->darkColors[0] && cmd->darkColors[0] == first->darkColors[0] &&
numIndices + cmd->numIndices < 0xffff) { numIndices + cmd->numIndices < 0xffff) {
numVertices += cmd->numVertices; numVertices += cmd->numVertices;
numIndices += cmd->numIndices; numIndices += cmd->numIndices;
} else { } else {
RenderCommand *batched = batchSubCommands(allocator, commands, startIndex, i - 1, numVertices, numIndices); RenderCommand *batched = batchSubCommands(allocator, commands, startIndex, i - 1, numVertices, numIndices);
if (!last) { if (!last) {
root = last = batched; root = last = batched;
} else { } else {
last->next = batched; last->next = batched;
last = batched; last = batched;
} }
if (i == (int) commands.size()) break; if (i == (int) commands.size()) break;
first = commands[i]; first = commands[i];
startIndex = i; startIndex = i;
numVertices = first->numVertices; numVertices = first->numVertices;
numIndices = first->numIndices; numIndices = first->numIndices;
} }
i++; i++;
} }
return root; return root;
} }
RenderCommand *SkeletonRenderer::render(Skeleton &skeleton) { RenderCommand *SkeletonRenderer::render(Skeleton &skeleton) {
_allocator.compress(); _allocator.compress();
_renderCommands.clear(); _renderCommands.clear();
SkeletonClipping &clipper = _clipping; SkeletonClipping &clipper = _clipping;
for (unsigned i = 0; i < skeleton.getSlots().size(); ++i) { for (unsigned i = 0; i < skeleton.getSlots().size(); ++i) {
Slot &slot = *skeleton.getDrawOrder()[i]; Slot &slot = *skeleton.getDrawOrder()[i];
Attachment *attachment = slot.getAttachment(); Attachment *attachment = slot.getAttachment();
if (!attachment) { if (!attachment) {
clipper.clipEnd(slot); clipper.clipEnd(slot);
continue; continue;
} }
// Early out if the slot color is 0 or the bone is not active // Early out if the slot color is 0 or the bone is not active
if (slot.getColor().a == 0 || !slot.getBone().isActive()) { if (slot.getColor().a == 0 || !slot.getBone().isActive()) {
clipper.clipEnd(slot); clipper.clipEnd(slot);
continue; continue;
} }
Vector<float> *worldVertices = &_worldVertices; Vector<float> *worldVertices = &_worldVertices;
Vector<unsigned short> *quadIndices = &_quadIndices; Vector<unsigned short> *quadIndices = &_quadIndices;
Vector<float> *vertices = worldVertices; Vector<float> *vertices = worldVertices;
int32_t verticesCount; int32_t verticesCount;
Vector<float> *uvs; Vector<float> *uvs;
Vector<unsigned short> *indices; Vector<unsigned short> *indices;
int32_t indicesCount; int32_t indicesCount;
Color *attachmentColor; Color *attachmentColor;
void *texture; void *texture;
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment *regionAttachment = (RegionAttachment *) attachment; RegionAttachment *regionAttachment = (RegionAttachment *) attachment;
attachmentColor = &regionAttachment->getColor(); attachmentColor = &regionAttachment->getColor();
// Early out if the slot color is 0 // Early out if the slot color is 0
if (attachmentColor->a == 0) { if (attachmentColor->a == 0) {
clipper.clipEnd(slot); clipper.clipEnd(slot);
continue; continue;
} }
worldVertices->setSize(8, 0); worldVertices->setSize(8, 0);
regionAttachment->computeWorldVertices(slot, *worldVertices, 0, 2); regionAttachment->computeWorldVertices(slot, *worldVertices, 0, 2);
verticesCount = 4; verticesCount = 4;
uvs = &regionAttachment->getUVs(); uvs = &regionAttachment->getUVs();
indices = quadIndices; indices = quadIndices;
indicesCount = 6; indicesCount = 6;
texture = regionAttachment->getRegion()->rendererObject; texture = regionAttachment->getRegion()->rendererObject;
} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
MeshAttachment *mesh = (MeshAttachment *) attachment; MeshAttachment *mesh = (MeshAttachment *) attachment;
attachmentColor = &mesh->getColor(); attachmentColor = &mesh->getColor();
// Early out if the slot color is 0 // Early out if the slot color is 0
if (attachmentColor->a == 0) { if (attachmentColor->a == 0) {
clipper.clipEnd(slot); clipper.clipEnd(slot);
continue; continue;
} }
worldVertices->setSize(mesh->getWorldVerticesLength(), 0); worldVertices->setSize(mesh->getWorldVerticesLength(), 0);
mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices->buffer(), 0, 2); mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices->buffer(), 0, 2);
verticesCount = (int32_t) (mesh->getWorldVerticesLength() >> 1); verticesCount = (int32_t) (mesh->getWorldVerticesLength() >> 1);
uvs = &mesh->getUVs(); uvs = &mesh->getUVs();
indices = &mesh->getTriangles(); indices = &mesh->getTriangles();
indicesCount = (int32_t) indices->size(); indicesCount = (int32_t) indices->size();
texture = mesh->getRegion()->rendererObject; texture = mesh->getRegion()->rendererObject;
} else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { } else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment(); ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment();
clipper.clipStart(slot, clip); clipper.clipStart(slot, clip);
continue; continue;
} else } else
continue; continue;
uint8_t r = static_cast<uint8_t>(skeleton.getColor().r * slot.getColor().r * attachmentColor->r * 255); uint8_t r = static_cast<uint8_t>(skeleton.getColor().r * slot.getColor().r * attachmentColor->r * 255);
uint8_t g = static_cast<uint8_t>(skeleton.getColor().g * slot.getColor().g * attachmentColor->g * 255); uint8_t g = static_cast<uint8_t>(skeleton.getColor().g * slot.getColor().g * attachmentColor->g * 255);
uint8_t b = static_cast<uint8_t>(skeleton.getColor().b * slot.getColor().b * attachmentColor->b * 255); uint8_t b = static_cast<uint8_t>(skeleton.getColor().b * slot.getColor().b * attachmentColor->b * 255);
uint8_t a = static_cast<uint8_t>(skeleton.getColor().a * slot.getColor().a * attachmentColor->a * 255); uint8_t a = static_cast<uint8_t>(skeleton.getColor().a * slot.getColor().a * attachmentColor->a * 255);
uint32_t color = (a << 24) | (r << 16) | (g << 8) | b; uint32_t color = (a << 24) | (r << 16) | (g << 8) | b;
uint32_t darkColor = 0xff000000; uint32_t darkColor = 0xff000000;
if (slot.hasDarkColor()) { if (slot.hasDarkColor()) {
Color &slotDarkColor = slot.getDarkColor(); Color &slotDarkColor = slot.getDarkColor();
darkColor = 0xff000000 | (static_cast<uint8_t>(slotDarkColor.r * 255) << 16) | (static_cast<uint8_t>(slotDarkColor.g * 255) << 8) | static_cast<uint8_t>(slotDarkColor.b * 255); darkColor = 0xff000000 | (static_cast<uint8_t>(slotDarkColor.r * 255) << 16) | (static_cast<uint8_t>(slotDarkColor.g * 255) << 8) | static_cast<uint8_t>(slotDarkColor.b * 255);
} }
if (clipper.isClipping()) { if (clipper.isClipping()) {
clipper.clipTriangles(*worldVertices, *indices, *uvs, 2); clipper.clipTriangles(*worldVertices, *indices, *uvs, 2);
vertices = &clipper.getClippedVertices(); vertices = &clipper.getClippedVertices();
verticesCount = (int32_t) (clipper.getClippedVertices().size() >> 1); verticesCount = (int32_t) (clipper.getClippedVertices().size() >> 1);
uvs = &clipper.getClippedUVs(); uvs = &clipper.getClippedUVs();
indices = &clipper.getClippedTriangles(); indices = &clipper.getClippedTriangles();
indicesCount = (int32_t) (clipper.getClippedTriangles().size()); indicesCount = (int32_t) (clipper.getClippedTriangles().size());
} }
RenderCommand *cmd = createRenderCommand(_allocator, verticesCount, indicesCount, slot.getData().getBlendMode(), texture); RenderCommand *cmd = createRenderCommand(_allocator, verticesCount, indicesCount, slot.getData().getBlendMode(), texture);
_renderCommands.add(cmd); _renderCommands.add(cmd);
memcpy(cmd->positions, vertices->buffer(), (verticesCount << 1) * sizeof(float)); memcpy(cmd->positions, vertices->buffer(), (verticesCount << 1) * sizeof(float));
memcpy(cmd->uvs, uvs->buffer(), (verticesCount << 1) * sizeof(float)); memcpy(cmd->uvs, uvs->buffer(), (verticesCount << 1) * sizeof(float));
for (int ii = 0; ii < verticesCount; ii++) { for (int ii = 0; ii < verticesCount; ii++) {
cmd->colors[ii] = color; cmd->colors[ii] = color;
cmd->darkColors[ii] = darkColor; cmd->darkColors[ii] = darkColor;
} }
memcpy(cmd->indices, indices->buffer(), indices->size() * sizeof(uint16_t)); memcpy(cmd->indices, indices->buffer(), indices->size() * sizeof(uint16_t));
clipper.clipEnd(slot); clipper.clipEnd(slot);
} }
clipper.clipEnd(); clipper.clipEnd();
return batchCommands(_allocator, _renderCommands); return batchCommands(_allocator, _renderCommands);
} }

View File

@ -9,92 +9,92 @@ using namespace spine;
int width = 800, height = 600; int width = 800, height = 600;
GLFWwindow* init_glfw() { GLFWwindow *init_glfw() {
if (!glfwInit()) { if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl; std::cerr << "Failed to initialize GLFW" << std::endl;
return nullptr; return nullptr;
} }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(width, height, "spine-glfw", NULL, NULL); GLFWwindow *window = glfwCreateWindow(width, height, "spine-glfw", NULL, NULL);
if (!window) { if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl; std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate(); glfwTerminate();
return nullptr; return nullptr;
} }
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
glbinding::initialize(glfwGetProcAddress); glbinding::initialize(glfwGetProcAddress);
return window; return window;
} }
int main() { int main() {
// Initialize GLFW and glbinding // Initialize GLFW and glbinding
GLFWwindow *window = init_glfw(); GLFWwindow *window = init_glfw();
if (!window) return -1; if (!window) return -1;
// We use a y-down coordinate system, see renderer_set_viewport_size() // We use a y-down coordinate system, see renderer_set_viewport_size()
Bone::setYDown(true); Bone::setYDown(true);
// Load the atlas and the skeleton data // Load the atlas and the skeleton data
GlTextureLoader textureLoader; GlTextureLoader textureLoader;
Atlas *atlas = new Atlas("data/spineboy-pma.atlas", &textureLoader); Atlas *atlas = new Atlas("data/spineboy-pma.atlas", &textureLoader);
SkeletonJson json(atlas); SkeletonJson json(atlas);
SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json"); SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json");
// Create a skeleton from the data, set the skeleton's position to the bottom center of // Create a skeleton from the data, set the skeleton's position to the bottom center of
// the screen and scale it to make it smaller. // the screen and scale it to make it smaller.
Skeleton skeleton(skeletonData); Skeleton skeleton(skeletonData);
skeleton.setPosition(width / 2, height - 100); skeleton.setPosition(width / 2, height - 100);
skeleton.setScaleX(0.3); skeleton.setScaleX(0.3);
skeleton.setScaleY(0.3); skeleton.setScaleY(0.3);
// Create an AnimationState to drive animations on the skeleton. Set the "portal" animation // Create an AnimationState to drive animations on the skeleton. Set the "portal" animation
// on track with index 0. // on track with index 0.
AnimationStateData animationStateData(skeletonData); AnimationStateData animationStateData(skeletonData);
AnimationState animationState(&animationStateData); AnimationState animationState(&animationStateData);
animationState.setAnimation(0, "portal", true); animationState.setAnimation(0, "portal", true);
// Create the renderer and set the viewport size to match the window size. This sets up a // Create the renderer and set the viewport size to match the window size. This sets up a
// pixel perfect orthogonal projection for 2D rendering. // pixel perfect orthogonal projection for 2D rendering.
renderer_t *renderer = renderer_create(); renderer_t *renderer = renderer_create();
renderer_set_viewport_size(renderer, width, height); renderer_set_viewport_size(renderer, width, height);
// Rendering loop // Rendering loop
double lastTime = glfwGetTime(); double lastTime = glfwGetTime();
while (!glfwWindowShouldClose(window)) { while (!glfwWindowShouldClose(window)) {
// Calculate the delta time in seconds // Calculate the delta time in seconds
double currTime = glfwGetTime(); double currTime = glfwGetTime();
float delta = currTime - lastTime; float delta = currTime - lastTime;
lastTime = currTime; lastTime = currTime;
// Update and apply the animation state to the skeleton // Update and apply the animation state to the skeleton
animationState.update(delta); animationState.update(delta);
animationState.apply(skeleton); animationState.apply(skeleton);
// Update the skeleton time (used for physics) // Update the skeleton time (used for physics)
skeleton.update(delta); skeleton.update(delta);
// Calculate the new pose // Calculate the new pose
skeleton.updateWorldTransform(spine::Physics_Update); skeleton.updateWorldTransform(spine::Physics_Update);
// Clear the screen // Clear the screen
gl::glClear(gl::GL_COLOR_BUFFER_BIT); gl::glClear(gl::GL_COLOR_BUFFER_BIT);
// Render the skeleton in its current pose // Render the skeleton in its current pose
renderer_draw(renderer, &skeleton, true); renderer_draw(renderer, &skeleton, true);
// Present the rendering results and poll for events // Present the rendering results and poll for events
glfwSwapBuffers(window); glfwSwapBuffers(window);
glfwPollEvents(); glfwPollEvents();
} }
// Dispose everything // Dispose everything
renderer_dispose(renderer); renderer_dispose(renderer);
delete skeletonData; delete skeletonData;
delete atlas; delete atlas;
// Kill the window and GLFW // Kill the window and GLFW
glfwTerminate(); glfwTerminate();
return 0; return 0;
} }

View File

@ -9,231 +9,230 @@ using namespace spine;
/// Set the default extension used for memory allocations and file I/O /// Set the default extension used for memory allocations and file I/O
SpineExtension *spine::getDefaultExtension() { SpineExtension *spine::getDefaultExtension() {
return new spine::DefaultSpineExtension(); return new spine::DefaultSpineExtension();
} }
/// A blend mode, see https://en.esotericsoftware.com/spine-slots#Blending /// A blend mode, see https://en.esotericsoftware.com/spine-slots#Blending
/// Encodes the OpenGL source and destination blend function for both premultiplied and /// Encodes the OpenGL source and destination blend function for both premultiplied and
/// non-premultiplied alpha blending. /// non-premultiplied alpha blending.
typedef struct { typedef struct {
unsigned int source_color; unsigned int source_color;
unsigned int source_color_pma; unsigned int source_color_pma;
unsigned int dest_color; unsigned int dest_color;
unsigned int source_alpha; unsigned int source_alpha;
} blend_mode_t; } blend_mode_t;
/// The 4 supported blend modes SPINE_BLEND_MODE_NORMAL, SPINE_BLEND_MODE_ADDITIVE, SPINE_BLEND_MODE_MULTIPLY, /// The 4 supported blend modes SPINE_BLEND_MODE_NORMAL, SPINE_BLEND_MODE_ADDITIVE, SPINE_BLEND_MODE_MULTIPLY,
/// and SPINE_BLEND_MODE_SCREEN, expressed as OpenGL blend functions. /// and SPINE_BLEND_MODE_SCREEN, expressed as OpenGL blend functions.
blend_mode_t blend_modes[] = { blend_mode_t blend_modes[] = {
{(unsigned int)GL_SRC_ALPHA, (unsigned int)GL_ONE, (unsigned int)GL_ONE_MINUS_SRC_ALPHA, (unsigned int)GL_ONE}, {(unsigned int) GL_SRC_ALPHA, (unsigned int) GL_ONE, (unsigned int) GL_ONE_MINUS_SRC_ALPHA, (unsigned int) GL_ONE},
{(unsigned int)GL_SRC_ALPHA, (unsigned int)GL_ONE, (unsigned int)GL_ONE, (unsigned int)GL_ONE}, {(unsigned int) GL_SRC_ALPHA, (unsigned int) GL_ONE, (unsigned int) GL_ONE, (unsigned int) GL_ONE},
{(unsigned int)GL_DST_COLOR, (unsigned int)GL_DST_COLOR, (unsigned int)GL_ONE_MINUS_SRC_ALPHA, (unsigned int)GL_ONE_MINUS_SRC_ALPHA}, {(unsigned int) GL_DST_COLOR, (unsigned int) GL_DST_COLOR, (unsigned int) GL_ONE_MINUS_SRC_ALPHA, (unsigned int) GL_ONE_MINUS_SRC_ALPHA},
{(unsigned int)GL_ONE, (unsigned int)GL_ONE, (unsigned int)GL_ONE_MINUS_SRC_COLOR, (unsigned int)GL_ONE_MINUS_SRC_COLOR} {(unsigned int) GL_ONE, (unsigned int) GL_ONE, (unsigned int) GL_ONE_MINUS_SRC_COLOR, (unsigned int) GL_ONE_MINUS_SRC_COLOR}};
};
mesh_t *mesh_create() { mesh_t *mesh_create() {
GLuint vao, vbo, ibo; GLuint vao, vbo, ibo;
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo); glGenBuffers(1, &vbo);
glGenBuffers(1, &ibo); glGenBuffers(1, &ibo);
glBindVertexArray(vao); glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, x)); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *) offsetof(vertex_t, x));
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertex_t), (void*)offsetof(vertex_t, color)); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertex_t), (void *) offsetof(vertex_t, color));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, u)); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *) offsetof(vertex_t, u));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertex_t), (void*)offsetof(vertex_t, darkColor)); glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertex_t), (void *) offsetof(vertex_t, darkColor));
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
glBindVertexArray(0); glBindVertexArray(0);
auto *mesh = (mesh_t*)malloc(sizeof(mesh_t)); auto *mesh = (mesh_t *) malloc(sizeof(mesh_t));
mesh->vao = vao; mesh->vao = vao;
mesh->vbo = vbo; mesh->vbo = vbo;
mesh->num_vertices = 0; mesh->num_vertices = 0;
mesh->ibo = ibo; mesh->ibo = ibo;
mesh->num_indices = 0; mesh->num_indices = 0;
return mesh; return mesh;
} }
void mesh_update(mesh_t *mesh, vertex_t *vertices, int num_vertices, uint16_t *indices, int num_indices) { void mesh_update(mesh_t *mesh, vertex_t *vertices, int num_vertices, uint16_t *indices, int num_indices) {
glBindVertexArray(mesh->vao); glBindVertexArray(mesh->vao);
glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo); glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(num_vertices * sizeof(vertex_t)), vertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr) (num_vertices * sizeof(vertex_t)), vertices, GL_STATIC_DRAW);
mesh->num_vertices = num_vertices; mesh->num_vertices = num_vertices;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(num_indices * sizeof(uint16_t)), indices, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr) (num_indices * sizeof(uint16_t)), indices, GL_STATIC_DRAW);
mesh->num_indices = num_indices; mesh->num_indices = num_indices;
glBindVertexArray(0); glBindVertexArray(0);
} }
void mesh_draw(mesh_t *mesh) { void mesh_draw(mesh_t *mesh) {
glBindVertexArray(mesh->vao); glBindVertexArray(mesh->vao);
glDrawElements(GL_TRIANGLES, mesh->num_indices, GL_UNSIGNED_SHORT, nullptr); glDrawElements(GL_TRIANGLES, mesh->num_indices, GL_UNSIGNED_SHORT, nullptr);
glBindVertexArray(0); glBindVertexArray(0);
} }
void mesh_dispose(mesh_t *mesh) { void mesh_dispose(mesh_t *mesh) {
glDeleteBuffers(1, &mesh->vbo); glDeleteBuffers(1, &mesh->vbo);
glDeleteBuffers(1, &mesh->ibo); glDeleteBuffers(1, &mesh->ibo);
glDeleteVertexArrays(1, &mesh->vao); glDeleteVertexArrays(1, &mesh->vao);
free(mesh); free(mesh);
} }
GLuint compile_shader(const char* source, GLenum type) { GLuint compile_shader(const char *source, GLenum type) {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr); glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader); glCompileShader(shader);
GLint success; GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) { if (!success) {
char infoLog[512]; char infoLog[512];
glGetShaderInfoLog(shader, 512, nullptr, infoLog); glGetShaderInfoLog(shader, 512, nullptr, infoLog);
printf("Error, shader compilation failed:\n%s\n", infoLog); printf("Error, shader compilation failed:\n%s\n", infoLog);
glDeleteShader(shader); glDeleteShader(shader);
return 0; return 0;
} }
return shader; return shader;
} }
shader_t shader_create(const char* vertex_shader, const char* fragment_shader) { shader_t shader_create(const char *vertex_shader, const char *fragment_shader) {
shader_t program; shader_t program;
GLuint vertex_shader_id = compile_shader(vertex_shader, GL_VERTEX_SHADER); GLuint vertex_shader_id = compile_shader(vertex_shader, GL_VERTEX_SHADER);
GLuint fragment_shader_id = compile_shader(fragment_shader, GL_FRAGMENT_SHADER); GLuint fragment_shader_id = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);
if (!vertex_shader_id || !fragment_shader_id) { if (!vertex_shader_id || !fragment_shader_id) {
glDeleteShader(vertex_shader_id); glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id); glDeleteShader(fragment_shader_id);
return 0; return 0;
} }
program = glCreateProgram(); program = glCreateProgram();
glAttachShader(program, vertex_shader_id); glAttachShader(program, vertex_shader_id);
glAttachShader(program, fragment_shader_id); glAttachShader(program, fragment_shader_id);
glLinkProgram(program); glLinkProgram(program);
GLint success; GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success); glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) { if (!success) {
char infoLog[512]; char infoLog[512];
glGetProgramInfoLog(program, 512, nullptr, infoLog); glGetProgramInfoLog(program, 512, nullptr, infoLog);
printf("Error, shader linking failed:\n%s\n", infoLog); printf("Error, shader linking failed:\n%s\n", infoLog);
glDeleteProgram(program); glDeleteProgram(program);
program = 0; program = 0;
} }
glDeleteShader(vertex_shader_id); glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id); glDeleteShader(fragment_shader_id);
return program; return program;
} }
void shader_set_matrix4(shader_t shader, const char* name, const float *matrix) { void shader_set_matrix4(shader_t shader, const char *name, const float *matrix) {
shader_use(shader); shader_use(shader);
GLint location = glGetUniformLocation(shader, name); GLint location = glGetUniformLocation(shader, name);
glUniformMatrix4fv(location, 1, GL_FALSE, matrix); glUniformMatrix4fv(location, 1, GL_FALSE, matrix);
} }
void shader_set_float(shader_t shader, const char* name, float value) { void shader_set_float(shader_t shader, const char *name, float value) {
shader_use(shader); shader_use(shader);
GLint location = glGetUniformLocation(shader, name); GLint location = glGetUniformLocation(shader, name);
glUniform1f(location, value); glUniform1f(location, value);
} }
void shader_set_int(shader_t shader, const char* name, int value) { void shader_set_int(shader_t shader, const char *name, int value) {
shader_use(shader); shader_use(shader);
GLint location = glGetUniformLocation(shader, name); GLint location = glGetUniformLocation(shader, name);
glUniform1i(location, value); glUniform1i(location, value);
} }
void shader_use(shader_t program) { void shader_use(shader_t program) {
glUseProgram(program); glUseProgram(program);
} }
void shader_dispose(shader_t program) { void shader_dispose(shader_t program) {
glDeleteProgram(program); glDeleteProgram(program);
} }
texture_t texture_load(const char *file_path) { texture_t texture_load(const char *file_path) {
int width, height, nrChannels; int width, height, nrChannels;
unsigned char *data = stbi_load(file_path, &width, &height, &nrChannels, 0); unsigned char *data = stbi_load(file_path, &width, &height, &nrChannels, 0);
if (!data) { if (!data) {
printf("Failed to load texture\n"); printf("Failed to load texture\n");
return 0; return 0;
} }
GLenum format = GL_RGBA; GLenum format = GL_RGBA;
if (nrChannels == 1) if (nrChannels == 1)
format = GL_RED; format = GL_RED;
else if (nrChannels == 3) else if (nrChannels == 3)
format = GL_RGB; format = GL_RGB;
else if (nrChannels == 4) else if (nrChannels == 4)
format = GL_RGBA; format = GL_RGBA;
texture_t texture; texture_t texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data); stbi_image_free(data);
return texture; return texture;
} }
void texture_use(texture_t texture) { void texture_use(texture_t texture) {
glActiveTexture(GL_TEXTURE0); // Set active texture unit to 0 glActiveTexture(GL_TEXTURE0);// Set active texture unit to 0
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
} }
void texture_dispose(texture_t texture) { void texture_dispose(texture_t texture) {
glDeleteTextures(1, &texture); glDeleteTextures(1, &texture);
} }
void matrix_ortho_projection(float *matrix, float width, float height) { void matrix_ortho_projection(float *matrix, float width, float height) {
memset(matrix, 0, 16 * sizeof(float)); memset(matrix, 0, 16 * sizeof(float));
float left = 0.0f; float left = 0.0f;
float right = width; float right = width;
float bottom = height; float bottom = height;
float top = 0.0f; float top = 0.0f;
float near = -1.0f; float near = -1.0f;
float far = 1.0f; float far = 1.0f;
matrix[0] = 2.0f / (right - left); matrix[0] = 2.0f / (right - left);
matrix[5] = 2.0f / (top - bottom); matrix[5] = 2.0f / (top - bottom);
matrix[10] = -2.0f / (far - near); matrix[10] = -2.0f / (far - near);
matrix[12] = -(right + left) / (right - left); matrix[12] = -(right + left) / (right - left);
matrix[13] = -(top + bottom) / (top - bottom); matrix[13] = -(top + bottom) / (top - bottom);
matrix[14] = -(far + near) / (far - near); matrix[14] = -(far + near) / (far - near);
matrix[15] = 1.0f; matrix[15] = 1.0f;
} }
void GlTextureLoader::load(spine::AtlasPage &page, const spine::String &path) { void GlTextureLoader::load(spine::AtlasPage &page, const spine::String &path) {
page.texture = (void *)(uintptr_t)texture_load(path.buffer()); page.texture = (void *) (uintptr_t) texture_load(path.buffer());
} }
void GlTextureLoader::unload(void *texture) { void GlTextureLoader::unload(void *texture) {
texture_dispose((texture_t)(uintptr_t)texture); texture_dispose((texture_t) (uintptr_t) texture);
} }
renderer_t *renderer_create() { renderer_t *renderer_create() {
shader_t shader = shader_create(R"( shader_t shader = shader_create(R"(
#version 330 core #version 330 core
layout (location = 0) in vec2 aPos; layout (location = 0) in vec2 aPos;
layout (location = 1) in vec4 aLightColor; layout (location = 1) in vec4 aLightColor;
@ -252,7 +251,8 @@ renderer_t *renderer_create() {
texCoord = aTexCoord; texCoord = aTexCoord;
gl_Position = uMatrix * vec4(aPos, 0.0, 1.0); gl_Position = uMatrix * vec4(aPos, 0.0, 1.0);
} }
)", R"( )",
R"(
#version 330 core #version 330 core
in vec4 lightColor; in vec4 lightColor;
in vec4 darkColor; in vec4 darkColor;
@ -267,71 +267,71 @@ renderer_t *renderer_create() {
fragColor.rgb = ((texColor.a - 1.0) * darkColor.a + 1.0 - texColor.rgb) * darkColor.rgb + texColor.rgb * lightColor.rgb; fragColor.rgb = ((texColor.a - 1.0) * darkColor.a + 1.0 - texColor.rgb) * darkColor.rgb + texColor.rgb * lightColor.rgb;
} }
)"); )");
if (!shader) return nullptr; if (!shader) return nullptr;
mesh_t *mesh = mesh_create(); mesh_t *mesh = mesh_create();
auto *renderer = (renderer_t*)malloc(sizeof(renderer_t)); auto *renderer = (renderer_t *) malloc(sizeof(renderer_t));
renderer->shader = shader; renderer->shader = shader;
renderer->mesh = mesh; renderer->mesh = mesh;
renderer->vertex_buffer_size = 0; renderer->vertex_buffer_size = 0;
renderer->vertex_buffer = nullptr; renderer->vertex_buffer = nullptr;
renderer->renderer = new SkeletonRenderer(); renderer->renderer = new SkeletonRenderer();
return renderer; return renderer;
} }
void renderer_set_viewport_size(renderer_t *renderer, int width, int height) { void renderer_set_viewport_size(renderer_t *renderer, int width, int height) {
float matrix[16]; float matrix[16];
matrix_ortho_projection(matrix, (float)width, (float)height); matrix_ortho_projection(matrix, (float) width, (float) height);
shader_use(renderer->shader); shader_use(renderer->shader);
shader_set_matrix4(renderer->shader, "uMatrix", matrix); shader_set_matrix4(renderer->shader, "uMatrix", matrix);
} }
void renderer_draw(renderer_t *renderer, Skeleton *skeleton, bool premultipliedAlpha) { void renderer_draw(renderer_t *renderer, Skeleton *skeleton, bool premultipliedAlpha) {
shader_use(renderer->shader); shader_use(renderer->shader);
shader_set_int(renderer->shader, "uTexture", 0); shader_set_int(renderer->shader, "uTexture", 0);
glEnable(GL_BLEND); glEnable(GL_BLEND);
RenderCommand *command = renderer->renderer->render(*skeleton); RenderCommand *command = renderer->renderer->render(*skeleton);
while (command) { while (command) {
int num_command_vertices = command->numVertices; int num_command_vertices = command->numVertices;
if (renderer->vertex_buffer_size < num_command_vertices) { if (renderer->vertex_buffer_size < num_command_vertices) {
renderer->vertex_buffer_size = num_command_vertices; renderer->vertex_buffer_size = num_command_vertices;
free(renderer->vertex_buffer); free(renderer->vertex_buffer);
renderer->vertex_buffer = (vertex_t *)malloc(sizeof(vertex_t) * renderer->vertex_buffer_size); renderer->vertex_buffer = (vertex_t *) malloc(sizeof(vertex_t) * renderer->vertex_buffer_size);
} }
float *positions = command->positions; float *positions = command->positions;
float *uvs = command->uvs; float *uvs = command->uvs;
int32_t *colors = command->colors; int32_t *colors = command->colors;
int32_t *darkColors = command->darkColors; int32_t *darkColors = command->darkColors;
for (int i = 0, j = 0; i < num_command_vertices; i++, j += 2) { for (int i = 0, j = 0; i < num_command_vertices; i++, j += 2) {
vertex_t *vertex = &renderer->vertex_buffer[i]; vertex_t *vertex = &renderer->vertex_buffer[i];
vertex->x = positions[j]; vertex->x = positions[j];
vertex->y = positions[j + 1]; vertex->y = positions[j + 1];
vertex->u = uvs[j]; vertex->u = uvs[j];
vertex->v = uvs[j+1]; vertex->v = uvs[j + 1];
uint32_t color = colors[i]; uint32_t color = colors[i];
vertex->color = (color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0x000000FF) << 16); vertex->color = (color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0x000000FF) << 16);
uint32_t darkColor = darkColors[i]; uint32_t darkColor = darkColors[i];
vertex->darkColor = (darkColor & 0xFF00FF00) | ((darkColor & 0x00FF0000) >> 16) | ((darkColor & 0x000000FF) << 16); vertex->darkColor = (darkColor & 0xFF00FF00) | ((darkColor & 0x00FF0000) >> 16) | ((darkColor & 0x000000FF) << 16);
} }
int num_command_indices = command->numIndices; int num_command_indices = command->numIndices;
uint16_t *indices = command->indices; uint16_t *indices = command->indices;
mesh_update(renderer->mesh, renderer->vertex_buffer, num_command_vertices, indices, num_command_indices); mesh_update(renderer->mesh, renderer->vertex_buffer, num_command_vertices, indices, num_command_indices);
blend_mode_t blend_mode = blend_modes[command->blendMode]; blend_mode_t blend_mode = blend_modes[command->blendMode];
glBlendFuncSeparate(premultipliedAlpha ? (GLenum)blend_mode.source_color_pma : (GLenum)blend_mode.source_color, (GLenum)blend_mode.dest_color, (GLenum)blend_mode.source_alpha, (GLenum)blend_mode.dest_color); glBlendFuncSeparate(premultipliedAlpha ? (GLenum) blend_mode.source_color_pma : (GLenum) blend_mode.source_color, (GLenum) blend_mode.dest_color, (GLenum) blend_mode.source_alpha, (GLenum) blend_mode.dest_color);
auto texture = (texture_t)(uintptr_t)command->texture; auto texture = (texture_t) (uintptr_t) command->texture;
texture_use(texture); texture_use(texture);
mesh_draw(renderer->mesh); mesh_draw(renderer->mesh);
command = command->next; command = command->next;
} }
} }
void renderer_dispose(renderer_t *renderer) { void renderer_dispose(renderer_t *renderer) {
shader_dispose(renderer->shader); shader_dispose(renderer->shader);
mesh_dispose(renderer->mesh); mesh_dispose(renderer->mesh);
free(renderer->vertex_buffer); free(renderer->vertex_buffer);
delete renderer->renderer; delete renderer->renderer;
free(renderer); free(renderer);
} }

View File

@ -5,20 +5,20 @@
/// A vertex of a mesh generated from a Spine skeleton /// A vertex of a mesh generated from a Spine skeleton
struct vertex_t { struct vertex_t {
float x, y; float x, y;
uint32_t color; uint32_t color;
float u, v; float u, v;
uint32_t darkColor; uint32_t darkColor;
}; };
/// A GPU-side mesh using OpenGL vertex arrays, vertex buffer, and /// A GPU-side mesh using OpenGL vertex arrays, vertex buffer, and
/// indices buffer. /// indices buffer.
typedef struct { typedef struct {
unsigned int vao; unsigned int vao;
unsigned int vbo; unsigned int vbo;
int num_vertices; int num_vertices;
unsigned int ibo; unsigned int ibo;
int num_indices; int num_indices;
} mesh_t; } mesh_t;
mesh_t *mesh_create(); mesh_t *mesh_create();
@ -33,13 +33,13 @@ typedef unsigned int shader_t;
shader_t shader_create(const char *vertex_shader, const char *fragment_shader); shader_t shader_create(const char *vertex_shader, const char *fragment_shader);
/// Sets a uniform matrix by name /// Sets a uniform matrix by name
void shader_set_matrix4(shader_t program, const char* name, const float *matrix); void shader_set_matrix4(shader_t program, const char *name, const float *matrix);
/// Sets a uniform float by name /// Sets a uniform float by name
void shader_set_float(shader_t program, const char* name, float value); void shader_set_float(shader_t program, const char *name, float value);
/// Sets a uniform int by name /// Sets a uniform int by name
void shader_set_int(shader_t program, const char* name, int value); void shader_set_int(shader_t program, const char *name, int value);
/// Binds the shader /// Binds the shader
void shader_use(shader_t shader); void shader_use(shader_t shader);
@ -60,20 +60,20 @@ void texture_use(texture_t texture);
void texture_dispose(texture_t texture); void texture_dispose(texture_t texture);
/// A TextureLoader implementation for OpenGL. Use this with spine::Atlas. /// A TextureLoader implementation for OpenGL. Use this with spine::Atlas.
class GlTextureLoader: public spine::TextureLoader { class GlTextureLoader : public spine::TextureLoader {
public: public:
void load(spine::AtlasPage &page, const spine::String &path); void load(spine::AtlasPage &page, const spine::String &path);
void unload(void *texture); void unload(void *texture);
}; };
/// Renderer capable of rendering a spine_skeleton_drawable, using a shader, a mesh, and a /// Renderer capable of rendering a spine_skeleton_drawable, using a shader, a mesh, and a
/// temporary CPU-side vertex buffer used to update the GPU-side mesh /// temporary CPU-side vertex buffer used to update the GPU-side mesh
typedef struct { typedef struct {
shader_t shader; shader_t shader;
mesh_t *mesh; mesh_t *mesh;
int vertex_buffer_size; int vertex_buffer_size;
vertex_t *vertex_buffer; vertex_t *vertex_buffer;
spine::SkeletonRenderer *renderer; spine::SkeletonRenderer *renderer;
} renderer_t; } renderer_t;
/// Creates a new renderer /// Creates a new renderer

View File

@ -35,68 +35,68 @@ spine::DebugExtension dbgExtension(spine::SpineExtension::getInstance());
extern spine::SkeletonRenderer *skeletonRenderer; extern spine::SkeletonRenderer *skeletonRenderer;
int main() { int main() {
spine::SpineExtension::setInstance(&dbgExtension); spine::SpineExtension::setInstance(&dbgExtension);
{ {
if (SDL_Init(SDL_INIT_VIDEO)) { if (SDL_Init(SDL_INIT_VIDEO)) {
printf("Error: %s", SDL_GetError()); printf("Error: %s", SDL_GetError());
return -1; return -1;
} }
SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600,
0); 0);
if (!window) { if (!window) {
printf("Error: %s", SDL_GetError()); printf("Error: %s", SDL_GetError());
return -1; return -1;
} }
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) { if (!renderer) {
printf("Error: %s", SDL_GetError()); printf("Error: %s", SDL_GetError());
return -1; return -1;
} }
spine::SDLTextureLoader textureLoader(renderer); spine::SDLTextureLoader textureLoader(renderer);
spine::Atlas atlas("data/spineboy-pma.atlas", &textureLoader); spine::Atlas atlas("data/spineboy-pma.atlas", &textureLoader);
spine::AtlasAttachmentLoader attachmentLoader(&atlas); spine::AtlasAttachmentLoader attachmentLoader(&atlas);
spine::SkeletonJson json(&attachmentLoader); spine::SkeletonJson json(&attachmentLoader);
json.setScale(0.5f); json.setScale(0.5f);
spine::SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json"); spine::SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json");
spine::SkeletonDrawable drawable(skeletonData); spine::SkeletonDrawable drawable(skeletonData);
drawable.usePremultipliedAlpha = true; drawable.usePremultipliedAlpha = true;
drawable.animationState->getData()->setDefaultMix(0.2f); drawable.animationState->getData()->setDefaultMix(0.2f);
drawable.skeleton->setPosition(400, 500); drawable.skeleton->setPosition(400, 500);
drawable.skeleton->setToSetupPose(); drawable.skeleton->setToSetupPose();
drawable.animationState->setAnimation(0, "portal", true); drawable.animationState->setAnimation(0, "portal", true);
drawable.animationState->addAnimation(0, "run", true, 0); drawable.animationState->addAnimation(0, "run", true, 0);
drawable.update(0, spine::Physics_Update); drawable.update(0, spine::Physics_Update);
bool quit = false; bool quit = false;
uint64_t lastFrameTime = SDL_GetPerformanceCounter(); uint64_t lastFrameTime = SDL_GetPerformanceCounter();
while (!quit) { while (!quit) {
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event) != 0) { while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) { if (event.type == SDL_QUIT) {
quit = true; quit = true;
break; break;
} }
} }
SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255); SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
uint64_t now = SDL_GetPerformanceCounter(); uint64_t now = SDL_GetPerformanceCounter();
double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency(); double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency();
lastFrameTime = now; lastFrameTime = now;
drawable.update(deltaTime, spine::Physics_Update); drawable.update(deltaTime, spine::Physics_Update);
drawable.draw(renderer); drawable.draw(renderer);
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
SDL_Quit(); SDL_Quit();
delete skeletonData; delete skeletonData;
} }
delete skeletonRenderer; delete skeletonRenderer;
dbgExtension.reportLeaks(); dbgExtension.reportLeaks();
return 0; return 0;
} }

View File

@ -62,76 +62,76 @@ void SkeletonDrawable::update(float delta, Physics physics) {
} }
inline void toSDLColor(uint32_t color, SDL_Color *sdlColor) { inline void toSDLColor(uint32_t color, SDL_Color *sdlColor) {
sdlColor->a = (color >> 24) & 0xFF; sdlColor->a = (color >> 24) & 0xFF;
sdlColor->r = (color >> 16) & 0xFF; sdlColor->r = (color >> 16) & 0xFF;
sdlColor->g = (color >> 8) & 0xFF; sdlColor->g = (color >> 8) & 0xFF;
sdlColor->b = color & 0xFF; sdlColor->b = color & 0xFF;
} }
void SkeletonDrawable::draw(SDL_Renderer *renderer) { void SkeletonDrawable::draw(SDL_Renderer *renderer) {
if (!skeletonRenderer) skeletonRenderer = new (__FILE__, __LINE__) SkeletonRenderer(); if (!skeletonRenderer) skeletonRenderer = new (__FILE__, __LINE__) SkeletonRenderer();
RenderCommand *command = skeletonRenderer->render(*skeleton); RenderCommand *command = skeletonRenderer->render(*skeleton);
while(command) { while (command) {
float *positions = command->positions; float *positions = command->positions;
float *uvs = command->uvs; float *uvs = command->uvs;
uint32_t *colors = command->colors; uint32_t *colors = command->colors;
sdlVertices.clear(); sdlVertices.clear();
for (int ii = 0; ii < command->numVertices << 1; ii += 2) { for (int ii = 0; ii < command->numVertices << 1; ii += 2) {
SDL_Vertex sdlVertex; SDL_Vertex sdlVertex;
sdlVertex.position.x = positions[ii]; sdlVertex.position.x = positions[ii];
sdlVertex.position.y = positions[ii + 1]; sdlVertex.position.y = positions[ii + 1];
sdlVertex.tex_coord.x = uvs[ii]; sdlVertex.tex_coord.x = uvs[ii];
sdlVertex.tex_coord.y = uvs[ii + 1]; sdlVertex.tex_coord.y = uvs[ii + 1];
toSDLColor(colors[ii >> 1], &sdlVertex.color); toSDLColor(colors[ii >> 1], &sdlVertex.color);
sdlVertices.add(sdlVertex); sdlVertices.add(sdlVertex);
} }
sdlIndices.clear(); sdlIndices.clear();
uint16_t *indices = command->indices; uint16_t *indices = command->indices;
for (int ii = 0; ii < command->numIndices; ii++) for (int ii = 0; ii < command->numIndices; ii++)
sdlIndices.add(indices[ii]); sdlIndices.add(indices[ii]);
BlendMode blendMode = command->blendMode; BlendMode blendMode = command->blendMode;
SDL_Texture *texture = (SDL_Texture *)command->texture; SDL_Texture *texture = (SDL_Texture *) command->texture;
if (!usePremultipliedAlpha) { if (!usePremultipliedAlpha) {
switch (blendMode) { switch (blendMode) {
case BlendMode_Normal: case BlendMode_Normal:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break; break;
case BlendMode_Multiply: case BlendMode_Multiply:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break; break;
case BlendMode_Additive: case BlendMode_Additive:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
break; break;
case BlendMode_Screen: case BlendMode_Screen:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break; break;
} }
} else { } else {
SDL_BlendMode target; SDL_BlendMode target;
switch (blendMode) { switch (blendMode) {
case BlendMode_Normal: case BlendMode_Normal:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD); target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target); SDL_SetTextureBlendMode(texture, target);
break; break;
case BlendMode_Multiply: case BlendMode_Multiply:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break; break;
case BlendMode_Additive: case BlendMode_Additive:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD); target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
break; break;
case BlendMode_Screen: case BlendMode_Screen:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD); target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break; break;
} }
} }
SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(), SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(),
command->numIndices); command->numIndices);
command = command->next; command = command->next;
} }
} }
SDL_Texture *loadTexture(SDL_Renderer *renderer, const String &path) { SDL_Texture *loadTexture(SDL_Renderer *renderer, const String &path) {

View File

@ -37,103 +37,102 @@ using namespace sf;
using namespace spine; using namespace spine;
sf::BlendMode blendModes[] = { sf::BlendMode blendModes[] = {
sf::BlendMode(sf::BlendMode::SrcAlpha, sf::BlendMode::OneMinusSrcAlpha), sf::BlendMode(sf::BlendMode::SrcAlpha, sf::BlendMode::OneMinusSrcAlpha),
sf::BlendMode(sf::BlendMode::SrcAlpha, sf::BlendMode::One), sf::BlendMode(sf::BlendMode::SrcAlpha, sf::BlendMode::One),
sf::BlendMode(sf::BlendMode::DstColor, sf::BlendMode::OneMinusSrcAlpha), sf::BlendMode(sf::BlendMode::DstColor, sf::BlendMode::OneMinusSrcAlpha),
sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcColor) sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcColor)};
};
sf::BlendMode blendModesPma[] = { sf::BlendMode blendModesPma[] = {
sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcAlpha), sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcAlpha),
sf::BlendMode(sf::BlendMode::One, sf::BlendMode::One), sf::BlendMode(sf::BlendMode::One, sf::BlendMode::One),
sf::BlendMode(sf::BlendMode::DstColor, sf::BlendMode::OneMinusSrcAlpha), sf::BlendMode(sf::BlendMode::DstColor, sf::BlendMode::OneMinusSrcAlpha),
sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcColor), sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcColor),
}; };
SkeletonRenderer *skeletonRenderer = nullptr; SkeletonRenderer *skeletonRenderer = nullptr;
SkeletonDrawable::SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *stateData) : timeScale(1), SkeletonDrawable::SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *stateData) : timeScale(1),
usePremultipliedAlpha(false), usePremultipliedAlpha(false),
vertexArray(new VertexArray(Triangles, skeletonData->getBones().size() * 4)) { vertexArray(new VertexArray(Triangles, skeletonData->getBones().size() * 4)) {
Bone::setYDown(true); Bone::setYDown(true);
skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData);
ownsAnimationStateData = stateData == 0; ownsAnimationStateData = stateData == 0;
if (ownsAnimationStateData) stateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData); if (ownsAnimationStateData) stateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData);
state = new (__FILE__, __LINE__) AnimationState(stateData); state = new (__FILE__, __LINE__) AnimationState(stateData);
} }
SkeletonDrawable::~SkeletonDrawable() { SkeletonDrawable::~SkeletonDrawable() {
delete vertexArray; delete vertexArray;
if (ownsAnimationStateData) delete state->getData(); if (ownsAnimationStateData) delete state->getData();
delete state; delete state;
delete skeleton; delete skeleton;
} }
void SkeletonDrawable::update(float deltaTime, Physics physics) { void SkeletonDrawable::update(float deltaTime, Physics physics) {
state->update(deltaTime * timeScale); state->update(deltaTime * timeScale);
state->apply(*skeleton); state->apply(*skeleton);
skeleton->update(deltaTime * timeScale); skeleton->update(deltaTime * timeScale);
skeleton->updateWorldTransform(physics); skeleton->updateWorldTransform(physics);
} }
inline void toSFMLColor(uint32_t color, sf::Color *sfmlColor) { inline void toSFMLColor(uint32_t color, sf::Color *sfmlColor) {
sfmlColor->a = (color >> 24) & 0xFF; sfmlColor->a = (color >> 24) & 0xFF;
sfmlColor->r = (color >> 16) & 0xFF; sfmlColor->r = (color >> 16) & 0xFF;
sfmlColor->g = (color >> 8) & 0xFF; sfmlColor->g = (color >> 8) & 0xFF;
sfmlColor->b = color & 0xFF; sfmlColor->b = color & 0xFF;
} }
void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const { void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
states.texture = NULL; states.texture = NULL;
vertexArray->clear(); vertexArray->clear();
if (!skeletonRenderer) skeletonRenderer = new (__FILE__, __LINE__) SkeletonRenderer(); if (!skeletonRenderer) skeletonRenderer = new (__FILE__, __LINE__) SkeletonRenderer();
RenderCommand *command = skeletonRenderer->render(*skeleton); RenderCommand *command = skeletonRenderer->render(*skeleton);
while (command) { while (command) {
Vertex vertex; Vertex vertex;
float *positions = command->positions; float *positions = command->positions;
float *uvs = command->uvs; float *uvs = command->uvs;
uint32_t *colors = command->colors; uint32_t *colors = command->colors;
uint16_t *indices = command->indices; uint16_t *indices = command->indices;
Texture *texture = (Texture *)command->texture; Texture *texture = (Texture *) command->texture;
Vector2u size = texture->getSize(); Vector2u size = texture->getSize();
for (int i = 0, n = command->numIndices; i < n; ++i) { for (int i = 0, n = command->numIndices; i < n; ++i) {
int ii = indices[i]; int ii = indices[i];
int index = ii << 1; int index = ii << 1;
vertex.position.x = positions[index]; vertex.position.x = positions[index];
vertex.position.y = positions[index + 1]; vertex.position.y = positions[index + 1];
vertex.texCoords.x = uvs[index] * size.x; vertex.texCoords.x = uvs[index] * size.x;
vertex.texCoords.y = uvs[index + 1] * size.y; vertex.texCoords.y = uvs[index + 1] * size.y;
toSFMLColor(colors[ii], &vertex.color); toSFMLColor(colors[ii], &vertex.color);
vertexArray->append(vertex); vertexArray->append(vertex);
} }
BlendMode blendMode = command->blendMode; BlendMode blendMode = command->blendMode;
states.blendMode = usePremultipliedAlpha ? blendModesPma[blendMode] : blendModes[blendMode]; states.blendMode = usePremultipliedAlpha ? blendModesPma[blendMode] : blendModes[blendMode];
states.texture = texture; states.texture = texture;
target.draw(*vertexArray, states); target.draw(*vertexArray, states);
vertexArray->clear(); vertexArray->clear();
command = command->next; command = command->next;
} }
} }
void SFMLTextureLoader::load(AtlasPage &page, const String &path) { void SFMLTextureLoader::load(AtlasPage &page, const String &path) {
Texture *texture = new Texture(); Texture *texture = new Texture();
if (!texture->loadFromFile(path.buffer())) return; if (!texture->loadFromFile(path.buffer())) return;
if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true); if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true);
if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true); if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true);
page.texture = texture; page.texture = texture;
Vector2u size = texture->getSize(); Vector2u size = texture->getSize();
page.width = size.x; page.width = size.x;
page.height = size.y; page.height = size.y;
} }
void SFMLTextureLoader::unload(void *texture) { void SFMLTextureLoader::unload(void *texture) {
delete (Texture *) texture; delete (Texture *) texture;
} }
SpineExtension *spine::getDefaultExtension() { SpineExtension *spine::getDefaultExtension() {
return new DefaultSpineExtension(); return new DefaultSpineExtension();
} }

View File

@ -62,7 +62,7 @@ namespace spine {
private: private:
bool ownsAnimationStateData; bool ownsAnimationStateData;
mutable bool usePremultipliedAlpha; mutable bool usePremultipliedAlpha;
sf::VertexArray *vertexArray; sf::VertexArray *vertexArray;
}; };
class SFMLTextureLoader : public TextureLoader { class SFMLTextureLoader : public TextureLoader {