导出数据转spine
This commit is contained in:
@@ -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"
|
||||
Reference in New Issue
Block a user