• 日本語
  • [spine-cpp]メッシュ描画時の頂点データの並びについての質問

こんにちは

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;
}
Related Discussions
...

インデックス付きの GL_TRIANGLESはあなたが望むものです。 インデックスを使用せず、三角形を描画するだけの場合は、MeshAttachmentのインデックス配列から三角形を作成する必要があります。 頂点とインデックスはそのままOpenGLにフィードでき、物事を並べ替える方法を作成する必要はありません。 ただし、インデックスなしでレンダリングする場合は、SFMLの手順に従ってください:https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-sfml/cpp/src/spine/spine- sfml.cpp#L266

GL_TRIANGLES with indices is what you want. If you don't want to use indices, and just draw triangles, you have to construct them from the indices array of the MeshAttachment. The vertices and indices can be fed to OpenGL as is, you don't have to create a way to sort things. However, if you want to render without indices, you can follow what we do for SFML here: spine-runtimes/spine-sfml.cpp at 3.8

教えていただいた箇所を見たことでランタイムの理解を深めることができました。
おかげでメッシュ画像を正しく表示しアニメーションが正しくできました。
I was able to deepen my understanding of the runtime.
I displayed the mesh image correctly and the animation was done correctly.

どうもありがとう。
Thank you very much!