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