こんにちは
Spineランタイムについて教えてほしいことがあります。
私はOpenGLとC++を使用してSpineランタイムを動かそうとしています。
SpineEditor: 3.8.97 PRO
SpineRuntime: 3.8 spine-cpp
開発環境: windows10 VisualStudio 2019
使っているライブラリ: GLFW,GLUT,lodepng,freetype
プログラム上でSpineランタイムのメッシュ変形のテクスチャを描画する所がうまく動作させることができません。
描画した画像が乱れてしまうのです。
ちなみにRegionAttachmentを使ったメッシュ変形のない描画は出来ました。
そこで、描画時にMeshAttachmentクラスのcomputeWorldVerticesから受け取った座標はどのように描画したらよいでしょうか。
問題と感じるのは受け取った座標の並びを見ると以下の描画モードでそのまま描画するには不適切だと思ったからです。
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_POLYGON
具体的には以下のようになっています。
見えにくいですが赤字が受け取った頂点のIndexです。
中心にある点が最後にあるため戸惑っています。
このデータはSpineでは以下のようにメッシュを分割しています。
このように四等分したピザのようにしたいので頂点を並び替える必要があるのですが、
どういった基準で座標を並び替えるアルゴリズムを作ればよいのか思いつきません。
この画像の場合"4"の点が計4つの三角ポリゴンに使われていますが、プログラムから見てそれ知る方法がわからないということです。
これはシンプルなデータで行っていますがデータをroptor-proをにすると、歩いているアニメーションは出るのですが画像が大きく乱れているところが多々出ます。
もし私が実装で間違いを犯している、もしくは何かよい解決方法をご存じならご教授のほどよろしくお願いします。
//デバッグエクステンション
static spine::DebugExtension* debugExtension = NULL;
spine::SpineExtension* spine::getDefaultExtension()
{
debugExtension = new DebugExtension(new DefaultSpineExtension());
return debugExtension;
}
//テクスチャローダ(このエンジン用)
class NSFrameworkSpineTextureLoader : public spine::TextureLoader {
public:
virtual void load(spine::AtlasPage& page, const spine::String& path) override {
NSFramework::Graphic2D* gfx = new NSFramework::Graphic2D();
gfx->Init(path.buffer());
page.setRendererObject(gfx);
}
virtual void unload(void* texture) override {
NSFramework::Graphic2D* t = (NSFramework::Graphic2D*)texture;
t->Dispose();
delete t;
}
};
//SpineDraw
void drawSkeleton(spine::Skeleton* skeleton) {
for (size_t i = 0, n = skeleton->getSlots().size(); i < n; ++i) {
spine::Slot& slot = *skeleton->getDrawOrder()[i];
spine::Attachment* attachment = slot.getAttachment();
if (!attachment) continue;
//ブレンド
switch (slot.getData().getBlendMode()) {
case spine::BlendMode_Normal:
NSSB->iGraphicManager.SetBlendModeNormal();
break;
case spine::BlendMode_Additive:
NSSB->iGraphicManager.SetBlendModeAdditive();
break;
case spine::BlendMode_Multiply:
NSSB->iGraphicManager.SetBlendModeMultiply();
break;
case spine::BlendMode_Screen:
NSSB->iGraphicManager.SetBlendModeScreen();
break;
default:
NSSB->iGraphicManager.SetBlendModeNormal();
}
// Fill the vertices array, indices, and texture depending on the type of attachment
if (attachment->getRTTI().isExactly(spine::RegionAttachment::rtti)) {
spine::RegionAttachment* regionAttachment = (spine::RegionAttachment*)attachment;
spine::Color attachmentColor = regionAttachment->getColor();
spine::Vector<float> worldVertices;
worldVertices.setSize(8, 0);
regionAttachment->computeWorldVertices(slot.getBone(), worldVertices, 0, 2);
std::vector<NSFramework::Vertex> points;
for (int i = 0; i < 4; i++) {
NSFramework::Vertex v;
v.x = worldVertices[(int)i * 2];
v.y = worldVertices[(int)i * 2 + 1];
v.u = regionAttachment->getUVs()[i * 2];
v.v = regionAttachment->getUVs()[i * 2 + 1];
v.color.iRed = (int)((float)UCHAR_MAX * attachmentColor.r);
v.color.iGreen = (int)((float)UCHAR_MAX * attachmentColor.g);
v.color.iBlue = (int)((float)UCHAR_MAX * attachmentColor.b);
v.color.iBlue = (int)((float)UCHAR_MAX * attachmentColor.a);
points.push_back(v);
}
NSFramework::Graphic2D* gfx = (NSFramework::Graphic2D[i])((spine::AtlasRegion[/i])regionAttachment->getRendererObject())->page->getRendererObject();
if (gfx != nullptr) {
gfx->DrawPoly(points);
}
}
else if (attachment->getRTTI().isExactly(spine::MeshAttachment::rtti)) {
spine::MeshAttachment* mesh = (spine::MeshAttachment*)attachment;
spine::Color attachmentColor = mesh->getColor();
spine::Vector<float> worldVertices;
worldVertices.setSize(mesh->getWorldVerticesLength(), 0);
mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices, 0, 2);
std::vector<NSFramework::Vertex> points;
int loop = (mesh->getWorldVerticesLength() / 2);
for (int i = 0; i < loop; i++) {
NSFramework::Vertex v;
v.x = worldVertices[(int)i * 2];
v.y = worldVertices[(int)i * 2 + 1];
v.u = mesh->getUVs()[i * 2];
v.v = mesh->getUVs()[i * 2 + 1];
v.color.iRed = (int)((float)UCHAR_MAX * attachmentColor.r);
v.color.iGreen = (int)((float)UCHAR_MAX * attachmentColor.g);
v.color.iBlue = (int)((float)UCHAR_MAX * attachmentColor.b);
v.color.iBlue = (int)((float)UCHAR_MAX * attachmentColor.a);
points.push_back(v);
}
NSFramework::Graphic2D* gfx = (NSFramework::Graphic2D[i])((spine::AtlasRegion[/i])mesh->getRendererObject())->page->getRendererObject();
if (gfx != nullptr) {
gfx->DrawPoly(points);
}
}
NSSB->iGraphicManager.SetBlendModeNormal();
}
}
int main()
{
NSFramework::SystemBase sys;
sys.Init();
//AtlasのロードとAtlasから画像をロードする
NSFrameworkSpineTextureLoader* tl = new NSFrameworkSpineTextureLoader();
spine::Atlas* atlas = new spine::Atlas("spine_data\\skeleton2.atlas", tl);
//スケルトンデータのロード
spine::SkeletonJson json(atlas);
json.setScale(1);
spine::SkeletonData* skeletonData = json.readSkeletonDataFile("spine_data\\skeleton2.json");
//アニメーションデータの準備
spine::AnimationStateData* animationStateData = new spine::AnimationStateData(skeletonData);
animationStateData->setDefaultMix(0.1f); //ミックスタイム
//スケルトンの作成
spine::Skeleton* skeleton = new spine::Skeleton(skeletonData);
//アニメーションステートの作成
spine::AnimationState* animationState = new spine::AnimationState(animationStateData);
animationState->setAnimation(0, "animation", true);
skeleton->setScaleY(-1.0F);
while (sys.iGraphicManager.IsEnd() == false)
{
sys.iGraphicManager.Clear();
skeleton->setX(NSSB->iGraphicManager.iWidth / 2);
skeleton->setY(NSSB->iGraphicManager.iHeight / 2);
animationState->update(0.01F);
animationState->apply(*skeleton);
skeleton->updateWorldTransform();
drawSkeleton(skeleton);
sys.iGraphicManager.RenderPresent();
glfwPollEvents();
}
delete animationState;
delete skeleton;
delete skeletonData;
delete atlas;
delete tl;
sys.Dispose();
return 0;
}
//描画関数 DrawFunc
void Graphic2D::DrawPoly(std::vector<NSFramework::Vertex> aVertecs)
{
if (glIsTexture(iTextureID) != GL_TRUE) {
return;
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D, iTextureID);
int size = aVertecs.size();
float* points = new float[size * 2];
GLbyte* colors = new GLbyte[size * 4];
float* coords = new float[size * 2];
for (int i = 0; i < size; i++) {
points[i * 2] = aVertecs[i].x;
points[i * 2 + 1] = aVertecs[i].y;
colors[i * 4] = aVertecs[i].color.iRed;
colors[i * 4 + 1] = aVertecs[i].color.iGreen;
colors[i * 4 + 2] = aVertecs[i].color.iBlue;
colors[i * 4 + 3] = aVertecs[i].color.iAlpha;
coords[i * 2] = aVertecs[i].u;
coords[i * 2 + 1] = aVertecs[i].v;
}
glVertexPointer(2, GL_FLOAT, 0, points);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
glTexCoordPointer(2, GL_FLOAT, 0, coords);
glDrawArrays(GL_TRIANGLE_FAN, 0, size);
//
---
//テスト 文字を描画 DrawNumber
for (int i = 0; i < size; i++) {
NSSB->iGraphicManager.DrawPoint(aVertecs[i].x, aVertecs[i].y, NSFramework::DefaultColor::Red);
NSSB->iAscii.Draw(aVertecs[i].x, aVertecs[i].y, i, NSFramework::DefaultColor::Red);
}
delete points;
delete colors;
delete coords;
}