rev |
line source |
nuclear@78
|
1 #include <stdio.h>
|
nuclear@76
|
2 #include <QtOpenGL/QtOpenGL>
|
nuclear@79
|
3 #include "opengl.h"
|
nuclear@76
|
4 #include <vmath/vmath.h>
|
nuclear@73
|
5 #include "goatview.h"
|
nuclear@74
|
6 #include "goat3d.h"
|
nuclear@74
|
7
|
nuclear@78
|
8 static void draw_node(goat3d_node *node);
|
nuclear@78
|
9
|
nuclear@74
|
10 goat3d *scene;
|
nuclear@75
|
11 QSettings *settings;
|
nuclear@75
|
12
|
nuclear@75
|
13 static long anim_time;
|
nuclear@75
|
14 static float cam_theta, cam_phi, cam_dist = 8;
|
nuclear@76
|
15 static float fov = 60.0;
|
nuclear@75
|
16
|
nuclear@76
|
17 bool load_scene(const char *fname)
|
nuclear@76
|
18 {
|
nuclear@76
|
19 if(scene) {
|
nuclear@76
|
20 goat3d_free(scene);
|
nuclear@76
|
21 }
|
nuclear@76
|
22 if(!(scene = goat3d_create()) || goat3d_load(scene, fname) == -1) {
|
nuclear@76
|
23 return false;
|
nuclear@76
|
24 }
|
nuclear@76
|
25
|
nuclear@76
|
26 float bmin[3], bmax[3];
|
nuclear@76
|
27 goat3d_get_bounds(scene, bmin, bmax);
|
nuclear@76
|
28 float bsize = (Vector3(bmax[0], bmax[1], bmax[2]) - Vector3(bmin[0], bmin[1], bmin[2])).length();
|
nuclear@76
|
29 cam_dist = bsize / tan(DEG_TO_RAD(fov) / 2.0) + bsize;
|
nuclear@76
|
30
|
nuclear@76
|
31 printf("bounds size: %f, cam_dist: %f\n", bsize, cam_dist);
|
nuclear@76
|
32 return true;
|
nuclear@76
|
33 }
|
nuclear@73
|
34
|
nuclear@73
|
35 GoatView::GoatView()
|
nuclear@73
|
36 {
|
nuclear@73
|
37 make_menu();
|
nuclear@73
|
38 make_dock();
|
nuclear@73
|
39 make_center();
|
nuclear@73
|
40
|
nuclear@73
|
41 statusBar();
|
nuclear@73
|
42
|
nuclear@73
|
43 setWindowTitle("GoatView");
|
nuclear@75
|
44 resize(settings->value("main/size", QSize(1024, 768)).toSize());
|
nuclear@75
|
45 move(settings->value("main/pos", QPoint(100, 100)).toPoint());
|
nuclear@73
|
46 }
|
nuclear@73
|
47
|
nuclear@73
|
48 GoatView::~GoatView()
|
nuclear@73
|
49 {
|
nuclear@73
|
50 }
|
nuclear@73
|
51
|
nuclear@75
|
52 void GoatView::closeEvent(QCloseEvent *ev)
|
nuclear@75
|
53 {
|
nuclear@75
|
54 settings->setValue("main/size", size());
|
nuclear@75
|
55 settings->setValue("main/pos", pos());
|
nuclear@75
|
56 }
|
nuclear@75
|
57
|
nuclear@73
|
58 bool GoatView::make_menu()
|
nuclear@73
|
59 {
|
nuclear@73
|
60 QMenu *menu_file = menuBar()->addMenu("&File");
|
nuclear@73
|
61
|
nuclear@73
|
62 QAction *act_open_sce = new QAction("&Open Scene", this);
|
nuclear@73
|
63 act_open_sce->setShortcuts(QKeySequence::Open);
|
nuclear@73
|
64 connect(act_open_sce, &QAction::triggered, this, &GoatView::open_scene);
|
nuclear@73
|
65 menu_file->addAction(act_open_sce);
|
nuclear@73
|
66
|
nuclear@73
|
67 QAction *act_open_anm = new QAction("Open &Animation", this);
|
nuclear@73
|
68 connect(act_open_anm, &QAction::triggered, this, &GoatView::open_anim);
|
nuclear@73
|
69 menu_file->addAction(act_open_anm);
|
nuclear@73
|
70
|
nuclear@73
|
71 QAction *act_quit = new QAction("&Quit", this);
|
nuclear@73
|
72 act_quit->setShortcuts(QKeySequence::Quit);
|
nuclear@73
|
73 connect(act_quit, &QAction::triggered, [&](){qApp->quit();});
|
nuclear@73
|
74 menu_file->addAction(act_quit);
|
nuclear@73
|
75 return true;
|
nuclear@73
|
76 }
|
nuclear@73
|
77
|
nuclear@73
|
78 bool GoatView::make_dock()
|
nuclear@73
|
79 {
|
nuclear@73
|
80 // ---- side-dock ----
|
nuclear@73
|
81 QWidget *dock_cont = new QWidget;
|
nuclear@73
|
82 QVBoxLayout *dock_vbox = new QVBoxLayout;
|
nuclear@73
|
83 dock_cont->setLayout(dock_vbox);
|
nuclear@73
|
84
|
nuclear@73
|
85 QPushButton *bn_quit = new QPushButton("quit");
|
nuclear@73
|
86 dock_vbox->addWidget(bn_quit);
|
nuclear@73
|
87 connect(bn_quit, &QPushButton::clicked, [&](){qApp->quit();});
|
nuclear@73
|
88
|
nuclear@73
|
89 QDockWidget *dock = new QDockWidget("Scene graph", this);
|
nuclear@73
|
90 dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
nuclear@73
|
91 dock->setWidget(dock_cont);
|
nuclear@73
|
92 addDockWidget(Qt::LeftDockWidgetArea, dock);
|
nuclear@73
|
93
|
nuclear@73
|
94 // ---- bottom dock ----
|
nuclear@73
|
95 dock_cont = new QWidget;
|
nuclear@73
|
96 QHBoxLayout *dock_hbox = new QHBoxLayout;
|
nuclear@73
|
97 dock_cont->setLayout(dock_hbox);
|
nuclear@73
|
98
|
nuclear@73
|
99 QSlider *slider_time = new QSlider(Qt::Orientation::Horizontal);
|
nuclear@73
|
100 slider_time->setDisabled(true);
|
nuclear@73
|
101 dock_hbox->addWidget(slider_time);
|
nuclear@73
|
102
|
nuclear@73
|
103 dock = new QDockWidget("Animation", this);
|
nuclear@73
|
104 dock->setAllowedAreas(Qt::BottomDockWidgetArea);
|
nuclear@73
|
105 dock->setWidget(dock_cont);
|
nuclear@73
|
106 addDockWidget(Qt::BottomDockWidgetArea, dock);
|
nuclear@73
|
107
|
nuclear@73
|
108 return true;
|
nuclear@73
|
109 }
|
nuclear@73
|
110
|
nuclear@73
|
111 bool GoatView::make_center()
|
nuclear@73
|
112 {
|
nuclear@73
|
113 GoatViewport *vport = new GoatViewport;
|
nuclear@73
|
114 setCentralWidget(vport);
|
nuclear@73
|
115 return true;
|
nuclear@73
|
116 }
|
nuclear@73
|
117
|
nuclear@73
|
118 void GoatView::open_scene()
|
nuclear@73
|
119 {
|
nuclear@74
|
120 std::string fname = QFileDialog::getOpenFileName(this, "Open scene file", "",
|
nuclear@74
|
121 "Goat3D Scene (*.goatsce);;All Files (*)").toStdString();
|
nuclear@74
|
122 if(fname.empty()) {
|
nuclear@74
|
123 statusBar()->showMessage("Abort: No file selected!");
|
nuclear@74
|
124 return;
|
nuclear@74
|
125 }
|
nuclear@74
|
126
|
nuclear@74
|
127 statusBar()->showMessage("opening scene file");
|
nuclear@76
|
128 if(!load_scene(fname.c_str())) {
|
nuclear@74
|
129 statusBar()->showMessage("failed to load scene file");
|
nuclear@74
|
130 }
|
nuclear@73
|
131 }
|
nuclear@73
|
132
|
nuclear@73
|
133 void GoatView::open_anim()
|
nuclear@73
|
134 {
|
nuclear@73
|
135 statusBar()->showMessage("opening animation...");
|
nuclear@73
|
136 }
|
nuclear@73
|
137
|
nuclear@73
|
138
|
nuclear@73
|
139 // ---- OpenGL viewport ----
|
nuclear@73
|
140 GoatViewport::GoatViewport()
|
nuclear@73
|
141 : QGLWidget(QGLFormat(QGL::DepthBuffer))
|
nuclear@73
|
142 {
|
nuclear@73
|
143 }
|
nuclear@73
|
144
|
nuclear@73
|
145 GoatViewport::~GoatViewport()
|
nuclear@73
|
146 {
|
nuclear@73
|
147 }
|
nuclear@73
|
148
|
nuclear@73
|
149 QSize GoatViewport::sizeHint() const
|
nuclear@73
|
150 {
|
nuclear@73
|
151 return QSize(800, 600);
|
nuclear@73
|
152 }
|
nuclear@73
|
153
|
nuclear@73
|
154 void GoatViewport::initializeGL()
|
nuclear@73
|
155 {
|
nuclear@79
|
156 init_opengl();
|
nuclear@79
|
157
|
nuclear@79
|
158 glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
nuclear@78
|
159
|
nuclear@78
|
160 glEnable(GL_DEPTH_TEST);
|
nuclear@78
|
161 glEnable(GL_CULL_FACE);
|
nuclear@73
|
162 }
|
nuclear@73
|
163
|
nuclear@73
|
164 void GoatViewport::resizeGL(int xsz, int ysz)
|
nuclear@73
|
165 {
|
nuclear@73
|
166 glViewport(0, 0, xsz, ysz);
|
nuclear@75
|
167 glMatrixMode(GL_PROJECTION);
|
nuclear@75
|
168 glLoadIdentity();
|
nuclear@75
|
169 gluPerspective(60.0, (float)xsz / (float)ysz, 0.5, 5000.0);
|
nuclear@73
|
170 }
|
nuclear@73
|
171
|
nuclear@73
|
172 void GoatViewport::paintGL()
|
nuclear@73
|
173 {
|
nuclear@75
|
174 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
nuclear@75
|
175
|
nuclear@75
|
176 glMatrixMode(GL_MODELVIEW);
|
nuclear@75
|
177 glLoadIdentity();
|
nuclear@75
|
178 glTranslatef(0, 0, -cam_dist);
|
nuclear@75
|
179 glRotatef(cam_phi, 1, 0, 0);
|
nuclear@75
|
180 glRotatef(cam_theta, 0, 1, 0);
|
nuclear@75
|
181
|
nuclear@75
|
182 if(scene) {
|
nuclear@75
|
183 int node_count = goat3d_get_node_count(scene);
|
nuclear@75
|
184 for(int i=0; i<node_count; i++) {
|
nuclear@75
|
185 goat3d_node *node = goat3d_get_node(scene, i);
|
nuclear@80
|
186 //if(!goat3d_get_node_parent(node)) {
|
nuclear@78
|
187 draw_node(node); // only draw root nodes, the rest will be drawn recursively
|
nuclear@80
|
188 //}
|
nuclear@75
|
189 }
|
nuclear@75
|
190 }
|
nuclear@73
|
191 }
|
nuclear@75
|
192
|
nuclear@75
|
193 static void draw_node(goat3d_node *node)
|
nuclear@75
|
194 {
|
nuclear@75
|
195 float xform[16];
|
nuclear@75
|
196 goat3d_get_node_matrix(node, xform, anim_time);
|
nuclear@78
|
197 for(int i=0; i<4; i++) {
|
nuclear@79
|
198 for(int j=0; j<i; j++) {
|
nuclear@79
|
199 float tmp = xform[i * 4 + j];
|
nuclear@79
|
200 xform[i * 4 + j] = xform[j * 4 + i];
|
nuclear@79
|
201 xform[j * 4 + i] = tmp;
|
nuclear@79
|
202 }
|
nuclear@78
|
203 }
|
nuclear@78
|
204
|
nuclear@78
|
205 glPushMatrix();
|
nuclear@80
|
206 glMultMatrixf(xform);
|
nuclear@75
|
207
|
nuclear@75
|
208 if(goat3d_get_node_type(node) == GOAT3D_NODE_MESH) {
|
nuclear@75
|
209 goat3d_mesh *mesh = (goat3d_mesh*)goat3d_get_node_object(node);
|
nuclear@75
|
210
|
nuclear@75
|
211 int num_faces = goat3d_get_mesh_face_count(mesh);
|
nuclear@75
|
212 int num_verts = goat3d_get_mesh_attrib_count(mesh, GOAT3D_MESH_ATTR_VERTEX);
|
nuclear@75
|
213
|
nuclear@75
|
214 glEnableClientState(GL_VERTEX_ARRAY);
|
nuclear@75
|
215 glVertexPointer(3, GL_FLOAT, 0, goat3d_get_mesh_attribs(mesh, GOAT3D_MESH_ATTR_VERTEX));
|
nuclear@75
|
216
|
nuclear@75
|
217 float *data;
|
nuclear@75
|
218 if((data = (float*)goat3d_get_mesh_attribs(mesh, GOAT3D_MESH_ATTR_NORMAL))) {
|
nuclear@75
|
219 glEnableClientState(GL_NORMAL_ARRAY);
|
nuclear@75
|
220 glNormalPointer(GL_FLOAT, 0, data);
|
nuclear@75
|
221 }
|
nuclear@75
|
222 if((data = (float*)goat3d_get_mesh_attribs(mesh, GOAT3D_MESH_ATTR_TEXCOORD))) {
|
nuclear@75
|
223 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
nuclear@75
|
224 glTexCoordPointer(2, GL_FLOAT, 0, data);
|
nuclear@75
|
225 }
|
nuclear@75
|
226
|
nuclear@75
|
227 int *indices;
|
nuclear@75
|
228 if((indices = goat3d_get_mesh_faces(mesh))) {
|
nuclear@75
|
229 glDrawElements(GL_TRIANGLES, num_faces * 3, GL_UNSIGNED_INT, indices);
|
nuclear@75
|
230 } else {
|
nuclear@75
|
231 glDrawArrays(GL_TRIANGLES, 0, num_verts * 3);
|
nuclear@75
|
232 }
|
nuclear@75
|
233
|
nuclear@75
|
234 glDisableClientState(GL_VERTEX_ARRAY);
|
nuclear@75
|
235 glDisableClientState(GL_NORMAL_ARRAY);
|
nuclear@75
|
236 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
nuclear@75
|
237 }
|
nuclear@75
|
238
|
nuclear@80
|
239 /*int num_child = goat3d_get_node_child_count(node);
|
nuclear@75
|
240 for(int i=0; i<num_child; i++) {
|
nuclear@75
|
241 draw_node(goat3d_get_node_child(node, i));
|
nuclear@80
|
242 }*/
|
nuclear@78
|
243
|
nuclear@78
|
244 glPopMatrix();
|
nuclear@78
|
245 }
|
nuclear@78
|
246
|
nuclear@78
|
247
|
nuclear@78
|
248 static float prev_x, prev_y;
|
nuclear@78
|
249 void GoatViewport::mousePressEvent(QMouseEvent *ev)
|
nuclear@78
|
250 {
|
nuclear@78
|
251 prev_x = ev->x();
|
nuclear@78
|
252 prev_y = ev->y();
|
nuclear@78
|
253 }
|
nuclear@78
|
254
|
nuclear@78
|
255 void GoatViewport::mouseMoveEvent(QMouseEvent *ev)
|
nuclear@78
|
256 {
|
nuclear@78
|
257 int dx = ev->x() - prev_x;
|
nuclear@78
|
258 int dy = ev->y() - prev_y;
|
nuclear@80
|
259 prev_x = ev->x();
|
nuclear@80
|
260 prev_y = ev->y();
|
nuclear@78
|
261
|
nuclear@78
|
262 if(!dx && !dy) return;
|
nuclear@78
|
263
|
nuclear@78
|
264 if(ev->buttons() & Qt::LeftButton) {
|
nuclear@78
|
265 cam_theta += dx * 0.5;
|
nuclear@78
|
266 cam_phi += dy * 0.5;
|
nuclear@78
|
267
|
nuclear@78
|
268 if(cam_phi < -90) cam_phi = -90;
|
nuclear@78
|
269 if(cam_phi > 90) cam_phi = 90;
|
nuclear@78
|
270 }
|
nuclear@78
|
271 if(ev->buttons() & Qt::RightButton) {
|
nuclear@78
|
272 cam_dist += dy * 0.1;
|
nuclear@78
|
273
|
nuclear@78
|
274 if(cam_dist < 0.0) cam_dist = 0.0;
|
nuclear@78
|
275 }
|
nuclear@78
|
276 updateGL();
|
nuclear@78
|
277 }
|