stlデータを読み込んでOpenGLにて表示するコード

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
けん

stlデータを読み込んでOpenGLにて表示するコード

#1

投稿記事 by けん » 9年前

stlデータを読み込んでOpenGLにて表示するコードを本などを参考に書いたのですがうまく表示されず困っております。
ウィンドウは表示されるのですが、読み込んだはずのデータが表示されず真っ白のままです。
バイナリ形、アスキー型どちらにも対応しております。

以下にコードを載せます。

abc.h

コード:

#define X 0
#define Y 1
#define Z 2

#define A 0
#define B 1
#define C 2
#define D 3
#define EPS 0.00001   //十分に微小な値
#define LARGE 1000000 //十分に大きな値

#define PI 3.141592
#define MAX_NUM_POINTS 1000000
#define MAX_NUM_EDGES 1000000
#define MAX_NUM_TRIANGLES 1000000

//一時的に利用する点の構造体
typedef struct tmp_point{
	double coord[3];//座標
	unsigned int index;//点に付随するインデックス
}tmp_point;

//一時的に利用する辺の構造体
typedef struct tmp_edge{
	unsigned int start;
	unsigned int end;
	tmp_edge *next;
} tmp_edge;
BasicGeom.cpp

コード:

#include<iostream>
#include<tchar.h>
#include<math.h>

#include"abc.h"

//vec0とvec1の内積の計算
double dot(double vec0[],double vec1[])
{
	return (vec0[X] * vec1[X] + vec0[Y] * vec1[Y] + vec0[Z] * vec1[Z]);
}

//vec0とvec1の外積
void cross(double vec0[],double vec1[],double vec2[])
{
	vec2[X] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
	vec2[Y] = vec0[Z] * vec1[X] - vec0[X] * vec1[Z];
	vec2[Z] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
}

//ベクトルの正規化
void normVec(double vec[])
{
	double norm;
	norm = sqrt(vec[X] * vec[X] + vec[Y] * vec[Y] + vec[Z] * vec[Z]);
	vec[X]/=norm;
	vec[Y]/=norm;
	vec[Z]/=norm;
}

//単位法線ベクトルの計算
void normal(double p0[],double p1[],double p2[],double normal[])
{
	unsigned int i;
	double v0[3],v1[3];

	//基本となる2つのベクトルの生成
	for(i = 0; i < 3; i++){
		v0[i] = p2[i] - p1[i];
		v1[i] = p0[i] - p1[i];
	}

	//生成したベクトルの外積を計算
	cross(v0,v1,normal);

	//外積によって得られた法線ベクトルを正規化
	normVec(normal);
}
stlloader.cpp

コード:

#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include <tchar.h>
#include"abc.h"


//点とその個数、一時的な利用
tmp_point *point_array[3*MAX_NUM_TRIANGLES];
unsigned int num_tmp_points;

//点に接続する辺のリスト、一時的な利用
tmp_edge **connecting_edge; 

//点
double point[MAX_NUM_POINTS][3];
unsigned int num_points;

//辺
unsigned int edge[MAX_NUM_EDGES][2];
unsigned int num_edges; 

//三角形ポリゴンの頂点と周辺の辺
unsigned int triangle[MAX_NUM_TRIANGLES][3];
unsigned int triangle_edge[MAX_NUM_TRIANGLES][3];
unsigned int num_triangles;

//アスキー形式のSTLファイルの読み込み
//読み込まれた座標は一時的にpoint_arrayに収納される
bool readASCIISTLFile(const char* STL_file)
	//const char* STL_fileはファイル名
{
	double x,y,z;
	char line[100];
	static char dummy[100];
	FILE *in;
	tmp_point *tmp_pnt;

	//stlファイルオープン
	in=fopen(STL_file, "r");
	if(in == NULL)
		return false;

	//ファイルから座標の読み込み
	printf("ファイルを読み込みます\n");
	num_tmp_points = 0;
	num_triangles = 0;
	while(fgets(line,100,in) != NULL){
		//vertexが見つかるまで読み飛ばし
		if(strstr(line,"vertex") == NULL)
			continue;

		//連続する3頂点を読み込み
		sscanf(line, "%s, %lf, %lf, %lf",dummy, &x, &y, &z);
		tmp_pnt = (tmp_point *)malloc(sizeof(tmp_point));
		point_array[num_tmp_points] = tmp_pnt;
		tmp_pnt->coord[X] = x;
		tmp_pnt->coord[Y] = y;
		tmp_pnt->coord[Z] = z;
		tmp_pnt->index = num_tmp_points;
		num_tmp_points++;

		fgets(line,100,in);
		sscanf(line,"%s, %lf, %lf, %lf",dummy, &x, &y, &z);
		tmp_pnt = (tmp_point *)malloc(sizeof(tmp_point));
		point_array[num_tmp_points] = tmp_pnt;
		tmp_pnt->coord[X] = x;
		tmp_pnt->coord[Y] = y;
		tmp_pnt->coord[Z] = z;
		tmp_pnt->index = num_tmp_points;
		num_tmp_points++;

		fgets(line,100,in);
		sscanf(line,"%s, %lf, %lf, %lf",dummy, &x, &y, &z);
		tmp_pnt = (tmp_point *)malloc(sizeof(tmp_point));
		point_array[num_tmp_points] = tmp_pnt;
		tmp_pnt->coord[X] = x;
		tmp_pnt->coord[Y] = y;
		tmp_pnt->coord[Z] = z;
		tmp_pnt->index = num_tmp_points;
		num_tmp_points++;
		num_triangles++;
	}
	fclose(in);
	if(num_triangles > 0)
		printf("完了しました\n");
	else
		printf("読込に失敗しました\n");
	return(num_triangles > 0);
}

//バイナリー形式のSTLファイルの読み込み
//読み込んだ座標は一時的にpoint_arrayに格納される
bool readBinarySTLFile(const char* STL_file)
{
	char line[85];
	FILE *in;
	float *coord;
	tmp_point *tmp_pnt;

	//STLファイルのオープン
	in = fopen(STL_file,"rb");
	if(in == NULL)
		return false;

	//最初の84バイトを読み飛ばす
	if(fread(line,1,84,in) != 84){
		return false;
	}
	printf("ファイルを読み込みます\n");
	while(fread(line,1,50,in) == 50){
		coord=(float *)line;

		//連続する3頂点の読み込み
		tmp_pnt = (tmp_point *)malloc(sizeof(tmp_point));
		point_array[num_tmp_points] = tmp_pnt;
		tmp_pnt->coord[X] = coord[3];
		tmp_pnt->coord[Y] = coord[4];
		tmp_pnt->coord[Z] = coord[5];
		tmp_pnt->index = num_tmp_points;
		num_tmp_points++;

		tmp_pnt = (tmp_point *)malloc(sizeof(tmp_point));
		point_array[num_tmp_points] = tmp_pnt;
		tmp_pnt->coord[X] = coord[6];
		tmp_pnt->coord[Y] = coord[7];
		tmp_pnt->coord[Z]=coord[8];
		tmp_pnt->index = num_tmp_points;
		num_tmp_points++;

		tmp_pnt = (tmp_point *)malloc(sizeof(tmp_point));
		point_array[num_tmp_points] = tmp_pnt;
		tmp_pnt->coord[X] = coord[9];
		tmp_pnt->coord[Y] = coord[10];
		tmp_pnt->coord[Z] = coord[11];
		tmp_pnt->index = num_tmp_points;
		num_tmp_points++;
		num_triangles++;
	}
	fclose(in);
	if(num_triangles > 0)
		printf("完了しました\n");
	else
		printf("読み込みに失敗しました\n");
	return (num_triangles > 0);
}

//点の比較関数
int compare(tmp_point *point0,tmp_point *point1)
 //tmp_point *point0,*point1は比較対象の点
{
	//X座標値で比較
	if(point0->coord[X] < (point1->coord[X]-(EPS)))
		return -1;
	else if(point0->coord[X] > (point1->coord[X]+(EPS)))
		return 1;

	//Y座標値で比較
	else{
		if(point0->coord[Y] < (point1->coord[Y]-(EPS)))
		return -1;
		else if(point0->coord[Y] > (point1->coord[Y]+(EPS)))
		return 1;

	//Z座標値で比較
		else{
		if(point0->coord[Z] < (point1->coord[Z]-(EPS)))
		return -1;
		else if(point0->coord[Z] > (point1->coord[Z]+(EPS)))
		return 1;
		else
		return 0; //2点を同一とみなす
		}
	}
}

//点列のクイックソート
void quickSort(int l,int r,tmp_point *point[])
 //int l,rはソート範囲の左側と右側のインデックス
 //tmp_point *point[]はソート対象の点列
{
	int i,j;
	tmp_point *m_p, *swap;
	if(l<r){
		m_p = point[(l+r)/2];
		i = l-1;
		j = r+1;
		while(true){
			while(compare(point[++i],m_p) == -1){};
			while(compare(point[--j],m_p) == 1){};
			if(i >= j)break;
			swap = point[i];
			point[i] = point[j];
			point[j] = swap;
		}
		quickSort(l,i-1,point);
		quickSort(j+1,r,point);
	}
}

bool loadSTLFile(const char* STL_file)
{
	unsigned int i, j, start,end;
	unsigned int tri_index, ver_index;
	double ref_pnt[3]={LARGE, LARGE, LARGE};
	tmp_edge *ed, *new_ed, *next_ed;

	//アスキー型ファイルの読み込み、一時的にpoint_arrayに格納
	if(readASCIISTLFile(STL_file))
		printf("Triangles: %d ",num_triangles);

	//バイナリー形式ファイルの読み込み、一時的にpoint_arrayに格納
	else if(readBinarySTLFile(STL_file))
		printf("Triangles: %d ",num_triangles);

	else{
		printf("ファイルが開けません\n");
		return(false);
	}

	//得られた点群をソートし同じ座標の点を一つにまとめる
	quickSort(0, num_tmp_points-1 ,point_array);

	//ソート済みの点列を先頭からスキャン
	num_points=0;
	for(i = 0; i <= num_tmp_points; i++){
		//最後の点が見つかる、もしくは参照点と異なる点がみつかる
		if((i == num_tmp_points)
			||(fabs(ref_pnt[X] - (point_array[i]->coord[X])) > (EPS))
			||(fabs(ref_pnt[Y] - (point_array[i]->coord[Y])) > (EPS))
			||(fabs(ref_pnt[Z] - (point_array[i]->coord[Z])) > (EPS))){

			//同一の点を同じ点として登録
			end=i;
			if(i > 0){
				point[num_points][X] = ref_pnt[X];
				point[num_points][Y] = ref_pnt[Y];
				point[num_points][Z] = ref_pnt[Z];
				for(j = start; j < end; j++){
					//その点を含むポリゴンが何番目のポリゴンか計算
					tri_index=point_array[j]->index/3;
					//点がそのポリゴンにおけるいくつめの頂点は計算
					ver_index=point_array[j]->index%3;
					triangle[tri_index][ver_index] = num_points;
				}
				num_points++;
			}

			//参照点の更新
			if(end < num_tmp_points){
				ref_pnt[X] = point_array[end]->coord[X];
				ref_pnt[Y] = point_array[end]->coord[Y];
				ref_pnt[Z] = point_array[end]->coord[Z];
				start = end;
			}
		}
	}

	for(i = 0; i < num_tmp_points; i++)
		free(point_array[i]);

//辺の登録
	num_edges = 0;
	//各点に接続する辺の記録場所の初期化
	connecting_edge = (tmp_edge **)malloc(num_points * sizeof(tmp_edge));
	for(i = 0; i <num_points; i++)
		connecting_edge[i] = NULL;

	//各三角形の周辺の辺と登録済みの辺を比較
	//i番目のポリゴンデータのj番目の頂点
	for(i = 0; i < num_triangles; i++){
		for(j = 0; j < 3; j++){
			ed = connecting_edge[triangle[i][j]];
			
			while(ed != NULL){
				if(((ed->start == triangle[i][j])
					&&(ed->end == triangle[i][(j+1)%3]))
				 ||((ed->start == triangle[i][(j+1)%3])
				    &&(ed->end == triangle[i][j])))
					goto try_next;
				ed = ed->next;
			}

			//未登録の辺を見つけて登録
			edge[num_edges][0] = triangle[i][j];
			edge[num_edges][1] = triangle[i][(j+1)%3];

			//辺を三角形に登録
			triangle_edge[i][j] = num_edges;
			num_edges++;

			//同時に点にも登録
			new_ed = (tmp_edge *)malloc(sizeof(tmp_edge));
			new_ed->start = triangle[i][j];
			new_ed->end = triangle[i][(j+1)%3];
			new_ed->next = connecting_edge[triangle[i][j]];
			connecting_edge[triangle[i][j]] = new_ed;
			new_ed=(tmp_edge *)malloc(sizeof(tmp_edge));
			new_ed->start = triangle[i][(j+1)%3];
			new_ed->end = triangle[i][j];
			new_ed->next = connecting_edge[triangle[i][(j+1)%3]];
			connecting_edge[triangle[i][(j+1)%3]] = new_ed;
try_next:;
		}
	}

	for(i = 0; i < num_points; i++){
		ed = connecting_edge[i];
		while(ed != NULL){
			next_ed = ed->next;
			free(ed);
			ed = next_ed;
		}
	}
	free(connecting_edge);
	printf("Edges:%d ",num_edges);
	printf("Points:%d \n",num_points);
	return(true);
	}
Display.cpp

コード:

#include<iostream>
#include<tchar.h>
#include<gl/glut.h>
#include<math.h>

#include"abc.h"


//視点位置に関する変数
double eye[3];
double center[3]={0.0, 0.0, 0.0};
double up[3];

//生投影の座標系
double x_axis[3], y_axis[3], z_axis[3];

//ウィンドウ上の座標とモデル座標の変換に用いるデータ
double corner[3];
double pixel_size, ref_far, ref_near;

//照明
GLfloat light_pos[4];

//点
extern double point[MAX_NUM_POINTS][3];
extern unsigned int num_points;

//三角形ポリゴン
extern unsigned int triangle[MAX_NUM_TRIANGLES][3];
extern unsigned int num_triangles;

//基本的な図形計算
extern double dot(double vec0[], double vec1[]);
extern void cross(double vec0[], double vec1[], double vec2[]);
extern void normVec(double vec[]);
extern void normal(double p0[], double p1[], double p2[], double normal[]);

//投影処理用の行列定義
void defineViewMatrix(double phi, double theta, unsigned int width,
	                  unsigned int height,double margin)
//double phi,theta 方位角と仰角
//unsigned int width,height ウィンドウサイズ
//double margin 表示の余裕
{
	unsigned int i,j;
	double c, s, xy_dist;
	double vec[3];
	double left, right, bottom, top, farVal, nearVal;
	double dx, dy, d_aspect, w_aspect, d;

	//視点の設定
	eye[Z] = sin(theta * PI / 180.0);
	xy_dist = cos(theta * PI / 180.0);
	c = cos(phi * PI / 180.0);
	s = sin(phi * PI / 180.0);
	eye[X] = xy_dist * c;
	eye[Y] = xy_dist * s;
	up[X] = -c * eye[Z];
	up[Y] = -s * eye[Z];
	up[Z] = s * eye[Y] + c * eye[X];
	normVec(up);

	//視点を原点とする座標系の定義
	for(i = 0; i < 3; i++)
		z_axis[i] = eye[i] - center[i];
	normVec(z_axis);
	cross(up,z_axis,x_axis);
	normVec(z_axis);
	cross(up,z_axis,x_axis);
	normVec(x_axis);
	cross(z_axis,x_axis,y_axis);

	//left,right,bottom,top,nearVal,farValの決定
	left = bottom = farVal = 10000.0;
	right = top = nearVal = -10000.0;
	
	for(i = 0; i < num_points; i++){
		for(j = 0; j < 3; j++)
			vec[j] = point[i][j] - eye[j];
		
		if(dot(x_axis,vec) < left)
			left = dot(x_axis,vec);
		
		if(dot(x_axis,vec) > right)
			right = dot(x_axis,vec);
		
		if(dot(y_axis,vec) < bottom)
			bottom = dot(x_axis,vec);
		
		if(dot(y_axis,vec) < top)
			top = dot(y_axis,vec);
		
		if(dot(z_axis,vec) < farVal)
			farVal = dot(z_axis,vec);
		
		if(dot(z_axis,vec) > nearVal)
			nearVal = dot(z_axis,vec);
	}

	//図形の周辺に5%ほど余裕を与える
	margin += (right-left)*0.05;
	left -= margin;
	right += margin;
	bottom -= margin;
	top += margin;
	farVal -= margin;
	nearVal += margin;
	ref_far = farVal;
	ref_near = nearVal;

	//表示範囲のアスペクト比とウィンドウのアスペクト比の比較
	dx = right - left;
	dy = top - bottom;
	d_aspect = dy/dx;
	w_aspect = (double)height/(double)width;

	//各ピクセルの実サイズ
	pixel_size = (right-left)/width;

	//視体積の左下隅奥の座標を記録
	for(i = 0;i < 3;i++){
		corner[i] = eye[i] + bottom * y_axis[i] + left * x_axis[i]
	    + farVal * z_axis[i];
	}

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(left, right, bottom, top, -nearVal, -farVal);
	glViewport(0, 0, width, height);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(eye[X], eye[Y], eye[Z], center[X], center[Y], center[Z],
		up[X], up[Y], up[Z]);
}

//モデル表示関数
void displayModel(void)
{
	unsigned int i;
	double nrml_vec[3];
	

	//光源の設定
	light_pos[0] = (float)eye[X];
	light_pos[1] = (float)eye[Y];
	light_pos[2] = (float)eye[Z];
	light_pos[3] = 0.0f;
	glLightfv(GL_LIGHT0, GL_POSITION,light_pos);


	//描写処理
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glBegin(GL_TRIANGLES);
	for(i = 0; i < num_triangles; i++){
		normal(point[triangle[i][0]],point[triangle[i][1]],
			point[triangle[i][2]], nrml_vec);
		if(dot(nrml_vec, z_axis) > 0.0)
			glNormal3dv(nrml_vec);
		else
			glNormal3d(-nrml_vec[X], -nrml_vec[Y], -nrml_vec[Z]);
		glVertex3dv(point[triangle[i][0]]);
		glVertex3dv(point[triangle[i][1]]);
		glVertex3dv(point[triangle[i][2]]);
	}
	glEnd();
	glFlush();
}


//ピクセルの横方向と縦方向の番地(h,v)とデプス値dから対応するモデル座標を得る
void pixelCoordToModelCoord(int h,int v,float d,double point[])
{
	unsigned int i;

	//cornerには左下隅奥に対応するモデル座標が格納
	//デプス値はnearが0.0,farが1.0に対応するように正規化してある
	for(i = 0; i < 3; i++){
		point[i] = corner[i] + pixel_size * h * x_axis[i]
		+ pixel_size * v * y_axis[i] + (ref_near - ref_far) * (1.0-d) * z_axis[i];
	}
}
Offset.cpp

コード:

#include<gl/glut.h>
#include<iostream>
#include<tchar.h>
#include<math.h>
#include"abc.h"


//STLデータのファイル名
#define STL_FILE "/*stlファイル名*/"

//視点位置
#define PHI 30.0
#define THETA 90.0
double phi=PHI;
double theta=THETA;

//ウィンドウサイズ
#define INIT_X_POS 128
#define INIT_Y_POS 128
#define INIT_WIDTH 600
#define INIT_HEIGHT 600

//画面の縦横サイズ
unsigned int window_height, window_width;

//球や円筒形を多面化近似する際の分割数
#define DIV 64

//表示モード
#define DISPLAY_MODEL 0
unsigned int display_mode = DISPLAY_MODEL;

//オフセット半径
double radius=10.0;

//マウス処理
int mouse_old_x,mouse_old_y;
bool motion_p;

//点
extern double point[MAX_NUM_POINTS][3];
extern unsigned int num_points;

//辺
extern unsigned int edge[MAX_NUM_EDGES][2];
extern unsigned int num_edged;

//三角形ポリゴンの頂点
extern unsigned int triangle[MAX_NUM_TRIANGLES][3];
extern unsigned int num_triangles;

//STLファイルの読み込み
extern bool loadSTLFile(const char* STL_file);

//基本的な図形計算
extern void cross(double vec0[], double vec1[], double vec2[]);
extern void normVec(double vec[]);
extern void normal(double p0[], double p1[], double p2[], double normal[]);

//基本的な表示関数
extern void defineViewMatrix(double phi, double theta, unsigned int width,
	unsigned int height, double margin);
extern void displayModel(void);
extern void pixelCoordToModelCoord(int h, int v, float d, double point[]);

//表示
void display (void)
{
	switch(display_mode){
	case DISPLAY_MODEL:
		defineViewMatrix(phi, theta, window_width, window_height, radius);
		displayModel();
		break;
	default:
		break;
	}
}

//キー処理
void keyboard(unsigned char key, int x, int y)
{
	switch(key){
	case'q':
	case'Q':
	case'\033':
		exit(0);

	//STLファイルの読み込み
	case'r':
	case'R':
		if(!loadSTLFile(STL_FILE))
			break;
		display_mode = DISPLAY_MODEL;
		glutPostRedisplay();
		break;

	//STLモデル表示
	case'd':
	case'D':
		display_mode = DISPLAY_MODEL;
		glutPostRedisplay();
		break;
	default:
		break;
	}
}

//OpenGL関係の初期設定
void initGL(void)
{
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_DEPTH_TEST);
	glClearDepth(1.0);
	glDepthFunc(GL_LESS);
	glEnable(GL_LIGHT0);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
}

void resize(int width,int height)
{
	printf("Size %d × %d\n",width,height);
	window_width = width;
	window_height = height;
}

//マウスボタン処理
void mouse_button(int button,int state,int x,int y)
{
	if((state == GLUT_DOWN) && (button == GLUT_LEFT_BUTTON))
		motion_p = true;
	else if (state==GLUT_UP)
		motion_p = false;
	mouse_old_x = x;
	mouse_old_y = y;
}

//マウスの移動処理
void mouse_motion(int x,int y)
{
	int dx, dy;
	dx = x-mouse_old_x;
	dy = y-mouse_old_y;

	if(motion_p){
		phi -= dx * 0.2;
		theta += dy * 0.2;
	}
	mouse_old_x = x;
	mouse_old_y = y;

	glutPostRedisplay();

}


//メイン関数
int main(int argc,char *argv[])
{
	printf("'R'でファイル読み込み、'D'でモデル表示、'Q'で終了\n");
	glutInitWindowPosition(128,128);
	glutInitWindowSize(800,800);
	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH);
	glutCreateWindow("Preview");
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutReshapeFunc(resize);
	glutMouseFunc(mouse_button);
	glutMotionFunc(mouse_motion);
	initGL();
	glutMainLoop();
	return 0;
}

“C言語何でも質問掲示板” へ戻る