1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12 #include <algorithm>
13 #include <list>
14 #include <vector>
15
16 #include "asteroid/asteroid.h"
17 #include "cmdline/cmdline.h"
18 #include "debris/debris.h"
19 #include "graphics/light.h"
20 #include "jumpnode/jumpnode.h"
21 #include "mission/missionparse.h"
22 #include "model/modelrender.h"
23 #include "nebula/neb.h"
24 #include "object/object.h"
25 #include "scripting/scripting.h"
26 #include "render/3d.h"
27 #include "render/batching.h"
28 #include "ship/ship.h"
29 #include "tracing/tracing.h"
30 #include "weapon/weapon.h"
31 #include "decals/decals.h"
32
33 class sorted_obj
34 {
35 public:
36 object *obj; // a pointer to the original object
37 float z, min_z, max_z; // The object's z values relative to viewer
38
sorted_obj()39 sorted_obj() :
40 obj(NULL), z(0.0f), min_z(0.0f), max_z(1.0f)
41 {
42 }
43
44 bool operator < (const sorted_obj &other) const;
45 };
46
operator <(const sorted_obj & other) const47 inline bool sorted_obj::operator < (const sorted_obj &other) const
48 {
49 int model_num_a = -1;
50 int model_num_b = -1;
51
52 if ( obj->type == OBJ_SHIP ) {
53 ship_info *sip = &Ship_info[Ships[obj->instance].ship_info_index];
54
55 model_num_a = sip->model_num;
56 } else if ( obj->type == OBJ_WEAPON ){
57 weapon_info *wip;
58
59 wip = &Weapon_info[Weapons[obj->instance].weapon_info_index];
60
61 if ( wip->render_type == WRT_POF ) {
62 model_num_a = wip->model_num;
63 }
64 } else if ( obj->type == OBJ_DEBRIS ) {
65 debris *db;
66
67 db = &Debris[obj->instance];
68 model_num_a = db->model_num;
69 } else if ( obj->type == OBJ_ASTEROID ) {
70 asteroid *asp;
71
72 asp = &Asteroids[obj->instance];
73 model_num_a = Asteroid_info[asp->asteroid_type].model_num[asp->asteroid_subtype];
74 }
75
76 if ( other.obj->type == OBJ_SHIP ) {
77 ship_info *sip = &Ship_info[Ships[other.obj->instance].ship_info_index];
78
79 model_num_b = sip->model_num;
80 } else if ( other.obj->type == OBJ_WEAPON ){
81 weapon_info *wip;
82
83 wip = &Weapon_info[Weapons[other.obj->instance].weapon_info_index];
84
85 if ( wip->render_type == WRT_POF ) {
86 model_num_b = wip->model_num;
87 }
88 } else if ( other.obj->type == OBJ_DEBRIS ) {
89 debris *db;
90
91 db = &Debris[other.obj->instance];
92 model_num_b = db->model_num;
93 } else if ( other.obj->type == OBJ_ASTEROID ) {
94 asteroid *asp;
95
96 asp = &Asteroids[other.obj->instance];
97 model_num_b = Asteroid_info[asp->asteroid_type].model_num[asp->asteroid_subtype];
98 }
99
100 if ( model_num_a == model_num_b ) {
101 return (max_z > other.max_z);
102 }
103
104 return model_num_a < model_num_b;
105 }
106
107
108 SCP_vector<sorted_obj> Sorted_objects;
109 SCP_vector<object*> effect_ships;
110 SCP_vector<object*> transparent_objects;
111 bool object_had_transparency = false;
112 // Used to (fairly) quicky find the 8 extreme
113 // points around an object.
114 vec3d check_offsets[8] = {
115 { { { -1.0f, -1.0f, -1.0f } } },
116 { { { -1.0f, -1.0f, 1.0f } } },
117 { { { -1.0f, 1.0f, -1.0f } } },
118 { { { -1.0f, 1.0f, 1.0f } } },
119 { { { 1.0f, -1.0f, -1.0f } } },
120 { { { 1.0f, -1.0f, 1.0f } } },
121 { { { 1.0f, 1.0f, -1.0f } } },
122 { { { 1.0f, 1.0f, 1.0f } } }
123 };
124
125 // See if an object is in the view cone.
126 // Returns:
127 // 0 if object isn't in the view cone
128 // 1 if object is in cone
129 // This routine could possibly be optimized. Right now, for an
130 // offscreen object, it has to rotate 8 points to determine it's
131 // offscreen. Not the best considering we're looking at a sphere.
obj_in_view_cone(object * objp)132 int obj_in_view_cone( object * objp )
133 {
134 int i;
135 vec3d tmp,pt;
136 ubyte codes;
137
138 // Center isn't in... are other points?
139 ubyte and_codes = 0xff;
140
141 for (i=0; i<8; i++ ) {
142 vm_vec_scale_add( &pt, &objp->pos, &check_offsets[i], objp->radius );
143 codes=g3_rotate_vector(&tmp,&pt);
144 if ( !codes ) {
145 //mprintf(( "A point is inside, so render it.\n" ));
146 return 1; // this point is in, so return 1
147 }
148 and_codes &= codes;
149 }
150
151 if (and_codes) {
152 //mprintf(( "All points offscreen, so don't render it.\n" ));
153 return 0; //all points off screen
154 }
155
156 //mprintf(( "All points inside, so render it, but doesn't need clipping.\n" ));
157 return 1;
158 }
159
obj_render_is_model(object * obj)160 inline bool obj_render_is_model(object *obj)
161 {
162 return obj->type == OBJ_SHIP
163 || (obj->type == OBJ_WEAPON
164 && Weapon_info[Weapons[obj->instance].weapon_info_index].render_type == WRT_POF)
165 || obj->type == OBJ_ASTEROID
166 || obj->type == OBJ_DEBRIS
167 || obj->type == OBJ_JUMP_NODE;
168 }
169
170 // Are there reasons to hide objects base on distance?
is_full_nebula()171 bool is_full_nebula()
172 {
173 return (The_mission.flags[Mission::Mission_Flags::Fullneb]) && (Neb2_render_mode != NEB2_RENDER_NONE) && !Fred_running;
174 }
175
176 // Sorts all the objects by Z and renders them
obj_render_all(const std::function<void (object *)> & render_function,bool * draw_viewer_last)177 void obj_render_all(const std::function<void(object*)>& render_function, bool *draw_viewer_last )
178 {
179 object *objp;
180 int i;
181 float fog_near, fog_far, fog_density;
182
183 objp = Objects;
184
185 for (i=0;i<=Highest_object_index;i++,objp++) {
186 if ( (objp->type != OBJ_NONE) && (objp->flags[Object::Object_Flags::Renders]) ) {
187 objp->flags.remove(Object::Object_Flags::Was_rendered);
188
189 if ( obj_in_view_cone(objp) ) {
190 sorted_obj osp;
191
192 osp.obj = objp;
193
194 vec3d to_obj;
195 vm_vec_sub( &to_obj, &objp->pos, &Eye_position );
196 osp.z = vm_vec_dot( &Eye_matrix.vec.fvec, &to_obj );
197 /*
198 if ( objp->type == OBJ_SHOCKWAVE )
199 osp.z -= 2*objp->radius;
200 */
201 // Make warp in effect draw after any ship in it
202 if ( objp->type == OBJ_FIREBALL ) {
203 //if ( fireball_is_warp(objp) ) {
204 osp.z -= 2*objp->radius;
205 //}
206 }
207
208 osp.min_z = osp.z - objp->radius;
209 osp.max_z = osp.z + objp->radius;
210
211 Sorted_objects.push_back(osp);
212 }
213 }
214 }
215
216 if ( Sorted_objects.empty() )
217 return;
218
219 std::sort(Sorted_objects.begin(), Sorted_objects.end());
220
221 gr_zbuffer_set( GR_ZBUFF_FULL );
222
223 bool full_neb = is_full_nebula();
224 bool c_viewer = (!Viewer_mode || (Viewer_mode & VM_PADLOCK_ANY) || (Viewer_mode & VM_OTHER_SHIP) || (Viewer_mode & VM_TRACK));
225
226 // now draw them
227 // only render models in this loop in order to minimize state changes
228 SCP_vector<sorted_obj>::iterator os;
229 for (os = Sorted_objects.begin(); os != Sorted_objects.end(); ++os) {
230 object *obj = os->obj;
231
232 obj->flags.set(Object::Object_Flags::Was_rendered);
233
234 //This is for ship cockpits. Bobb, feel free to optimize this any way you see fit
235 if ( (obj == Viewer_obj)
236 && (obj->type == OBJ_SHIP)
237 && c_viewer
238 && (Ship_info[Ships[obj->instance].ship_info_index].flags[Ship::Info_Flags::Show_ship_model]) )
239 {
240 (*draw_viewer_last) = true;
241 continue;
242 }
243
244 // if we're fullneb, fire up the fog - this also generates a fog table
245 if (full_neb) {
246 // get the fog values
247 neb2_get_adjusted_fog_values(&fog_near, &fog_far, &fog_density, obj);
248
249 // maybe skip rendering an object because its obscured by the nebula
250 if(neb2_skip_render(obj, os->z)){
251 continue;
252 }
253 }
254
255 if ( obj_render_is_model(obj) ) {
256 if( ((obj->type == OBJ_SHIP) && Ships[obj->instance].shader_effect_active) || (obj->type == OBJ_FIREBALL) )
257 effect_ships.push_back(obj);
258 else
259 render_function(obj);
260 }
261 if(object_had_transparency)
262 {
263 object_had_transparency = false;
264 transparent_objects.push_back(obj);
265 }
266 }
267 gr_deferred_lighting_end();
268
269 // we're done rendering models so flush render states
270 gr_clear_states();
271
272 // render everything else that isn't a model
273 for (os = Sorted_objects.begin(); os != Sorted_objects.end(); ++os) {
274 object *obj = os->obj;
275
276 obj->flags.set(Object::Object_Flags::Was_rendered);
277
278 if ( obj_render_is_model(obj) )
279 continue;
280
281 // maybe skip rendering an object because its obscured by the nebula
282 if(full_neb && neb2_skip_render(obj, os->z)){
283 continue;
284 }
285
286 render_function(obj);
287 }
288
289 Sorted_objects.clear();
290
291 batching_render_all();
292 batching_render_all(true);
293 }
294
obj_render_queue_all()295 void obj_render_queue_all()
296 {
297 GR_DEBUG_SCOPE("Render all objects");
298 TRACE_SCOPE(tracing::RenderScene);
299
300 object *objp;
301 int i;
302 model_draw_list scene;
303
304 objp = Objects;
305
306 gr_deferred_lighting_begin();
307
308 scene.init();
309
310 bool full_neb = is_full_nebula();
311
312 for ( i = 0; i <= Highest_object_index; i++,objp++ ) {
313 if ( (objp->type != OBJ_NONE) && ( objp->flags [Object::Object_Flags::Renders] ) ) {
314 objp->flags.remove(Object::Object_Flags::Was_rendered);
315
316 if ( !obj_in_view_cone(objp) ) {
317 continue;
318 }
319
320 if ( full_neb ) {
321 vec3d to_obj;
322 vm_vec_sub( &to_obj, &objp->pos, &Eye_position );
323 float z = vm_vec_dot( &Eye_matrix.vec.fvec, &to_obj );
324
325 if ( neb2_skip_render(objp, z) ){
326 continue;
327 }
328 }
329
330
331 if ( (objp->type == OBJ_SHIP) && Ships[objp->instance].shader_effect_active ) {
332 effect_ships.push_back(objp);
333 continue;
334 }
335
336 objp->flags.set(Object::Object_Flags::Was_rendered);
337 obj_queue_render(objp, &scene);
338 }
339 }
340
341 scene.init_render();
342
343 scene.render_all(ZBUFFER_TYPE_FULL);
344 gr_zbuffer_set(ZBUFFER_TYPE_READ);
345 gr_zbias(0);
346 gr_set_cull(0);
347
348 gr_clear_states();
349 gr_set_fill_mode(GR_FILL_MODE_SOLID);
350
351 decals::renderAll();
352
353 gr_deferred_lighting_end();
354 gr_deferred_lighting_finish();
355
356 gr_zbuffer_set(ZBUFFER_TYPE_READ);
357
358 gr_reset_lighting();
359 gr_set_lighting(false, false);
360
361 // now render transparent meshes
362 scene.render_all(ZBUFFER_TYPE_READ);
363 scene.render_all(ZBUFFER_TYPE_NONE);
364
365 // render electricity effects and insignias
366 scene.render_outlines();
367 scene.render_insignias();
368 scene.render_arcs();
369
370 gr_zbuffer_set(ZBUFFER_TYPE_READ);
371 gr_zbias(0);
372 gr_set_cull(0);
373 gr_set_fill_mode(GR_FILL_MODE_SOLID);
374
375 gr_clear_states();
376
377 gr_reset_lighting();
378 gr_set_lighting(false, false);
379
380 batching_render_all();
381
382 gr_zbias(0);
383 gr_zbuffer_set(ZBUFFER_TYPE_READ);
384 gr_set_cull(0);
385 gr_set_fill_mode(GR_FILL_MODE_SOLID);
386
387 gr_clear_states();
388
389 gr_reset_lighting();
390 gr_set_lighting(false, false);
391 }
392