导出数据转spine
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
#include "RuntimeApi.h"
|
||||
|
||||
#include <spine/Animation.h>
|
||||
#include <spine/AnimationState.h>
|
||||
#include <spine/AnimationStateData.h>
|
||||
#include <spine/Atlas.h>
|
||||
#include <spine/ClippingAttachment.h>
|
||||
#include <spine/Extension.h>
|
||||
#include <spine/MeshAttachment.h>
|
||||
#include <spine/RegionAttachment.h>
|
||||
#include <spine/Skeleton.h>
|
||||
#include <spine/SkeletonBinary.h>
|
||||
#include <spine/SkeletonClipping.h>
|
||||
#include <spine/SkeletonData.h>
|
||||
#include <spine/SkeletonJson.h>
|
||||
#include <spine/Skin.h>
|
||||
#include <spine/Slot.h>
|
||||
#include <spine/SlotData.h>
|
||||
#include <spine/TextureLoader.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef DTS_SPINE_LINE
|
||||
#error DTS_SPINE_LINE must be defined before including LegacyRuntimeAdapter.inl
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
struct TextureRecord { std::string path; };
|
||||
class MetadataTextureLoader final : public spine::TextureLoader {
|
||||
public:
|
||||
void load(spine::AtlasPage& page, const spine::String& path) override {
|
||||
#if defined(DTS_SPINE_41)
|
||||
page.texture = new TextureRecord{path.buffer()};
|
||||
#else
|
||||
page.setRendererObject(new TextureRecord{path.buffer()});
|
||||
#endif
|
||||
}
|
||||
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;
|
||||
spine::SkeletonClipping clipper;
|
||||
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) {
|
||||
value.animationNames.emplace_back(animations[index]->getName().buffer());
|
||||
value.animationDurations.push_back(animations[index]->getDuration());
|
||||
}
|
||||
value.skinNames.clear();
|
||||
auto& skins = value.skeletonData->getSkins();
|
||||
for (std::size_t index = 0; index < skins.size(); ++index)
|
||||
value.skinNames.emplace_back(skins[index]->getName().buffer());
|
||||
}
|
||||
spine::AtlasRegion* regionFor(spine::RegionAttachment& attachment) {
|
||||
#if defined(DTS_SPINE_41)
|
||||
return static_cast<spine::AtlasRegion*>(attachment.getRegion());
|
||||
#else
|
||||
return static_cast<spine::AtlasRegion*>(attachment.getRendererObject());
|
||||
#endif
|
||||
}
|
||||
spine::AtlasRegion* regionFor(spine::MeshAttachment& attachment) {
|
||||
#if defined(DTS_SPINE_41)
|
||||
return static_cast<spine::AtlasRegion*>(attachment.getRegion());
|
||||
#else
|
||||
return static_cast<spine::AtlasRegion*>(attachment.getRendererObject());
|
||||
#endif
|
||||
}
|
||||
std::uint32_t packedColor(spine::Skeleton& skeleton, spine::Slot& slot,
|
||||
spine::Color& attachment) {
|
||||
const auto component = [](float value) {
|
||||
return static_cast<std::uint32_t>(std::clamp(value, 0.0F, 1.0F) * 255.0F);
|
||||
};
|
||||
const auto& skeletonColor = skeleton.getColor();
|
||||
const auto& slotColor = slot.getColor();
|
||||
const auto red = component(skeletonColor.r * slotColor.r * attachment.r);
|
||||
const auto green = component(skeletonColor.g * slotColor.g * attachment.g);
|
||||
const auto blue = component(skeletonColor.b * slotColor.b * attachment.b);
|
||||
const auto alpha = component(skeletonColor.a * slotColor.a * attachment.a);
|
||||
return (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
void appendBatch(RuntimeContext& value, spine::Slot& slot, spine::Color& color,
|
||||
spine::AtlasRegion* region, spine::Vector<float>& positions,
|
||||
spine::Vector<float>& uvs, spine::Vector<unsigned short>& indices) {
|
||||
if (!region || !region->page) return;
|
||||
#if defined(DTS_SPINE_41)
|
||||
auto* texture = static_cast<TextureRecord*>(region->page->texture);
|
||||
#else
|
||||
auto* texture = static_cast<TextureRecord*>(region->page->getRendererObject());
|
||||
#endif
|
||||
if (!texture) return;
|
||||
auto* sourcePositions = &positions;
|
||||
auto* sourceUvs = &uvs;
|
||||
auto* sourceIndices = &indices;
|
||||
if (value.clipper.isClipping()) {
|
||||
value.clipper.clipTriangles(positions, indices, uvs, 2);
|
||||
sourcePositions = &value.clipper.getClippedVertices();
|
||||
sourceUvs = &value.clipper.getClippedUVs();
|
||||
sourceIndices = &value.clipper.getClippedTriangles();
|
||||
}
|
||||
RenderBatch batch;
|
||||
const auto vertexCount = sourcePositions->size() / 2;
|
||||
batch.vertices.reserve(vertexCount);
|
||||
const auto packed = packedColor(*value.skeleton, slot, color);
|
||||
for (std::size_t index = 0; index < vertexCount; ++index)
|
||||
batch.vertices.push_back({(*sourcePositions)[index * 2], -(*sourcePositions)[index * 2 + 1],
|
||||
(*sourceUvs)[index * 2], (*sourceUvs)[index * 2 + 1], packed});
|
||||
batch.indices.assign(sourceIndices->buffer(), sourceIndices->buffer() + sourceIndices->size());
|
||||
batch.texturePath = texture->path;
|
||||
batch.blendMode = static_cast<std::uint32_t>(slot.getData().getBlendMode());
|
||||
value.renderBatches.push_back(std::move(batch));
|
||||
}
|
||||
void collectDrawCommands(RuntimeContext& value) {
|
||||
value.renderBatches.clear();
|
||||
spine::Vector<float> positions;
|
||||
spine::Vector<unsigned short> quadIndices;
|
||||
quadIndices.add(0); quadIndices.add(1); quadIndices.add(2);
|
||||
quadIndices.add(2); quadIndices.add(3); quadIndices.add(0);
|
||||
auto& drawOrder = value.skeleton->getDrawOrder();
|
||||
for (std::size_t index = 0; index < drawOrder.size(); ++index) {
|
||||
auto& slot = *drawOrder[index];
|
||||
auto* attachment = slot.getAttachment();
|
||||
if (!attachment) {
|
||||
value.clipper.clipEnd(slot);
|
||||
continue;
|
||||
}
|
||||
if (attachment->getRTTI().isExactly(spine::RegionAttachment::rtti)) {
|
||||
auto& region = *static_cast<spine::RegionAttachment*>(attachment);
|
||||
positions.setSize(8, 0);
|
||||
#if defined(DTS_SPINE_41)
|
||||
region.computeWorldVertices(slot, positions, 0, 2);
|
||||
#else
|
||||
region.computeWorldVertices(slot.getBone(), positions, 0, 2);
|
||||
#endif
|
||||
appendBatch(value, slot, region.getColor(), regionFor(region),
|
||||
positions, region.getUVs(), quadIndices);
|
||||
} else if (attachment->getRTTI().isExactly(spine::MeshAttachment::rtti)) {
|
||||
auto& mesh = *static_cast<spine::MeshAttachment*>(attachment);
|
||||
positions.setSize(mesh.getWorldVerticesLength(), 0);
|
||||
mesh.computeWorldVertices(slot, 0, mesh.getWorldVerticesLength(),
|
||||
positions.buffer(), 0, 2);
|
||||
appendBatch(value, slot, mesh.getColor(), regionFor(mesh),
|
||||
positions, mesh.getUVs(), mesh.getTriangles());
|
||||
} else if (attachment->getRTTI().isExactly(spine::ClippingAttachment::rtti)) {
|
||||
value.clipper.clipStart(slot, static_cast<spine::ClippingAttachment*>(attachment));
|
||||
}
|
||||
value.clipper.clipEnd(slot);
|
||||
}
|
||||
value.clipper.clipEnd();
|
||||
}
|
||||
} // 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 = DTS_SPINE_LINE;
|
||||
info->adapterBuild = "preview-1";
|
||||
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->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.get());
|
||||
loader.setScale(scale);
|
||||
value->skeletonData.reset(loader.readSkeletonDataFile(request->skeletonPathUtf8));
|
||||
if (!value->skeletonData) value->error = loader.getError().buffer();
|
||||
} else {
|
||||
spine::SkeletonBinary loader(value->atlas.get());
|
||||
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.get());
|
||||
value->animationStateData = std::make_unique<spine::AnimationStateData>(value->skeletonData.get());
|
||||
value->animationState = std::make_unique<spine::AnimationState>(value->animationStateData.get());
|
||||
if (!value->animationNames.empty())
|
||||
value->animationState->setAnimation(0, value->animationNames.front().c_str(), true);
|
||||
value->animationState->apply(*value->skeleton);
|
||||
value->skeleton->updateWorldTransform();
|
||||
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;
|
||||
auto* data = value->skeletonData.get();
|
||||
info->boneCount = static_cast<uint32_t>(data->getBones().size());
|
||||
info->slotCount = static_cast<uint32_t>(data->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>(data->getEvents().size());
|
||||
info->constraintCount = static_cast<uint32_t>(data->getIkConstraints().size()
|
||||
+ data->getTransformConstraints().size() + data->getPathConstraints().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* name, int32_t loop) {
|
||||
auto* value = context(handle);
|
||||
if (!value || !value->animationState || !name) return -1;
|
||||
if (!value->skeletonData->findAnimation(name)) {
|
||||
value->error = "Animation was not found.";
|
||||
return -2;
|
||||
}
|
||||
value->skeleton->setToSetupPose();
|
||||
value->animationState->setAnimation(0, name, loop != 0);
|
||||
return 0;
|
||||
}
|
||||
int32_t dtsRuntimeUpdate(DtsRuntimeHandle handle, float seconds) {
|
||||
auto* value = context(handle);
|
||||
if (!value || !value->animationState || !value->skeleton) return -1;
|
||||
value->animationState->update(std::max(0.0F, seconds));
|
||||
value->animationState->apply(*value->skeleton);
|
||||
value->skeleton->updateWorldTransform();
|
||||
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