1 /*
2 * Portions of this file are copyright Rebirth contributors and licensed as
3 * described in COPYING.txt.
4 * Portions of this file are copyright Parallax Software and licensed
5 * according to the Parallax license below.
6 * See COPYING.txt for license details.
7
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
18 */
19
20 /*
21 *
22 * Functions for sizing segments
23 *
24 */
25
26 #include <stdlib.h>
27 #include "editor.h"
28 #include "editor/esegment.h"
29 #include "dxxerror.h"
30 #include "gameseg.h"
31 #include "kdefs.h"
32
33 #include "compiler-range_for.h"
34 #include "d_range.h"
35
36 #define XDIM 0
37 #define YDIM 1
38 #define ZDIM 2
39
40 #define MAX_MODIFIED_VERTICES 32
41 namespace {
42 static std::array<vertnum_t, MAX_MODIFIED_VERTICES> Modified_vertices;
43 int Modified_vertex_index = 0;
44 }
45
46 namespace dsx {
47
48 namespace {
49
50 // ------------------------------------------------------------------------------------------
validate_modified_segments(void)51 static void validate_modified_segments(void)
52 {
53 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
54 auto &Vertices = LevelSharedVertexState.get_vertices();
55 visited_segment_bitarray_t modified_segments;
56 auto &vcvertptr = Vertices.vcptr;
57 for (int v=0; v<Modified_vertex_index; v++) {
58 const auto v0 = Modified_vertices[v];
59
60 range_for (const auto &&segp, vmsegptridx)
61 {
62 if (segp->segnum != segment_none)
63 {
64 if (modified_segments[segp])
65 continue;
66 range_for (const auto w, segp->verts)
67 if (w == v0)
68 {
69 modified_segments[segp] = true;
70 validate_segment(vcvertptr, segp);
71 for (unsigned s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
72 Num_tilings = 1;
73 assign_default_uvs_to_side(segp, s);
74 }
75 break;
76 }
77 }
78 }
79 }
80 }
81
82 // ------------------------------------------------------------------------------------------
83 // Scale vertex *vertp by vector *vp, scaled by scale factor scale_factor
scale_vert_aux(const vertnum_t vertex_ind,const vms_vector & vp,const fix scale_factor)84 static void scale_vert_aux(const vertnum_t vertex_ind, const vms_vector &vp, const fix scale_factor)
85 {
86 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
87 auto &Vertices = LevelSharedVertexState.get_vertices();
88 auto &vmvertptr = Vertices.vmptr;
89 auto &vertp = *vmvertptr(vertex_ind);
90
91 vertp.x += fixmul(vp.x,scale_factor)/2;
92 vertp.y += fixmul(vp.y,scale_factor)/2;
93 vertp.z += fixmul(vp.z,scale_factor)/2;
94
95 Assert(Modified_vertex_index < MAX_MODIFIED_VERTICES);
96 Modified_vertices[Modified_vertex_index++] = vertex_ind;
97 }
98
99 // ------------------------------------------------------------------------------------------
scale_vert(const shared_segment & sp,const vertnum_t vertex_ind,const vms_vector & vp,const fix scale_factor)100 static void scale_vert(const shared_segment &sp, const vertnum_t vertex_ind, const vms_vector &vp, const fix scale_factor)
101 {
102 auto &verts = sp.verts;
103 switch (SegSizeMode) {
104 case SEGSIZEMODE_FREE:
105 if (is_free_vertex(vcsegptr, vertex_ind))
106 scale_vert_aux(vertex_ind, vp, scale_factor);
107 break;
108 case SEGSIZEMODE_ALL:
109 scale_vert_aux(vertex_ind, vp, scale_factor);
110 break;
111 case SEGSIZEMODE_CURSIDE: {
112 range_for (const auto v, Side_to_verts[Curside])
113 if (verts[v] == vertex_ind)
114 scale_vert_aux(vertex_ind, vp, scale_factor);
115 break;
116 }
117 case SEGSIZEMODE_EDGE: {
118 auto &sv = Side_to_verts[Curside];
119 const auto edge = Curedge;
120 range_for (const int v, xrange(2u))
121 if (verts[sv[(edge + v) % 4]] == vertex_ind)
122 scale_vert_aux(vertex_ind, vp, scale_factor);
123 break;
124 }
125 case SEGSIZEMODE_VERTEX:
126 if (verts[Side_to_verts[Curside][Curvert]] == vertex_ind)
127 scale_vert_aux(vertex_ind, vp, scale_factor);
128 break;
129 default:
130 Error("Unsupported SegSizeMode in ksegsize.c/scale_vert = %i\n", SegSizeMode);
131 }
132
133 }
134
135 // ------------------------------------------------------------------------------------------
scale_free_verts(const shared_segment & sp,const vms_vector & vp,const unsigned side,fix scale_factor)136 static void scale_free_verts(const shared_segment &sp, const vms_vector &vp, const unsigned side, fix scale_factor)
137 {
138 range_for (auto &v, Side_to_verts[side])
139 {
140 const auto vertex_ind = sp.verts[v];
141 if (SegSizeMode || is_free_vertex(vcsegptr, vertex_ind))
142 scale_vert(sp, vertex_ind, vp, scale_factor);
143 }
144
145 }
146
147
148 // -----------------------------------------------------------------------------
149 // Make segment *sp bigger in dimension dimension by amount amount.
med_scale_segment_new(const shared_segment & sp,const int dimension,const fix amount)150 static void med_scale_segment_new(const shared_segment &sp, const int dimension, const fix amount)
151 {
152 vms_matrix mat;
153
154 Modified_vertex_index = 0;
155
156 med_extract_matrix_from_segment(sp, mat);
157
158 const vms_vector *vec;
159 unsigned side0, side1;
160 switch (dimension) {
161 case XDIM:
162 side0 = WLEFT;
163 side1 = WRIGHT;
164 vec = &mat.rvec;
165 break;
166 case YDIM:
167 side0 = WBOTTOM;
168 side1 = WTOP;
169 vec = &mat.uvec;
170 break;
171 case ZDIM:
172 side0 = WFRONT;
173 side1 = WBACK;
174 vec = &mat.fvec;
175 break;
176 default:
177 return;
178 }
179 scale_free_verts(sp, *vec, side0, -amount);
180 scale_free_verts(sp, *vec, side1, +amount);
181
182 validate_modified_segments();
183 }
184
185 // ------------------------------------------------------------------------------------------
186 // Extract a vector from a segment. The vector goes from the start face to the end face.
187 // The point on each face is the average of the four points forming the face.
extract_vector_from_segment_side(const shared_segment & sp,const unsigned side,vms_vector & vp,const unsigned vla,const unsigned vlb,const unsigned vra,const unsigned vrb)188 static void extract_vector_from_segment_side(const shared_segment &sp, const unsigned side, vms_vector &vp, const unsigned vla, const unsigned vlb, const unsigned vra, const unsigned vrb)
189 {
190 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
191 auto &Vertices = LevelSharedVertexState.get_vertices();
192 auto &sv = Side_to_verts[side];
193 auto &verts = sp.verts;
194 auto &vcvertptr = Vertices.vcptr;
195 const auto v1 = vm_vec_sub(vcvertptr(verts[sv[vra]]), vcvertptr(verts[sv[vla]]));
196 const auto v2 = vm_vec_sub(vcvertptr(verts[sv[vrb]]), vcvertptr(verts[sv[vlb]]));
197 vm_vec_add(vp, v1, v2);
198 vm_vec_scale(vp, F1_0/2);
199 }
200
201 }
202
203 // ------------------------------------------------------------------------------------------
204 // Extract the right vector from segment *sp, return in *vp.
205 // The forward vector is defined to be the vector from the the center of the left face of the segment
206 // to the center of the right face of the segment.
med_extract_right_vector_from_segment_side(const shared_segment & sp,int sidenum,vms_vector & vp)207 void med_extract_right_vector_from_segment_side(const shared_segment &sp, int sidenum, vms_vector &vp)
208 {
209 extract_vector_from_segment_side(sp, sidenum, vp, 3, 2, 0, 1);
210 }
211
212 // ------------------------------------------------------------------------------------------
213 // Extract the up vector from segment *sp, return in *vp.
214 // The forward vector is defined to be the vector from the the center of the bottom face of the segment
215 // to the center of the top face of the segment.
med_extract_up_vector_from_segment_side(const shared_segment & sp,int sidenum,vms_vector & vp)216 void med_extract_up_vector_from_segment_side(const shared_segment &sp, int sidenum, vms_vector &vp)
217 {
218 extract_vector_from_segment_side(sp, sidenum, vp, 1, 2, 0, 3);
219 }
220
221 namespace {
222
223 // -----------------------------------------------------------------------------
224 // Increase the size of Cursegp in dimension dimension by amount
segsize_common(int dimension,fix amount)225 static int segsize_common(int dimension, fix amount)
226 {
227 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
228 auto &Vertices = LevelSharedVertexState.get_vertices();
229 vms_vector uvec, rvec, fvec, scalevec;
230
231 Degenerate_segment_found = 0;
232
233 med_scale_segment_new(Cursegp, dimension, amount);
234
235 med_extract_up_vector_from_segment_side(Cursegp, Curside, uvec);
236 med_extract_right_vector_from_segment_side(Cursegp, Curside, rvec);
237 auto &vcvertptr = Vertices.vcptr;
238 extract_forward_vector_from_segment(vcvertptr, Cursegp, fvec);
239
240 scalevec.x = vm_vec_mag(rvec);
241 scalevec.y = vm_vec_mag(uvec);
242 scalevec.z = vm_vec_mag(fvec);
243
244 if (Degenerate_segment_found) {
245 Degenerate_segment_found = 0;
246 editor_status("Applying scale would create degenerate segments. Aborting scale.");
247 med_scale_segment_new(Cursegp, dimension, -amount);
248 return 1;
249 }
250
251 med_create_new_segment(scalevec);
252
253 // For all segments to which Cursegp is connected, propagate tmap (uv coordinates) from the connected
254 // segment back to Cursegp. This will meaningfully propagate uv coordinates to all sides which havve
255 // an incident edge. It will also do some sides more than once. And it is probably just not what you want.
256 std::array<int, MAX_SIDES_PER_SEGMENT> propagated = {};
257 for (int i=0; i<MAX_SIDES_PER_SEGMENT; i++)
258 {
259 const auto c = Cursegp->children[i];
260 if (IS_CHILD(c))
261 {
262 range_for (auto &s, propagated)
263 ++s;
264 propagated[static_cast<int>(Side_opposite[i])]--;
265 med_propagate_tmaps_to_segments(vmsegptridx(c), Cursegp, 1);
266 }
267 }
268
269 // Now, for all sides that were not adjacent to another side, and therefore did not get tmaps
270 // propagated to them, treat as a back side.
271 for (int i=0; i<MAX_SIDES_PER_SEGMENT; i++)
272 if (!propagated[i]) {
273 med_propagate_tmaps_to_back_side(Cursegp, i, 1);
274 }
275
276 // New stuff, assign default texture to all affected sides.
277
278 Update_flags |= UF_WORLD_CHANGED;
279 mine_changed = 1;
280 return 1;
281 }
282
283 }
284
285 // -----------------------------------------------------------------------------
286 // ---------- segment size control ----------
287
IncreaseSegLength()288 int IncreaseSegLength()
289 {
290 return segsize_common(ZDIM,+F1_0);
291 }
292
DecreaseSegLength()293 int DecreaseSegLength()
294 {
295 return segsize_common(ZDIM,-F1_0);
296 }
297
DecreaseSegWidth()298 int DecreaseSegWidth()
299 {
300 return segsize_common(XDIM,-F1_0);
301 }
302
IncreaseSegWidth()303 int IncreaseSegWidth()
304 {
305 return segsize_common(XDIM,+F1_0);
306 }
307
IncreaseSegHeight()308 int IncreaseSegHeight()
309 {
310 return segsize_common(YDIM,+F1_0);
311 }
312
DecreaseSegHeight()313 int DecreaseSegHeight()
314 {
315 return segsize_common(YDIM,-F1_0);
316 }
317
318
IncreaseSegLengthBig()319 int IncreaseSegLengthBig()
320 {
321 return segsize_common(ZDIM,+5 * F1_0);
322 }
323
DecreaseSegLengthBig()324 int DecreaseSegLengthBig()
325 {
326 return segsize_common(ZDIM,-5 * F1_0);
327 }
328
DecreaseSegWidthBig()329 int DecreaseSegWidthBig()
330 {
331 return segsize_common(XDIM,-5 * F1_0);
332 }
333
IncreaseSegWidthBig()334 int IncreaseSegWidthBig()
335 {
336 return segsize_common(XDIM,+5 * F1_0);
337 }
338
IncreaseSegHeightBig()339 int IncreaseSegHeightBig()
340 {
341 return segsize_common(YDIM,+5 * F1_0);
342 }
343
DecreaseSegHeightBig()344 int DecreaseSegHeightBig()
345 {
346 return segsize_common(YDIM,-5 * F1_0);
347 }
348
349
IncreaseSegLengthDefault()350 int IncreaseSegLengthDefault()
351 {
352 return segsize_common(ZDIM,+40 *F1_0);
353 }
354
DecreaseSegLengthDefault()355 int DecreaseSegLengthDefault()
356 {
357 return segsize_common(ZDIM,-40*F1_0);
358 }
359
IncreaseSegWidthDefault()360 int IncreaseSegWidthDefault()
361 {
362 return segsize_common(XDIM,+40*F1_0);
363 }
364
DecreaseSegWidthDefault()365 int DecreaseSegWidthDefault()
366 {
367 return segsize_common(XDIM,-40*F1_0);
368 }
369
IncreaseSegHeightDefault()370 int IncreaseSegHeightDefault()
371 {
372 return segsize_common(YDIM,+40 * F1_0);
373 }
374
DecreaseSegHeightDefault()375 int DecreaseSegHeightDefault()
376 {
377 return segsize_common(YDIM,-40 * F1_0);
378 }
379
380
381 // ---------------------------------------------------------------------------
ToggleSegSizeMode(void)382 int ToggleSegSizeMode(void)
383 {
384 SegSizeMode++;
385 if (SegSizeMode > SEGSIZEMODE_MAX)
386 SegSizeMode = SEGSIZEMODE_MIN;
387
388 return 1;
389 }
390
391 namespace {
392
393 // ---------------------------------------------------------------------------
PerturbCursideCommon(fix amount)394 static int PerturbCursideCommon(fix amount)
395 {
396 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
397 auto &Vertices = LevelSharedVertexState.get_vertices();
398 int saveSegSizeMode = SegSizeMode;
399 vms_vector fvec, rvec, uvec;
400 SegSizeMode = SEGSIZEMODE_CURSIDE;
401
402 Modified_vertex_index = 0;
403
404 auto &vcvertptr = Vertices.vcptr;
405 extract_forward_vector_from_segment(vcvertptr, Cursegp, fvec);
406 extract_right_vector_from_segment(vcvertptr, Cursegp, rvec);
407 extract_up_vector_from_segment(vcvertptr, Cursegp, uvec);
408
409 const auto fmag = vm_vec_mag(fvec);
410 const auto rmag = vm_vec_mag(rvec);
411 const auto umag = vm_vec_mag(uvec);
412
413 range_for (const auto v, Side_to_verts[Curside])
414 {
415 vms_vector perturb_vec;
416 perturb_vec.x = fixmul(rmag, d_rand()*2 - 32767);
417 perturb_vec.y = fixmul(umag, d_rand()*2 - 32767);
418 perturb_vec.z = fixmul(fmag, d_rand()*2 - 32767);
419 scale_vert(Cursegp, Cursegp->verts[v], perturb_vec, amount);
420 }
421
422 // validate_segment(Cursegp);
423 // if (SegSizeMode) {
424 // for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
425 // if (Cursegp->children[i] != -1)
426 // validate_segment(&Segments[Cursegp->children[i]]);
427 // }
428
429 validate_modified_segments();
430 SegSizeMode = saveSegSizeMode;
431
432 Update_flags |= UF_WORLD_CHANGED;
433 mine_changed = 1;
434
435 return 1;
436 }
437
438 }
439
440 // ---------------------------------------------------------------------------
PerturbCurside(void)441 int PerturbCurside(void)
442 {
443 PerturbCursideCommon(F1_0/10);
444
445 return 1;
446 }
447
448 // ---------------------------------------------------------------------------
PerturbCursideBig(void)449 int PerturbCursideBig(void)
450 {
451 PerturbCursideCommon(F1_0/2);
452
453 return 1;
454 }
455
456 }
457