mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 01:06:00 +08:00
[glfw] MVP
This commit is contained in:
parent
99d6693979
commit
4b5102fddf
@ -40,6 +40,9 @@ elseif((${SPINE_SDL}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sdl"))
|
||||
add_subdirectory(spine-c)
|
||||
add_subdirectory(spine-cpp)
|
||||
add_subdirectory(spine-sdl)
|
||||
elseif((${SPINE_GLFW}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-glfw"))
|
||||
add_subdirectory(spine-cpp)
|
||||
add_subdirectory(spine-glfw)
|
||||
else()
|
||||
add_subdirectory(spine-c)
|
||||
add_subdirectory(spine-cpp)
|
||||
|
||||
@ -15,7 +15,7 @@ add_library(spine-cpp STATIC ${SOURCES} ${INCLUDES})
|
||||
target_include_directories(spine-cpp PUBLIC spine-cpp/include)
|
||||
|
||||
add_library(spine-cpp-lite STATIC ${SOURCES} ${INCLUDES} spine-cpp-lite/spine-cpp-lite.cpp)
|
||||
target_include_directories(spine-cpp-lite PUBLIC spine-cpp/include)
|
||||
target_include_directories(spine-cpp-lite PUBLIC spine-cpp/include spine-cpp-lite)
|
||||
|
||||
# Install target
|
||||
install(TARGETS spine-cpp EXPORT spine-cpp_TARGETS DESTINATION dist/lib)
|
||||
|
||||
@ -135,6 +135,7 @@ typedef struct _spine_render_command {
|
||||
float *positions;
|
||||
float *uvs;
|
||||
int32_t *colors;
|
||||
int32_t *darkColors;
|
||||
int32_t numVertices;
|
||||
uint16_t *indices;
|
||||
int32_t numIndices;
|
||||
@ -653,6 +654,7 @@ static _spine_render_command *spine_render_command_create(BlockAllocator &alloca
|
||||
cmd->positions = allocator.allocate<float>(numVertices << 1);
|
||||
cmd->uvs = allocator.allocate<float>(numVertices << 1);
|
||||
cmd->colors = allocator.allocate<int32_t>(numVertices);
|
||||
cmd->darkColors = allocator.allocate<int32_t>(numVertices);
|
||||
cmd->numVertices = numVertices;
|
||||
cmd->indices = allocator.allocate<uint16_t>(numIndices);
|
||||
cmd->numIndices = numIndices;
|
||||
@ -697,6 +699,7 @@ static _spine_render_command *batch_sub_commands(BlockAllocator &allocator, Vect
|
||||
float *positions = batched->positions;
|
||||
float *uvs = batched->uvs;
|
||||
int32_t *colors = batched->colors;
|
||||
int32_t *darkColors = batched->darkColors;
|
||||
uint16_t *indices = batched->indices;
|
||||
int indicesOffset = 0;
|
||||
for (int i = first; i <= last; i++) {
|
||||
@ -704,12 +707,14 @@ static _spine_render_command *batch_sub_commands(BlockAllocator &allocator, Vect
|
||||
memcpy(positions, cmd->positions, sizeof(float) * 2 * cmd->numVertices);
|
||||
memcpy(uvs, cmd->uvs, sizeof(float) * 2 * cmd->numVertices);
|
||||
memcpy(colors, cmd->colors, sizeof(int32_t) * cmd->numVertices);
|
||||
memcpy(darkColors, cmd->darkColors, sizeof(int32_t) * cmd->numVertices);
|
||||
for (int ii = 0; ii < cmd->numIndices; ii++)
|
||||
indices[ii] = cmd->indices[ii] + indicesOffset;
|
||||
indicesOffset += cmd->numVertices;
|
||||
positions += 2 * cmd->numVertices;
|
||||
uvs += 2 * cmd->numVertices;
|
||||
colors += cmd->numVertices;
|
||||
darkColors += cmd->numVertices;
|
||||
indices += cmd->numIndices;
|
||||
}
|
||||
return batched;
|
||||
@ -728,9 +733,16 @@ static _spine_render_command *batch_commands(BlockAllocator &allocator, Vector<_
|
||||
int numIndices = first->numIndices;
|
||||
while (i <= (int) commands.size()) {
|
||||
_spine_render_command *cmd = i < (int) commands.size() ? commands[i] : nullptr;
|
||||
|
||||
if (cmd && cmd->numVertices == 0 && cmd->numIndices == 0) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd != nullptr && cmd->atlasPage == first->atlasPage &&
|
||||
cmd->blendMode == first->blendMode &&
|
||||
cmd->colors[0] == first->colors[0] &&
|
||||
cmd->darkColors[0] == first->darkColors[0] &&
|
||||
numIndices + cmd->numIndices < 0xffff) {
|
||||
numVertices += cmd->numVertices;
|
||||
numIndices += cmd->numIndices;
|
||||
@ -836,6 +848,11 @@ spine_render_command spine_skeleton_drawable_render(spine_skeleton_drawable draw
|
||||
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);
|
||||
uint32_t color = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
uint32_t darkColor = 0xff000000;
|
||||
if (slot.hasDarkColor()) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (clipper.isClipping()) {
|
||||
clipper.clipTriangles(*worldVertices, *indices, *uvs, 2);
|
||||
@ -850,7 +867,10 @@ spine_render_command spine_skeleton_drawable_render(spine_skeleton_drawable draw
|
||||
_drawable->renderCommands.add(cmd);
|
||||
memcpy(cmd->positions, vertices->buffer(), (verticesCount << 1) * sizeof(float));
|
||||
memcpy(cmd->uvs, uvs->buffer(), (verticesCount << 1) * sizeof(float));
|
||||
for (int ii = 0; ii < verticesCount; ii++) cmd->colors[ii] = color;
|
||||
for (int ii = 0; ii < verticesCount; ii++) {
|
||||
cmd->colors[ii] = color;
|
||||
cmd->darkColors[ii] = darkColor;
|
||||
}
|
||||
memcpy(cmd->indices, indices->buffer(), indices->size() * sizeof(uint16_t));
|
||||
clipper.clipEnd(slot);
|
||||
}
|
||||
@ -895,6 +915,11 @@ int32_t *spine_render_command_get_colors(spine_render_command command) {
|
||||
return ((_spine_render_command *) command)->colors;
|
||||
}
|
||||
|
||||
int32_t *spine_render_command_get_dark_colors(spine_render_command command) {
|
||||
if (!command) return nullptr;
|
||||
return ((_spine_render_command *) command)->darkColors;
|
||||
}
|
||||
|
||||
int32_t spine_render_command_get_num_vertices(spine_render_command command) {
|
||||
if (!command) return 0;
|
||||
return ((_spine_render_command *) command)->numVertices;
|
||||
@ -1697,6 +1722,13 @@ void spine_skeleton_set_y(spine_skeleton skeleton, float y) {
|
||||
_skeleton->setY(y);
|
||||
}
|
||||
|
||||
void spine_skeleton_set_scale(spine_skeleton skeleton, float scaleX, float scaleY) {
|
||||
if (skeleton == nullptr) return;
|
||||
Skeleton *_skeleton = (Skeleton *) skeleton;
|
||||
_skeleton->setScaleX(scaleX);
|
||||
_skeleton->setScaleY(scaleY);
|
||||
}
|
||||
|
||||
float spine_skeleton_get_scale_x(spine_skeleton skeleton) {
|
||||
if (skeleton == nullptr) return 0;
|
||||
Skeleton *_skeleton = (Skeleton *) skeleton;
|
||||
|
||||
@ -292,6 +292,8 @@ SPINE_CPP_LITE_EXPORT float *spine_render_command_get_positions(spine_render_com
|
||||
SPINE_CPP_LITE_EXPORT float *spine_render_command_get_uvs(spine_render_command command);
|
||||
// @ignore
|
||||
SPINE_CPP_LITE_EXPORT int32_t *spine_render_command_get_colors(spine_render_command command);
|
||||
// @ignore
|
||||
SPINE_CPP_LITE_EXPORT int32_t *spine_render_command_get_dark_colors(spine_render_command command);
|
||||
SPINE_CPP_LITE_EXPORT int32_t spine_render_command_get_num_vertices(spine_render_command command);
|
||||
SPINE_CPP_LITE_EXPORT uint16_t *spine_render_command_get_indices(spine_render_command command);
|
||||
SPINE_CPP_LITE_EXPORT int32_t spine_render_command_get_num_indices(spine_render_command command);
|
||||
@ -452,6 +454,7 @@ SPINE_CPP_LITE_EXPORT float spine_skeleton_get_x(spine_skeleton skeleton);
|
||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_x(spine_skeleton skeleton, float x);
|
||||
SPINE_CPP_LITE_EXPORT float spine_skeleton_get_y(spine_skeleton skeleton);
|
||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_y(spine_skeleton skeleton, float y);
|
||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_scale(spine_skeleton skeleton, float scaleX, float scaleY);
|
||||
SPINE_CPP_LITE_EXPORT float spine_skeleton_get_scale_x(spine_skeleton skeleton);
|
||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_scale_x(spine_skeleton skeleton, float scaleX);
|
||||
SPINE_CPP_LITE_EXPORT float spine_skeleton_get_scale_y(spine_skeleton skeleton);
|
||||
|
||||
61
spine-glfw/CMakeLists.txt
Normal file
61
spine-glfw/CMakeLists.txt
Normal file
@ -0,0 +1,61 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(spine-glfw)
|
||||
if(MSVC)
|
||||
message("MSCV detected")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
|
||||
else()
|
||||
message("${CMAKE_C_FLAGS}")
|
||||
message("${CMAKE_CXX_FLAGS}")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -std=c99")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wnon-virtual-dtor -pedantic -Wno-unused-parameter -std=c++11 -fno-exceptions -fno-rtti")
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
# Fetch GLFW
|
||||
FetchContent_Declare(
|
||||
glfw
|
||||
GIT_REPOSITORY https://github.com/glfw/glfw.git
|
||||
GIT_TAG latest
|
||||
)
|
||||
FetchContent_MakeAvailable(glfw)
|
||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL " " FORCE)
|
||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL " " FORCE)
|
||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL " " FORCE)
|
||||
set(GLFW_INSTALL OFF CACHE BOOL " " FORCE)
|
||||
|
||||
# Fetch glbinding
|
||||
FetchContent_Declare(
|
||||
glbinding
|
||||
GIT_REPOSITORY https://github.com/cginternals/glbinding.git
|
||||
GIT_TAG master
|
||||
)
|
||||
FetchContent_MakeAvailable(glbinding)
|
||||
set(OPTION_BUILD_DOCS OFF CACHE BOOL " " FORCE)
|
||||
set(OPTION_BUILD_EXAMPLES OFF CACHE BOOL " " FORCE)
|
||||
set(OPTION_BUILD_TEST OFF CACHE BOOL " " FORCE)
|
||||
set(OPTION_BUILD_TOOLS OFF CACHE BOOL " " FORCE)
|
||||
|
||||
include_directories(${glbinding_SOURCE_DIR}/include)
|
||||
include_directories(src)
|
||||
|
||||
# Find local OpenGL lib
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
# spine-glfw library
|
||||
add_library(spine-glfw STATIC src/spine-glfw.cpp src/spine-glfw.h src/stb_image.h)
|
||||
target_link_libraries(spine-glfw PRIVATE glbinding::glbinding)
|
||||
target_link_libraries(spine-glfw LINK_PUBLIC spine-cpp spine-cpp-lite)
|
||||
|
||||
# Example
|
||||
add_executable(spine-glfw-example example/main.cpp)
|
||||
target_link_libraries(spine-glfw-example PRIVATE glfw)
|
||||
target_link_libraries(spine-glfw-example PRIVATE OpenGL::GL)
|
||||
target_link_libraries(spine-glfw-example LINK_PUBLIC spine-glfw)
|
||||
target_link_libraries(spine-glfw-example PRIVATE glbinding::glbinding)
|
||||
|
||||
# copy data to build directory
|
||||
add_custom_command(TARGET spine-glfw-example PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_LIST_DIR}/data $<TARGET_FILE_DIR:spine-glfw-example>/data)
|
||||
95
spine-glfw/data/spineboy-pma.atlas
Normal file
95
spine-glfw/data/spineboy-pma.atlas
Normal file
@ -0,0 +1,95 @@
|
||||
spineboy-pma.png
|
||||
size: 1024, 256
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
crosshair
|
||||
bounds: 352, 7, 45, 45
|
||||
eye-indifferent
|
||||
bounds: 862, 105, 47, 45
|
||||
eye-surprised
|
||||
bounds: 505, 79, 47, 45
|
||||
front-bracer
|
||||
bounds: 826, 66, 29, 40
|
||||
front-fist-closed
|
||||
bounds: 786, 65, 38, 41
|
||||
front-fist-open
|
||||
bounds: 710, 51, 43, 44
|
||||
rotate: 90
|
||||
front-foot
|
||||
bounds: 210, 6, 63, 35
|
||||
front-shin
|
||||
bounds: 665, 128, 41, 92
|
||||
rotate: 90
|
||||
front-thigh
|
||||
bounds: 2, 2, 23, 56
|
||||
rotate: 90
|
||||
front-upper-arm
|
||||
bounds: 250, 205, 23, 49
|
||||
goggles
|
||||
bounds: 665, 171, 131, 83
|
||||
gun
|
||||
bounds: 798, 152, 105, 102
|
||||
head
|
||||
bounds: 2, 27, 136, 149
|
||||
hoverboard-board
|
||||
bounds: 2, 178, 246, 76
|
||||
hoverboard-thruster
|
||||
bounds: 722, 96, 30, 32
|
||||
rotate: 90
|
||||
hoverglow-small
|
||||
bounds: 275, 81, 137, 38
|
||||
mouth-grind
|
||||
bounds: 614, 97, 47, 30
|
||||
mouth-oooo
|
||||
bounds: 612, 65, 47, 30
|
||||
mouth-smile
|
||||
bounds: 661, 64, 47, 30
|
||||
muzzle-glow
|
||||
bounds: 382, 54, 25, 25
|
||||
muzzle-ring
|
||||
bounds: 275, 54, 25, 105
|
||||
rotate: 90
|
||||
muzzle01
|
||||
bounds: 911, 95, 67, 40
|
||||
rotate: 90
|
||||
muzzle02
|
||||
bounds: 792, 108, 68, 42
|
||||
muzzle03
|
||||
bounds: 956, 171, 83, 53
|
||||
rotate: 90
|
||||
muzzle04
|
||||
bounds: 275, 7, 75, 45
|
||||
muzzle05
|
||||
bounds: 140, 3, 68, 38
|
||||
neck
|
||||
bounds: 250, 182, 18, 21
|
||||
portal-bg
|
||||
bounds: 140, 43, 133, 133
|
||||
portal-flare1
|
||||
bounds: 554, 65, 56, 30
|
||||
portal-flare2
|
||||
bounds: 759, 112, 57, 31
|
||||
rotate: 90
|
||||
portal-flare3
|
||||
bounds: 554, 97, 58, 30
|
||||
portal-shade
|
||||
bounds: 275, 121, 133, 133
|
||||
portal-streaks1
|
||||
bounds: 410, 126, 126, 128
|
||||
portal-streaks2
|
||||
bounds: 538, 129, 125, 125
|
||||
rear-bracer
|
||||
bounds: 857, 67, 28, 36
|
||||
rear-foot
|
||||
bounds: 663, 96, 57, 30
|
||||
rear-shin
|
||||
bounds: 414, 86, 38, 89
|
||||
rotate: 90
|
||||
rear-thigh
|
||||
bounds: 756, 63, 28, 47
|
||||
rear-upper-arm
|
||||
bounds: 60, 5, 20, 44
|
||||
rotate: 90
|
||||
torso
|
||||
bounds: 905, 164, 49, 90
|
||||
BIN
spine-glfw/data/spineboy-pma.png
Normal file
BIN
spine-glfw/data/spineboy-pma.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 239 KiB |
8723
spine-glfw/data/spineboy-pro.json
Normal file
8723
spine-glfw/data/spineboy-pro.json
Normal file
File diff suppressed because it is too large
Load Diff
95
spine-glfw/example/main.cpp
Normal file
95
spine-glfw/example/main.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include <glbinding/glbinding.h>
|
||||
#include <glbinding/gl/gl.h>
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <spine-glfw.h>
|
||||
|
||||
int width = 800, height = 600;
|
||||
|
||||
GLFWwindow* init_glfw() {
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
GLFWwindow* window = glfwCreateWindow(width, height, "spine-glfw", NULL, NULL);
|
||||
if (!window) {
|
||||
std::cerr << "Failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
return nullptr;
|
||||
}
|
||||
glfwMakeContextCurrent(window);
|
||||
glbinding::initialize(glfwGetProcAddress);
|
||||
return window;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Initialize GLFW and glbinding
|
||||
GLFWwindow *window = init_glfw();
|
||||
if (!window) return -1;
|
||||
|
||||
// We use a y-down coordinate system, see renderer_set_viewport_size()
|
||||
spine_bone_set_is_y_down(true);
|
||||
|
||||
// Load the atlas and the skeleton data
|
||||
atlas_t *atlas = atlas_load("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas");
|
||||
spine_skeleton_data skeleton_data = skeleton_data_load("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json", atlas);
|
||||
|
||||
// Create a skeleton drawable from the data, set the skeleton's position to the bottom center of
|
||||
// the screen and scale it to make it smaller.
|
||||
spine_skeleton_drawable drawable = spine_skeleton_drawable_create(skeleton_data);
|
||||
spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable);
|
||||
spine_skeleton_set_position(skeleton, width / 2, height - 100);
|
||||
spine_skeleton_set_scale(skeleton, 0.3, 0.3);
|
||||
|
||||
// Set the "portal" animation on track 0 of the animation state, looping
|
||||
spine_animation_state animation_state = spine_skeleton_drawable_get_animation_state(drawable);
|
||||
spine_animation_state_set_animation_by_name(animation_state, 0, "portal", true);
|
||||
|
||||
// Create the renderer and set the viewport size to match the window size. This sets up a
|
||||
// pixel perfect orthogonal projection for 2D rendering.
|
||||
renderer_t *renderer = renderer_create();
|
||||
renderer_set_viewport_size(renderer, width, height);
|
||||
|
||||
// Rendering loop
|
||||
double lastTime = glfwGetTime();
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
// Calculate the delta time in seconds
|
||||
double currTime = glfwGetTime();
|
||||
float delta = currTime - lastTime;
|
||||
lastTime = currTime;
|
||||
|
||||
// Update and apply the animation state to the skeleton
|
||||
spine_animation_state_update(animation_state, delta);
|
||||
spine_animation_state_apply(animation_state, skeleton);
|
||||
|
||||
// Update the skeleton time (used for physics)
|
||||
spine_skeleton_update(skeleton, delta);
|
||||
|
||||
// Calculate the new pose
|
||||
spine_skeleton_update_world_transform(skeleton, SPINE_PHYSICS_UPDATE);
|
||||
|
||||
// Clear the screen
|
||||
gl::glClear(gl::GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Render the skeleton in its current pose
|
||||
renderer_draw(renderer, drawable, atlas);
|
||||
|
||||
// Present the rendering results and poll for events
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
// Dispose everything
|
||||
renderer_dispose(renderer);
|
||||
spine_skeleton_drawable_dispose(drawable);
|
||||
spine_skeleton_data_dispose(skeleton_data);
|
||||
atlas_dispose(atlas);
|
||||
|
||||
// Kill the window and GLFW
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
416
spine-glfw/src/spine-glfw.cpp
Normal file
416
spine-glfw/src/spine-glfw.cpp
Normal file
@ -0,0 +1,416 @@
|
||||
#include "spine-glfw.h"
|
||||
#include <stdio.h>
|
||||
#include <glbinding/gl/gl.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
using namespace gl;
|
||||
|
||||
/// A blend mode, see https://en.esotericsoftware.com/spine-slots#Blending
|
||||
/// Encodes the OpenGL source and destination blend function for both premultiplied and
|
||||
/// non-premultiplied alpha blending.
|
||||
typedef struct {
|
||||
unsigned int source_color;
|
||||
unsigned int source_color_pma;
|
||||
unsigned int dest_color;
|
||||
unsigned int source_alpha;
|
||||
} blend_mode_t;
|
||||
|
||||
/// 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.
|
||||
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, (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_ONE, (unsigned int)GL_ONE, (unsigned int)GL_ONE_MINUS_SRC_COLOR, (unsigned int)GL_ONE_MINUS_SRC_COLOR}
|
||||
};
|
||||
|
||||
mesh_t *mesh_create() {
|
||||
GLuint vao, vbo, ibo;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo);
|
||||
glGenBuffers(1, &ibo);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, x));
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertex_t), (void*)offsetof(vertex_t, color));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, u));
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertex_t), (void*)offsetof(vertex_t, darkColor));
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
mesh_t *mesh = (mesh_t*)malloc(sizeof(mesh_t));
|
||||
mesh->vao = vao;
|
||||
mesh->vbo = vbo;
|
||||
mesh->num_vertices = 0;
|
||||
mesh->ibo = ibo;
|
||||
mesh->num_indices = 0;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void mesh_update(mesh_t *mesh, vertex_t *vertices, int num_vertices, uint16_t *indices, int num_indices) {
|
||||
glBindVertexArray(mesh->vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, num_vertices * sizeof(vertex_t), vertices, GL_STATIC_DRAW);
|
||||
mesh->num_vertices = num_vertices;
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_indices * sizeof(uint16_t), indices, GL_STATIC_DRAW);
|
||||
mesh->num_indices = num_indices;
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void mesh_draw(mesh_t *mesh) {
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawElements(GL_TRIANGLES, mesh->num_indices, GL_UNSIGNED_SHORT, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void mesh_dispose(mesh_t *mesh) {
|
||||
glDeleteBuffers(1, &mesh->vbo);
|
||||
glDeleteBuffers(1, &mesh->ibo);
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
GLuint compile_shader(const char* source, GLenum type) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
printf("Error, shader compilation failed:\n%s\n", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
shader_t shader_create(const char* vertex_shader, const char* fragment_shader) {
|
||||
shader_t program;
|
||||
|
||||
GLuint vertex_shader_id = compile_shader(vertex_shader, GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader_id = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);
|
||||
if (!vertex_shader_id || !fragment_shader_id) {
|
||||
glDeleteShader(vertex_shader_id);
|
||||
glDeleteShader(fragment_shader_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
program = glCreateProgram();
|
||||
glAttachShader(program, vertex_shader_id);
|
||||
glAttachShader(program, fragment_shader_id);
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(program, 512, nullptr, infoLog);
|
||||
printf("Error, shader linking failed:\n%s\n", infoLog);
|
||||
glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
glDeleteShader(vertex_shader_id);
|
||||
glDeleteShader(fragment_shader_id);
|
||||
return program;
|
||||
}
|
||||
|
||||
void shader_set_matrix4(shader_t shader, const char* name, const float *matrix) {
|
||||
shader_use(shader);
|
||||
GLint location = glGetUniformLocation(shader, name);
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, matrix);
|
||||
}
|
||||
|
||||
void shader_set_float(shader_t shader, const char* name, float value) {
|
||||
shader_use(shader);
|
||||
GLint location = glGetUniformLocation(shader, name);
|
||||
glUniform1f(location, value);
|
||||
}
|
||||
|
||||
void shader_set_int(shader_t shader, const char* name, int value) {
|
||||
shader_use(shader);
|
||||
GLint location = glGetUniformLocation(shader, name);
|
||||
glUniform1i(location, value);
|
||||
}
|
||||
|
||||
void shader_use(shader_t program) {
|
||||
glUseProgram(program);
|
||||
}
|
||||
|
||||
void shader_dispose(shader_t program) {
|
||||
glDeleteProgram(program);
|
||||
}
|
||||
|
||||
texture_t texture_load(const char *file_path) {
|
||||
int width, height, nrChannels;
|
||||
unsigned char *data = stbi_load(file_path, &width, &height, &nrChannels, 0);
|
||||
if (!data) {
|
||||
printf("Failed to load texture\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLenum format = GL_RGBA;
|
||||
if (nrChannels == 1)
|
||||
format = GL_RED;
|
||||
else if (nrChannels == 3)
|
||||
format = GL_RGB;
|
||||
else if (nrChannels == 4)
|
||||
format = GL_RGBA;
|
||||
|
||||
texture_t texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
stbi_image_free(data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void texture_use(texture_t texture) {
|
||||
glActiveTexture(GL_TEXTURE0); // Set active texture unit to 0
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
void texture_dispose(texture_t texture) {
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
void matrix_ortho_projection(float *matrix, float width, float height) {
|
||||
std::memset(matrix, 0, 16 * sizeof(float));
|
||||
|
||||
float left = 0.0f;
|
||||
float right = width;
|
||||
float bottom = height;
|
||||
float top = 0.0f;
|
||||
float near = -1.0f;
|
||||
float far = 1.0f;
|
||||
|
||||
matrix[0] = 2.0f / (right - left);
|
||||
matrix[5] = 2.0f / (top - bottom);
|
||||
matrix[10] = -2.0f / (far - near);
|
||||
matrix[12] = -(right + left) / (right - left);
|
||||
matrix[13] = -(top + bottom) / (top - bottom);
|
||||
matrix[14] = -(far + near) / (far - near);
|
||||
matrix[15] = 1.0f;
|
||||
}
|
||||
|
||||
const uint8_t *file_read(const char *path, int *length) {
|
||||
uint8_t *data;
|
||||
FILE *file = fopen(path, "rb");
|
||||
if (!file) return 0;
|
||||
fseek(file, 0, SEEK_END);
|
||||
*length = (int) ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
data = (uint8_t*)(malloc(*length + 1));
|
||||
fread(data, 1, *length, file);
|
||||
fclose(file);
|
||||
data[*length] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
atlas_t *atlas_load(const char *file_path) {
|
||||
int length = 0;
|
||||
utf8 *atlas_data = (utf8*)file_read(file_path, &length);
|
||||
if (!atlas_data) {
|
||||
printf("Could not load atlas %s\n", file_path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
spine_atlas spine_atlas = spine_atlas_load(atlas_data);
|
||||
free(atlas_data);
|
||||
if (!spine_atlas) {
|
||||
printf("Could not load atlas %s\n", file_path);
|
||||
return nullptr;
|
||||
}
|
||||
atlas_t *atlas = (atlas_t*)malloc(sizeof(atlas_t));
|
||||
atlas->atlas = spine_atlas;
|
||||
int num_textures = spine_atlas_get_num_image_paths(spine_atlas);
|
||||
atlas->textures = (texture_t*)malloc(sizeof(texture_t) * num_textures);
|
||||
memset(atlas->textures, 0, sizeof(texture_t) * num_textures);
|
||||
|
||||
char parent_dir[1024];
|
||||
strncpy(parent_dir, file_path, sizeof(parent_dir));
|
||||
char *last_slash = strrchr(parent_dir, '/');
|
||||
if (last_slash) {
|
||||
*(last_slash + 1) = '\0';
|
||||
} else {
|
||||
parent_dir[0] = '\0';
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_textures; i++) {
|
||||
char *relative_path = spine_atlas_get_image_path(spine_atlas, i);
|
||||
char full_path[1024];
|
||||
snprintf(full_path, sizeof(full_path), "%s%s", parent_dir, relative_path);
|
||||
texture_t texture = texture_load(full_path);
|
||||
if (!texture) {
|
||||
printf("Could not load atlas texture %s\n", full_path);
|
||||
atlas_dispose(atlas);
|
||||
return nullptr;
|
||||
}
|
||||
atlas->textures[i] = texture;
|
||||
}
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
void atlas_dispose(atlas_t *atlas) {
|
||||
for (int i = 0; i < spine_atlas_get_num_image_paths(atlas->atlas); i++) {
|
||||
texture_dispose(atlas->textures[i]);
|
||||
}
|
||||
spine_atlas_dispose(atlas->atlas);
|
||||
free(atlas->textures);
|
||||
free(atlas);
|
||||
}
|
||||
|
||||
spine_skeleton_data skeleton_data_load(const char *file_path, atlas_t *atlas) {
|
||||
int length = 0;
|
||||
uint8_t *data = (uint8_t*)file_read(file_path, &length);
|
||||
if (!data) {
|
||||
printf("Could not load skeleton data file %s\n", file_path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
spine_skeleton_data_result result;
|
||||
const char *ext = strrchr(file_path, '.');
|
||||
if (ext && strcmp(ext, ".skel") == 0) {
|
||||
result = spine_skeleton_data_load_binary(atlas->atlas, data, length);
|
||||
} else {
|
||||
result = spine_skeleton_data_load_json(atlas->atlas, (utf8*)data);
|
||||
}
|
||||
free(data);
|
||||
|
||||
if (spine_skeleton_data_result_get_error(result)) {
|
||||
printf("Could not load skeleton data file %s:\n%s\n", file_path, spine_skeleton_data_result_get_error(result));
|
||||
spine_skeleton_data_result_dispose(result);
|
||||
return nullptr;
|
||||
}
|
||||
spine_skeleton_data skeleton_data = spine_skeleton_data_result_get_data(result);
|
||||
spine_skeleton_data_result_dispose(result);
|
||||
return skeleton_data;
|
||||
}
|
||||
|
||||
renderer_t *renderer_create() {
|
||||
shader_t shader = shader_create(R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 aPos;
|
||||
layout (location = 1) in vec4 aLightColor;
|
||||
layout (location = 2) in vec2 aTexCoord;
|
||||
layout (location = 3) in vec4 aDarkColor;
|
||||
|
||||
uniform mat4 uMatrix;
|
||||
|
||||
out vec4 lightColor;
|
||||
out vec4 darkColor;
|
||||
out vec2 texCoord;
|
||||
|
||||
void main() {
|
||||
lightColor = aLightColor;
|
||||
darkColor = aDarkColor;
|
||||
texCoord = aTexCoord;
|
||||
gl_Position = uMatrix * vec4(aPos, 0.0, 1.0);
|
||||
}
|
||||
)", R"(
|
||||
#version 330 core
|
||||
in vec4 lightColor;
|
||||
in vec4 darkColor;
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D uTexture;
|
||||
void main() {
|
||||
vec4 texColor = texture(uTexture, texCoord);
|
||||
float alpha = texColor.a * lightColor.a;
|
||||
fragColor.a = alpha;
|
||||
fragColor.rgb = ((texColor.a - 1.0) * darkColor.a + 1.0 - texColor.rgb) * darkColor.rgb + texColor.rgb * lightColor.rgb;
|
||||
}
|
||||
)");
|
||||
if (!shader) return nullptr;
|
||||
mesh_t *mesh = mesh_create();
|
||||
renderer_t *renderer = (renderer_t*)malloc(sizeof(renderer_t));
|
||||
renderer->shader = shader;
|
||||
renderer->mesh = mesh;
|
||||
renderer->vertex_buffer_size = 0;
|
||||
renderer->vertex_buffer = nullptr;
|
||||
return renderer;
|
||||
}
|
||||
|
||||
void renderer_set_viewport_size(renderer_t *renderer, int width, int height) {
|
||||
float matrix[16];
|
||||
matrix_ortho_projection(matrix, width, height);
|
||||
shader_use(renderer->shader);
|
||||
shader_set_matrix4(renderer->shader, "uMatrix", matrix);
|
||||
}
|
||||
|
||||
void renderer_draw(renderer_t *renderer, spine_skeleton_drawable drawable, atlas_t *atlas) {
|
||||
shader_use(renderer->shader);
|
||||
shader_set_int(renderer->shader, "uTexture", 0);
|
||||
gl::glEnable(gl::GLenum::GL_BLEND);
|
||||
|
||||
spine_render_command command = spine_skeleton_drawable_render(drawable);
|
||||
while (command) {
|
||||
int num_command_vertices = spine_render_command_get_num_vertices(command);
|
||||
if (renderer->vertex_buffer_size < num_command_vertices) {
|
||||
renderer->vertex_buffer_size = num_command_vertices;
|
||||
free(renderer->vertex_buffer);
|
||||
renderer->vertex_buffer = (vertex_t *)malloc(sizeof(vertex_t) * renderer->vertex_buffer_size);
|
||||
}
|
||||
float *positions = spine_render_command_get_positions(command);
|
||||
float *uvs = spine_render_command_get_uvs(command);
|
||||
int32_t *colors = spine_render_command_get_colors(command);
|
||||
int32_t *darkColors = spine_render_command_get_dark_colors(command);
|
||||
for (int i = 0, j = 0; i < num_command_vertices; i++, j += 2) {
|
||||
vertex_t *vertex = &renderer->vertex_buffer[i];
|
||||
vertex->x = positions[j];
|
||||
vertex->y = positions[j + 1];
|
||||
vertex->u = uvs[j];
|
||||
vertex->v = uvs[j+1];
|
||||
uint32_t color = colors[i];
|
||||
vertex->color = (color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0x000000FF) << 16);
|
||||
uint32_t darkColor = darkColors[i];
|
||||
vertex->darkColor = (darkColor & 0xFF00FF00) | ((darkColor & 0x00FF0000) >> 16) | ((darkColor & 0x000000FF) << 16);
|
||||
}
|
||||
int num_command_indices = spine_render_command_get_num_indices(command);
|
||||
uint16_t *indices = spine_render_command_get_indices(command);
|
||||
mesh_update(renderer->mesh, renderer->vertex_buffer, num_command_vertices, indices, num_command_indices);
|
||||
|
||||
blend_mode_t blend_mode = blend_modes[spine_render_command_get_blend_mode(command)];
|
||||
gl::glBlendFuncSeparate(spine_atlas_is_pma(atlas->atlas) ? (gl::GLenum)blend_mode.source_color_pma : (gl::GLenum)blend_mode.source_color, (gl::GLenum)blend_mode.dest_color, (gl::GLenum)blend_mode.source_alpha, (gl::GLenum)blend_mode.dest_color);
|
||||
|
||||
texture_t texture = atlas->textures[spine_render_command_get_atlas_page(command)];
|
||||
texture_use(texture);
|
||||
|
||||
mesh_draw(renderer->mesh);
|
||||
command = spine_render_command_get_next(command);
|
||||
}
|
||||
}
|
||||
|
||||
void renderer_dispose(renderer_t *renderer) {
|
||||
shader_dispose(renderer->shader);
|
||||
mesh_dispose(renderer->mesh);
|
||||
free(renderer->vertex_buffer);
|
||||
free(renderer);
|
||||
}
|
||||
98
spine-glfw/src/spine-glfw.h
Normal file
98
spine-glfw/src/spine-glfw.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <spine-cpp-lite.h>
|
||||
|
||||
/// A vertex of a mesh generated from a Spine skeleton
|
||||
struct vertex_t {
|
||||
float x, y;
|
||||
uint32_t color;
|
||||
float u, v;
|
||||
uint32_t darkColor;
|
||||
};
|
||||
|
||||
/// A GPU-side mesh using OpenGL vertex arrays, vertex buffer, and
|
||||
/// indices buffer.
|
||||
typedef struct {
|
||||
unsigned int vao;
|
||||
unsigned int vbo;
|
||||
int num_vertices;
|
||||
unsigned int ibo;
|
||||
int num_indices;
|
||||
} mesh_t;
|
||||
|
||||
mesh_t *mesh_create();
|
||||
void mesh_update(mesh_t *mesh, vertex_t *vertices, int num_vertices, uint16_t *indices, int num_indices);
|
||||
void mesh_draw(mesh_t *mesh);
|
||||
void mesh_dispose(mesh_t *mesh);
|
||||
|
||||
/// A shader (the OpenGL shader program id)
|
||||
typedef unsigned int shader_t;
|
||||
|
||||
/// Creates a shader program from the vertex and fragment shader
|
||||
shader_t shader_create(const char *vertex_shader, const char *fragment_shader);
|
||||
|
||||
/// Sets a uniform matrix by name
|
||||
void shader_set_matrix4(shader_t program, const char* name, const float *matrix);
|
||||
|
||||
/// Sets a uniform float by name
|
||||
void shader_set_float(shader_t program, const char* name, float value);
|
||||
|
||||
/// Sets a uniform int by name
|
||||
void shader_set_int(shader_t program, const char* name, int value);
|
||||
|
||||
/// Binds the shader
|
||||
void shader_use(shader_t shader);
|
||||
|
||||
/// Disposes the shader
|
||||
void shader_dispose(shader_t shader);
|
||||
|
||||
/// A texture (the OpenGL texture object id)
|
||||
typedef unsigned int texture_t;
|
||||
|
||||
/// Loads the given image and creates an OpenGL texture with default settings and auto-generated mipmap levels
|
||||
texture_t texture_load(const char *file_path);
|
||||
|
||||
/// Binds the texture to texture unit 0
|
||||
void texture_use(texture_t texture);
|
||||
|
||||
/// Disposes the texture
|
||||
void texture_dispose(texture_t texture);
|
||||
|
||||
/// Helper struct that contains a Spine atlas and the textures for each
|
||||
/// atlas page
|
||||
typedef struct {
|
||||
spine_atlas atlas;
|
||||
texture_t *textures;
|
||||
} atlas_t;
|
||||
|
||||
/// Loads the .atlas file and its associated atlas pages as OpenGL textures
|
||||
atlas_t *atlas_load(const char *file_path);
|
||||
|
||||
/// Disposes the atlas data and its associated OpenGL textures
|
||||
void atlas_dispose(atlas_t *atlas);
|
||||
|
||||
/// Loads the skeleton data from the .skel or .json file using the given atlas
|
||||
spine_skeleton_data skeleton_data_load(const char *file_path, atlas_t *atlas);
|
||||
|
||||
/// 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
|
||||
typedef struct {
|
||||
shader_t shader;
|
||||
mesh_t *mesh;
|
||||
int vertex_buffer_size;
|
||||
vertex_t *vertex_buffer;
|
||||
} renderer_t;
|
||||
|
||||
/// Creates a new renderer
|
||||
renderer_t *renderer_create();
|
||||
|
||||
/// Sets the viewport size for the 2D orthographic projection
|
||||
void renderer_set_viewport_size(renderer_t *renderer, int width, int height);
|
||||
|
||||
/// Draws the given skeleton drawbale. The atlas must be the atlas from which the drawable
|
||||
/// was constructed.
|
||||
void renderer_draw(renderer_t *renderer, spine_skeleton_drawable drawable, atlas_t *atlas);
|
||||
|
||||
/// Disposes the renderer
|
||||
void renderer_dispose(renderer_t *renderer);
|
||||
7979
spine-glfw/src/stb_image.h
Normal file
7979
spine-glfw/src/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user