#pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
#include <stdio.h>
#include <GL/freeglut/freeglut.h>
#include <GL/glext.h>
#include "PNG.h"
/*
** トラックボール処理用関数の宣言
*/
/*
** 簡易トラックボール処理
*/
extern void trackballInit(void);
extern void trackballRegion(int w, int h);
extern void trackballStart(int x, int y);
extern void trackballMotion(int x, int y);
extern void trackballStop(int x, int y);
extern const double *trackballRotation(void);
/*
** 簡易トラックボール処理
*/
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
/* ドラッグ開始位置 */
static int cx, cy;
/* マウスの絶対位置→ウィンドウ内での相対位置の換算係数 */
static double sx, sy;
/* マウスの相対位置→回転角の換算係数 */
#define SCALE (2.0 * M_PI)
/* 回転の初期値 (クォータニオン) */
static double cq[4] = { 1.0, 0.0, 0.0, 0.0 };
/* ドラッグ中の回転 (クォータニオン) */
static double tq[4];
/* ドラッグ中か否か */
static int drag = 0;
/*
** r <- p x q
*/
static void qmul(double r[], const double p[], const double q[])
{
r[0] = p[0] * q[0] - p[1] * q[1] - p[2] * q[2] - p[3] * q[3];
r[1] = p[0] * q[1] + p[1] * q[0] + p[2] * q[3] - p[3] * q[2];
r[2] = p[0] * q[2] - p[1] * q[3] + p[2] * q[0] + p[3] * q[1];
r[3] = p[0] * q[3] + p[1] * q[2] - p[2] * q[1] + p[3] * q[0];
}
/*
** 回転変換行列 r <- クォータニオン q
*/
static void qrot(double r[], double q[])
{
double x2 = q[1] * q[1] * 2.0;
double y2 = q[2] * q[2] * 2.0;
double z2 = q[3] * q[3] * 2.0;
double xy = q[1] * q[2] * 2.0;
double yz = q[2] * q[3] * 2.0;
double zx = q[3] * q[1] * 2.0;
double xw = q[1] * q[0] * 2.0;
double yw = q[2] * q[0] * 2.0;
double zw = q[3] * q[0] * 2.0;
r[ 0] = 1.0 - y2 - z2;
r[ 1] = xy + zw;
r[ 2] = zx - yw;
r[ 4] = xy - zw;
r[ 5] = 1.0 - z2 - x2;
r[ 6] = yz + xw;
r[ 8] = zx + yw;
r[ 9] = yz - xw;
r[10] = 1.0 - x2 - y2;
r[ 3] = r[ 7] = r[11] = r[12] = r[13] = r[14] = 0.0;
r[15] = 1.0;
}
/*
** トラックボール処理の初期化
** プログラムの初期化処理のところで実行する
*/
void trackballInit(void)
{
/* 単位クォーターニオン */
cq[0] = 1.0;
cq[1] = 0.0;
cq[2] = 0.0;
cq[3] = 0.0;
/* ドラッグ中ではない */
drag = 0;
}
/*
** トラックボールする領域
** Reshape コールバック (resize) の中で実行する
*/
void trackballRegion(int w, int h)
{
/* マウスポインタ位置のウィンドウ内の相対的位置への換算用 */
sx = 1.0 / (double)w;
sy = 1.0 / (double)h;
}
/*
** ドラッグ開始
** マウスボタンを押したときに実行する
*/
void trackballStart(int x, int y)
{
/* ドラッグ開始 */
drag = 1;
/* ドラッグ開始点を記録 */
cx = x;
cy = y;
}
/*
** ドラッグ中
** マウスのドラッグ中に実行する
*/
void trackballMotion(int x, int y)
{
if (drag) {
double dx, dy, a;
/* マウスポインタの位置のドラッグ開始位置からの変位 */
dx = (x - cx) * sx;
dy = (y - cy) * sy;
/* マウスポインタの位置のドラッグ開始位置からの距離 */
a = sqrt(dx * dx + dy * dy);
if (a != 0.0) {
double ar = a * SCALE * 0.5;
double as = sin(ar) / a;
double dq[4] = { cos(ar), dy * as, dx * as, 0.0 };
/* クォータニオンを掛けて回転を合成 */
qmul(tq, dq, cq);
}
}
}
/*
** 停止
** マウスボタンを離したときに実行する
*/
void trackballStop(int x, int y)
{
/* ドラッグ終了点における回転を求める */
trackballMotion(x, y);
/* 回転の保存 */
cq[0] = tq[0];
cq[1] = tq[1];
cq[2] = tq[2];
cq[3] = tq[3];
/* ドラッグ終了 */
drag = 0;
}
/*
** 回転の変換行列を戻す
** 戻り値を glMultMatrixd() などで使用してオブジェクトを回転する
*/
const double *trackballRotation(void)
{
/* 回転の変換行列 */
static double rt[16];
/* クォータニオンから回転の変換行列を求める */
qrot(rt, tq);
return rt;
}
/*
** 形状データ
*/
typedef float vec[3];
typedef float tex[2];
typedef unsigned int idx[3];
class Obj {
int nv, nf;
vec *vert, *norm, *fnorm;
tex *texc;
idx *face;
void copy(const Obj &);
public:
Obj(void);
Obj(const Obj &);
virtual ~Obj();
Obj &operator=(const Obj &);
int load(char *);
void calcTexCoord(const GLfloat *, const GLfloat *);
void draw(void);
void line_draw(void);
};
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cmath>
/*
** オブジェクトのコピー
*/
void Obj::copy(const Obj &o)
{
nv = o.nv;
nf = o.nf;
try {
if (nv > 0) {
vert = new vec[nv];
norm = new vec[nv];
texc = new tex[nv];
memcpy(vert, o.vert, sizeof(vec) * nv);
memcpy(norm, o.norm, sizeof(vec) * nv);
memcpy(texc, o.texc, sizeof(tex) * nv);
}
else {
vert = norm = 0;
texc = 0;
}
if (nf > 0) {
fnorm = new vec[nf];
face = new idx[nf];
memcpy(fnorm, o.fnorm, sizeof(vec) * nf);
memcpy(face, o.face, sizeof(idx) * nf);
}
else {
fnorm = 0;
face = 0;
}
}
catch (std::bad_alloc e) {
std::cerr << "メモリが足りません"<< std::endl;
abort();
}
}
/*
** コンストラクタ
*/
Obj::Obj(void)
{
nv = nf = 0;
vert = norm = fnorm = 0;
texc = 0;
face = 0;
}
/*
** コピーコンストラクタ
*/
Obj::Obj(const Obj &o)
{
copy(o);
}
/*
** デストラクタ
*/
Obj::~Obj()
{
if (vert) delete[] vert;
if (norm) delete[] norm;
if (texc) delete[] texc;
if (fnorm) delete[] fnorm;
if (face) delete[] face;
}
/*
** 代入演算子
*/
Obj &Obj::operator=(const Obj &o)
{
if (this != &o) {
this->~Obj();
copy(o);
}
return *this;
}
/*
** ファイルの読み込み
*/
int Obj::load(char *name)
{
std::ifstream file(name, std::ios::in | std::ios::binary);
char buf[1024];
int i, v, f;
if (!file) {
std::cerr << name << " が開けません" << std::endl;
return 1;
}
/* データの数を調べる */
v = f = 0;
while (file.getline(buf, sizeof buf)) {
if (buf[0] == 'v' && buf[1] == ' ') {
++v;
}
else if (buf[0] == 'f' && buf[1] == ' ') {
++f;
}
}
nv = v;
nf = f;
try {
vert = new vec[v];
norm = new vec[v];
texc = new tex[v];
fnorm = new vec[f];
face = new idx[f];
}
catch (std::bad_alloc e) {
std::cerr << "メモリが足りません" << std::endl;
abort();
}
/* ファイルの巻き戻し */
file.clear();
file.seekg(0L, std::ios::beg);
/* データの読み込み */
v = f = 0;
while (file.getline(buf, sizeof buf)) {
if (buf[0] == 'v' && buf[1] == ' ') {
sscanf(buf, "%*s %f %f %f", vert[v], vert[v] + 1, vert[v] + 2);
++v;
}
else if (buf[0] == 'f' && buf[1] == ' ') {
if (sscanf(buf + 2, "%d/%*d/%*d %d/%*d/%*d %d/%*d/%*d", face[f], face[f]
+ 1, face[f] + 2) != 3) {
if (sscanf(buf + 2, "%d//%*d %d//%*d %d//%*d", face[f], face[f] + 1,
face[f] + 2) != 3) {
sscanf(buf + 2, "%d %d %d", face[f], face[f] + 1, face[f] + 2);
}
}
--face[f][0];
--face[f][1];
--face[f][2];
++f;
}
}
/* 面法線ベクトルの算出 */
for (i = 0; i < f; ++i) {
float dx1 = vert[face[i][1]][0] - vert[face[i][0]][0];
float dy1 = vert[face[i][1]][1] - vert[face[i][0]][1];
float dz1 = vert[face[i][1]][2] - vert[face[i][0]][2];
float dx2 = vert[face[i][2]][0] - vert[face[i][0]][0];
float dy2 = vert[face[i][2]][1] - vert[face[i][0]][1];
float dz2 = vert[face[i][2]][2] - vert[face[i][0]][2];
fnorm[i][0] = dy1 * dz2 - dz1 * dy2;
fnorm[i][1] = dz1 * dx2 - dx1 * dz2;
fnorm[i][2] = dx1 * dy2 - dy1 * dx2;
}
/* 頂点の仮想法線ベクトルの算出 */
for (i = 0; i < v; ++i) {
norm[i][0] = norm[i][1] = norm[i][2] = 0.0;
}
for (i = 0; i < f; ++i) {
norm[face[i][0]][0] += fnorm[i][0];
norm[face[i][0]][1] += fnorm[i][1];
norm[face[i][0]][2] += fnorm[i][2];
norm[face[i][1]][0] += fnorm[i][0];
norm[face[i][1]][1] += fnorm[i][1];
norm[face[i][1]][2] += fnorm[i][2];
norm[face[i][2]][0] += fnorm[i][0];
norm[face[i][2]][1] += fnorm[i][1];
norm[face[i][2]][2] += fnorm[i][2];
}
/* 頂点の仮想法線ベクトルの正規化 */
for (i = 0; i < v; ++i) {
float a = sqrt(norm[i][0] * norm[i][0]
+ norm[i][1] * norm[i][1]
+ norm[i][2] * norm[i][2]);
if (a != 0.0) {
norm[i][0] /= a;
norm[i][1] /= a;
norm[i][2] /= a;
}
}
return 0;
}
/*
** テクスチャ座標の計算
*/
void Obj::calcTexCoord(const GLfloat *lpos, const GLfloat *epos)
{
GLfloat lp[3], lv[3], ep[3], ev[3];
/*
** 光源位置/ベクトルの算出
*/
if (lpos[3] != 0.0) {
/* 実座標を求める */
lp[0] = lpos[0] / lpos[3];
lp[1] = lpos[1] / lpos[3];
lp[2] = lpos[2] / lpos[3];
}
else {
/* 光源方向の単位ベクトルを求める */
lv[0] = lpos[0];
lv[1] = lpos[1];
lv[2] = lpos[2];
GLfloat a = sqrtf(lv[0] * lv[0] + lv[1] * lv[1] + lv[2] * lv[2]);
if (a != 0.0) {
lv[0] /= a;
lv[1] /= a;
lv[2] /= a;
}
}
/*
** 視点位置/ベクトルの算出
*/
if (epos[3] != 0.0) {
/* 実座標を求める */
ep[0] = epos[0] / epos[3];
ep[1] = epos[1] / epos[3];
ep[2] = epos[2] / epos[3];
}
else {
/* 視線方向の単位ベクトルを求める */
ev[0] = epos[0];
ev[1] = epos[1];
ev[2] = epos[2];
GLfloat a = sqrtf(ev[0] * ev[0] + ev[1] * ev[1] + ev[2] * ev[2]);
if (a != 0.0) {
ev[0] /= a;
ev[1] /= a;
ev[2] /= a;
}
}
/*
** テクスチャ座標の算出
*/
for (int i = 0; i < nv; ++i) {
if (lpos[3] != 0.0) {
/* 頂点ごとに光源方向の単位ベクトルを求める */
lv[0] = lp[0] - vert[i][0];
lv[1] = lp[1] - vert[i][1];
lv[2] = lp[2] - vert[i][2];
GLfloat a = sqrtf(lv[0] * lv[0] + lv[1] * lv[1] + lv[2] * lv[2]);
if (a != 0.0) {
lv[0] /= a;
lv[1] /= a;
lv[2] /= a;
}
}
if (epos[3] != 0.0) {
/* 頂点ごとに視線方向の単位ベクトルを求める */
ev[0] = ep[0] - vert[i][0];
ev[1] = ep[1] - vert[i][1];
ev[2] = ep[2] - vert[i][2];
GLfloat a = sqrtf(ev[0] * ev[0] + ev[1] * ev[1] + ev[2] * ev[2]);
if (a != 0.0) {
ev[0] /= a;
ev[1] /= a;
ev[2] /= a;
}
}
/* テクスチャの s 座標は明度 */
texc[i][0] = norm[i][0] * lv[0] + norm[i][1] * lv[1] + norm[i][2] *
lv[2];
/* テクスチャの t 座標はエッジ */
texc[i][1] = norm[i][0] * ev[0] + norm[i][1] * ev[1] + norm[i][2] *
ev[2];
}
}
/*
** 図形の表示
*/
void Obj::draw(void)
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glNormalPointer(GL_FLOAT, 0, norm);
glVertexPointer(3, GL_FLOAT, 0, vert);
glTexCoordPointer(2, GL_FLOAT, 0, texc);
glEnable(GL_TEXTURE_2D);
glDrawElements(GL_TRIANGLES, nf * 3, GL_UNSIGNED_INT, face);
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
void Obj::line_draw(void)
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glNormalPointer(GL_FLOAT, 0, norm);
glVertexPointer(3, GL_FLOAT, 0, vert);
glDisable(GL_TEXTURE_2D);
glDrawElements(GL_TRIANGLES, nf * 3, GL_UNSIGNED_INT, face);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
Obj *data;
/*
** 視点の距離
*/
static double offset = -200.0;
/*
** 光源
*/
static const GLfloat lightpos[] = { 0.0, 0.0, 1.0, 0.0 }; /* 位置 */
GLuint texName;
TEXTURE *texture;
#define TEX_WIDTH 256
#define TEX_HEIGHT 256
void TexCreate(char* FileName){
glGenTextures(1, &texName);
texture = new TEXTURE(FileName);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//テクスチャオブジェクトの作成
glBindTexture(GL_TEXTURE_2D, texName);
//テクスチャの指定
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,texture->Width,texture->Height,0,GL_RGBA,GL_UNSIGNED_BYTE,texture->image);
//テクスチャの繰り返し方法の指定
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);//GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);//GL_CLAMP);
//テクスチャを拡大・縮小する方法の指定
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, 0);
}
/*
** 初期化
*/
static void init(void)
{
TexCreate("toon.png");
/* 初期設定 */
glClearColor(0.3, 0.3, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
/* 光源の初期設定 */
glDisable(GL_LIGHTING);
/* 形状データオブジェクトの作成 */
data = new Obj;
/* 形状データの読み込み */
data->load("bunny.obj");
}
/*
** シーンの描画
*/
void scene(void)
{
GLfloat eyepos[] = { 0.0f, 0.0f, (GLfloat)-offset, 1.0f };
GLfloat lpos[4], epos[4];
const double *rt;
int i, j;
/*
** 回転の変換行列を取り出す
*/
rt = trackballRotation();
/*
** 光源を物体の回転と逆方向に回転する(回転行列の転置行列をかける)
*/
for (i = 0; i < 4; ++i) {
lpos[i] = 0.0;
for (j = 0; j < 4; ++j) {
lpos[i] += lightpos[j] * rt[i * 4 + j];
}
}
/*
** 視点を物体の回転と逆方向に回転する(回転行列の転置行列をかける)
*/
for (i = 0; i < 4; ++i) {
epos[i] = 0.0;
for (j = 0; j < 4; ++j) {
epos[i] += eyepos[j] * rt[i * 4 + j];
}
}
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texName);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
data->calcTexCoord(lpos, epos);
data->draw();
float size =1.02;
GLfloat black[] = { 0.0, 0.0, 0.0, 1.0 };
glMaterialfv(GL_FRONT, GL_DIFFUSE, black);
glScalef(size,size,size);
glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glColor3f(0.0f,0.0f,0.0f);
glCullFace(GL_FRONT);
data->line_draw();
}
/****************************
** GLUT のコールバック関数 **
****************************/
static void display(void)
{
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 視点の移動(物体の方を奥に移動)*/
glTranslated(0.0, 0.0, offset);
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* トラックボール処理による回転 */
glMultMatrixd(trackballRotation());
/* 画面クリア */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* シーンの描画 */
scene();
/* ダブルバッファリング */
glutSwapBuffers();
}
static void resize(int w, int h)
{
/* トラックボールする範囲 */
trackballRegion(w, h);
/* ウィンドウ全体をビューポートにする */
glViewport(0, 0, w, h);
/* 透視変換行列の指定 */
glMatrixMode(GL_PROJECTION);
/* 透視変換行列の初期化 */
glLoadIdentity();
gluPerspective(60.0, (double)w / (double)h, 1.0, 500.0);
}
static void idle(void)
{
/* 画面の描き替え */
glutPostRedisplay();
}
static void mouse(int button, int state, int x, int y)
{
switch (button) {
case GLUT_LEFT_BUTTON:
switch (state) {
case GLUT_DOWN:
/* トラックボール開始 */
trackballStart(x, y);
glutIdleFunc(idle);
break;
case GLUT_UP:
/* トラックボール停止 */
glutIdleFunc(0);
trackballStop(x, y);
break;
default:
break;
}
break;
default:
break;
}
}
static void motion(int x, int y)
{
/* トラックボール移動 */
trackballMotion(x, y);
}
static void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case '+':
offset += 10.0;
glutPostRedisplay();
break;
case '-':
offset -= 10.0;
glutPostRedisplay();
break;
case 'q':
case 'Q':
case '\033':
/* ESC か q か Q をタイプしたら終了 */
exit(0);
default:
break;
}
}
/*
** メインプログラム
*/
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(640, 480);
glutCreateWindow("トゥーンレンダリング");
glutDisplayFunc(display);
glutReshapeFunc(resize);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutKeyboardFunc(keyboard);
init();
glutMainLoop();
return 0;
}
|