FS2_Open
Open source remastering of the Freespace 2 engine
trails.cpp
Go to the documentation of this file.
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 "cmdline/cmdline.h"
13 #include "globalincs/systemvars.h"
15 #include "io/timer.h"
16 #include "render/3d.h"
17 #include "ship/ship.h"
18 #include "weapon/trails.h"
19 
22 
24 
25 // Reset everything between levels
27 {
28  Num_trails = 0;
29  Trails.next = &Trails;
30 
31  if (Trail_buffer_object < 0) {
33  }
34 }
35 
37 {
38  trail *nextp;
39  for(trail *trailp = Trails.next; trailp != &Trails; trailp = nextp)
40  {
41  nextp = trailp->next;
42 
43  //Now we can delete it
44  delete trailp;
45  }
46 
47  Num_trails=0;
48 }
49 
50 //returns the number of a free trail
51 //returns -1 if no free trails
53 {
54  // standalone server should never create trails
55  // No trails at slot 0
57  return NULL;
58 
59  // Make a new trail
60  trail *trailp = new trail;
61 
62  // increment counter
63  Num_trails++;
64 
65  // Init the trail data
66  trailp->info = *info;
67  trailp->tail = 0;
68  trailp->head = 0;
69  trailp->object_died = false;
70  trailp->trail_stamp = timestamp(trailp->info.stamp);
71 
72  //Add it to the front of the list
73  //This is quickest since there are no prev vars
74  trailp->next = Trails.next;
75  Trails.next = trailp;
76 
77  return trailp;
78 }
79 
80 // output top and bottom vectors
81 // fvec == forward vector (eye viewpoint basically. in world coords)
82 // pos == world coordinate of the point we're calculating "around"
83 // w == width of the diff between top and bottom around pos
84 void trail_calc_facing_pts( vec3d *top, vec3d *bot, vec3d *fvec, vec3d *pos, float w )
85 {
86  vec3d uvec, rvec;
87 
88  vm_vec_sub( &rvec, &Eye_position, pos );
89  if (!IS_VEC_NULL(&rvec))
90  vm_vec_normalize( &rvec );
91 
92  vm_vec_cross(&uvec,fvec,&rvec);
93  if (!IS_VEC_NULL(&uvec))
94  vm_vec_normalize(&uvec);
95 
96  vm_vec_scale_add( top, pos, &uvec, w * 0.5f );
97  vm_vec_scale_add( bot, pos, &uvec, -w * 0.5f );
98 }
99 
100 // trail is on ship
102 {
103  if(trailp == NULL)
104  return 0;
105 
106  for(int idx=0; idx<MAX_SHIP_CONTRAILS; idx++){
107  if(shipp->trail_ptr[idx] == trailp){
108  return 1;
109  }
110  }
111 
112  // nope
113  return 0;
114 }
115 
116 // Render the trail behind a missile.
117 // Basically a queue of points that face the viewer
118 extern int Cmdline_nohtl;
119 
120 static vertex *Trail_v_list = NULL;
121 static int Trail_verts_allocated = 0;
122 
123 static void deallocate_trail_verts()
124 {
125  if (Trail_v_list != NULL) {
126  vm_free(Trail_v_list);
127  Trail_v_list = NULL;
128  }
129 }
130 
131 static void allocate_trail_verts(int num_verts)
132 {
133  if (num_verts <= 0)
134  return;
135 
136  if (num_verts <= Trail_verts_allocated)
137  return;
138 
139  if (Trail_v_list != NULL) {
140  vm_free(Trail_v_list);
141  Trail_v_list = NULL;
142  }
143 
144  Trail_v_list = (vertex*) vm_malloc( num_verts * sizeof(vertex) );
145 
146  memset( Trail_v_list, 0, sizeof(vertex) * Trail_verts_allocated );
147 
148  Trail_verts_allocated = num_verts;
149 
150 
151  static bool will_free_at_exit = false;
152 
153  if ( !will_free_at_exit ) {
154  atexit(deallocate_trail_verts);
155  will_free_at_exit = true;
156  }
157 }
158 
159 void trail_add_batch(trail * trailp)
160 {
161  int sections[NUM_TRAIL_SECTIONS];
162  int num_sections = 0;
163  int i;
164  vec3d topv, botv, *fvec, last_pos, tmp_fvec;
165  vertex top, bot, top_prev, bot_prev;
166  float w;
167  ubyte l;
168  vec3d centerv;
169 
170  if (trailp->tail == trailp->head)
171  return;
172 
173  // if this trail is on the player ship, and he's in any padlock view except rear view, don't draw
174  if ((Player_ship != NULL) && trail_is_on_ship(trailp, Player_ship) &&
176  {
177  return;
178  }
179 
180  trail_info *ti = &trailp->info;
181 
182  int n = trailp->tail;
183 
184  do {
185  n--;
186 
187  if (n < 0)
188  n = NUM_TRAIL_SECTIONS - 1;
189 
190  if (trailp->val[n] > 1.0f)
191  break;
192 
193  sections[num_sections++] = n;
194  } while (n != trailp->head);
195 
196  if (num_sections <= 0)
197  return;
198 
199  Assertion(ti->texture.bitmap_id != -1, "Weapon trail %s could not be loaded", ti->texture.filename); // We can leave this as an assert, but tell them how to fix it. --Chief
200 
201  memset(&top, 0, sizeof(vertex));
202  memset(&bot, 0, sizeof(vertex));
203  memset(&top_prev, 0, sizeof(vertex));
204  memset(&bot_prev, 0, sizeof(vertex));
205 
206  float w_size = (ti->w_end - ti->w_start);
207  float a_size = (ti->a_end - ti->a_start);
208  int num_faded_sections = ti->n_fade_out_sections;
209 
210  for (i = 0; i < num_sections; i++) {
211  n = sections[i];
212  float init_fade_out = 1.0f;
213 
214  if ((num_faded_sections > 0) && (i < num_faded_sections)) {
215  init_fade_out = ((float)i) / (float)num_faded_sections;
216  }
217 
218  w = trailp->val[n] * w_size + ti->w_start;
219  if (init_fade_out != 1.0f) {
220  l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f * init_fade_out * init_fade_out);
221  }
222  else {
223  l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f);
224  }
225 
226  if (i == 0) {
227  if (num_sections > 1) {
228  vm_vec_sub(&tmp_fvec, &trailp->pos[n], &trailp->pos[sections[i + 1]]);
229  vm_vec_normalize_safe(&tmp_fvec);
230  fvec = &tmp_fvec;
231  }
232  else {
233  fvec = &tmp_fvec;
234  fvec->xyz.x = 0.0f;
235  fvec->xyz.y = 0.0f;
236  fvec->xyz.z = 1.0f;
237  }
238  }
239  else {
240  vm_vec_sub(&tmp_fvec, &last_pos, &trailp->pos[n]);
241  vm_vec_normalize_safe(&tmp_fvec);
242  fvec = &tmp_fvec;
243  }
244 
245  trail_calc_facing_pts(&topv, &botv, fvec, &trailp->pos[n], w);
246 
247  if (!Cmdline_nohtl) {
248  g3_transfer_vertex(&top, &topv);
249  g3_transfer_vertex(&bot, &botv);
250  }
251  else {
252  g3_rotate_vertex(&top, &topv);
253  g3_rotate_vertex(&bot, &botv);
254  }
255 
256  top.r = top.g = top.b = l;
257  bot.r = bot.g = bot.b = l;
258  top.a = bot.a = l;
259 
260  float U = i2fl(i);
261 
262  top.texture_position.u = U;
263  top.texture_position.v = 1.0f;
264 
265  bot.texture_position.u = U;
266  bot.texture_position.v = 0.0f;
267 
268  if (i > 0) {
269  if (i == num_sections - 1) {
270  // Last one...
271  vm_vec_avg(&centerv, &topv, &botv);
272 
273  vertex center_vert = vertex();
274 
275  if (!Cmdline_nohtl)
276  g3_transfer_vertex(&center_vert, &centerv);
277  else
278  g3_rotate_vertex(&center_vert, &centerv);
279 
280  center_vert.texture_position.u = U + 1.0f;
281  center_vert.texture_position.v = 0.5f;
282  center_vert.a = center_vert.r = center_vert.g = center_vert.b = l;
283 
284  vertex tri[3];
285 
286  tri[1] = top_prev;
287  tri[2] = bot_prev;
288  tri[0] = center_vert;
289 
291  ti->texture.bitmap_id,
293  tri,
294  1.0f
295  );
296  } else {
297  vertex quad[4];
298 
299  quad[0] = top_prev;
300  quad[1] = bot_prev;
301  quad[2] = bot;
302  quad[3] = top;
303 
305  ti->texture.bitmap_id,
307  quad,
308  1.0f
309  );
310  }
311  }
312 
313  last_pos = trailp->pos[n];
314  top_prev = top;
315  bot_prev = bot;
316  }
317 }
318 
319 void trail_render( trail * trailp )
320 {
321  int sections[NUM_TRAIL_SECTIONS];
322  int num_sections = 0;
323  int i;
324  vec3d topv, botv, *fvec, last_pos, tmp_fvec;
325  vertex top, bot;
326  int nv = 0;
327  float w;
328  ubyte l;
329  vec3d centerv;
330 
331  if (trailp->tail == trailp->head)
332  return;
333 
334  // if this trail is on the player ship, and he's in any padlock view except rear view, don't draw
335  if ( (Player_ship != NULL) && trail_is_on_ship(trailp, Player_ship) &&
337  {
338  return;
339  }
340 
341  trail_info *ti = &trailp->info;
342 
343  int n = trailp->tail;
344 
345  do {
346  n--;
347 
348  if (n < 0)
349  n = NUM_TRAIL_SECTIONS-1;
350 
351  if (trailp->val[n] > 1.0f)
352  break;
353 
354  sections[num_sections++] = n;
355  } while ( n != trailp->head );
356 
357  if (num_sections <= 0)
358  return;
359 
360  Assertion(ti->texture.bitmap_id != -1, "Weapon trail %s could not be loaded", ti->texture.filename); // We can leave this as an assert, but tell them how to fix it. --Chief
361 
362  memset( &top, 0, sizeof(vertex) );
363  memset( &bot, 0, sizeof(vertex) );
364 
365  // it's a tristrip, so allocate for 2+1
366  allocate_trail_verts((num_sections * 2) + 1);
367 
368  float w_size = (ti->w_end - ti->w_start);
369  float a_size = (ti->a_end - ti->a_start);
370  int num_faded_sections = ti->n_fade_out_sections;
371 
372  for (i = 0; i < num_sections; i++) {
373  n = sections[i];
374  float init_fade_out = 1.0f;
375 
376  if ((num_faded_sections > 0) && (i < num_faded_sections)) {
377  init_fade_out = ((float) i) / (float) num_faded_sections;
378  }
379 
380  w = trailp->val[n] * w_size + ti->w_start;
381  if (init_fade_out != 1.0f) {
382  l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f * init_fade_out * init_fade_out);
383  } else {
384  l = (ubyte)fl2i((trailp->val[n] * a_size + ti->a_start) * 255.0f);
385  }
386 
387  if ( i == 0 ) {
388  if ( num_sections > 1 ) {
389  vm_vec_sub(&tmp_fvec, &trailp->pos[n], &trailp->pos[sections[i+1]] );
390  vm_vec_normalize_safe(&tmp_fvec);
391  fvec = &tmp_fvec;
392  } else {
393  fvec = &tmp_fvec;
394  fvec->xyz.x = 0.0f;
395  fvec->xyz.y = 0.0f;
396  fvec->xyz.z = 1.0f;
397  }
398  } else {
399  vm_vec_sub(&tmp_fvec, &last_pos, &trailp->pos[n] );
400  vm_vec_normalize_safe(&tmp_fvec);
401  fvec = &tmp_fvec;
402  }
403 
404  trail_calc_facing_pts( &topv, &botv, fvec, &trailp->pos[n], w );
405 
406  if ( !Cmdline_nohtl ) {
407  g3_transfer_vertex( &top, &topv );
408  g3_transfer_vertex( &bot, &botv );
409  } else {
410  g3_rotate_vertex( &top, &topv );
411  g3_rotate_vertex( &bot, &botv );
412  }
413 
414  top.a = bot.a = l;
415 
416  if (i > 0) {
417  float U = i2fl(i);
418 
419  if (i == num_sections-1) {
420  // Last one...
421  vm_vec_avg( &centerv, &topv, &botv );
422 
423  if ( !Cmdline_nohtl )
424  g3_transfer_vertex( &Trail_v_list[nv+2], &centerv );
425  else
426  g3_rotate_vertex( &Trail_v_list[nv+2], &centerv );
427 
428  Trail_v_list[nv].a = l;
429 
430  Trail_v_list[nv].texture_position.u = U;
431  Trail_v_list[nv].texture_position.v = 1.0f;
432  Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
433  nv++;
434 
435  Trail_v_list[nv].texture_position.u = U;
436  Trail_v_list[nv].texture_position.v = 0.0f;
437  Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
438  nv++;
439 
440  Trail_v_list[nv].texture_position.u = U + 1.0f;
441  Trail_v_list[nv].texture_position.v = 0.5f;
442  Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = 0;
443  nv++;
444  } else {
445  Trail_v_list[nv].texture_position.u = U;
446  Trail_v_list[nv].texture_position.v = 1.0f;
447  Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
448  nv++;
449 
450  Trail_v_list[nv].texture_position.u = U;
451  Trail_v_list[nv].texture_position.v = 0.0f;
452  Trail_v_list[nv].r = Trail_v_list[nv].g = Trail_v_list[nv].b = l;
453  nv++;
454  }
455  }
456 
457  last_pos = trailp->pos[n];
458  Trail_v_list[nv] = top;
459  Trail_v_list[nv+1] = bot;
460  }
461 
462  if ( !nv )
463  return;
464 
465  if (nv < 3)
466  Error( LOCATION, "too few verts in trail render\n" );
467 
468  // there should always be three verts in the last section and 2 everyware else, therefore there should always be an odd number of verts
469  if ( (nv % 2) != 1 )
470  Warning( LOCATION, "even number of verts in trail render\n" );
471 
472  profile_begin("Trail Draw");
475  profile_end("Trail Draw");
476 }
477 
478 
479 
480 void trail_add_segment( trail *trailp, vec3d *pos )
481 {
482  int next = trailp->tail;
483  trailp->tail++;
484  if ( trailp->tail >= NUM_TRAIL_SECTIONS )
485  trailp->tail = 0;
486 
487  if ( trailp->head == trailp->tail ) {
488  // wrapped!!
489  trailp->head++;
490  if ( trailp->head >= NUM_TRAIL_SECTIONS )
491  trailp->head = 0;
492  }
493 
494  trailp->pos[next] = *pos;
495  trailp->val[next] = 0.0f;
496 }
497 
498 void trail_set_segment( trail *trailp, vec3d *pos )
499 {
500  int next = trailp->tail-1;
501  if ( next < 0 ) {
502  next = NUM_TRAIL_SECTIONS-1;
503  }
504 
505  trailp->pos[next] = *pos;
506 }
507 
508 void trail_move_all(float frametime)
509 {
510  int num_alive_segments,n;
511  float time_delta;
512  trail *next_trail;
513  trail *prev_trail = &Trails;
514 
515  for (trail *trailp = Trails.next; trailp != &Trails; trailp = next_trail) {
516  next_trail = trailp->next;
517 
518  num_alive_segments = 0;
519 
520  if ( trailp->tail != trailp->head ) {
521  n = trailp->tail;
522  time_delta = frametime / trailp->info.max_life;
523  do {
524  n--;
525  if ( n < 0 ) n = NUM_TRAIL_SECTIONS-1;
526 
527  trailp->val[n] += time_delta;
528 
529  if ( trailp->val[n] <= 1.0f ) {
530  num_alive_segments++; // Record how many still alive.
531  }
532 
533  } while ( n != trailp->head );
534  }
535 
536  if ( (num_alive_segments < 1) && trailp->object_died)
537  {
538  prev_trail->next = trailp->next;
539  delete trailp;
540 
541  // decrement counter
542  Num_trails--;
543  }
544  else
545  {
546  prev_trail = trailp;
547  }
548  }
549 }
550 
551 void trail_object_died( trail *trailp )
552 {
553  trailp->object_died = true;
554 }
555 
557 {
558  // No trails at slot 0
559  if ( !Detail.weapon_extras )
560  return;
561 
562  for(trail *trailp = Trails.next; trailp!=&Trails; trailp = trailp->next )
563  {
564  //trail_add_batch(trailp);
565  trail_render(trailp);
566  }
567 
568  //profile_begin("Batch Render Trails");
569  //batch_render_all(Trail_buffer_object);
570  //profile_end("Batch Render Trails");
571 }
573 {
574  return timestamp_elapsed(trailp->trail_stamp);
575 }
576 
577 void trail_set_stamp(trail *trailp)
578 {
579  trailp->trail_stamp = timestamp(trailp->info.stamp);
580 }
int timestamp(int delta_ms)
Definition: timer.cpp:226
int stamp
Definition: trails.h:29
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
int batch_add_quad(int texture, int tmap_flags, vertex *verts, float alpha)
Definition: grbatch.cpp:858
int Cmdline_nohtl
Definition: cmdline.cpp:438
struct vertex vertex
int Trail_buffer_object
Definition: trails.cpp:23
int Game_mode
Definition: systemvars.cpp:24
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
void trail_level_init()
Definition: trails.cpp:26
float v
Definition: pstypes.h:135
Definition: trails.h:34
ubyte g
Definition: pstypes.h:175
int trail_stamp_elapsed(trail *trailp)
Definition: trails.cpp:572
int head
Definition: trails.h:35
int trail_stamp
Definition: trails.h:39
ubyte g3_transfer_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:84
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
int tail
Definition: trails.h:35
Definition: pstypes.h:88
#define gr_render
Definition: 2d.h:805
struct vec3d::@225::@227 xyz
void trail_object_died(trail *trailp)
Definition: trails.cpp:551
#define TMAP_HTL_3D_UNLIT
Definition: tmapper.h:63
GLclampf f
Definition: Glext.h:7097
#define Assertion(expr, msg,...)
Definition: clang.h:41
int bitmap_id
Definition: generic.h:53
void profile_begin(const char *name)
Definition: profiling.cpp:80
void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
Definition: 2d.cpp:2105
ship * shipp
Definition: lua.cpp:9162
#define IS_VEC_NULL(v)
Definition: vecmat.h:28
uv_pair texture_position
Definition: pstypes.h:174
ubyte a
Definition: pstypes.h:175
int Num_trails
Definition: trails.cpp:20
#define VM_PADLOCK_RIGHT
Definition: systemvars.h:41
#define GR_ALPHABLEND_FILTER
Definition: 2d.h:349
#define NUM_TRAIL_SECTIONS
Definition: trails.h:18
#define TMAP_FLAG_GOURAUD
Definition: tmapper.h:40
detail_levels Detail
Definition: systemvars.cpp:478
void trail_render_all()
Definition: trails.cpp:556
#define w(p)
Definition: modelsinc.h:68
ubyte g3_rotate_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:97
float u
Definition: pstypes.h:135
float a_start
Definition: trails.h:26
Definition: ship.h:534
bool object_died
Definition: trails.h:38
struct trail * next
Definition: trails.h:44
float vm_vec_normalize_safe(vec3d *v)
Definition: vecmat.cpp:471
int idx
Definition: multiui.cpp:761
trail * trail_ptr[MAX_SHIP_CONTRAILS]
Definition: ship.h:721
GLclampd n
Definition: Glext.h:7286
unsigned char ubyte
Definition: pstypes.h:62
void trail_add_segment(trail *trailp, vec3d *pos)
Definition: trails.cpp:480
vec3d pos[NUM_TRAIL_SECTIONS]
Definition: trails.h:36
ship * Player_ship
Definition: ship.cpp:124
void trail_add_batch(trail *trailp)
Definition: trails.cpp:159
#define TMAP_FLAG_RGB
Definition: tmapper.h:39
ubyte b
Definition: pstypes.h:175
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
#define GM_STANDALONE_SERVER
Definition: systemvars.h:27
trail_info info
Definition: trails.h:42
int trail_is_on_ship(trail *trailp, ship *shipp)
Definition: trails.cpp:101
#define vm_malloc(size)
Definition: pstypes.h:547
trail * trail_create(trail_info *info)
Definition: trails.cpp:52
void vm_vec_sub(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:168
#define VM_PADLOCK_UP
Definition: systemvars.h:38
vec3d Eye_position
Definition: 3dsetup.cpp:27
void trail_render(trail *trailp)
Definition: trails.cpp:319
void trail_move_all(float frametime)
Definition: trails.cpp:508
void profile_end(const char *name)
Definition: profiling.cpp:130
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
trail Trails
Definition: trails.cpp:21
float val[NUM_TRAIL_SECTIONS]
Definition: trails.h:37
GLubyte GLubyte GLubyte GLubyte w
Definition: Glext.h:5679
#define VM_PADLOCK_LEFT
Definition: systemvars.h:40
#define fl2i(fl)
Definition: floating.h:33
void trail_set_stamp(trail *trailp)
Definition: trails.cpp:577
void trail_level_close()
Definition: trails.cpp:36
#define LOCATION
Definition: pstypes.h:245
#define TMAP_FLAG_ALPHA
Definition: tmapper.h:53
vec3d * vm_vec_avg(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:217
#define TMAP_FLAG_TEXTURED
Definition: tmapper.h:36
int Viewer_mode
Definition: systemvars.cpp:28
#define timestamp_elapsed(stamp)
Definition: timer.h:102
hull_check pos
Definition: lua.cpp:5050
int batch_add_tri(int texture, int tmap_flags, vertex *verts, float alpha)
Definition: grbatch.cpp:828
void trail_calc_facing_pts(vec3d *top, vec3d *bot, vec3d *fvec, vec3d *pos, float w)
Definition: trails.cpp:84
void trail_set_segment(trail *trailp, vec3d *pos)
Definition: trails.cpp:498
#define TMAP_FLAG_TRISTRIP
Definition: tmapper.h:67
#define i2fl(i)
Definition: floating.h:32
#define MAX_SHIP_CONTRAILS
Definition: ship.h:520
#define GR_BITBLT_MODE_NORMAL
Definition: 2d.h:351
#define gr_create_stream_buffer
Definition: 2d.h:886
float w_start
Definition: trails.h:24
char filename[MAX_FILENAME_LEN]
Definition: generic.h:52
vec3d * vm_vec_cross(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:645
float a_end
Definition: trails.h:27
struct trail trail
float w_end
Definition: trails.h:25
GLdouble GLdouble GLdouble GLdouble top
Definition: Glext.h:10330
generic_bitmap texture
Definition: trails.h:30
int n_fade_out_sections
Definition: trails.h:31
ubyte r
Definition: pstypes.h:175
float vm_vec_normalize(vec3d *v)
Definition: vecmat.cpp:460