导出数据转spine

This commit is contained in:
tianmo
2026-09-07 21:29:15 +08:00
parent d258d4f435
commit 3a7c0b4092
65 changed files with 6000 additions and 0 deletions
@@ -0,0 +1,272 @@
#include "RuntimeApi.h"
#include <spine/Animation.h>
#include <spine/AnimationState.h>
#include <spine/AnimationStateData.h>
#include <spine/Atlas.h>
#include <spine/Extension.h>
#include <spine/Skeleton.h>
#include <spine/SkeletonBinary.h>
#include <spine/SkeletonData.h>
#include <spine/SkeletonJson.h>
#include <spine/SkeletonRenderer.h>
#include <spine/Skin.h>
#include <spine/TextureLoader.h>
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
namespace {
struct TextureRecord {
std::string path;
};
class MetadataTextureLoader final : public spine::TextureLoader {
public:
void load(spine::AtlasPage& page, const spine::String& path) override {
page.texture = new TextureRecord{path.buffer()};
}
void unload(void* texture) override { delete static_cast<TextureRecord*>(texture); }
};
struct RenderBatch {
std::vector<DtsRenderVertex> vertices;
std::vector<std::uint16_t> indices;
std::string texturePath;
std::uint32_t blendMode = 0;
};
struct RuntimeContext {
MetadataTextureLoader textureLoader;
std::unique_ptr<spine::Atlas> atlas;
std::unique_ptr<spine::SkeletonData> skeletonData;
std::unique_ptr<spine::Skeleton> skeleton;
std::unique_ptr<spine::AnimationStateData> animationStateData;
std::unique_ptr<spine::AnimationState> animationState;
std::unique_ptr<spine::SkeletonRenderer> renderer;
std::vector<std::string> animationNames;
std::vector<float> animationDurations;
std::vector<std::string> skinNames;
std::vector<RenderBatch> renderBatches;
std::string error;
};
RuntimeContext* context(DtsRuntimeHandle handle) {
return static_cast<RuntimeContext*>(handle);
}
std::string lowercaseExtension(const std::string& path) {
const auto dot = path.find_last_of('.');
auto extension = dot == std::string::npos ? std::string{} : path.substr(dot);
std::transform(extension.begin(), extension.end(), extension.begin(),
[](unsigned char value) { return static_cast<char>(std::tolower(value)); });
return extension;
}
void collectMetadata(RuntimeContext& value) {
value.animationNames.clear();
value.animationDurations.clear();
auto& animations = value.skeletonData->getAnimations();
for (std::size_t index = 0; index < animations.size(); ++index) {
auto* animation = animations[index];
value.animationNames.emplace_back(animation->getName().buffer());
value.animationDurations.push_back(animation->getDuration());
}
value.skinNames.clear();
auto& skins = value.skeletonData->getSkins();
for (std::size_t index = 0; index < skins.size(); ++index) {
auto* skin = skins[index];
value.skinNames.emplace_back(skin->getName().buffer());
}
}
void collectDrawCommands(RuntimeContext& value) {
value.renderBatches.clear();
for (auto* command = value.renderer->render(*value.skeleton); command;
command = command->next) {
RenderBatch batch;
batch.blendMode = static_cast<std::uint32_t>(command->blendMode);
if (const auto* texture = static_cast<TextureRecord*>(command->texture))
batch.texturePath = texture->path;
batch.vertices.reserve(static_cast<std::size_t>(command->numVertices));
for (int index = 0; index < command->numVertices; ++index) {
batch.vertices.push_back({
command->positions[index * 2], command->positions[index * 2 + 1],
command->uvs[index * 2], command->uvs[index * 2 + 1],
command->colors[index]});
}
batch.indices.assign(command->indices, command->indices + command->numIndices);
value.renderBatches.push_back(std::move(batch));
}
}
} // namespace
namespace spine {
SpineExtension* getDefaultExtension() {
return new DefaultSpineExtension();
}
} // namespace spine
extern "C" {
uint32_t dtsRuntimeGetApiVersion() {
return DTS_RUNTIME_API_VERSION;
}
int32_t dtsRuntimeGetInfo(DtsRuntimeInfo* info) {
if (!info || info->structSize < sizeof(DtsRuntimeInfo)) {
return -1;
}
info->apiVersion = DTS_RUNTIME_API_VERSION;
info->spineVersionLine = "4.3";
info->adapterBuild = "prototype-2";
info->capabilities = DTS_RUNTIME_CAP_SKELETON_METADATA | DTS_RUNTIME_CAP_DRAW_COMMANDS;
return 0;
}
DtsRuntimeHandle dtsRuntimeCreate() {
return new RuntimeContext;
}
void dtsRuntimeDestroy(DtsRuntimeHandle handle) {
delete context(handle);
}
int32_t dtsRuntimeLoad(DtsRuntimeHandle handle, const DtsLoadRequest* request) {
auto* value = context(handle);
if (!value || !request || request->structSize < sizeof(DtsLoadRequest)
|| !request->skeletonPathUtf8 || !request->atlasPathUtf8) {
return -1;
}
value->error.clear();
value->renderBatches.clear();
value->renderer.reset();
value->animationState.reset();
value->animationStateData.reset();
value->skeleton.reset();
value->skeletonData.reset();
value->atlas.reset();
value->atlas = std::make_unique<spine::Atlas>(
request->atlasPathUtf8, &value->textureLoader, true);
const float scale = request->scale > 0 ? request->scale : 1.0F;
if (lowercaseExtension(request->skeletonPathUtf8) == ".json") {
spine::SkeletonJson loader(*value->atlas);
loader.setScale(scale);
value->skeletonData.reset(loader.readSkeletonDataFile(request->skeletonPathUtf8));
if (!value->skeletonData) value->error = loader.getError().buffer();
} else {
spine::SkeletonBinary loader(*value->atlas);
loader.setScale(scale);
value->skeletonData.reset(loader.readSkeletonDataFile(request->skeletonPathUtf8));
if (!value->skeletonData) value->error = loader.getError().buffer();
}
if (!value->skeletonData) {
if (value->error.empty()) value->error = "Spine Runtime could not load skeleton data.";
return -2;
}
collectMetadata(*value);
value->skeleton = std::make_unique<spine::Skeleton>(*value->skeletonData);
value->animationStateData = std::make_unique<spine::AnimationStateData>(
*value->skeletonData);
value->animationState = std::make_unique<spine::AnimationState>(
*value->animationStateData);
value->renderer = std::make_unique<spine::SkeletonRenderer>();
if (!value->animationNames.empty())
value->animationState->setAnimation(0, value->animationNames.front().c_str(), true);
value->animationState->apply(*value->skeleton);
value->skeleton->updateWorldTransform(spine::Physics_Update);
collectDrawCommands(*value);
return 0;
}
int32_t dtsRuntimeGetSkeletonInfo(DtsRuntimeHandle handle, DtsSkeletonInfo* info) {
auto* value = context(handle);
if (!value || !value->skeletonData || !info || info->structSize < sizeof(DtsSkeletonInfo)) {
return -1;
}
info->boneCount = static_cast<uint32_t>(value->skeletonData->getBones().size());
info->slotCount = static_cast<uint32_t>(value->skeletonData->getSlots().size());
info->skinCount = static_cast<uint32_t>(value->skinNames.size());
info->animationCount = static_cast<uint32_t>(value->animationNames.size());
info->eventCount = static_cast<uint32_t>(value->skeletonData->getEvents().size());
info->constraintCount = static_cast<uint32_t>(value->skeletonData->getConstraints().size());
return 0;
}
const char* dtsRuntimeGetAnimationName(DtsRuntimeHandle handle, uint32_t index) {
auto* value = context(handle);
return value && index < value->animationNames.size()
? value->animationNames[index].c_str() : nullptr;
}
float dtsRuntimeGetAnimationDuration(DtsRuntimeHandle handle, uint32_t index) {
auto* value = context(handle);
return value && index < value->animationDurations.size()
? value->animationDurations[index] : 0.0F;
}
const char* dtsRuntimeGetSkinName(DtsRuntimeHandle handle, uint32_t index) {
auto* value = context(handle);
return value && index < value->skinNames.size() ? value->skinNames[index].c_str() : nullptr;
}
const char* dtsRuntimeGetLastError(DtsRuntimeHandle handle) {
auto* value = context(handle);
return value ? value->error.c_str() : "Invalid runtime handle.";
}
int32_t dtsRuntimeSetAnimation(
DtsRuntimeHandle handle, const char* animationNameUtf8, int32_t loop) {
auto* value = context(handle);
if (!value || !value->animationState || !animationNameUtf8) return -1;
if (!value->skeletonData->findAnimation(animationNameUtf8)) {
value->error = "Animation was not found.";
return -2;
}
value->skeleton->setupPose();
value->animationState->setAnimation(0, animationNameUtf8, loop != 0);
return 0;
}
int32_t dtsRuntimeUpdate(DtsRuntimeHandle handle, float deltaSeconds) {
auto* value = context(handle);
if (!value || !value->animationState || !value->skeleton || !value->renderer) return -1;
value->animationState->update(std::max(0.0F, deltaSeconds));
value->animationState->apply(*value->skeleton);
value->skeleton->updateWorldTransform(spine::Physics_Update);
collectDrawCommands(*value);
return 0;
}
uint32_t dtsRuntimeGetDrawCommandCount(DtsRuntimeHandle handle) {
auto* value = context(handle);
return value ? static_cast<uint32_t>(value->renderBatches.size()) : 0;
}
int32_t dtsRuntimeGetDrawCommand(
DtsRuntimeHandle handle, uint32_t index, DtsDrawCommand* command) {
auto* value = context(handle);
if (!value || index >= value->renderBatches.size() || !command
|| command->structSize < sizeof(DtsDrawCommand)) return -1;
const auto& batch = value->renderBatches[index];
command->vertices = batch.vertices.data();
command->vertexCount = static_cast<uint32_t>(batch.vertices.size());
command->indices = batch.indices.data();
command->indexCount = static_cast<uint32_t>(batch.indices.size());
command->texturePathUtf8 = batch.texturePath.c_str();
command->blendMode = batch.blendMode;
return 0;
}
} // extern "C"