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