LoginSignup
7
7

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-12-02

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 アプリケーションなどが開発されることを期待しています.

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7