X operations(XOPS)に非常に近いFPSゲームを制作・リメイクし、成果物をオープンソースとして公開することを目的としたプロジェクトです。
Rev. | 39 |
---|---|
Größe | 41,203 Bytes |
Zeit | 2015-02-17 20:00:15 |
Autor | xops-mikan |
Log Message | コンソールのコマンドを2個追加(event・radar)、ミッション完了時に照準が消える問題の修正 |
//! @file collision.cpp
//! @brief Collisionクラスの定義
//--------------------------------------------------------------------------------
//
// OpenXOPS
// Copyright (c) 2014-2015, OpenXOPS Project / [-_-;](mikan) All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the OpenXOPS Project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL OpenXOPS Project BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//--------------------------------------------------------------------------------
#include "collision.h"
//! @brief コンストラクタ
Collision::Collision()
{
blockdata = NULL;
bmin_x = new float[MAX_BLOCKS];
bmin_y = new float[MAX_BLOCKS];
bmin_z = new float[MAX_BLOCKS];
bmax_x = new float[MAX_BLOCKS];
bmax_y = new float[MAX_BLOCKS];
bmax_z = new float[MAX_BLOCKS];
BoardBlock = new bool[MAX_BLOCKS];
bdata_worldgroup = new int[MAX_BLOCKS];
for(int i=0; i<MAX_BLOCKS; i++){
bmin_x[i] = 0.0f;
bmin_y[i] = 0.0f;
bmin_z[i] = 0.0f;
bmax_x[i] = 0.0f;
bmax_y[i] = 0.0f;
bmax_z[i] = 0.0f;
BoardBlock[i] = false;
bdata_worldgroup[i] = 0;
}
}
//! @brief ディストラクタ
Collision::~Collision()
{
if( bmin_x != NULL ){ delete [] bmin_x; }
if( bmin_y != NULL ){ delete [] bmin_y; }
if( bmin_z != NULL ){ delete [] bmin_z; }
if( bmax_x != NULL ){ delete [] bmax_x; }
if( bmax_y != NULL ){ delete [] bmax_y; }
if( bmax_z != NULL ){ delete [] bmax_z; }
if( BoardBlock != NULL ){ delete [] BoardBlock; }
if( bdata_worldgroup != NULL ){ delete [] bdata_worldgroup; }
}
//! @brief ブロックデータを取り込む
//! @param in_blockdata ブロックデータ
int Collision::InitCollision(BlockDataInterface* in_blockdata)
{
int bs;
struct blockdata data;
int vID[4];
float g0, g1, costheta;
D3DXVECTOR3 dv1, dv2, dv3;
if( in_blockdata == NULL ){ return 1; }
blockdata = in_blockdata;
bs = blockdata->GetTotaldatas();
for(int i=0; i<bs; i++){
BoardBlock[i] = false;
}
//判定用平面作成
for(int i=0; i<bs; i++){
blockdata->Getdata(&data, i);
for(int j=0; j<6; j++){
blockdataface(j, &vID[0], NULL);
dv1 = D3DXVECTOR3( data.x[ vID[1] ], data.y[ vID[1] ], data.z[ vID[1] ] );
dv2 = D3DXVECTOR3( data.x[ vID[2] ], data.y[ vID[2] ], data.z[ vID[2] ] );
dv3 = D3DXVECTOR3( data.x[ vID[0] ], data.y[ vID[0] ], data.z[ vID[0] ] );
D3DXPlaneFromPoints(&bdata_plane[i][j][0], &dv1, &dv2, &dv3);
dv1 = D3DXVECTOR3( data.x[ vID[0] ], data.y[ vID[0] ], data.z[ vID[0] ] );
dv2 = D3DXVECTOR3( data.x[ vID[2] ], data.y[ vID[2] ], data.z[ vID[2] ] );
dv3 = D3DXVECTOR3( data.x[ vID[3] ], data.y[ vID[3] ], data.z[ vID[3] ] );
D3DXPlaneFromPoints(&bdata_plane[i][j][1], &dv1, &dv2, &dv3);
//2つの三角形が持つ法線のなす角を求める
g0 = sqrt(bdata_plane[i][j][0].a * bdata_plane[i][j][0].a + bdata_plane[i][j][0].b * bdata_plane[i][j][0].b + bdata_plane[i][j][0].c * bdata_plane[i][j][0].c);
g1 = sqrt(bdata_plane[i][j][1].a * bdata_plane[i][j][1].a + bdata_plane[i][j][1].b * bdata_plane[i][j][1].b + bdata_plane[i][j][1].c * bdata_plane[i][j][1].c);
costheta = (bdata_plane[i][j][0].a * bdata_plane[i][j][1].a + bdata_plane[i][j][0].b * bdata_plane[i][j][1].b + bdata_plane[i][j][0].c * bdata_plane[i][j][1].c) / (g0 * g1);
//1つの面で法線が90度以上違う(=異常)なら〜
if( acos(costheta) > (float)M_PI/2 ){
//違う三角形で作る
dv1 = D3DXVECTOR3( data.x[ vID[2] ], data.y[ vID[2] ], data.z[ vID[2] ] );
dv2 = D3DXVECTOR3( data.x[ vID[3] ], data.y[ vID[3] ], data.z[ vID[3] ] );
dv3 = D3DXVECTOR3( data.x[ vID[1] ], data.y[ vID[1] ], data.z[ vID[1] ] );
D3DXPlaneFromPoints(&bdata_plane[i][j][0], &dv1, &dv2, &dv3);
dv1 = D3DXVECTOR3( data.x[ vID[1] ], data.y[ vID[1] ], data.z[ vID[1] ] );
dv2 = D3DXVECTOR3( data.x[ vID[3] ], data.y[ vID[3] ], data.z[ vID[3] ] );
dv3 = D3DXVECTOR3( data.x[ vID[0] ], data.y[ vID[0] ], data.z[ vID[0] ] );
D3DXPlaneFromPoints(&bdata_plane[i][j][1], &dv1, &dv2, &dv3);
}
}
}
//板状のブロックを検出
for(int i=0; i<bs; i++){
blockdata->Getdata(&data, i);
BoardBlock[i] = false;
for(int j=0; j<8; j++){
for(int k=j+1; k<8; k++){
//一ヵ所でも頂点が同じなら、板状になっていると判定。
if( (data.x[j] == data.x[k])&&(data.y[j] == data.y[k])&&(data.z[j] == data.z[k]) ){
BoardBlock[i] = true;
}
}
}
}
//ブロックAABB作成
for(int i=0; i<bs; i++){
blockdata->Getdata(&data, i);
GetBlockPosMINMAX(data, &bmin_x[i], &bmin_y[i], &bmin_z[i], &bmax_x[i], &bmax_y[i], &bmax_z[i]);
}
//ブロックの空間分割グループを計算
for(int i=0; i<bs; i++){
///*
blockdata->Getdata(&data, i);
bdata_worldgroup[i] = GetWorldGroup(bmin_x[i], bmin_z[i]);
if( GetWorldGroup(bmax_x[i], bmax_z[i]) != bdata_worldgroup[i] ){
bdata_worldgroup[i] = 0;
}
//*/
//bdata_worldgroup[i] = 0;
}
return 0;
}
//! @brief ブロックの座標最大値・最小値を返す
//! @param data ブロックデータの構造体
//! @param *min_x 最小 X座標を返すポインタ
//! @param *min_y 最小 Y座標を返すポインタ
//! @param *min_z 最小 Z座標を返すポインタ
//! @param *max_x 最大 X座標を返すポインタ
//! @param *max_y 最大 Y座標を返すポインタ
//! @param *max_z 最大 Z座標を返すポインタ
void Collision::GetBlockPosMINMAX(struct blockdata data, float *min_x, float *min_y, float *min_z, float *max_x, float *max_y, float *max_z)
{
*min_x = data.x[0];
*min_y = data.y[0];
*min_z = data.z[0];
*max_x = data.x[0];
*max_y = data.y[0];
*max_z = data.z[0];
for(int i=1; i<8; i++){
if( *min_x > data.x[i] ){ *min_x = data.x[i]; }
if( *min_y > data.y[i] ){ *min_y = data.y[i]; }
if( *min_z > data.z[i] ){ *min_z = data.z[i]; }
if( *max_x < data.x[i] ){ *max_x = data.x[i]; }
if( *max_y < data.y[i] ){ *max_y = data.y[i]; }
if( *max_z < data.z[i] ){ *max_z = data.z[i]; }
}
}
//! @brief 空間分割のグループを算出
//! @param x X座標
//! @param z Z座標
//! @return グループ番号
//! @attention 「グループ番号」の概念は別途ドキュメントを参照
int Collision::GetWorldGroup(float x, float z)
{
if( (x > 0)&&(z > 0) ){ return 1; }
if( (x < 0)&&(z > 0) ){ return 2; }
if( (x < 0)&&(z < 0) ){ return 3; }
if( (x > 0)&&(z < 0) ){ return 4; }
return 0;
}
//! @brief ブロックに埋まっていないか調べる
//! @param blockid 判定するブロック番号
//! @param x X座標
//! @param y X座標
//! @param z X座標
//! @param worldgroup 空間のグループを利用して計算省略を試みる(true:有効・計算省略 false:無効・完全検索)
//! @param *planeid 表にある面番号(NULL可)
//! @return 埋っている:true 埋っていない:false
//! @warning *planeid が返す表面(0〜5)は、複数の面が該当する場合でも、最初に見つけた1面のみ返します。
bool Collision::CheckBlockInside(int blockid, float x, float y, float z, bool worldgroup, int *planeid)
{
D3DXVECTOR3 dv;
if( blockdata == NULL ){ return false; }
if( (blockid < 0)||(blockdata->GetTotaldatas() <= blockid) ){ return false; }
//板状のブロックは計算外
if( BoardBlock[blockid] == true ){ return false; }
//判定の荒削り
if( worldgroup == true ){
//空間分割
if( bdata_worldgroup[blockid] != 0 ){
//観測点の空間のグループを取得
int worldgroup = GetWorldGroup(x, z);
if( worldgroup != 0 ){
//空間のグループが違えば計算外
if( bdata_worldgroup[blockid] != worldgroup ){
return false;
}
}
}
//範囲で検索
if( (x < bmin_x[blockid])||(bmax_x[blockid] < x) ){ return false; }
if( (y < bmin_y[blockid])||(bmax_y[blockid] < y) ){ return false; }
if( (z < bmin_z[blockid])||(bmax_z[blockid] < z) ){ return false; }
}
//6面から見て全て裏面かどうか
for(int i=0; i<6; i++){
dv = D3DXVECTOR3( x, y, z );
if( (D3DXPlaneDotCoord(&bdata_plane[blockid][i][0], &dv) > 0)||(D3DXPlaneDotCoord(&bdata_plane[blockid][i][1], &dv) > 0) ){
if( planeid != NULL ){ *planeid = i; }
return false; //表面ならば終了
}
}
return true;
}
//! @brief 全てのブロックに埋まっていないか調べる
//! @param x X座標
//! @param y X座標
//! @param z X座標
//! @return 埋っている:true 埋っていない:false
bool Collision::CheckALLBlockInside(float x, float y, float z)
{
if( blockdata == NULL ){ return false; }
int bs = blockdata->GetTotaldatas();
for(int i=0; i<bs; i++){
if( CheckBlockInside(i, x, y, z, true, NULL) == true ){ return true; }
}
return false;
}
//! @brief ブロックとレイ(光線)のあたり判定
//! @param blockid 判定するブロック番号
//! @param RayPos_x レイの位置(始点)を指定する X座標
//! @param RayPos_y レイの位置(始点)を指定する Y座標
//! @param RayPos_z レイの位置(始点)を指定する Z座標
//! @param RayDir_x レイのベクトルを指定する X成分
//! @param RayDir_y レイのベクトルを指定する Y成分
//! @param RayDir_z レイのベクトルを指定する Z成分
//! @param face 当たったブロックの面番号(0〜5)を受け取るポインタ (NULL可)
//! @param Dist 当たったブロックとの距離を受け取るポインタ
//! @param maxDist 判定を行う最大距離 (0.0 未満で無効・無限)
//! @return 当たっている:true 当たっていない:false
//! @warning RayPos(始点)と RayDir(ベクトル)を間違えないこと。
//! @warning 判定を行う最大距離を指定しないと、パフォーマンスが大幅に低下します。
//! @attention レイの始点から裏側になるブロックの面は無視されます。厚さをゼロに変形させた板状のブロックも無視します。
//! @attention また、レイが複数のブロックに当たる場合は、レイの始点から一番近い判定を返します。
bool Collision::CheckBlockIntersectRay(int blockid, float RayPos_x, float RayPos_y, float RayPos_z, float RayDir_x, float RayDir_y, float RayDir_z, int *face, float *Dist, float maxDist)
{
if( blockdata == NULL ){ return false; }
struct blockdata data;
int vID[4];
float pDist;
float min_pDist = FLT_MAX;
int min_blockface = -1;
float rmin_x = 0.0f;
float rmin_y = 0.0f;
float rmin_z = 0.0f;
float rmax_x = 0.0f;
float rmax_y = 0.0f;
float rmax_z = 0.0f;
int worldgroupA = 0;
int worldgroupB = 0;
//板状のブロックは計算外
if( BoardBlock[blockid] == true ){
if( face != NULL ){ *face = 0; }
*Dist = 0.0f;
return false;
}
if( maxDist > 0.0f ){
//レイのAABBを作る
rmin_x = RayPos_x + RayDir_x * maxDist;
rmin_y = RayPos_y + RayDir_y * maxDist;
rmin_z = RayPos_z + RayDir_z * maxDist;
rmax_x = rmin_x;
rmax_y = rmin_y;
rmax_z = rmin_z;
if( rmin_x > RayPos_x ){ rmin_x = RayPos_x; }
if( rmin_y > RayPos_y ){ rmin_y = RayPos_y; }
if( rmin_z > RayPos_z ){ rmin_z = RayPos_z; }
if( rmax_x < RayPos_x ){ rmax_x = RayPos_x; }
if( rmax_y < RayPos_y ){ rmax_y = RayPos_y; }
if( rmax_z < RayPos_z ){ rmax_z = RayPos_z; }
}
D3DXVECTOR3 pRayPos(RayPos_x, RayPos_y, RayPos_z);
D3DXVECTOR3 pRayDir(RayDir_x, RayDir_y, RayDir_z);
if( maxDist > 0.0f ){
//始点と終点の空間グループを取得
worldgroupA = GetWorldGroup(RayPos_x, RayPos_z);
worldgroupB = GetWorldGroup(RayPos_x + RayDir_x * maxDist, RayPos_z + RayDir_z * maxDist);
}
blockdata->Getdata(&data, blockid);
if( maxDist > 0.0f ){
if( bdata_worldgroup[blockid] != 0 ){
//始点と終点が、空間のグループから出ていなければ
if( (worldgroupA != 0)&&(worldgroupA == worldgroupB) ){
//空間のグループが違えば計算外
if( bdata_worldgroup[blockid] == worldgroupA ){ //worldgroupA == worldgroupB
//境界ボックス同士で判定
if( CollideBoxAABB(bmin_x[blockid], bmin_y[blockid], bmin_z[blockid], bmax_x[blockid], bmax_y[blockid], bmax_z[blockid], rmin_x, rmin_y, rmin_z, rmax_x, rmax_y, rmax_z) == false ){
//当たってなければ、このブロックは調べない。
if( face != NULL ){ *face = 0; }
*Dist = 0.0f;
return false;
}
}
}
}
}
//境界ボックスとレイで判定
if( CollideAABBRay(bmin_x[blockid], bmin_y[blockid], bmin_z[blockid], bmax_x[blockid], bmax_y[blockid], bmax_z[blockid], RayPos_x, RayPos_y, RayPos_z, RayDir_x, RayDir_y, RayDir_z, NULL, -1.0f) == false ){
//当たってなければ、このブロックは調べない。
if( face != NULL ){ *face = 0; }
*Dist = 0.0f;
return false;
}
//各ポリゴン単位で判定
for(int i=0; i<6; i++){
blockdataface(i, vID, NULL);
if( (D3DXPlaneDotCoord(&bdata_plane[blockid][i][0], &pRayPos) >= 0)||(D3DXPlaneDotCoord(&bdata_plane[blockid][i][1], &pRayPos) >= 0) ){
D3DXVECTOR3 dv11, dv12, dv13;
D3DXVECTOR3 dv21, dv22, dv23;
dv11 = D3DXVECTOR3( data.x[vID[0]], data.y[vID[0]], data.z[vID[0]]);
dv12 = D3DXVECTOR3( data.x[vID[1]], data.y[vID[1]], data.z[vID[1]]);
dv13 = D3DXVECTOR3( data.x[vID[2]], data.y[vID[2]], data.z[vID[2]]);
dv21 = D3DXVECTOR3( data.x[vID[2]], data.y[vID[2]], data.z[vID[2]]);
dv22 = D3DXVECTOR3( data.x[vID[3]], data.y[vID[3]], data.z[vID[3]]);
dv23 = D3DXVECTOR3( data.x[vID[0]], data.y[vID[0]], data.z[vID[0]]);
if( (D3DXIntersectTri(&dv11, &dv12, &dv13, &pRayPos, &pRayDir, NULL, NULL, &pDist) == TRUE)||
(D3DXIntersectTri(&dv21, &dv22, &dv23, &pRayPos, &pRayDir, NULL, NULL, &pDist) == TRUE)
){
if( min_pDist > pDist ){
min_pDist = pDist;
min_blockface = i;
}
}
}
}
//見つからなければ、ポインタに適当な数字を入れて返す。
if( min_blockface == -1 ){
if( face != NULL ){ *face = 0; }
*Dist = 0.0f;
return false;
}
//計算結果を入れて返す
if( face != NULL ){ *face = min_blockface; }
*Dist = min_pDist;
return true;
}
//! @brief 全てのブロックとレイ(光線)のあたり判定
//! @param RayPos_x レイの位置(始点)を指定する X座標
//! @param RayPos_y レイの位置(始点)を指定する Y座標
//! @param RayPos_z レイの位置(始点)を指定する Z座標
//! @param RayDir_x レイのベクトルを指定する X成分
//! @param RayDir_y レイのベクトルを指定する Y成分
//! @param RayDir_z レイのベクトルを指定する Z成分
//! @param id 当たったブロックのIDを受け取るポインタ (NULL可)
//! @param face 当たったブロックの面番号(0〜5)を受け取るポインタ (NULL可)
//! @param Dist 当たったブロックとの距離を受け取るポインタ
//! @param maxDist 判定を行う最大距離 (0.0 未満で無効・無限)
//! @return 当たっている:true 当たっていない:false
//! @warning RayPos(始点)と RayDir(ベクトル)を間違えないこと。
//! @warning 判定を行う最大距離を指定しないと、パフォーマンスが大幅に低下します。
//! @warning 使い方は CheckALLBlockIntersectDummyRay()関数 と類似していますが、同関数より高精度で低速です。
//! @attention レイの始点から裏側になるブロックの面は無視されます。厚さをゼロに変形させた板状のブロックも無視します。
//! @attention また、レイが複数のブロックに当たる場合は、<b>レイの始点から一番近い判定</b>を返します。
bool Collision::CheckALLBlockIntersectRay(float RayPos_x, float RayPos_y, float RayPos_z, float RayDir_x, float RayDir_y, float RayDir_z, int *id, int *face, float *Dist, float maxDist)
{
if( blockdata == NULL ){ return false; }
int bs = blockdata->GetTotaldatas();
struct blockdata data;
int vID[4];
float pDist;
float min_pDist = FLT_MAX;
int min_blockid = -1;
int min_blockface = -1;
float rmin_x = 0.0f;
float rmin_y = 0.0f;
float rmin_z = 0.0f;
float rmax_x = 0.0f;
float rmax_y = 0.0f;
float rmax_z = 0.0f;
int worldgroupA = 0;
int worldgroupB = 0;
if( maxDist > 0.0f ){
//レイのAABBを作る
rmin_x = RayPos_x + RayDir_x * maxDist;
rmin_y = RayPos_y + RayDir_y * maxDist;
rmin_z = RayPos_z + RayDir_z * maxDist;
rmax_x = rmin_x;
rmax_y = rmin_y;
rmax_z = rmin_z;
if( rmin_x > RayPos_x ){ rmin_x = RayPos_x; }
if( rmin_y > RayPos_y ){ rmin_y = RayPos_y; }
if( rmin_z > RayPos_z ){ rmin_z = RayPos_z; }
if( rmax_x < RayPos_x ){ rmax_x = RayPos_x; }
if( rmax_y < RayPos_y ){ rmax_y = RayPos_y; }
if( rmax_z < RayPos_z ){ rmax_z = RayPos_z; }
}
D3DXVECTOR3 pRayPos(RayPos_x, RayPos_y, RayPos_z);
D3DXVECTOR3 pRayDir(RayDir_x, RayDir_y, RayDir_z);
if( maxDist > 0.0f ){
//始点と終点の空間グループを取得
worldgroupA = GetWorldGroup(RayPos_x, RayPos_z);
worldgroupB = GetWorldGroup(RayPos_x + RayDir_x * maxDist, RayPos_z + RayDir_z * maxDist);
}
for(int i=0; i<bs; i++){
//板状のブロックは計算外
if( BoardBlock[i] == true ){ continue; }
blockdata->Getdata(&data, i);
if( maxDist > 0.0f ){
if( bdata_worldgroup[i] != 0 ){
//始点と終点が、空間のグループから出ていなければ
if( (worldgroupA != 0)&&(worldgroupA == worldgroupB) ){
//空間のグループが違えば計算外
if( bdata_worldgroup[i] == worldgroupA ){ //worldgroupA == worldgroupB
//境界ボックス同士で判定
if( CollideBoxAABB(bmin_x[i], bmin_y[i], bmin_z[i], bmax_x[i], bmax_y[i], bmax_z[i], rmin_x, rmin_y, rmin_z, rmax_x, rmax_y, rmax_z) == false ){
continue; //当たってなければ、このブロックは調べない。
}
}
}
}
}
//境界ボックスとレイで判定
if( CollideAABBRay(bmin_x[i], bmin_y[i], bmin_z[i], bmax_x[i], bmax_y[i], bmax_z[i], RayPos_x, RayPos_y, RayPos_z, RayDir_x, RayDir_y, RayDir_z, NULL, -1.0f) == false ){
continue; //当たってなければ、このブロックは調べない。
}
//各ポリゴン単位で判定
for(int j=0; j<6; j++){
blockdataface(j, vID, NULL);
if( (D3DXPlaneDotCoord(&bdata_plane[i][j][0], &pRayPos) >= 0)||(D3DXPlaneDotCoord(&bdata_plane[i][j][1], &pRayPos) >= 0) ){
D3DXVECTOR3 dv11, dv12, dv13;
D3DXVECTOR3 dv21, dv22, dv23;
dv11 = D3DXVECTOR3( data.x[vID[0]], data.y[vID[0]], data.z[vID[0]]);
dv12 = D3DXVECTOR3( data.x[vID[1]], data.y[vID[1]], data.z[vID[1]]);
dv13 = D3DXVECTOR3( data.x[vID[2]], data.y[vID[2]], data.z[vID[2]]);
dv21 = D3DXVECTOR3( data.x[vID[2]], data.y[vID[2]], data.z[vID[2]]);
dv22 = D3DXVECTOR3( data.x[vID[3]], data.y[vID[3]], data.z[vID[3]]);
dv23 = D3DXVECTOR3( data.x[vID[0]], data.y[vID[0]], data.z[vID[0]]);
if( (D3DXIntersectTri(&dv11, &dv12, &dv13, &pRayPos, &pRayDir, NULL, NULL, &pDist) == TRUE)||
(D3DXIntersectTri(&dv21, &dv22, &dv23, &pRayPos, &pRayDir, NULL, NULL, &pDist) == TRUE)
){
if( min_pDist > pDist ){
min_pDist = pDist;
min_blockid = i;
min_blockface = j;
}
}
}
}
}
//見つけた距離が最大距離を超えていれば、判定を無効に。
if( maxDist >= 0.0f ){
if( min_pDist > maxDist ){
min_blockid = -1;
}
}
//見つからなければ、ポインタに適当な数字を入れて返す。
if( min_blockid == -1 ){
if( id != NULL ){ *id = 0; }
if( face != NULL ){ *face = 0; }
*Dist = 0.0f;
return false;
}
//計算結果を入れて返す
if( id != NULL ){ *id = min_blockid; }
if( face != NULL ){ *face = min_blockface; }
*Dist = min_pDist;
return true;
}
//! @brief 全てのブロックと衝突しているか判定
//! @param RayPos_x 始点 X座標
//! @param RayPos_y 始点 Y座標
//! @param RayPos_z 始点 Z座標
//! @param RayDir_x ベクトル X成分
//! @param RayDir_y ベクトル Y成分
//! @param RayDir_z ベクトル Z成分
//! @param id 当たったブロックのIDを受け取るポインタ (NULL可)
//! @param face 当たったブロックの面番号(0〜5)を受け取るポインタ (NULL可)
//! @param Dist ダミー変数のポインタ(常に 0.0f を返す)
//! @param maxDist 判定を行う最大距離 (0.0 未満指定 不可)
//! @return 当たっている:true 当たっていない:false
//! @warning 始点と ベクトルを間違えないこと。
//! @warning 使い方は CheckALLBlockIntersectRay()関数 と類似していますが、同関数より高速で低精度です。
//! @attention レイの始点から裏側になるブロックの面は無視されます。厚さをゼロに変形させた板状のブロックも無視します。
//! @attention また、レイが複数のブロックに当たる場合は、<b>一番最初に発見した判定</b>を返します。
bool Collision::CheckALLBlockIntersectDummyRay(float RayPos_x, float RayPos_y, float RayPos_z, float RayDir_x, float RayDir_y, float RayDir_z, int *id, int *face, float *Dist, float maxDist)
{
if( blockdata == NULL ){ return false; }
if( maxDist <= 0.0f ){ return false; }
float RayPos2_x = RayPos_x + RayDir_x * maxDist/2;
float RayPos2_y = RayPos_y + RayDir_y * maxDist/2;
float RayPos2_z = RayPos_z + RayDir_z * maxDist/2;
float RayPos3_x = RayPos_x + RayDir_x * maxDist;
float RayPos3_y = RayPos_y + RayDir_y * maxDist;
float RayPos3_z = RayPos_z + RayDir_z * maxDist;
if( (id == NULL)&&(face == NULL) ){
for(int i=0; i<MAX_BLOCKS; i++){
//終了時点
if( CheckBlockInside(i, RayPos3_x, RayPos3_y, RayPos3_z, true, NULL) == true ){
*Dist = 0.0f;
return true;
}
//中間時点
if( CheckBlockInside(i, RayPos2_x, RayPos2_y, RayPos2_z, true, NULL) == true ){
*Dist = 0.0f;
return true;
}
}
*Dist = 0.0f;
return false;
}
for(int i=0; i<MAX_BLOCKS; i++){
int surface;
//開始地点
CheckBlockInside(i, RayPos_x, RayPos_y, RayPos_z, false, &surface);
//終了時点
if( CheckBlockInside(i, RayPos3_x, RayPos3_y, RayPos3_z, true, &surface) == true ){
if( id != NULL ){ *id = i; }
if( face != NULL ){ *face = surface; }
*Dist = 0.0f;
return true;
}
//中間時点
if( CheckBlockInside(i, RayPos2_x, RayPos2_y, RayPos2_z, true, &surface) == true ){
if( id != NULL ){ *id = i; }
if( face != NULL ){ *face = surface; }
*Dist = 0.0f;
return true;
}
}
if( id != NULL ){ *id = 0; }
if( face != NULL ){ *face = 0; }
*Dist = 0.0f;
return false;
}
//! @brief ブロックに沿って移動するベクトルを求める
void Collision::ScratchVector(BlockDataInterface* in_blockdata, int id, int face, float in_vx, float in_vy, float in_vz, float *out_vx, float *out_vy, float *out_vz)
{
struct blockdata bdata;
in_blockdata->Getdata(&bdata, id);
D3DXVECTOR3 out;
D3DXVECTOR3 front(in_vx, in_vy, in_vz);
D3DXVECTOR3 normal(bdata.material[face].vx, bdata.material[face].vy, bdata.material[face].vz);
//D3DXVec3Normalize(&out, &(front - D3DXVec3Dot(&front, &normal) * normal));
out = (front - D3DXVec3Dot(&front, &normal) * normal);
*out_vx = out.x;
*out_vy = out.y;
*out_vz = out.z;
}
//! @brief ブロックに反射するベクトルを求める
void Collision::ReflectVector(BlockDataInterface* in_blockdata, int id, int face, float in_vx, float in_vy, float in_vz, float *out_vx, float *out_vy, float *out_vz)
{
struct blockdata bdata;
in_blockdata->Getdata(&bdata, id);
D3DXVECTOR3 out;
D3DXVECTOR3 front(in_vx, in_vy, in_vz);
D3DXVECTOR3 normal(bdata.material[face].vx, bdata.material[face].vy, bdata.material[face].vz);
//D3DXVec3Normalize(&out, &(front - 2.0f * D3DXVec3Dot(&front, &normal) * normal));
out = (front - 2.0f * D3DXVec3Dot(&front, &normal) * normal);
*out_vx = out.x;
*out_vy = out.y;
*out_vz = out.z;
}
//! @brief AABBによるあたり判定
//! @param box1_min_x 物体Aの最少 X座標
//! @param box1_min_y 物体Aの最少 Y座標
//! @param box1_min_z 物体Aの最少 Z座標
//! @param box1_max_x 物体Aの最大 X座標
//! @param box1_max_y 物体Aの最大 Y座標
//! @param box1_max_z 物体Aの最大 Z座標
//! @param box2_min_x 物体Bの最少 X座標
//! @param box2_min_y 物体Bの最少 Y座標
//! @param box2_min_z 物体Bの最少 Z座標
//! @param box2_max_x 物体Bの最大 X座標
//! @param box2_max_y 物体Bの最大 Y座標
//! @param box2_max_z 物体Bの最大 Z座標
//! @return 当たっている:true 当たっていない:false
//! @attention エラーがある場合「当たっていない:false」が返されます。
bool CollideBoxAABB(float box1_min_x, float box1_min_y, float box1_min_z, float box1_max_x, float box1_max_y, float box1_max_z, float box2_min_x, float box2_min_y, float box2_min_z, float box2_max_x, float box2_max_y, float box2_max_z)
{
//エラー対策
if( box1_min_x > box1_max_x ){ return false; }
if( box1_min_y > box1_max_y ){ return false; }
if( box1_min_z > box1_max_z ){ return false; }
if( box2_min_x > box2_max_x ){ return false; }
if( box2_min_y > box2_max_y ){ return false; }
if( box2_min_z > box2_max_z ){ return false; }
if(
(box1_min_x < box2_max_x)&&(box1_max_x > box2_min_x)&&
(box1_min_y < box2_max_y)&&(box1_max_y > box2_min_y)&&
(box1_min_z < box2_max_z)&&(box1_max_z > box2_min_z)
){
return true;
}
return false;
}
//! @brief 円柱同士の当たり判定
//! @param c1_x 円柱1 底辺のx座標
//! @param c1_y 円柱1 底辺のy座標
//! @param c1_z 円柱1 底辺のz座標
//! @param c1_r 円柱1 の半径
//! @param c1_h 円柱1 の高さ
//! @param c2_x 円柱2 底辺のx座標
//! @param c2_y 円柱2 底辺のy座標
//! @param c2_z 円柱2 底辺のz座標
//! @param c2_r 円柱2 の半径
//! @param c2_h 円柱2 の高さ
//! @param *angle 円柱1からみた接触角度を受け取るポインタ(NULL可)
//! @param *length 円柱1からみた接触距離を受け取るポインタ(NULL可)
//! @return 当たっている:true 当たっていない:false
bool CollideCylinder(float c1_x, float c1_y, float c1_z, float c1_r, float c1_h, float c2_x, float c2_y, float c2_z, float c2_r, float c2_h, float *angle, float *length)
{
//先にお手軽な高さで判定
if( (c1_y < c2_y + c2_h)&&(c1_y + c1_h > c2_y) ){
//距離で判定
float x = c1_x - c2_x;
float z = c1_z - c2_z;
float caser = x*x + z*z;
float minr = (c1_r+c2_r) * (c1_r+c2_r);
if( caser < minr ){
if( angle != NULL ){ *angle = atan2(z, x); }
if( length != NULL ){ *length = sqrt(minr) - sqrt(caser); }
return true;
}
}
return false;
}
//! @brief 球体とレイ(光線)のあたり判定
//! @param s_x 球体のx座標
//! @param s_y 球体のy座標
//! @param s_z 球体のz座標
//! @param s_r 球体の半径
//! @param RayPos_x レイの位置(始点)を指定する X座標
//! @param RayPos_y レイの位置(始点)を指定する Y座標
//! @param RayPos_z レイの位置(始点)を指定する Z座標
//! @param RayDir_x レイのベクトルを指定する X成分
//! @param RayDir_y レイのベクトルを指定する Y成分
//! @param RayDir_z レイのベクトルを指定する Z成分
//! @param Dist 当たった球体との距離を受け取るポインタ
//! @param maxDist 判定を行う最大距離 (0.0 未満で無効・無限)
//! @return 当たっている:true 当たっていない:false
//! @warning RayPos(始点)と RayDir(ベクトル)を間違えないこと。
//! @warning 判定を行う最大距離を指定しないと、パフォーマンスが大幅に低下します。
bool CollideSphereRay(float s_x, float s_y, float s_z, float s_r, float RayPos_x, float RayPos_y, float RayPos_z, float RayDir_x, float RayDir_y, float RayDir_z, float *Dist, float maxDist)
{
if( maxDist > 0.0f ){
float pmin_x, pmin_y, pmin_z, pmax_x, pmax_y, pmax_z;
//レイのAABBを作る
pmin_x = RayPos_x + RayDir_x * maxDist;
pmin_y = RayPos_y + RayDir_y * maxDist;
pmin_z = RayPos_z + RayDir_z * maxDist;
pmax_x = pmin_x;
pmax_y = pmin_y;
pmax_z = pmin_z;
if( pmin_x > RayPos_x ){ pmin_x = RayPos_x; }
if( pmin_y > RayPos_y ){ pmin_y = RayPos_y; }
if( pmin_z > RayPos_z ){ pmin_z = RayPos_z; }
if( pmax_x < RayPos_x ){ pmax_x = RayPos_x; }
if( pmax_y < RayPos_y ){ pmax_y = RayPos_y; }
if( pmax_z < RayPos_z ){ pmax_z = RayPos_z; }
//境界ボックス同士で判定
if( CollideBoxAABB(s_x - s_r, s_y - s_r, s_z - s_r, s_x + s_r, s_y + s_r, s_z + s_r, pmin_x, pmin_y, pmin_z, pmax_x, pmax_y, pmax_z) == FALSE ){
return false;
}
}
D3DXVECTOR3 pCenter(s_x, s_y, s_z);
D3DXVECTOR3 pRayPos(RayPos_x, RayPos_y, RayPos_z);
D3DXVECTOR3 pRayDir(RayDir_x, RayDir_y, RayDir_z);
//球体とレイの判定
if( D3DXSphereBoundProbe(&pCenter, s_r, &pRayPos, &pRayDir) == TRUE ){
if( Dist != NULL ){
if( maxDist < 0.0f ){ maxDist = s_r * 2; }
//点とレイ始点の距離
float x, y, z, d;
x = s_x - RayPos_x;
y = s_y - RayPos_y;
z = s_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
//レイ始点が半径より近い(=めり込んでいる)
if( d < s_r ){
*Dist = 0.0f;
return true;
}
//点(球体の中心)とレイの最短距離を求める
float MinDist, RayDist, RDist;
//点とレイの最短距離
MinDist = DistancePosRay(s_x, s_y, s_z, RayPos_x, RayPos_y, RayPos_z, RayDir_x, RayDir_y, RayDir_z, maxDist);
RayDist = sqrt(d*d - MinDist*MinDist); //(レイ始点から)点に最も近づく距離
RDist = sqrt(s_r*s_r - MinDist*MinDist); //(点半径から)点に最も近づく距離
*Dist = RayDist - RDist; //レイ視点最短 - 半径最短 = レイ視点から半径までの最短
}
return true;
}
return false;
}
//! @brief AABBとレイ(光線)のあたり判定
//! @param box_min_x 物体の最少 X座標
//! @param box_min_y 物体の最少 Y座標
//! @param box_min_z 物体の最少 Z座標
//! @param box_max_x 物体の最大 X座標
//! @param box_max_y 物体の最大 Y座標
//! @param box_max_z 物体の最大 Z座標
//! @param RayPos_x レイの位置(始点)を指定する X座標
//! @param RayPos_y レイの位置(始点)を指定する Y座標
//! @param RayPos_z レイの位置(始点)を指定する Z座標
//! @param RayDir_x レイのベクトルを指定する X成分
//! @param RayDir_y レイのベクトルを指定する Y成分
//! @param RayDir_z レイのベクトルを指定する Z成分
//! @param Dist 当たったAABBとの距離を受け取るポインタ
//! @param maxDist 判定を行う最大距離 (0.0 未満で無効・無限)
//! @return 当たっている:true 当たっていない:false
//! @warning RayPos(始点)と RayDir(ベクトル)を間違えないこと。
//! @warning 判定を行う最大距離を指定しないと、パフォーマンスが大幅に低下します。
//! @todo 当たった距離として近似値を返すので、正確な値を求める。
bool CollideAABBRay(float box_min_x, float box_min_y, float box_min_z, float box_max_x, float box_max_y, float box_max_z, float RayPos_x, float RayPos_y, float RayPos_z, float RayDir_x, float RayDir_y, float RayDir_z, float *Dist, float maxDist)
{
if( box_min_x > box_max_x ){ return false; }
if( box_min_y > box_max_y ){ return false; }
if( box_min_z > box_max_z ){ return false; }
if( maxDist > 0.0f ){
float pmin_x, pmin_y, pmin_z, pmax_x, pmax_y, pmax_z;
//レイのAABBを作る
pmin_x = RayPos_x + RayDir_x * maxDist;
pmin_y = RayPos_y + RayDir_y * maxDist;
pmin_z = RayPos_z + RayDir_z * maxDist;
pmax_x = pmin_x;
pmax_y = pmin_y;
pmax_z = pmin_z;
if( pmin_x > RayPos_x ){ pmin_x = RayPos_x; }
if( pmin_y > RayPos_y ){ pmin_y = RayPos_y; }
if( pmin_z > RayPos_z ){ pmin_z = RayPos_z; }
if( pmax_x < RayPos_x ){ pmax_x = RayPos_x; }
if( pmax_y < RayPos_y ){ pmax_y = RayPos_y; }
if( pmax_z < RayPos_z ){ pmax_z = RayPos_z; }
//境界ボックス同士で判定
if( CollideBoxAABB(box_min_x, box_min_y, box_min_z, box_max_x, box_max_y, box_max_z, pmin_x, pmin_y, pmin_z, pmax_x, pmax_y, pmax_z) == FALSE ){
return false;
}
}
D3DXVECTOR3 pMin(box_min_x, box_min_y, box_min_z);
D3DXVECTOR3 pMax(box_max_x, box_max_y, box_max_z);
D3DXVECTOR3 pRayPos(RayPos_x, RayPos_y, RayPos_z);
D3DXVECTOR3 pRayDir(RayDir_x, RayDir_y, RayDir_z);
//AABBとレイ(光線)の判定
if( D3DXBoxBoundProbe(&pMin, &pMax, &pRayPos, &pRayDir) == TRUE ){
if( Dist != NULL ){
float x, y, z;
float d, mind;
//AABBの各頂点から距離を算出して、一番近い距離を「当たった距離」とする。
// ちゃんと計算しろよ ><
x = box_min_x - RayPos_x;
y = box_min_y - RayPos_y;
z = box_min_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
mind = d;
x = box_max_x - RayPos_x;
y = box_min_y - RayPos_y;
z = box_min_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
x = box_min_x - RayPos_x;
y = box_max_y - RayPos_y;
z = box_min_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
x = box_max_x - RayPos_x;
y = box_max_y - RayPos_y;
z = box_min_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
x = box_min_x - RayPos_x;
y = box_min_y - RayPos_y;
z = box_max_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
x = box_max_x - RayPos_x;
y = box_min_y - RayPos_y;
z = box_max_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
x = box_min_x - RayPos_x;
y = box_max_y - RayPos_y;
z = box_max_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
x = box_max_x - RayPos_x;
y = box_max_y - RayPos_y;
z = box_max_z - RayPos_z;
d = sqrt(x*x + y*y + z*z);
if( d < mind ){ mind = d; }
*Dist = mind;
}
return true;
}
return false;
}
//! @brief 点とレイの最短距離を求める
//! @param Pos_x 点のX座標
//! @param Pos_y 点のY座標
//! @param Pos_z 点のZ座標
//! @param RayPos_x レイの位置(始点)を指定する X座標
//! @param RayPos_y レイの位置(始点)を指定する Y座標
//! @param RayPos_z レイの位置(始点)を指定する Z座標
//! @param RayDir_x レイのベクトルを指定する X成分
//! @param RayDir_y レイのベクトルを指定する Y成分
//! @param RayDir_z レイのベクトルを指定する Z成分
//! @param maxDist 判定を行う最大距離
//! @return 最短距離
//! @warning RayPos(始点)と RayDir(ベクトル)を間違えないこと。
float DistancePosRay(float Pos_x, float Pos_y, float Pos_z, float RayPos_x, float RayPos_y, float RayPos_z, float RayDir_x, float RayDir_y, float RayDir_z, float maxDist)
{
float x1, y1, z1;
float x2, y2, z2;
D3DXVECTOR3 in1, in2, out;
x1 = Pos_x - RayPos_x;
y1 = Pos_y - RayPos_y;
z1 = Pos_z - RayPos_z;
x2 = RayDir_x * maxDist;
y2 = RayDir_y * maxDist;
z2 = RayDir_z * maxDist;
in1 = D3DXVECTOR3(x1, y1, z1);
in2 = D3DXVECTOR3(x2, y2, z2);
D3DXVec3Cross(&out, &in1, &in2);
return sqrt(out.x*out.x + out.y*out.y + out.z*out.z) / maxDist;
}
//! @brief 線分と線分の当たり判定(2D)
//! @param A1x 線分Aの始点 X座標
//! @param A1y 線分Aの始点 Y座標
//! @param A2x 線分Aの終点 X座標
//! @param A2y 線分Aの終点 Y座標
//! @param B1x 線分Bの始点 X座標
//! @param B1y 線分Bの始点 Y座標
//! @param B2x 線分Bの終点 X座標
//! @param B2y 線分Bの終点 Y座標
//! @param out_x 交点の X座標 を受け取るポインタ(NULL可)
//! @param out_y 交点の Y座標 を受け取るポインタ(NULL可)
//! @return 交差する:true 交差しない:false
bool Collide2DLine(int A1x, int A1y, int A2x, int A2y, int B1x, int B1y, int B2x, int B2y, int *out_x, int *out_y)
{
//線分のベクトルを求める
int Avx = A2x - A1x;
int Avy = A2y - A1y;
int Bvx = B2x - B1x;
int Bvy = B2y - B1y;
float v1_v2 = (float)(Avx * Bvy - Avy * Bvx); //外積
if( v1_v2 == 0.0f ){
return false; //平行
}
float vx = (float)(B1x - A1x);
float vy = (float)(B1y - A1y);
float v_v1 = vx * Avy - vy * Avx; //外積
float v_v2 = vx * Bvy - vy * Bvx; //外積
float t1 = v_v2 / v1_v2;
float t2 = v_v1 / v1_v2;
if( (t1 <= 0)||(1 <= t1)||(t2 <= 0)||(1 <= t2) ){
return false; //交差してない
}
if( out_x != NULL ){ *out_x = (int)(A1x + Avx * t1); }
if( out_y != NULL ){ *out_y = (int)(A1y + Avy * t1); }
return true;
}
//! @brief 四角形に収まる線分を求める(2D)
//! @param line_x1 線分の始点 X座標
//! @param line_y1 線分の始点 Y座標
//! @param line_x2 線分の終点 X座標
//! @param line_y2 線分の終点 Y座標
//! @param box_x1 四角形の左上 X座標
//! @param box_y1 四角形の左上 Y座標
//! @param box_x2 四角形の右下 X座標
//! @param box_y2 四角形の右下 Y座標
//! @param out_line_x1 四角形に収まる 線分の始点 X座標 を受け取るポインタ
//! @param out_line_y1 四角形に収まる 線分の始点 Y座標 を受け取るポインタ
//! @param out_line_x2 四角形に収まる 線分の終点 X座標 を受け取るポインタ
//! @param out_line_y2 四角形に収まる 線分の終点 Y座標 を受け取るポインタ
//! @return 有効(描画する):true 無効(描画しない):false
//! @warning 引数は必ず「box_x1 < box_x2」かつ「box_x1 < box_x2」にすること
//! @note 簡易レーダーのマップ表示用
bool Get2DLineInBox(int line_x1, int line_y1, int line_x2, int line_y2, int box_x1, int box_y1, int box_x2, int box_y2, int *out_line_x1, int *out_line_y1, int *out_line_x2, int *out_line_y2)
{
//四角形指定が異常
if( (box_x1 >= box_x2)||(box_x1 >= box_x2) ){ return false; }
//上下左右の空間にあるなら、的外れ
if( (line_x1 < box_x1)&&(line_x2 < box_x1) ){ return false; }
if( (line_y1 < box_y1)&&(line_y2 < box_y1) ){ return false; }
if( (box_x2 < line_x1)&&(box_x2 < line_x2) ){ return false; }
if( (box_y2 < line_x1)&&(box_y2 < line_x2) ){ return false; }
//既に四角形に収まる
if( (box_x1 <= line_x1)&&(line_x1 <= box_x2)&&(box_y1 <= line_y1)&&(line_y1 <= box_y2) ){
if( (box_x1 <= line_x2)&&(line_x2 <= box_x2)&&(box_y1 <= line_y2)&&(line_y2 <= box_y2) ){
*out_line_x1 = line_x1;
*out_line_y1 = line_y1;
*out_line_x2 = line_x2;
*out_line_y2 = line_y2;
return true;
}
}
int x, y;
//上辺
if( Collide2DLine(box_x1, box_y1, box_x2, box_y1, line_x1, line_y1, line_x2, line_y2, &x, &y) == true ){
//始点が四角形の内側なら終点を、違えば(=終点が内側)始点を書き換える。
if( (box_x1 <= line_x1)&&(line_x1 <= box_x2)&&(box_y1 <= line_y1)&&(line_y1 <= box_y2) ){
line_x2 = x;
line_y2 = y;
}
else{
line_x1 = x;
line_y1 = y;
}
}
//右辺
if( Collide2DLine(box_x2, box_y1, box_x2, box_y2, line_x1, line_y1, line_x2, line_y2, &x, &y) == true ){
//始点が四角形の内側なら終点を、違えば(=終点が内側)始点を書き換える。
if( (box_x1 <= line_x1)&&(line_x1 <= box_x2)&&(box_y1 <= line_y1)&&(line_y1 <= box_y2) ){
line_x2 = x;
line_y2 = y;
}
else{
line_x1 = x;
line_y1 = y;
}
}
//下辺
if( Collide2DLine(box_x2, box_y2, box_x1, box_y2, line_x1, line_y1, line_x2, line_y2, &x, &y) == true ){
//始点が四角形の内側なら終点を、違えば(=終点が内側)始点を書き換える。
if( (box_x1 <= line_x1)&&(line_x1 <= box_x2)&&(box_y1 <= line_y1)&&(line_y1 <= box_y2) ){
line_x2 = x;
line_y2 = y;
}
else{
line_x1 = x;
line_y1 = y;
}
}
//左辺
if( Collide2DLine(box_x1, box_y2, box_x1, box_y1, line_x1, line_y1, line_x2, line_y2, &x, &y) == true ){
//始点が四角形の内側なら終点を、違えば(=終点が内側)始点を書き換える。
if( (box_x1 <= line_x1)&&(line_x1 <= box_x2)&&(box_y1 <= line_y1)&&(line_y1 <= box_y2) ){
line_x2 = x;
line_y2 = y;
}
else{
line_x1 = x;
line_y1 = y;
}
}
//改めて四角形に収まるか確認
if( (box_x1 <= line_x1)&&(line_x1 <= box_x2)&&(box_y1 <= line_y1)&&(line_y1 <= box_y2) ){
if( (box_x1 <= line_x2)&&(line_x2 <= box_x2)&&(box_y1 <= line_y2)&&(line_y2 <= box_y2) ){
*out_line_x1 = line_x1;
*out_line_y1 = line_y1;
*out_line_x2 = line_x2;
*out_line_y2 = line_y2;
return true;
}
}
return false;
}
//! @brief 観測点から対象点への 距離判定・角度算出
//! @param pos_x 観測点のX座標
//! @param pos_y 観測点のY座標
//! @param pos_z 観測点のZ座標
//! @param rx 観測点の水平角度
//! @param ry 観測点の垂直角度
//! @param target_x 対象点のX座標
//! @param target_y 対象点のY座標
//! @param target_z 対象点のZ座標
//! @param checkdist 判定距離(0.0f以下で判定無効)
//! @param out_rx 対象点への水平角度(π〜-π)を受け取るポインタ(NULL可)
//! @param out_ry 対象点への垂直角度を受け取るポインタ(NULL可)
//! @param out_dist2 対象点への距離<b>の二乗</b>を受け取るポインタ(NULL可)
//! @return 成功:true 失敗:false
//! @warning out_dist2は距離の<b>二乗</b>です。必要に応じて改めて sqrt()関数 などを用いてください。
//! @attention 引数 checkdist に有効な距離を与えた場合は、観測点から対象点への距離判定も行います。指定された距離より離れている場合、角度を計算せずに false を返します。
//! @attention 逆に、引数 checkdist に0.0f以下を与えた場合、距離による判定を行いません。関数は常に true を返します。
bool CheckTargetAngle(float pos_x, float pos_y, float pos_z, float rx, float ry, float target_x, float target_y, float target_z, float checkdist, float *out_rx, float *out_ry, float *out_dist2)
{
float x, y, z;
float dist2 = 0.0f;
float angleX, angleY;
x = target_x - pos_x;
y = target_y - pos_y;
z = target_z - pos_z;
if( (checkdist > 0.0f)||(out_dist2 != NULL) ){
dist2 = (x*x + y*y + z*z);
}
if( checkdist > 0.0f ){
if( dist2 > checkdist * checkdist ){
return false;
}
}
if( out_rx != NULL ){
angleX = atan2(z, x) - rx;
for(; angleX > (float)M_PI; angleX -= (float)M_PI*2){}
for(; angleX < (float)M_PI*-1; angleX += (float)M_PI*2){}
*out_rx = angleX;
}
if( out_ry != NULL ){
angleY = atan2(y, sqrt(x*x + z*z)) - ry;
*out_ry = angleY;
}
if( out_dist2 != NULL ){ *out_dist2 = dist2; }
return true;
}