]> icculus.org git repositories - btb/d2x.git/blob - main/editor/medmisc.c
imported missing editor files from d1x
[btb/d2x.git] / main / editor / medmisc.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13 /*
14  * $Source: /cvs/cvsroot/d2x/main/editor/medmisc.c,v $
15  * $Revision: 1.1 $
16  * $Author: btb $
17  * $Date: 2004-12-19 13:54:27 $
18  * 
19  * Miscellaneous functions stripped out of med.c
20  * 
21  * $Log: not supported by cvs2svn $
22  * Revision 1.1.1.1  1999/06/14 22:03:51  donut
23  * Import of d1x 1.37 source.
24  *
25  * Revision 2.1  1995/03/06  15:20:50  john
26  * New screen mode method.
27  * 
28  * Revision 2.0  1995/02/27  11:36:40  john
29  * Version 2.0. Ansi-fied.
30  * 
31  * Revision 1.31  1994/11/27  23:17:20  matt
32  * Made changes for new mprintf calling convention
33  * 
34  * Revision 1.30  1994/11/17  14:48:11  mike
35  * validation functions moved from editor to game.
36  * 
37  * Revision 1.29  1994/08/25  21:56:15  mike
38  * IS_CHILD stuff.
39  * 
40  * Revision 1.28  1994/08/09  16:06:00  john
41  * Added the ability to place players.  Made old
42  * Player variable be ConsoleObject.
43  * 
44  * Revision 1.27  1994/07/21  17:25:43  matt
45  * Took out unused func medlisp_create_new_mine() and its prototype
46  * 
47  * Revision 1.26  1994/07/21  13:27:01  matt
48  * Cleaned up render code and added error checking
49  * 
50  * Revision 1.25  1994/07/20  15:32:52  matt
51  * Added func to call g3_point_2_vec() for texture-mapped window
52  * 
53  * Revision 1.24  1994/07/15  15:26:53  yuan
54  * Fixed warning
55  * 
56  * Revision 1.23  1994/07/14  14:45:16  yuan
57  * Added function to set default segment and attach.
58  * 
59  * Revision 1.22  1994/07/14  09:46:34  yuan
60  * Make E attach segment as well as make default.
61  * 
62  * 
63  * Revision 1.21  1994/07/11  18:39:17  john
64  * Reversed y axis roll.
65  * 
66  * Revision 1.20  1994/07/06  16:36:32  mike
67  * Add hook for game to render wireframe view: draw_world_from_game.
68  * 
69  * Revision 1.19  1994/06/24  14:08:31  john
70  * Changed calling params for render_frame.
71  * 
72  * Revision 1.18  1994/06/23  15:54:02  matt
73  * Finished hacking in 3d rendering in big window
74  * 
75  * Revision 1.17  1994/06/22  00:32:56  matt
76  * New version, without all the errors of the last version. Sorry.
77  * 
78  * Revision 1.15  1994/05/23  14:48:54  mike
79  * make current segment be add segment.
80  * 
81  * Revision 1.14  1994/05/19  12:09:35  matt
82  * Use new vecmat macros and globals
83  * 
84  * Revision 1.13  1994/05/14  17:17:55  matt
85  * Got rid of externs in source (non-header) files
86  * 
87  * Revision 1.12  1994/05/09  23:35:06  mike
88  * Add ClearFoundList, which is probably no longer being called.
89  * 
90  * Revision 1.11  1994/05/04  14:11:40  mike
91  * Increase render depth from 4 to 6 by default.
92  * 
93  * Revision 1.10  1994/04/27  21:00:25  matt
94  * Made texture-mapped window redraw when editor state variables (such as
95  * current object) have changed.
96  * 
97  * Revision 1.9  1994/03/31  12:03:38  matt
98  * Cleaned up includes
99  * 
100  * Revision 1.8  1994/02/17  11:31:21  matt
101  * Changes in object system
102  * 
103  * Revision 1.7  1994/02/11  11:05:14  yuan
104  * Make chase mode unsettable... Gives a warning on the mono.
105  * 
106  * Revision 1.6  1994/01/21  17:37:24  matt
107  * Moved code from render_frame() to caller, making code cleaner
108  * 
109  * Revision 1.5  1994/01/11  18:12:43  yuan
110  * compress_mines removed.  Now it is called within
111  * the gamesave.min save whenever we go into the game.
112  * 
113  * Revision 1.4  1994/01/05  10:54:15  john
114  * New object code by John
115  * 
116  * Revision 1.3  1993/12/29  16:15:27  mike
117  * Kill scale field from segment struct.
118  * 
119  * Revision 1.2  1993/12/17  12:05:00  john
120  * Took stuff out of med.c; moved into medsel.c, meddraw.c, medmisc.c
121  * 
122  * Revision 1.1  1993/12/17  08:35:47  john
123  * Initial revision
124  * 
125  * 
126  */
127
128
129 #ifdef RCS
130 static char rcsid[] = "$Id: medmisc.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
131 #endif
132
133 #include <stdio.h>
134 #include <stdlib.h>
135 #include <stdarg.h>
136 #include <string.h>
137 #ifdef __MSDOS__
138 #include <process.h>
139 #endif
140
141 #include "gr.h"
142 #include "ui.h"
143 #include "3d.h"
144
145 #include "u_mem.h"
146 #include "error.h"
147 #include "mono.h"
148 #include "key.h"
149 #include "func.h"
150
151 #include "inferno.h"
152 #include "editor/editor.h"
153 #include "segment.h"
154
155 #include "render.h"
156 #include "screens.h"
157 #include "object.h"
158
159 #include "texpage.h"            // For texpage_goto_first
160 #include "meddraw.h"            // For draw_World
161 #include "game.h"
162
163 //return 2d distance, i.e, sqrt(x*x + y*y)
164 #ifdef __WATCOMC__
165 long dist_2d(long x,long y);
166
167 #pragma aux dist_2d parm [eax] [ebx] value [eax] modify [ecx edx] = \
168         "imul   eax"                    \
169         "xchg   ebx,eax"                \
170         "mov    ecx,edx"                \
171         "imul   eax"                    \
172         "add    eax,ebx"                \
173         "adc    edx,ecx"                \
174         "call   quad_sqrt";
175 #else
176 #include <math.h>
177 long dist_2d(long x,long y) {
178         return (long)sqrt((double)x * (double)x + (double)y * (double)y);
179 }
180 #endif
181
182 // Given mouse movement in dx, dy, returns a 3x3 rotation matrix in RotMat.
183 // Taken from Graphics Gems III, page 51, "The Rolling Ball"
184
185 void GetMouseRotation( int idx, int idy, vms_matrix * RotMat )
186 {
187         fix dr, cos_theta, sin_theta, denom, cos_theta1;
188         fix Radius = i2f(100);
189         fix dx,dy;
190         fix dxdr,dydr;
191
192         idy *= -1;
193
194         dx = i2f(idx); dy = i2f(idy);
195
196         dr = dist_2d(dx,dy);
197
198         denom = dist_2d(Radius,dr);
199
200         cos_theta = fixdiv(Radius,denom);
201         sin_theta = fixdiv(dr,denom);
202
203         cos_theta1 = f1_0 - cos_theta;
204
205         dxdr = fixdiv(dx,dr);
206         dydr = fixdiv(dy,dr);
207
208         RotMat->rvec.x = cos_theta + fixmul(fixmul(dydr,dydr),cos_theta1);
209         RotMat->uvec.x = - fixmul(fixmul(dxdr,dydr),cos_theta1);
210         RotMat->fvec.x = fixmul(dxdr,sin_theta);
211
212         RotMat->rvec.y = RotMat->uvec.x;
213         RotMat->uvec.y = cos_theta + fixmul(fixmul(dxdr,dxdr),cos_theta1);
214         RotMat->fvec.y = fixmul(dydr,sin_theta);
215
216         RotMat->rvec.z = -RotMat->fvec.x;
217         RotMat->uvec.z = -RotMat->fvec.y;
218         RotMat->fvec.z = cos_theta;
219
220 }
221
222 int Gameview_lockstep;          //if set, view is locked to Curseg
223
224 int ToggleLockstep()
225 {
226         Gameview_lockstep = !Gameview_lockstep;
227     if (Gameview_lockstep == 0) {
228         if (last_keypress != KEY_L)
229             diagnostic_message("[L] - Lock mode OFF");
230         else
231             diagnostic_message("Lock mode OFF");
232     }
233     if (Gameview_lockstep) {
234         if (last_keypress != KEY_L)
235             diagnostic_message("[L] Lock mode ON");
236         else
237             diagnostic_message("Lock mode ON");
238
239       Cursegp = &Segments[ConsoleObject->segnum];
240                 med_create_new_segment_from_cursegp();
241                 set_view_target_from_segment(Cursegp);
242                 Update_flags = UF_ED_STATE_CHANGED;
243         }
244     return Gameview_lockstep;
245 }
246
247 int medlisp_delete_segment(void)
248 {
249     if (!med_delete_segment(Cursegp)) {
250         if (Lock_view_to_cursegp)
251             set_view_target_from_segment(Cursegp);
252                   autosave_mine(mine_filename);
253                   strcpy(undo_status[Autosave_count], "Delete Segment UNDONE.");
254         Update_flags |= UF_WORLD_CHANGED;
255         mine_changed = 1;
256         diagnostic_message("Segment deleted.");
257         warn_if_concave_segments();     // This could be faster -- just check if deleted segment was concave, warn accordingly
258     }
259
260         return 1;
261 }
262
263 int medlisp_scale_segment(void)
264 {
265         vms_matrix      rotmat;
266         vms_vector      scale;
267
268         scale.x = fl2f((float) func_get_param(0));
269         scale.y = fl2f((float) func_get_param(1));
270         scale.z = fl2f((float) func_get_param(2));
271         med_create_new_segment(&scale);
272         med_rotate_segment(Cursegp,vm_angles_2_matrix(&rotmat,&Seg_orientation));
273         Update_flags |= UF_WORLD_CHANGED;
274         mine_changed = 1;
275
276         return 1;
277 }
278
279 int medlisp_rotate_segment(void)
280 {
281         vms_matrix      rotmat;
282
283         Seg_orientation.p = func_get_param(0);
284         Seg_orientation.b = func_get_param(1);
285         Seg_orientation.h = func_get_param(2);
286         med_rotate_segment(Cursegp,vm_angles_2_matrix(&rotmat,&Seg_orientation));
287         Update_flags |= UF_WORLD_CHANGED | UF_VIEWPOINT_MOVED;
288         mine_changed = 1;
289         return 1;
290 }
291
292 int ToggleLockViewToCursegp(void)
293 {
294         Lock_view_to_cursegp = !Lock_view_to_cursegp;
295         Update_flags = UF_ED_STATE_CHANGED;
296         if (Lock_view_to_cursegp) {
297         if (last_keypress != KEY_V+KEY_CTRLED)
298             diagnostic_message("[ctrl-V] View locked to Cursegp.");
299         else
300             diagnostic_message("View locked to Cursegp.");
301         set_view_target_from_segment(Cursegp);
302     } else {
303         if (last_keypress != KEY_V+KEY_CTRLED)
304             diagnostic_message("[ctrl-V] View not locked to Cursegp.");
305         else
306             diagnostic_message("View not locked to Cursegp.");
307     }
308     return Lock_view_to_cursegp;
309 }
310
311 int ToggleDrawAllSegments()
312 {
313         Draw_all_segments = !Draw_all_segments;
314         Update_flags = UF_ED_STATE_CHANGED;
315     if (Draw_all_segments == 1) {
316         if (last_keypress != KEY_A+KEY_CTRLED)
317             diagnostic_message("[ctrl-A] Draw all segments ON.");
318         else
319             diagnostic_message("Draw all segments ON.");
320     }
321     if (Draw_all_segments == 0) {
322         if (last_keypress != KEY_A+KEY_CTRLED)
323             diagnostic_message("[ctrl-A] Draw all segments OFF.");
324         else
325             diagnostic_message("Draw all segments OFF.");
326     }
327     return Draw_all_segments;
328 }
329
330 int     Big_depth=6;
331
332 int IncreaseDrawDepth(void)
333 {
334         Big_depth++;
335         Update_flags = UF_ED_STATE_CHANGED;
336         return 1;
337 }
338
339 int DecreaseDrawDepth(void)
340 {
341         if (Big_depth > 1) {
342                 Big_depth--;
343                 Update_flags = UF_ED_STATE_CHANGED;
344         }
345         return 1;
346 }
347
348
349 int ToggleCoordAxes()
350 {
351                         //  Toggle display of coordinate axes.
352         Show_axes_flag = !Show_axes_flag;
353         LargeView.ev_changed = 1;
354     if (Show_axes_flag == 1) {
355         if (last_keypress != KEY_D+KEY_CTRLED)
356             diagnostic_message("[ctrl-D] Coordinate axes ON.");
357         else
358             diagnostic_message("Coordinate axes ON.");
359     }
360     if (Show_axes_flag == 0) {
361         if (last_keypress != KEY_D+KEY_CTRLED)
362             diagnostic_message("[ctrl-D] Coordinate axes OFF.");
363         else
364             diagnostic_message("Coordinate axes OFF.");
365     }
366     return Show_axes_flag;
367 }
368
369 int med_keypad_goto_prev()
370 {
371         ui_pad_goto_prev();
372         return 0;
373 }
374
375 int med_keypad_goto_next()
376 {
377         ui_pad_goto_next();
378         return 0;
379 }
380
381 int med_keypad_goto()
382 {
383         ui_pad_goto(func_get_param(0));
384         return 0;
385 }
386
387 int render_3d_in_big_window=0;
388
389 int medlisp_update_screen()
390 {
391         int vn;
392
393 if (!render_3d_in_big_window)
394         for (vn=0;vn<N_views;vn++)
395                 if (Views[vn]->ev_changed || (Update_flags & (UF_WORLD_CHANGED|UF_VIEWPOINT_MOVED|UF_ED_STATE_CHANGED))) {
396                         draw_world(Views[vn]->ev_canv,Views[vn],Cursegp,Big_depth);
397                         Views[vn]->ev_changed = 0;
398                 }
399
400         if (Update_flags & (UF_WORLD_CHANGED|UF_GAME_VIEW_CHANGED|UF_ED_STATE_CHANGED)) {
401                 grs_canvas temp_canvas;
402                 grs_canvas *render_canv,*show_canv;
403                 
404                 if (render_3d_in_big_window) {
405                         
406                         gr_init_sub_canvas(&temp_canvas,canv_offscreen,0,0,
407                                 LargeView.ev_canv->cv_bitmap.bm_w,LargeView.ev_canv->cv_bitmap.bm_h);
408
409                         render_canv = &temp_canvas;
410                         show_canv = LargeView.ev_canv;
411
412                 }
413                 else {
414                         render_canv     = VR_offscreen_buffer;
415                         show_canv       = Canv_editor_game;
416                 }
417
418                 gr_set_current_canvas(render_canv);
419                 render_frame(0);
420
421                 Assert(render_canv->cv_bitmap.bm_w == show_canv->cv_bitmap.bm_w &&
422                                  render_canv->cv_bitmap.bm_h == show_canv->cv_bitmap.bm_h);
423
424                 ui_mouse_hide();
425                 gr_bm_ubitblt(show_canv->cv_bitmap.bm_w,show_canv->cv_bitmap.bm_h,
426                                                   0,0,0,0,&render_canv->cv_bitmap,&show_canv->cv_bitmap);
427                 ui_mouse_show();
428         }
429
430         Update_flags=UF_NONE;       //clear flags
431
432         return 1;
433 }
434
435 void med_point_2_vec(grs_canvas *canv,vms_vector *v,short sx,short sy)
436 {
437         gr_set_current_canvas(canv);
438
439         g3_start_frame();
440         g3_set_view_matrix(&Viewer->pos,&Viewer->orient,Render_zoom);
441
442         g3_point_2_vec(v,sx,sy);
443
444         g3_end_frame();
445 }
446
447
448  
449 void draw_world_from_game(void)
450 {
451         if (ModeFlag == 2)
452                 draw_world(Views[0]->ev_canv,Views[0],Cursegp,Big_depth);
453 }
454
455 int UndoCommand()
456 {   int u;
457
458     u = undo();
459     if (Lock_view_to_cursegp)
460                 set_view_target_from_segment(Cursegp);
461     if (u == 0) {
462         if (Autosave_count==9) diagnostic_message(undo_status[0]);
463             else
464                 diagnostic_message(undo_status[Autosave_count+1]);
465         }
466         else
467          if (u == 1) diagnostic_message("Can't Undo.");
468         else
469          if (u == 2) diagnostic_message("Can't Undo - Autosave OFF");
470     Update_flags |= UF_WORLD_CHANGED;
471          mine_changed = 1;
472     warn_if_concave_segments();
473     return 1;
474 }
475
476
477 int ToggleAutosave()
478 {
479         Autosave_flag = !Autosave_flag;
480         if (Autosave_flag == 1) 
481       diagnostic_message("Autosave ON.");
482         else
483                 diagnostic_message("Autosave OFF.");
484    return Autosave_flag;
485 }
486
487
488 int AttachSegment()
489 {
490    if (med_attach_segment(Cursegp, &New_segment, Curside, AttachSide)==4) // Used to be WBACK instead of Curside
491         diagnostic_message("Cannot attach segment - already a connection on current side.");
492    else {
493                 if (Lock_view_to_cursegp)
494                         set_view_target_from_segment(Cursegp);
495                 vm_angvec_make(&Seg_orientation,0,0,0);
496                 Curside = WBACK;
497                 Update_flags |= UF_WORLD_CHANGED;
498            autosave_mine(mine_filename);
499            strcpy(undo_status[Autosave_count], "Attach Segment UNDONE.\n");
500                 mine_changed = 1;
501                 warn_if_concave_segment(Cursegp);
502       }
503         return 1;
504 }
505
506 int ForceTotalRedraw()
507 {
508         Update_flags = UF_ALL;
509         return 1;
510 }
511
512
513 #if ORTHO_VIEWS
514 int SyncLargeView()
515 {
516         // Make large view be same as one of the orthogonal views.
517         Large_view_index = (Large_view_index + 1) % 3;  // keep in 0,1,2 for top, front, right
518         switch (Large_view_index) {
519                 case 0: LargeView.ev_matrix = TopView.ev_matrix; break;
520                 case 1: LargeView.ev_matrix = FrontView.ev_matrix; break;
521                 case 2: LargeView.ev_matrix = RightView.ev_matrix; break;
522         }
523         Update_flags |= UF_VIEWPOINT_MOVED;
524         return 1;
525 }
526 #endif
527
528 int DeleteCurSegment()
529 {
530         // Delete current segment.
531     med_delete_segment(Cursegp);
532     autosave_mine(mine_filename);
533     strcpy(undo_status[Autosave_count], "Delete segment UNDONE.");
534     if (Lock_view_to_cursegp)
535                 set_view_target_from_segment(Cursegp);
536     Update_flags |= UF_WORLD_CHANGED;
537     mine_changed = 1;
538     diagnostic_message("Segment deleted.");
539     warn_if_concave_segments();     // This could be faster -- just check if deleted segment was concave, warn accordingly
540
541     return 1;
542 }
543
544 int CreateDefaultNewSegment()
545 {
546         // Create a default segment for New_segment.
547         vms_vector  tempvec;
548         med_create_new_segment(vm_vec_make(&tempvec,DEFAULT_X_SIZE,DEFAULT_Y_SIZE,DEFAULT_Z_SIZE));
549         mine_changed = 1;
550
551         return 1;
552 }
553
554 int CreateDefaultNewSegmentandAttach()
555 {
556         CreateDefaultNewSegment();
557         AttachSegment();
558
559         return 1;
560 }
561
562 int ExchangeMarkandCurseg()
563 {
564         // If Markedsegp != Cursegp, and Markedsegp->segnum != -1, exchange Markedsegp and Cursegp
565         if (Markedsegp)
566                 if (Markedsegp->segnum != -1) {
567                         segment *tempsegp;
568                         int     tempside;
569                         tempsegp = Markedsegp;  Markedsegp = Cursegp;   Cursegp = tempsegp;
570                         tempside = Markedside;  Markedside = Curside;   Curside = tempside;
571                         med_create_new_segment_from_cursegp();
572                         Update_flags |= UF_ED_STATE_CHANGED;
573                         mine_changed = 1;
574                 }
575         return 1;
576 }
577
578 int medlisp_add_segment()
579 {
580         AttachSegment();
581 //segment *ocursegp = Cursegp;
582 //      med_attach_segment(Cursegp, &New_segment, Curside, WFRONT); // Used to be WBACK instead of Curside
583 //med_propagate_tmaps_to_segments(ocursegp,Cursegp);
584 //      set_view_target_from_segment(Cursegp);
585 ////    while (!vm_angvec_make(&Seg_orientation,0,0,0));
586 //      Curside = WBACK;
587
588         return 1;
589 }
590
591
592 int ClearSelectedList(void)
593 {
594         N_selected_segs = 0;
595         Update_flags |= UF_WORLD_CHANGED;
596
597         diagnostic_message("Selected list cleared.");
598
599         return 1;
600 }
601
602
603 int ClearFoundList(void)
604 {
605         N_found_segs = 0;
606         Update_flags |= UF_WORLD_CHANGED;
607
608         diagnostic_message("Found list cleared.");
609
610         return 1;
611 }
612
613
614
615
616 // ---------------------------------------------------------------------------------------------------
617 // Do chase mode.
618 //      View current segment (Cursegp) from the previous segment.
619 void set_chase_matrix(segment *sp)
620 {
621         int                     v;
622         vms_vector      forvec = ZERO_VECTOR, upvec;
623         vms_vector      tv = ZERO_VECTOR;
624         segment         *psp;
625
626         // move back two segments, if possible, else move back one, if possible, else use current
627         if (IS_CHILD(sp->children[WFRONT])) {
628                 psp = &Segments[sp->children[WFRONT]];
629                 if (IS_CHILD(psp->children[WFRONT]))
630                         psp = &Segments[psp->children[WFRONT]];
631         } else
632                 psp = sp;
633
634         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
635                 vm_vec_add2(&forvec,&Vertices[sp->verts[v]]);
636         vm_vec_scale(&forvec,F1_0/MAX_VERTICES_PER_SEGMENT);
637
638         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
639                 vm_vec_add2(&tv,&Vertices[psp->verts[v]]);
640         vm_vec_scale(&tv,F1_0/MAX_VERTICES_PER_SEGMENT);
641
642         Ed_view_target = forvec;
643
644         vm_vec_sub2(&forvec,&tv);
645
646         extract_up_vector_from_segment(psp,&upvec);
647
648         if (!((forvec.x == 0) && (forvec.y == 0) && (forvec.z == 0)))
649                 vm_vector_2_matrix(&LargeView.ev_matrix,&forvec,&upvec,NULL);
650 }
651
652
653
654 // ---------------------------------------------------------------------------------------------------
655 void set_view_target_from_segment(segment *sp)
656 {
657         vms_vector      tv = ZERO_VECTOR;
658         int                     v;
659
660         if (Funky_chase_mode)
661                 {
662                 mprintf((0, "Trying to set chase mode\n"));
663                 //set_chase_matrix(sp);
664                 }
665         else {
666                 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
667                         vm_vec_add2(&tv,&Vertices[sp->verts[v]]);
668
669                 vm_vec_scale(&tv,F1_0/MAX_VERTICES_PER_SEGMENT);
670
671                 Ed_view_target = tv;
672
673         }
674         Update_flags |= UF_VIEWPOINT_MOVED;
675
676 }
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693