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