DrawModel.cpp
#include "StdAfx.h"
#include "DrawModel.h"
#include "AxisOfCoordinate.h"
#include "AxisOfRotation.h"
#include "Model.h"
#include "Link.h"
#include "Joint.h"
#include "Shape.h"
#include "ConnectionRelation.h"
#include "SupportPolygon.h"
#include "Torque.h"
using namespace Core;
using namespace Robotics;
using namespace Microsoft;
using namespace System;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;
// DirectXのライブラリによる const/volatile 修飾子の使用の警告を抑制する
#pragma warning( disable : 4400 )
/// [ Constructor ]
DrawModel::DrawModel( Microsoft::DirectX::Direct3D::Device^ device, System::Object^ obj ) :
m_device( device ),
m_model( safe_cast< Model^ >( obj ) )
{
// モデルの図形を生成する
CreateModelFigure();
// 座標軸を生成する
m_axisOfCoordinate = gcnew AxisOfCoordinate( m_device, 0.05f );
// 回転軸を生成する
m_axisOfRotation = gcnew AxisOfRotation( m_device, 0.15f );
}
/// モデルの図形を生成する
void DrawModel::CreateModelFigure()
{
m_model->CreateFigure( m_device );
}
/// 描画する
void DrawModel::Render()
{
// リンクを描画する
DrawLink();
// 重心を描画する
m_model->Render( m_device );
// 支持多角形を取得する
SupportPolygon^ supportPolygon = m_model->SupportPolygon;
// ... それの図形を生成する
supportPolygon->CreateFigure( m_device );
// ... それを描画する
supportPolygon->Render( m_device );
}
/// リンクを描画する
void DrawModel::DrawLink()
{
// トルクを生成する
Torque^ torque = gcnew Torque( m_model );
for each( Link^ link in m_model->Links )
{
double momentOfForce = Math::Abs( torque[ link ] ); // リンクのトルク
// ... 最大値に対する割合を求める ( 1.0を超えるならば 1.0に補正する )
double ratio = Math::Min( momentOfForce / Torque::Maximum, 1.0 );
const double HueOfBlue = 240.0; // 青の色相
const double HueOfRed = 0.0; // 赤の色相
// 色を HSB色空間から取得する
Drawing::Color jointColor = GetColorFromHSB(
HueOfBlue - ( HueOfBlue - HueOfRed ) * ratio, // 色相
1.0, // 彩度
1.0 // 明度
);
// ... それを関節の色に設定する
link->Joint->Color = jointColor;
// リンクの位置へ移動する
TransferToLinkPosition( link );
switch( m_expressionMode )
{
case ExpressionModel::RealModel:
// リンクの リアルなモデルを描画する
link->RenderRealModel( m_device );
break;
case ExpressionModel::MechanismModel:
// リンクの 機構モデルを描画する
link->RenderMechanismModel( m_device );
break;
default:
Debug::Fail( "表示モードが設定されていない" );
}
// ... 選択されているリンクを描画する
DrawSelectedLink( link );
}
}
/// 選択されているリンクを描画する
void DrawModel::DrawSelectedLink( Link^ link )
{
// 選択されているリンクがなければ、何もしない
if( m_selectedLink == nullptr ) return;
if( m_selectedLink == link->Joint )
{
// 関節が選択されているならば、その位置を取得する
AxisVector^ position = link->Joint->AxialDirection;
// ... 回転軸を描画する
m_axisOfRotation->Render( m_device, position );
}
else if( m_selectedLink == link->Shape )
{
// 形状が選択されているならば、その位置を取得する
PositionVector^ position = link->Shape->RelativePosition;
/// @todo
/// スクリーンの最前面へ 位置を移動する
///
/// デバイスからカメラの情報を取得して、
/// そこから求められるかも知れない。
// ... 座標軸を描画する
m_axisOfCoordinate->Render( m_device, position );
}
}
/// リンクを選択する
Distinction^ DrawModel::SelectLink( System::Drawing::Point location )
{
// 選択されているリンクをクリアする
m_selectedLink = nullptr;
// floatへキャストする
Drawing::PointF locationF(
safe_cast< float >( location.X ),
safe_cast< float >( location.Y )
);
// 交点までの距離を 最大値で初期化する
float distanceToIntersection = Single::MaxValue;
for each( Link^ link in m_model->Links )
{
float distance;
// 関節との交差を調べる
distance = ComputeDistanceToIntersection( link->Joint, locationF );
if( distance < distanceToIntersection )
{
// 手前にあるならば、交点までの距離を更新する
distanceToIntersection = distance;
// 対象とした関節を 選択されているリンクとする
m_selectedLink = link->Joint;
}
// 形状との交差を調べる
distance = ComputeDistanceToIntersection( link->Shape, locationF );
if( distance < distanceToIntersection )
{
// 手前にあるならば、交点までの距離を更新する
distanceToIntersection = distance;
// 対象とした形状を 選択されているリンクとする
m_selectedLink = link->Shape;
}
}
return m_selectedLink;
}
/// オブジェクトとの交点までの距離を取得する
float DrawModel::ComputeDistanceToIntersection( Distinction^ distinction, System::Drawing::PointF location )
{
float result = Single::MaxValue; // 最長距離
if( distinction != nullptr )
{
// 図形を有するか確認する
if( distinction->Figure != nullptr )
{
DirectX::Vector3 near( location.X, location.Y, m_device->Viewport.MinZ ); // 近平面の位置
DirectX::Vector3 far( location.X, location.Y, m_device->Viewport.MaxZ ); // 遠平面の位置
// 近平面を オブジェクトのローカル空間に座標変換する
DirectX::Vector3 vNear = DirectX::Vector3::Unproject(
near,
m_device->Viewport, // ビューポート
m_device->Transform->Projection, // 投影行列
m_device->Transform->View, // ビュー行列
distinction->WorldTransformOfFigure
);
// 遠平面を オブジェクトのローカル空間に座標変換する
DirectX::Vector3 vFar = DirectX::Vector3::Unproject(
far,
m_device->Viewport, // ビューポート
m_device->Transform->Projection, // 投影行列
m_device->Transform->View, // ビュー行列
distinction->WorldTransformOfFigure
);
// ... ローカル空間での レイの方向ベクトルを生成する
DirectX::Vector3 rayDirection = DirectX::Vector3::Normalize( vFar - vNear );
// 交点を記録するための構造体を生成する
DirectX::Direct3D::IntersectInformation closestHit;
// ... 2つの座標を結ぶベクトルと オブジェクトとの交差を調べる
if( distinction->Figure->Intersect( vNear, rayDirection, closestHit ) )
{
// 交差しているならば、その交点までの距離を取得する
result = closestHit.Dist;
}
}
}
return result;
}
/// リンクの位置へ移動する
void DrawModel::TransferToLinkPosition( Link^ link )
{
RotationMatrix^ posture;
// 胴体か?
if( link->IsBody() )
{
posture = link->Posture; // 胴体の姿勢
}
else
{
posture = link->Connection->Upper->Posture; // 接続元のリンクの姿勢
}
// リンクの関節の 絶対位置を取得する
PositionVector^ position = link->Joint->AbsolutePosition;
// ... ワールド座標系における位置と姿勢を求める
m_device->Transform->World
= ConvertToMatrix( posture )
* DirectX::Matrix::Translation(
safe_cast< float >( position->X ),
safe_cast< float >( position->Y ),
safe_cast< float >( position->Z )
);
}
/// 表示モードを設定する [ Property ]
void DrawModel::ExpressionMode::set( ExpressionModel value )
{
m_expressionMode = value;
for each( Link^ link in m_model->Links )
{
// 形状に 表示モードを設定する
link->Shape->SetExpressionMode( value );
}
}
/// 行列に変換する
Microsoft::DirectX::Matrix DrawModel::ConvertToMatrix( Robotics::RotationMatrix^ matrix )
{
// それぞれの軸周りに回転させる
DirectX::Matrix result
= DirectX::Matrix::RotationX( safe_cast< float >( matrix->Roll ) )
* DirectX::Matrix::RotationY( safe_cast< float >( matrix->Pitch ) )
* DirectX::Matrix::RotationZ( safe_cast< float >( matrix->Yaw ) );
return result;
/// @note
/// 回転行列クラスが返す ロール・ピッチ・ヨー角にバグがあるため、
/// Matrix::RotationYawPitchRoll メソッドを使用していない。
}
/// HSB色空間から Colorを取得する
System::Drawing::Color DrawModel::GetColorFromHSB( double hue, double saturation, double brightness )
{
Debug::Assert( 0.0 <= hue && hue < 360.0, "色相は 0-360の範囲である" );
Debug::Assert( 0.0 <= saturation && saturation <= 1.0, "彩度は 0.0-1.0の範囲である" );
Debug::Assert( 0.0 <= brightness && brightness <= 1.0, "明度は 0.0-1.0の範囲である" );
int red = 0; // Red
int green = 0; // Green
int blue = 0; // Blue
// 明度を 0-255の値に変換する
int iBrightness = safe_cast< int >( 255 * brightness );
if( saturation == 0.0 )
{
// 彩度が 0.0ならば、モノクロームになる
red = iBrightness;
green = iBrightness;
blue = iBrightness;
}
else
{
// 色相を 0-5の値に変換する
int iHue = safe_cast< int >( hue / 60.0 );
double f = ( hue / 60.0 ) - iHue;
int p = safe_cast< int >( iBrightness * ( 1.0 - saturation ) );
int q = safe_cast< int >( iBrightness * ( 1.0 - f * saturation ) );
int t = safe_cast< int >( iBrightness * ( 1.0 - ( 1.0 - f ) * saturation ) );
switch( iHue )
{
case 0:
red = iBrightness;
green = t;
blue = p;
break;
case 1:
red = q;
green = iBrightness;
blue = p;
break;
case 2:
red = p;
green = iBrightness;
blue = t;
break;
case 3:
red = p;
green = q;
blue = iBrightness;
break;
case 4:
red = t;
green = p;
blue = iBrightness;
break;
case 5:
red = iBrightness;
green = p;
blue = q;
break;
default:
Debug::Fail( "予期しない値である" );
};
}
// RGBから Colorを生成して返す
return Drawing::Color::FromArgb( red, green, blue );
// [ Reference ] HSV色空間 - Wikipedia ( http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93 )
}