rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <assert.h>
|
nuclear@0
|
4 #include <vector>
|
nuclear@3
|
5 #include <algorithm>
|
nuclear@0
|
6 #include "opengl.h"
|
nuclear@2
|
7 #ifdef __APPLE__
|
nuclear@2
|
8 #include <GLUT/glut.h>
|
nuclear@2
|
9 #else
|
nuclear@0
|
10 #include <GL/glut.h>
|
nuclear@2
|
11 #endif
|
nuclear@0
|
12 #include "scene.h"
|
nuclear@0
|
13 #include "meshgen.h"
|
nuclear@0
|
14 #include "pnoise.h"
|
nuclear@0
|
15 #include "image.h"
|
nuclear@0
|
16 #include "rng.h"
|
nuclear@0
|
17 #include "sdr.h"
|
nuclear@0
|
18 #include "audio/audio.h"
|
nuclear@0
|
19 #include "audio/ovstream.h"
|
nuclear@0
|
20 #include "dsys/dsys.h"
|
nuclear@0
|
21
|
nuclear@0
|
22 bool init();
|
nuclear@0
|
23 void cleanup();
|
nuclear@0
|
24 void display();
|
nuclear@0
|
25 void idle();
|
nuclear@0
|
26 void reshape(int x, int y);
|
nuclear@0
|
27 void keyb(unsigned char key, int x, int y);
|
nuclear@0
|
28 void mouse(int bn, int st, int x, int y);
|
nuclear@0
|
29 void motion(int x, int y);
|
nuclear@0
|
30
|
nuclear@0
|
31 float cam_theta, cam_phi = 25, cam_dist = 6;
|
nuclear@0
|
32 Scene *scn;
|
nuclear@0
|
33 bool wireframe;
|
nuclear@0
|
34 int num_shells = 64;
|
nuclear@0
|
35 float shell_scale = 1.015;
|
nuclear@0
|
36 bool anim = true;
|
nuclear@0
|
37 bool fullscr = true;
|
nuclear@0
|
38
|
nuclear@0
|
39 static std::vector<Image*> images;
|
nuclear@0
|
40 static Object *pkin_obj;
|
nuclear@0
|
41 static Mesh *pkin_mesh;
|
nuclear@0
|
42 static unsigned int pkin_tex;
|
nuclear@0
|
43
|
nuclear@0
|
44 static unsigned int sdr_spot;
|
nuclear@0
|
45
|
nuclear@0
|
46 static float ramp[3];
|
nuclear@0
|
47
|
nuclear@0
|
48
|
nuclear@0
|
49 static OggVorbisStream *music;
|
nuclear@0
|
50 static struct dsys_demo *demo;
|
nuclear@0
|
51
|
nuclear@0
|
52
|
nuclear@0
|
53 int main(int argc, char **argv)
|
nuclear@0
|
54 {
|
nuclear@0
|
55 glutInit(&argc, argv);
|
nuclear@0
|
56 glutInitWindowSize(1280, 720);
|
nuclear@0
|
57 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
|
nuclear@0
|
58 glutCreateWindow("DBF Halloween 2015 compo entry");
|
nuclear@0
|
59
|
nuclear@0
|
60 if(argv[1]) {
|
nuclear@0
|
61 if(strcmp(argv[1], "-w") == 0 || strcmp(argv[1], "-windowed") == 0) {
|
nuclear@0
|
62 fullscr = false;
|
nuclear@0
|
63 } else {
|
nuclear@0
|
64 fprintf(stderr, "unrecognized argument: %s\nUse -w or -windowed to run in a window\n", argv[1]);
|
nuclear@0
|
65 return 1;
|
nuclear@0
|
66 }
|
nuclear@0
|
67 }
|
nuclear@0
|
68 if(fullscr) {
|
nuclear@0
|
69 glutFullScreen();
|
nuclear@0
|
70 }
|
nuclear@0
|
71
|
nuclear@0
|
72 glutDisplayFunc(display);
|
nuclear@0
|
73 glutIdleFunc(idle);
|
nuclear@0
|
74 glutReshapeFunc(reshape);
|
nuclear@0
|
75 glutKeyboardFunc(keyb);
|
nuclear@0
|
76 glutMouseFunc(mouse);
|
nuclear@0
|
77 glutMotionFunc(motion);
|
nuclear@0
|
78
|
nuclear@0
|
79 if(!init()) {
|
nuclear@0
|
80 return 1;
|
nuclear@0
|
81 }
|
nuclear@0
|
82 glutMainLoop();
|
nuclear@0
|
83 return 0;
|
nuclear@0
|
84 }
|
nuclear@0
|
85
|
nuclear@0
|
86 static float ground_func(float u, float v, void *cls)
|
nuclear@0
|
87 {
|
nuclear@0
|
88 return fbm2(u * 3.0, v * 3.0, 2) * 0.8;
|
nuclear@0
|
89 }
|
nuclear@0
|
90
|
nuclear@0
|
91 static Vector2 pumpkin_func(float u, float v, void *cls)
|
nuclear@0
|
92 {
|
nuclear@0
|
93 float theta = v * M_PI;
|
nuclear@0
|
94 float r = sin(theta) * 0.5 + 0.5;
|
nuclear@0
|
95
|
nuclear@0
|
96 // modulate a high-frequency short-amp wave along u to make the ribs(?)
|
nuclear@0
|
97 r += fabs(sin(u * M_PI * 12.0)) * 0.02;// + 1.0;
|
nuclear@0
|
98
|
nuclear@0
|
99 // throw some noise in there for good measure
|
nuclear@0
|
100 r += pnoise2(u * 16.0, v * 8.0, 16) * 0.05;// + 1.0;
|
nuclear@0
|
101
|
nuclear@0
|
102 float x = sin(theta) * r;
|
nuclear@0
|
103 float y = cos(theta) * r;
|
nuclear@0
|
104 return Vector2(x, y);
|
nuclear@0
|
105 }
|
nuclear@0
|
106
|
nuclear@0
|
107 #define CLAMP(x, a, b) std::max<float>(std::min<float>(x, b), a)
|
nuclear@0
|
108
|
nuclear@0
|
109 bool init()
|
nuclear@0
|
110 {
|
nuclear@0
|
111 if(init_opengl() == -1) {
|
nuclear@0
|
112 return false;
|
nuclear@0
|
113 }
|
nuclear@0
|
114 Mesh::use_custom_sdr_attr = false;
|
nuclear@0
|
115
|
nuclear@0
|
116 glEnable(GL_DEPTH_TEST);
|
nuclear@0
|
117 glEnable(GL_CULL_FACE);
|
nuclear@0
|
118 glEnable(GL_LIGHTING);
|
nuclear@0
|
119 glEnable(GL_NORMALIZE);
|
nuclear@0
|
120
|
nuclear@0
|
121 if(!(demo = dsys_open("data/demo.script"))) {
|
nuclear@0
|
122 fprintf(stderr, "failed to load sequencing file\n");
|
nuclear@0
|
123 return false;
|
nuclear@0
|
124 }
|
nuclear@0
|
125 for(int i=0; i<3; i++) {
|
nuclear@0
|
126 char name[16];
|
nuclear@0
|
127 sprintf(name, "ramp%d", i);
|
nuclear@0
|
128 struct dsys_event *ev = dsys_event(demo, name);
|
nuclear@0
|
129 if(ev) {
|
nuclear@0
|
130 printf("found event: %s\n", name);
|
nuclear@0
|
131 dsys_event_link(ev, ramp + i);
|
nuclear@0
|
132 }
|
nuclear@0
|
133 }
|
nuclear@0
|
134
|
nuclear@0
|
135
|
nuclear@0
|
136 if(!(sdr_spot = create_program_load("sdr/default.v.glsl", "sdr/spot.p.glsl"))) {
|
nuclear@0
|
137 return false;
|
nuclear@0
|
138 }
|
nuclear@0
|
139
|
nuclear@0
|
140 Matrix4x4 xform;
|
nuclear@0
|
141
|
nuclear@0
|
142 scn = new Scene;
|
nuclear@0
|
143
|
nuclear@0
|
144 Light *lt = new Light;
|
nuclear@0
|
145 lt->pos = Vector3(-10, 10, 10);
|
nuclear@0
|
146 scn->add_light(lt);
|
nuclear@0
|
147
|
nuclear@0
|
148 // floor
|
nuclear@0
|
149 Mesh *mesh = new Mesh;
|
nuclear@0
|
150 gen_heightmap(mesh, 20, 20, 50, 50, ground_func);
|
nuclear@0
|
151 xform.set_rotation(Vector3(-M_PI / 2.0, 0, 0));
|
nuclear@0
|
152 mesh->apply_xform(xform);
|
nuclear@0
|
153
|
nuclear@0
|
154 Object *obj = new Object;
|
nuclear@0
|
155 obj->set_mesh(mesh);
|
nuclear@0
|
156 obj->xform().set_translation(Vector3(0, -0.9, 0));
|
nuclear@0
|
157 obj->mtl.diffuse = Vector3(0.7, 0.7, 0.7);
|
nuclear@0
|
158 obj->set_shader(sdr_spot);
|
nuclear@0
|
159 scn->add_object(obj);
|
nuclear@0
|
160
|
nuclear@0
|
161 Image *img = new Image;
|
nuclear@0
|
162 if(!img->load("data/grass02.jpg")) {
|
nuclear@0
|
163 return false;
|
nuclear@0
|
164 }
|
nuclear@0
|
165 images.push_back(img);
|
nuclear@0
|
166 obj->tex_xform().set_scaling(Vector3(5.0, 10.0, 1.0));
|
nuclear@0
|
167 obj->set_texture(img->texture());
|
nuclear@0
|
168
|
nuclear@0
|
169 // pumpkin texture
|
nuclear@0
|
170 img = new Image;
|
nuclear@0
|
171 if(!img->create(1024, 512)) {
|
nuclear@0
|
172 return false;
|
nuclear@0
|
173 }
|
nuclear@0
|
174 images.push_back(img);
|
nuclear@0
|
175
|
nuclear@0
|
176 static const Vector3 pkin_col = Vector3(0.824, 0.325, 0.063);
|
nuclear@0
|
177 unsigned char *pptr = img->pixels;
|
nuclear@0
|
178 for(int i=0; i<img->height; i++) {
|
nuclear@0
|
179 float v = (float)i / (float)img->height;
|
nuclear@0
|
180 for(int j=0; j<img->width; j++) {
|
nuclear@0
|
181 float u = (float)j / (float)img->width;
|
nuclear@0
|
182
|
nuclear@0
|
183 float val = pfbm2(u * 64.0, v * 32.0, 3, 128) * 0.1 + 1.0;
|
nuclear@0
|
184 val *= fabs(sin(u * M_PI * 12.0)) * 0.2 + 0.8;
|
nuclear@0
|
185 Vector3 col = pkin_col * val;
|
nuclear@0
|
186
|
nuclear@0
|
187 *pptr++ = (int)(CLAMP(col.x, 0.0, 1.0) * 255.0f);
|
nuclear@0
|
188 *pptr++ = (int)(CLAMP(col.y, 0.0, 1.0) * 255.0f);
|
nuclear@0
|
189 *pptr++ = (int)(CLAMP(col.z, 0.0, 1.0) * 255.0f);
|
nuclear@0
|
190 *pptr++ = 255;
|
nuclear@0
|
191 }
|
nuclear@0
|
192 }
|
nuclear@0
|
193
|
nuclear@0
|
194 // pumpkin
|
nuclear@0
|
195 pkin_mesh = new Mesh;
|
nuclear@0
|
196 gen_revol(pkin_mesh, 64, 20, pumpkin_func);
|
nuclear@0
|
197
|
nuclear@0
|
198 pkin_obj = new Object;
|
nuclear@0
|
199 pkin_obj->xform().set_rotation(Vector3(DEG_TO_RAD(-10), DEG_TO_RAD(90), 0));
|
nuclear@0
|
200 pkin_obj->set_mesh(pkin_mesh);
|
nuclear@0
|
201 pkin_obj->set_texture(img->texture());
|
nuclear@0
|
202 obj->set_shader(sdr_spot);
|
nuclear@0
|
203 scn->add_object(pkin_obj);
|
nuclear@0
|
204
|
nuclear@0
|
205 int num_pumpkins = 20;
|
nuclear@0
|
206 float dtheta = 1.0 / (float)num_pumpkins;
|
nuclear@0
|
207
|
nuclear@0
|
208 for(int i=0; i<num_pumpkins; i++) {
|
nuclear@0
|
209 float theta = (((float)i + rng_frand() * 0.5) * dtheta) * 2.0 * M_PI * 2.0;
|
nuclear@0
|
210 float r = 5.0 * (float)i / (float)num_pumpkins + 4.0;
|
nuclear@0
|
211
|
nuclear@0
|
212 float x = cos(theta) * r;
|
nuclear@0
|
213 float y = sin(theta) * r;
|
nuclear@0
|
214
|
nuclear@0
|
215 float tu = (x + 10.0) / 20.0;
|
nuclear@0
|
216 float tv = (10.0 - y) / 20.0;
|
nuclear@0
|
217 float h = ground_func(tu, tv, 0);
|
nuclear@0
|
218
|
nuclear@0
|
219 obj = new Object;
|
nuclear@0
|
220 obj->xform().translate(Vector3(x, h - 0.5, y));
|
nuclear@0
|
221 obj->xform().rotate(Vector3(DEG_TO_RAD(rng_frand() * 40.0 - 20.0), rng_frand() * M_PI * 2.0, 0));
|
nuclear@0
|
222 obj->xform().scale(Vector3(0.7, 0.7, 0.7));
|
nuclear@0
|
223 obj->set_mesh(pkin_mesh);
|
nuclear@0
|
224 obj->set_texture(img->texture());
|
nuclear@0
|
225 obj->set_shader(sdr_spot);
|
nuclear@0
|
226 scn->add_object(obj);
|
nuclear@0
|
227 }
|
nuclear@0
|
228
|
nuclear@0
|
229 // pumpkin glow mask
|
nuclear@0
|
230 img = new Image;
|
nuclear@0
|
231 if(!img->load("data/pumpkin_mask_blured.png")) {
|
nuclear@0
|
232 return false;
|
nuclear@0
|
233 }
|
nuclear@0
|
234 images.push_back(img);
|
nuclear@0
|
235 pkin_tex = img->texture();
|
nuclear@0
|
236
|
nuclear@0
|
237 if(!init_audio()) {
|
nuclear@0
|
238 fprintf(stderr, "failed to initialize audio\n");
|
nuclear@0
|
239 return false;
|
nuclear@0
|
240 }
|
nuclear@0
|
241 music = new OggVorbisStream;
|
nuclear@0
|
242 if(!music->open("data/welcome_to_horrorland.ogg")) {
|
nuclear@0
|
243 fprintf(stderr, "failed to open music\n");
|
nuclear@0
|
244 return false;
|
nuclear@0
|
245 }
|
nuclear@0
|
246 music->play(AUDIO_PLAYMODE_LOOP);
|
nuclear@0
|
247
|
nuclear@0
|
248 return true;
|
nuclear@0
|
249 }
|
nuclear@0
|
250
|
nuclear@0
|
251 void cleanup()
|
nuclear@0
|
252 {
|
nuclear@0
|
253 music->stop();
|
nuclear@0
|
254 delete music;
|
nuclear@0
|
255 destroy_audio();
|
nuclear@0
|
256
|
nuclear@0
|
257 delete scn;
|
nuclear@0
|
258
|
nuclear@0
|
259 for(size_t i=0; i<images.size(); i++) {
|
nuclear@0
|
260 delete images[i];
|
nuclear@0
|
261 }
|
nuclear@0
|
262 images.clear();
|
nuclear@0
|
263 }
|
nuclear@0
|
264
|
nuclear@0
|
265 void display()
|
nuclear@0
|
266 {
|
nuclear@0
|
267 unsigned int msec = glutGet(GLUT_ELAPSED_TIME);
|
nuclear@0
|
268 float sec = (float)msec / 1000.0;
|
nuclear@0
|
269
|
nuclear@0
|
270 dsys_update(demo, dsys_msec_to_dtime(msec));
|
nuclear@0
|
271 if(!dsys_is_running(demo)) {
|
nuclear@0
|
272 exit(0);
|
nuclear@0
|
273 }
|
nuclear@0
|
274
|
nuclear@0
|
275 float offs[3];
|
nuclear@0
|
276
|
nuclear@0
|
277 if(anim) {
|
nuclear@0
|
278 float t = sec * 0.5;
|
nuclear@0
|
279 cam_theta = cos(t) * 35.0;// + fbm1(t * 6.0, 1) * 2.0;
|
nuclear@0
|
280 cam_phi = sin(t * 2.0) * 10.0 + 25.0;// + fbm1(t * 8.0, 1) * 3.0;
|
nuclear@0
|
281
|
nuclear@0
|
282 float mag = std::max(fmod(ramp[0], 1.0), std::max(fmod(ramp[1], 1.0), fmod(ramp[2], 1.0))) * 0.8 + 0.15;
|
nuclear@0
|
283 //printf("ramps: %g %g %g, mag: %g\n", ramp[0], ramp[1], ramp[2], mag);
|
nuclear@0
|
284 for(int i=0; i<3; i++) {
|
nuclear@0
|
285 offs[i] = turbulence1(sec * 4.0 + i, 2) * mag;
|
nuclear@0
|
286 }
|
nuclear@0
|
287 }
|
nuclear@0
|
288
|
nuclear@0
|
289 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
nuclear@0
|
290
|
nuclear@0
|
291 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
292 glLoadIdentity();
|
nuclear@0
|
293 glTranslatef(0, 0, -cam_dist);
|
nuclear@0
|
294 glRotatef(cam_phi, 1, 0, 0);
|
nuclear@0
|
295 glRotatef(cam_theta, 0, 1, 0);
|
nuclear@0
|
296 glTranslatef(offs[0], offs[1], offs[2]);
|
nuclear@0
|
297
|
nuclear@0
|
298 scn->draw();
|
nuclear@0
|
299
|
nuclear@0
|
300 glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT);
|
nuclear@0
|
301 glDisable(GL_CULL_FACE);
|
nuclear@0
|
302
|
nuclear@0
|
303 glDepthMask(0);
|
nuclear@0
|
304 glEnable(GL_BLEND);
|
nuclear@0
|
305 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
nuclear@0
|
306
|
nuclear@0
|
307 glBindTexture(GL_TEXTURE_2D, pkin_tex);
|
nuclear@0
|
308 glEnable(GL_TEXTURE_2D);
|
nuclear@0
|
309
|
nuclear@0
|
310 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
311 glPushMatrix();
|
nuclear@0
|
312 glMultTransposeMatrixf(pkin_obj->xform()[0]);
|
nuclear@0
|
313
|
nuclear@0
|
314 num_shells = fbm1(sec * 2.0, 2) * 28.0 + 45;
|
nuclear@0
|
315
|
nuclear@0
|
316 for(int i=0; i<num_shells; i++) {
|
nuclear@0
|
317 float x = (float)i / (float)num_shells;
|
nuclear@0
|
318 float alpha = 0.025 / (x * x);
|
nuclear@0
|
319
|
nuclear@0
|
320 float black[] = {0, 0, 0, std::min(alpha, 1.0f) * 0.3f};
|
nuclear@0
|
321 float glow_col[] = {0.7, 0.5, 0.25, 1.0};
|
nuclear@0
|
322 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, black);
|
nuclear@0
|
323 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black);
|
nuclear@0
|
324 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, glow_col);
|
nuclear@0
|
325
|
nuclear@0
|
326 glScalef(shell_scale, shell_scale, shell_scale);
|
nuclear@0
|
327 pkin_mesh->draw();
|
nuclear@0
|
328 }
|
nuclear@0
|
329 glDepthMask(1);
|
nuclear@0
|
330
|
nuclear@0
|
331 glPopMatrix();
|
nuclear@0
|
332 glPopAttrib();
|
nuclear@0
|
333
|
nuclear@0
|
334
|
nuclear@0
|
335 glutSwapBuffers();
|
nuclear@0
|
336 assert(glGetError() == GL_NO_ERROR);
|
nuclear@0
|
337 }
|
nuclear@0
|
338
|
nuclear@0
|
339 void idle()
|
nuclear@0
|
340 {
|
nuclear@0
|
341 glutPostRedisplay();
|
nuclear@0
|
342 }
|
nuclear@0
|
343
|
nuclear@0
|
344 void reshape(int x, int y)
|
nuclear@0
|
345 {
|
nuclear@0
|
346 glViewport(0, 0, x, y);
|
nuclear@0
|
347
|
nuclear@0
|
348 glMatrixMode(GL_PROJECTION);
|
nuclear@0
|
349 glLoadIdentity();
|
nuclear@0
|
350 gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
|
nuclear@0
|
351 }
|
nuclear@0
|
352
|
nuclear@0
|
353 void keyb(unsigned char key, int x, int y)
|
nuclear@0
|
354 {
|
nuclear@0
|
355 switch(key) {
|
nuclear@0
|
356 case 27:
|
nuclear@0
|
357 exit(0);
|
nuclear@0
|
358
|
nuclear@0
|
359 case 'w':
|
nuclear@0
|
360 wireframe = !wireframe;
|
nuclear@0
|
361 break;
|
nuclear@0
|
362
|
nuclear@0
|
363 case 'f':
|
nuclear@0
|
364 case 'F':
|
nuclear@0
|
365 {
|
nuclear@0
|
366 static int prev_pos[2] = {50, 80};
|
nuclear@0
|
367
|
nuclear@0
|
368 fullscr = !fullscr;
|
nuclear@0
|
369 if(fullscr) {
|
nuclear@0
|
370 prev_pos[0] = glutGet(GLUT_WINDOW_X);
|
nuclear@0
|
371 prev_pos[1] = glutGet(GLUT_WINDOW_Y);
|
nuclear@0
|
372 glutFullScreen();
|
nuclear@0
|
373 } else {
|
nuclear@0
|
374 glutPositionWindow(prev_pos[0], prev_pos[1]);
|
nuclear@0
|
375 }
|
nuclear@0
|
376 }
|
nuclear@0
|
377 break;
|
nuclear@0
|
378 }
|
nuclear@0
|
379 }
|
nuclear@0
|
380
|
nuclear@0
|
381 static int prev_x, prev_y;
|
nuclear@0
|
382 static bool bnstate[16];
|
nuclear@0
|
383
|
nuclear@0
|
384 void mouse(int bn, int st, int x, int y)
|
nuclear@0
|
385 {
|
nuclear@0
|
386 prev_x = x;
|
nuclear@0
|
387 prev_y = y;
|
nuclear@0
|
388 bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN;
|
nuclear@0
|
389
|
nuclear@0
|
390 anim = !(st == GLUT_DOWN);
|
nuclear@0
|
391 }
|
nuclear@0
|
392
|
nuclear@0
|
393 void motion(int x, int y)
|
nuclear@0
|
394 {
|
nuclear@0
|
395 int dx = x - prev_x;
|
nuclear@0
|
396 int dy = y - prev_y;
|
nuclear@0
|
397 prev_x = x;
|
nuclear@0
|
398 prev_y = y;
|
nuclear@0
|
399
|
nuclear@0
|
400 if(!dx && !dy) return;
|
nuclear@0
|
401
|
nuclear@0
|
402 if(bnstate[0]) {
|
nuclear@0
|
403 //anim = false;
|
nuclear@0
|
404 cam_theta += dx * 0.5;
|
nuclear@0
|
405 cam_phi += dy * 0.5;
|
nuclear@0
|
406 if(cam_phi < -90) cam_phi = -90;
|
nuclear@0
|
407 if(cam_phi > 90) cam_phi = 90;
|
nuclear@0
|
408 }
|
nuclear@0
|
409 if(bnstate[2]) {
|
nuclear@0
|
410 //anim = false;
|
nuclear@0
|
411 cam_dist += dy * 0.1;
|
nuclear@0
|
412 if(cam_dist < 0.0) cam_dist = 0.0;
|
nuclear@0
|
413 }
|
nuclear@0
|
414 }
|