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 #include "hud/hud.h"
12 #include "jumpnode/jumpnode.h"
13 #include "model/model.h"
14 #include "model/modelrender.h"
15 
16 SCP_list<CJumpNode> Jump_nodes;
17 
18 /**
19  * Constructor for CJumpNode class, default
20  */
CJumpNode()21 CJumpNode::CJumpNode()
22 {
23     gr_init_alphacolor(&m_display_color, 0, 255, 0, 255);
24 
25 	m_name[0] = '\0';
26 
27     m_pos.xyz.x = 0.0f;
28     m_pos.xyz.y = 0.0f;
29     m_pos.xyz.z = 0.0f;
30 }
31 
32 /**
33  * Constructor for CJumpNode class, with world position argument
34  */
CJumpNode(const vec3d * position)35 CJumpNode::CJumpNode(const vec3d* position)
36 {
37 	Assert(position != NULL);
38 
39 	gr_init_alphacolor(&m_display_color, 0, 255, 0, 255);
40 
41 	// Set m_name
42 	sprintf(m_name, XSTR( "Jump Node %d", 632), Jump_nodes.size());
43 
44 	// Set m_modelnum and m_radius
45 	m_modelnum = model_load(NOX("subspacenode.pof"), 0, NULL, 0);
46 	if (m_modelnum == -1) {
47 		Warning(LOCATION, "Could not load default model for %s", m_name);
48 	} else {
49 		m_radius = model_get_radius(m_modelnum);
50 
51 		// set up animation in case of instrinsic_rotate
52 		polymodel* pm = model_get(m_modelnum);
53 
54 		if (pm->flags & PM_FLAG_HAS_INTRINSIC_ROTATE) {
55 			m_polymodel_instance_num = model_create_instance(false, m_modelnum);
56 		}
57 	}
58 
59     m_pos.xyz.x = position->xyz.x;
60     m_pos.xyz.y = position->xyz.y;
61     m_pos.xyz.z = position->xyz.z;
62 
63 	// Create the object
64     flagset<Object::Object_Flags> default_flags;
65     default_flags.set(Object::Object_Flags::Renders);
66     m_objnum = obj_create(OBJ_JUMP_NODE, -1, -1, NULL, &m_pos, m_radius, default_flags);
67 }
68 
CJumpNode(CJumpNode && other)69 CJumpNode::CJumpNode(CJumpNode&& other) noexcept
70 	: m_radius(other.m_radius), m_modelnum(other.m_modelnum), m_objnum(other.m_objnum), m_polymodel_instance_num(other.m_polymodel_instance_num), m_flags(other.m_flags)
71 {
72 	other.m_radius = 0.0f;
73 	other.m_modelnum = -1;
74 	other.m_objnum = -1;
75 	other.m_polymodel_instance_num = -1;
76 	other.m_flags = 0;
77 
78 	m_display_color = other.m_display_color;
79 	m_pos = other.m_pos;
80 
81 	strcpy_s(m_name, other.m_name);
82 }
83 
operator =(CJumpNode && other)84 CJumpNode& CJumpNode::operator=(CJumpNode&& other) noexcept
85 {
86 	if (this != &other)
87 	{
88 		m_radius = other.m_radius;
89 		m_modelnum = other.m_modelnum;
90 		m_objnum = other.m_objnum;
91 		m_flags = other.m_flags;
92 		m_polymodel_instance_num = other.m_polymodel_instance_num;
93 
94 		other.m_radius = 0.0f;
95 		other.m_modelnum = -1;
96 		other.m_objnum = -1;
97 		other.m_flags = 0;
98 		other.m_polymodel_instance_num = -1;
99 
100 		m_display_color = other.m_display_color;
101 		m_pos = other.m_pos;
102 
103 		strcpy_s(m_name, other.m_name);
104 	}
105 
106 	return *this;
107 }
108 
109 /**
110  * Destructor for CJumpNode class
111  */
~CJumpNode()112 CJumpNode::~CJumpNode()
113 {
114 	if (m_modelnum >= 0)
115 	{
116 		model_unload(m_modelnum);
117 	}
118 
119 	if (m_objnum >= 0 && Objects[m_objnum].type != OBJ_NONE)
120 	{
121 		obj_delete(m_objnum);
122 	}
123 }
124 
125 // Accessor functions for private variables
126 
127 /**
128  * @return Name of jump node
129  */
GetName()130 const char *CJumpNode::GetName()
131 {
132 	return m_name;
133 }
134 
135 /**
136  * @return Handle to model
137  */
GetModelNumber()138 int CJumpNode::GetModelNumber()
139 {
140 	return m_modelnum;
141 }
142 
143 /**
144  * @return Index into Objects[]
145  */
GetSCPObjectNumber()146 int CJumpNode::GetSCPObjectNumber()
147 {
148 	return m_objnum;
149 }
150 
151 /**
152  * @return Object
153  */
GetSCPObject()154 object *CJumpNode::GetSCPObject()
155 {
156 	Assert(m_objnum != -1);
157     return &Objects[m_objnum];
158 }
159 
160 /**
161  * @return Color of jump node when rendered
162  */
GetColor()163 color CJumpNode::GetColor()
164 {
165 	return m_display_color;
166 }
167 
168 /**
169  * @return World position of jump node
170  */
GetPosition()171 vec3d *CJumpNode::GetPosition()
172 {
173 	return &m_pos;
174 }
175 
176 /*
177 * @return Polymodel Instance Index
178 */
GetPolymodelInstanceNum()179 int CJumpNode::GetPolymodelInstanceNum()
180 {
181 	return m_polymodel_instance_num;
182 }
183 
184 // Settor functions for private variables
185 
186 /**
187  * Set jump node alpha and color
188  *
189  * @param r Red component
190  * @param g Green component
191  * @param b Blue component
192  * @param alpha Alpha component
193  */
SetAlphaColor(int r,int g,int b,int alpha)194 void CJumpNode::SetAlphaColor(int r, int g, int b, int alpha)
195 {
196 	CLAMP(r, 0, 255);
197 	CLAMP(g, 0, 255);
198 	CLAMP(b, 0, 255);
199 	CLAMP(alpha, 0, 255);
200 
201 	m_flags |= JN_USE_DISPLAY_COLOR;
202 	gr_init_alphacolor(&m_display_color, r, g, b, alpha);
203 }
204 
205 /**
206  * Set jump node model to render
207  *
208  * @param model_name Name of model file to load
209  * @param show_polys Whether to render wireframe or not
210  */
SetModel(const char * model_name,bool show_polys)211 void CJumpNode::SetModel(const char *model_name, bool show_polys)
212 {
213 	Assert(model_name != NULL);
214 
215 	//Try to load the new model; if we can't, then we can't set it
216 	int new_model = model_load(model_name, 0, NULL, 0);
217 
218 	if(new_model == -1)
219 	{
220 		Warning(LOCATION, "Couldn't load model file %s for jump node %s", model_name, m_name);
221 		return;
222 	}
223 
224 	//If there's an old model, unload it
225 	if(m_modelnum != -1)
226 		model_unload(m_modelnum);
227 
228 	//Now actually set stuff
229 	m_modelnum = new_model;
230 	m_flags |= JN_SPECIAL_MODEL;
231 	m_radius = model_get_radius(m_modelnum);
232 
233 	//Do we want to change poly showing?
234 	if(show_polys)
235 		m_flags |= JN_SHOW_POLYS;
236 	else
237 		m_flags &= ~JN_SHOW_POLYS;
238 }
239 
240 /**
241  * Set jump node name
242  *
243  * @param new_name New name to set
244  */
SetName(const char * new_name)245 void CJumpNode::SetName(const char *new_name)
246 {
247 	Assert(new_name != NULL);
248 
249 	#ifndef NDEBUG
250 	CJumpNode* check = jumpnode_get_by_name(new_name);
251 	Assertion((check == this || !check), "Jumpnode %s is being renamed to %s, but a jump node with that name already exists in the mission!\n", m_name, new_name);
252 	#endif
253 
254 	strcpy_s(m_name, new_name);
255 }
256 
257 /**
258  * Set appearance, hidden or not
259  *
260  * @param enabled Visibility to set
261  */
SetVisibility(bool enabled)262 void CJumpNode::SetVisibility(bool enabled)
263 {
264 	if(enabled)
265 	{
266 		m_flags&=~JN_HIDE;
267 	}
268 	else
269 	{
270 		// Untarget this node if it is already targeted
271 		if ( Player_ai->target_objnum == m_objnum )
272 			Player_ai->target_objnum = -1;
273 		m_flags|=JN_HIDE;
274 	}
275 }
276 
277 // Query functions
278 
279 /**
280  * @return Is the jump node hidden when rendering?
281  */
IsHidden()282 bool CJumpNode::IsHidden()
283 {
284 	if(m_flags & JN_HIDE)
285 		return true;
286 	else
287 		return false;
288 }
289 
290 /**
291  * @return Is the jump node colored any other color than default white?
292  */
IsColored()293 bool CJumpNode::IsColored()
294 {
295 	return ((m_flags & JN_USE_DISPLAY_COLOR) != 0);
296 }
297 
298 /**
299  * @return Is the jump node model set differently from the default one?
300  */
IsSpecialModel()301 bool CJumpNode::IsSpecialModel()
302 {
303 	return ((m_flags & JN_SPECIAL_MODEL) != 0);
304 }
305 
306 /**
307 * Render jump node. Creates its own draw list to render
308 *
309 * @param pos		World position
310 * @param view_pos	Viewer's world position, can be NULL
311 */
Render(vec3d * pos,vec3d * view_pos)312 void CJumpNode::Render(vec3d *pos, vec3d *view_pos)
313 {
314 	model_draw_list scene;
315 
316 	Render(&scene, pos, view_pos);
317 
318 	scene.init_render();
319 	scene.render_all();
320 	scene.render_outlines();
321 
322 	gr_set_fill_mode(GR_FILL_MODE_SOLID);
323 	gr_clear_states();
324 }
325 
326 /**
327 * Render jump node
328 *
329 * @param scene		A scene's draw list
330 * @param pos		World position
331 * @param view_pos	Viewer's world position, can be NULL
332 */
Render(model_draw_list * scene,vec3d * pos,vec3d * view_pos)333 void CJumpNode::Render(model_draw_list* scene, vec3d *pos, vec3d *view_pos)
334 {
335 	Assert(pos != NULL);
336 	// Assert(view_pos != NULL); - view_pos can be NULL
337 
338 	if(m_flags & JN_HIDE)
339 		return;
340 
341 	if(m_modelnum < 0)
342 		return;
343 
344 	matrix node_orient = IDENTITY_MATRIX;
345 
346 	int mr_flags = MR_NO_LIGHTING | MR_NO_BATCH;
347 	if(!(m_flags & JN_SHOW_POLYS)) {
348 		mr_flags |= MR_NO_CULL | MR_NO_POLYS | MR_SHOW_OUTLINE | MR_SHOW_OUTLINE_HTL | MR_NO_TEXTURING;
349 	}
350 
351 	model_render_params render_info;
352 
353 	render_info.set_object_number(m_objnum);
354 	render_info.set_detail_level_lock(0);
355 	render_info.set_flags(mr_flags);
356 
357 	if ( Fred_running ) {
358 		render_info.set_color(m_display_color);
359 
360 		model_render_queue(&render_info, scene, m_modelnum, &node_orient, pos);
361 	} else {
362 		if (m_flags & JN_USE_DISPLAY_COLOR) {
363 			//gr_set_color_fast(&m_display_color);
364 			render_info.set_color(m_display_color);
365 		}
366 		else if ( view_pos != NULL) {
367 			int alpha_index = HUD_color_alpha;
368 
369 			// generate alpha index based on distance to jump this
370 			float dist;
371 
372 			dist = vm_vec_dist_quick(view_pos, pos);
373 
374 			// linearly interpolate alpha.  At 1000m or less, full intensity.  At 10000m or more 1/2 intensity.
375 			if ( dist < 1000 ) {
376 				alpha_index = HUD_COLOR_ALPHA_USER_MAX - 2;
377 			} else if ( dist > 10000 ) {
378 				alpha_index = HUD_COLOR_ALPHA_USER_MIN;
379 			} else {
380 				alpha_index = (int)std::lround( HUD_COLOR_ALPHA_USER_MAX - 2 + (dist-1000) * (HUD_COLOR_ALPHA_USER_MIN-HUD_COLOR_ALPHA_USER_MAX-2) / (9000));
381 				if ( alpha_index < HUD_COLOR_ALPHA_USER_MIN ) {
382 					alpha_index = HUD_COLOR_ALPHA_USER_MIN;
383 				}
384 			}
385 
386 			render_info.set_color(HUD_color_defaults[alpha_index]);
387 		} else {
388 			render_info.set_color(HUD_color_red, HUD_color_green, HUD_color_blue);
389 		}
390 
391 		model_render_queue(&render_info, scene, m_modelnum, &node_orient, pos);
392 	}
393 
394 }
395 
396 /**
397  * Get jump node by given name
398  *
399  * @param name Name of jump node
400  * @return Jump node object
401  */
jumpnode_get_by_name(const char * name)402 CJumpNode *jumpnode_get_by_name(const char* name)
403 {
404 	Assert(name != NULL);
405 	SCP_list<CJumpNode>::iterator jnp;
406 
407 	for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
408 		if(!stricmp(jnp->GetName(), name))
409 			return &(*jnp);
410 	}
411 
412 	return NULL;
413 }
414 
415 /**
416  * Get jump node object by the object number
417  *
418  * @param objnum to search for
419  * @return Jump node object pointer
420  */
jumpnode_get_by_objnum(int objnum)421 CJumpNode *jumpnode_get_by_objnum(int objnum)
422 {
423 	Assert(objnum > -1);
424 
425 	for (CJumpNode &jnp : Jump_nodes) {
426 		if (jnp.GetSCPObjectNumber() == objnum)
427 			return &(jnp);
428 	}
429 
430 	return nullptr;
431 }
432 
433 /**
434  * Given an object, returns which jump node it's inside (if any)
435  *
436  * @param objp Object
437  * @return Jump node object or NULL if not in one
438  */
jumpnode_get_which_in(const object * objp)439 CJumpNode *jumpnode_get_which_in(const object *objp)
440 {
441 	Assert(objp != NULL);
442 	SCP_list<CJumpNode>::iterator jnp;
443 	float radius, dist;
444 
445 	for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
446 		if(jnp->GetModelNumber() < 0)
447 			continue;
448 
449 		radius = model_get_radius( jnp->GetModelNumber() );
450 		dist = vm_vec_dist( &objp->pos, &jnp->GetSCPObject()->pos );
451 		if ( dist <= radius ) {
452 			return &(*jnp);
453 		}
454 	}
455 
456 	return NULL;
457 }
458 
459 /**
460  * Render all function
461  *
462  * @note Only called by FRED
463  */
jumpnode_render_all()464 void jumpnode_render_all()
465 {
466 	SCP_list<CJumpNode>::iterator jnp;
467 
468 	for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
469 		jnp->Render(&jnp->GetSCPObject()->pos);
470 	}
471 }
472 
473 /**
474  * Level cleanup
475  */
jumpnode_level_close()476 void jumpnode_level_close()
477 {
478 	Jump_nodes.clear();
479 }
480