Help us understand the problem. What is going on with this article?

次期 Siv3D 用 3D 物理演算機能の紹介

More than 1 year has passed since last update.

Siv3D AdventCalendar 2017 2 日目の記事です.

概要

本記事では,次期 Siv3D である OpenSiv3D の 3D 機能に向けた,物理演算機能のプロトタイプを紹介します.

hako.gif

Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    Physics3DWorld world;
    Physics3DBody ground = world.createBox(Vec3(0, -50, 0), Vec3(50, 50, 50), 0.0);

    Array<Physics3DBody> bodies;

    for (int y = 0; y < 10; y++)
    {
        for (int z = 0; z < 10; z++)
        {
            for (int x = 0; x < 10; x++)
            {
                const Vec3 pos(x, 10 + y, z);

                bodies.push_back(world.createBox(pos, Vec3::One * 0.5, 1.0));
            }
        }
    }


    while (System::Update())
    {
        world.update();

        ground.draw();

        for (const auto& body : bodies)
        {
            body.draw();
        }
    }
}

3D 物体の扱いと物理演算

C++で 3D のゲームやアプリケーションなどを開発する際に,3D 物体の扱いには苦労した経験はありませんか.3D の物体同士の相互作用には,面倒な衝突判定・幾何学計算・数値計算を行う必要があります.この面倒さを解決する手段として,3D 物理演算ライブラリが使用される場合があります.3D 物理演算ライブラリとは,3D の形状同士の衝突判定や,物理的な挙動の計算をサポートする機能群であり,Open Dynamics Engine(ODE)Havok PhysicsBullet Physics などが知られています.現在は次期 Siv3D である OpenSiv3D に向けて,短い C++ ソースコードで 3D 物理演算 (Bullet Physics) を利用できる機能を開発しています.本記事では物理演算機能のプロトタイプを紹介します.

  • OpenSiv3Dの3D機能に搭載予定で現時点では使用することはできません.
  • 本プロトタイプは Siv3D August 2016 で開発しています.

物体の生成

box.gif

Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    Physics3DWorld world;
    Physics3DBody ground = world.createBox(Vec3(0, -50, 0), Vec3(50, 50, 50), 0.0);

    Physics3DBody body = world.createBox({0.0, 5.0, 0.0}, Vec3::One * 0.5, 1.0);

    while (System::Update())
    {
        world.update();

        ground.draw();

        body.draw();
    }
}

マウスとのインタラクション

mouse.gif

Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    Physics3DWorld world;
    Physics3DBody ground = world.createBox(Vec3(0, -50, 0), Vec3(50, 50, 50), 0.0);

    Array<Physics3DBody> bodies;

    bodies.push_back(world.createBox(RandomVec3({ -1.0, 1.0 }, { 0.0, 1.0 }, { -1.0, 1.0 }), Vec3::One * 0.5, 1.0));
    bodies.push_back(world.createSphere(RandomVec3({ -1.0, 1.0 }, { 0.0, 1.0 }, { -1.0, 1.0 }), 0.5, 1.0));
    bodies.push_back(world.createCylinder(RandomVec3({ -1.0, 1.0 }, { 0.0, 1.0 }, { -1.0, 1.0 }), 0.5, 1.0, 1.0));
    bodies.push_back(world.createCapsule(RandomVec3({ -1.0, 1.0 }, { 0.0, 1.0 }, { -1.0, 1.0 }), 0.5, 1.0, 1.0));

    Physics3DCompoundShape compound;

    compound.addShape(Physics3DCapsuleShape(0.3, 2.0));
    compound.addShape(Physics3DCapsuleShape(0.3, 2.0), Vec3(0.0, 1.0, 0.0), Quaternion(Vec3(0.0, 0.0, 1.0), Math::HalfPi));
    compound.addShape(Physics3DCapsuleShape(0.3, 2.0), Vec3(0.0, -1.0, 0.0), Quaternion(Vec3(1.0, 0.0, 0.0), Math::HalfPi));

    bodies.push_back(world.createBody(compound, RandomVec3({ -1.0, 1.0 }, { 0.0, 1.0 }, { -1.0, 1.0 }), 1.0));

    Physics3DMousePickManager mousePick;

    while (System::Update())
    {
        mousePick.update(world);

        world.update();

        ground.draw();

        for (const auto& body : bodies)
        {
            body.draw();
        }

        mousePick.draw();
    }
}

拘束条件

hashi.gif

Main.cpp
# include <Siv3D.hpp>
# include <HamFramework.hpp>

void Main()
{
    Physics3DWorld world;
    Physics3DBody ground = world.createBox(Vec3(0, -50, 0), Vec3(50, 50, 50), 0.0);

    Array<Physics3DBody> bodies;
    Array<Physics3DPointToPointConstraint> constraints;

    const double plankWidth = 0.4;
    const double plankHeight = 0.2;
    const double plankBreadth = 1.0;
    const double plankOffset = plankWidth;
    const double bridgeWidth = plankWidth * 10.0 + plankOffset*(10.0 - 1.0);
    const double bridgeHeight = 5.0;
    const double halfBridgeWidth = bridgeWidth*0.5;
    const double mass = 1.0;
    const int lastBoxIndex = 10 - 1;

    for (int i = 0; i < 10; ++i)
    {
        double t = static_cast<double>(i) / lastBoxIndex;
        t = -(t * 2.0 - 1.0) * halfBridgeWidth;
        bodies.push_back(world.createBox(Vec3(t, bridgeHeight, 0), Vec3(plankWidth, plankHeight, plankBreadth), (i == 0 || i == lastBoxIndex) ? 0.0 : mass));
    }

    for (int i = 0; i<10 - 1; ++i)
    {
        constraints.push_back(world.createPointToPointConstraint(bodies[i], bodies[i + 1], Vec3(-0.5, 0, -0.5), Vec3(0.5, 0, -0.5)));
        constraints.push_back(world.createPointToPointConstraint(bodies[i], bodies[i + 1], Vec3(-0.5, 0, 0.5), Vec3(0.5, 0, 0.5)));
    }

    while (System::Update())
    {
        if (Input::MouseL.clicked)
        {
            bodies.push_back(world.createSphere(Vec3(Random(-1.0, 1.0), 10.0 + Random(-1.0, 1.0), Random(-1.0, 1.0)), 0.5, 1.0));
        }

        world.update();

        ground.draw();

        for (const auto& body : bodies)
        {
            body.draw();
        }
    }
}

応用例

ギア

gear.gif

kuruma.gif

おわりに

本記事では,次期 Siv3D である OpenSiv3D の 3D 機能に向けた,物理演算機能のプロトタイプを紹介しました.本機能により,より多くの 3D ゲームや 3D アプリケーションなどが開発されることを期待しています.

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away