http://dixq.net/forum/viewtopic.php?f=3&t=7611で作っていただいた
3Dの座標を2Dに変換するプログラムです。
描画はWin32 APIなので、DXライブラリなどを使う際は描画関数(draw3dlineとdraw3dtext)を書きなおしてください。
3d.h
#include <windows.h>
#if 0
#define ENABLE_INFO
#endif
/*
x,y,z:座標
cx:縦の首振り(正=上)
cy:横の首振り(正=右)
cz:回転(正=反時計回り)
genkai:視野(ピクセル)
genkaiangle:視野(角度)
centerx,centery:中心の座標(二次元)
*/
typedef struct {
double x,y,z;
double cx,cy,cz;
double genkai,genkaiangle;
double centerx,centery;
} camera_t;
extern int henkan3dzahyou(double* ox,double* oy,
double x,double y,double z,const camera_t* camera);
#ifdef ENABLE_INFO
extern int draw3dline(HDC hdc,double sx,double sy,double sz,
double dx,double dy,double dz,const camera_t* camera,char *info);
#else
extern int draw3dline(HDC hdc,double sx,double sy,double sz,
double dx,double dy,double dz,const camera_t* camera);
#endif
extern int draw3dtext(HDC hdc,double x,double y,double z,
const char* text,const camera_t* camera);
#define dec2rad(a) ((a)*3.141592653589/180.0)
#include <math.h>
#include "3d.h"
// マトリクスの設定
static void setMatrix( double matrix[][4],
double mat00, double mat01, double mat02, double mat03,
double mat10, double mat11, double mat12, double mat13,
double mat20, double mat21, double mat22, double mat23,
double mat30, double mat31, double mat32, double mat33 )
{
matrix[0][0]=mat00; matrix[0][1]=mat01; matrix[0][2]=mat02; matrix[0][3]=mat03;
matrix[1][0]=mat10; matrix[1][1]=mat11; matrix[1][2]=mat12; matrix[1][3]=mat13;
matrix[2][0]=mat20; matrix[2][1]=mat21; matrix[2][2]=mat22; matrix[2][3]=mat23;
matrix[3][0]=mat30; matrix[3][1]=mat31; matrix[3][2]=mat32; matrix[3][3]=mat33;
}
// マトリックス・ポイント計算
static void calcMatrixPoint( double matrix[][4],double out[],double in[])
{
int i,j;
// 行列の演算。
for( i=0 ; i<4; i++ ) {
out[i] = 0;
for( j=0 ; j<4; j++ ) {
out[i] += in[j] * matrix[j][i];
}
}
}
// コピーマトリックス
static void copyMatrix( double dstMat[][4],double srcMat[][4])
{
int i,j;
// コピー
for( i=0 ; i<4; i++ ) {
for( j=0 ; j<4; j++ ) {
dstMat[i][j] = srcMat[i][j];
}
}
}
// マトリックス計算
static void calcMatrix( double dstMat[][4],double srcMat[][4])
{
int i,j,k;
double matrix[4][4];
// クリア
for( i=0 ; i<4; i++ ) {
for( j=0 ; j<4; j++ ) {
matrix[i][j] = 0;
}
}
// 行列の演算。
for( i=0 ; i<4; i++ ) {
for( j=0 ; j<4; j++ ) {
for( k=0 ; k<4; k++ ) {
matrix[i][j] += dstMat[i][k] * srcMat[k][j];
}
}
}
// コピー
copyMatrix(dstMat,matrix);
}
int henkan3dzahyou(double* ox,double* oy,
double x,double y,double z,const camera_t* camera)
{
double Mmatrix[4][4];//移動変換行列
double Pmatrix[4][4];//射影変換行列
double point3D[4]; //変換前座標
double Mpoint3D[4]; //移動変換後座標
double Ppoint3D[4]; //射影変換後座標
int i,j;
// 3D座標値を設定する。
point3D[0] = x;
point3D[1] = y;
point3D[2] = z;
point3D[3] = 1;
// カメラビュー変換。
{
double RXmatrix[4][4];//X回転行列
double RYmatrix[4][4];//Y回転行列
double RZmatrix[4][4];//Z回転行列
double MVmatrix[4][4];//平行移動行列
double rot;
// 単位マトリックスを設定
setMatrix( Mmatrix,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 );
// X回転マトリックス
rot = -camera->cx;
setMatrix( RXmatrix,
1, 0, 0, 0,
0, cos(rot), sin(rot), 0,
0, -sin(rot), cos(rot), 0,
0, 0, 0, 1 );
// Y回転マトリックス
rot = -camera->cy;
setMatrix( RYmatrix,
cos(rot), 0, -sin(rot), 0,
0, 1, 0, 0,
sin(rot), 0, cos(rot), 0,
0, 0, 0, 1 );
// Z回転マトリックス
rot = -camera->cz;
setMatrix( RZmatrix,
cos(rot), sin(rot), 0, 0,
-sin(rot), cos(rot), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 );
// 平行移動マトリックス
setMatrix( MVmatrix,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-camera->x, -camera->y, -camera->z, 1 );
// マトリックスの合成
calcMatrix(Mmatrix,MVmatrix);
calcMatrix(Mmatrix,RXmatrix);
calcMatrix(Mmatrix,RYmatrix);
calcMatrix(Mmatrix,RZmatrix);
}
// 行列の演算。
calcMatrixPoint(Mmatrix,Mpoint3D,point3D);
// 射影変換行列を設定する。
{
double depth,ctan;
double cnear,cfar,width,height;
cnear = 0.1;
cfar = 1000.0;
ctan = tan(camera->genkaiangle*0.5);
width = 1.0 / ctan;
height = 1.0 / ctan;
depth = cfar / (cfar - cnear);
setMatrix( Pmatrix,
width, 0, 0, 0,
0, height, 0, 0,
0, 0, depth, 1,
0, 0, -depth * cnear, 0 );
}
// 行列の演算。
calcMatrixPoint(Pmatrix,Ppoint3D,Mpoint3D);
// 投影できないかチェック
if( Ppoint3D[2] < 0 ) {
return 0;
}
// スクリーン座標変換。アスペクトは1:1
*ox = camera->centery * Ppoint3D[0] / Ppoint3D[2] + camera->centerx;
*oy = camera->centery * Ppoint3D[1] / Ppoint3D[2] + camera->centery;
return 1;
}
#ifdef ENABLE_INFO
int draw3dline(HDC hdc,double sx,double sy,double sz,
double dx,double dy,double dz,const camera_t* camera,char *info) {
#else
int draw3dline(HDC hdc,double sx,double sy,double sz,
double dx,double dy,double dz,const camera_t* camera) {
#endif
double hx1,hy1,hx2,hy2;
if(!henkan3dzahyou(&hx1,&hy1,sx,sy,sz,camera))return 0;
if(!henkan3dzahyou(&hx2,&hy2,dx,dy,dz,camera))return 0;
if(!MoveToEx(hdc,(int)hx1,(int)hy1,NULL))return 0;
if(!LineTo(hdc,(int)hx2,(int)hy2))return 0;
#ifdef ENABLE_INFO
if( info != NULL ) {
wsprintf(info,"ライン座標(%d,%d)-(%d,%d)",(int)hx1,(int)hy1,(int)hx2,(int)hy2);
}
#endif
return 1;
}
int draw3dtext(HDC hdc,double x,double y,double z,
const char* text,const camera_t* camera) {
double hx,hy;
if(!henkan3dzahyou(&hx,&hy,x,y,z,camera))return 0;
if(!TextOut(hdc,(int)hx,(int)hy,text,lstrlen(text)))return 0;
return 1;
}
サンプルの操作方法
矢印キー上下:y軸方向にカメラを移動
矢印キー左右:x軸方向にカメラを移動
Ctrl+矢印キー上下:z軸方向にカメラを移動
Shift+矢印キー上下:上下にカメラを回転
Shift+矢印キー左右:左右にカメラを回転
Ctrl+Shift+矢印キー上下:カメラをひねる
F1:カメラの座標をリセット
1~5:描画するものを切り替え(6~9、0を押すと何も描画されません、未実装なので)
訂正:czの回転は時計回りです。