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  * Texture map assignment.
23  *
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <math.h>
30 #include <string.h>
31 #include "segment.h"
32 #include "seguvs.h"
33 #include "editor.h"
34 #include "editor/esegment.h"
35 #include "maths.h"
36 #include "kdefs.h"
37 
38 #include "compiler-range_for.h"
39 
40 static uvl compute_uv_side_center(const unique_segment &segp, sidenum_fast_t sidenum);
41 static void rotate_uv_points_on_side(unique_segment &segp, sidenum_fast_t sidenum, const std::array<fix, 4> &rotmat, const uvl &uvcenter);
42 
43 //	-----------------------------------------------------------
TexFlipX()44 int	TexFlipX()
45 {
46 	const auto uvcenter = compute_uv_side_center(Cursegp, Curside);
47 	std::array<fix, 4> rotmat;
48 	//	Create a rotation matrix
49 	rotmat[0] = -0xffff;
50 	rotmat[1] = 0;
51 	rotmat[2] = 0;
52 	rotmat[3] = 0xffff;
53 
54 	rotate_uv_points_on_side(Cursegp, Curside, rotmat, uvcenter);
55 
56  	Update_flags |= UF_WORLD_CHANGED;
57 
58 	return	1;
59 }
60 
61 //	-----------------------------------------------------------
TexFlipY()62 int	TexFlipY()
63 {
64 	const auto uvcenter = compute_uv_side_center(Cursegp, Curside);
65 	std::array<fix, 4> rotmat;
66 	//	Create a rotation matrix
67 	rotmat[0] = 0xffff;
68 	rotmat[1] = 0;
69 	rotmat[2] = 0;
70 	rotmat[3] = -0xffff;
71 
72 	rotate_uv_points_on_side(Cursegp, Curside, rotmat, uvcenter);
73 
74  	Update_flags |= UF_WORLD_CHANGED;
75 
76 	return	1;
77 }
78 
79 //	-----------------------------------------------------------
DoTexSlideLeft(int value)80 static int DoTexSlideLeft(int value)
81 {
82 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
83 	auto &Vertices = LevelSharedVertexState.get_vertices();
84 	uvl	duvl03;
85 	fix	dist;
86 	auto &vp = Side_to_verts[Curside];
87 	auto &uvls = Cursegp->unique_segment::sides[Curside].uvls;
88 
89 	auto &vcvertptr = Vertices.vcptr;
90 	dist = vm_vec_dist(vcvertptr(Cursegp->verts[vp[3]]), vcvertptr(Cursegp->verts[vp[0]]));
91 	dist *= value;
92 	if (dist < F1_0/(64*value))
93 		dist = F1_0/(64*value);
94 
95 	duvl03.u = fixdiv(uvls[3].u - uvls[0].u,dist);
96 	duvl03.v = fixdiv(uvls[3].v - uvls[0].v,dist);
97 
98 	range_for (auto &v, uvls)
99 	{
100 		v.u -= duvl03.u;
101 		v.v -= duvl03.v;
102 	}
103 
104 	Update_flags |= UF_WORLD_CHANGED;
105 
106 	return	1;
107 }
108 
TexSlideLeft()109 int TexSlideLeft()
110 {
111 	return DoTexSlideLeft(3);
112 }
113 
TexSlideLeftBig()114 int TexSlideLeftBig()
115 {
116 	return DoTexSlideLeft(1);
117 }
118 
119 //	-----------------------------------------------------------
DoTexSlideUp(int value)120 static int DoTexSlideUp(int value)
121 {
122 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
123 	auto &Vertices = LevelSharedVertexState.get_vertices();
124 	uvl	duvl03;
125 	fix	dist;
126 	auto &vp = Side_to_verts[Curside];
127 	auto &uvls = Cursegp->unique_segment::sides[Curside].uvls;
128 
129 	auto &vcvertptr = Vertices.vcptr;
130 	dist = vm_vec_dist(vcvertptr(Cursegp->verts[vp[1]]), vcvertptr(Cursegp->verts[vp[0]]));
131 	dist *= value;
132 
133 	if (dist < F1_0/(64*value))
134 		dist = F1_0/(64*value);
135 
136 	duvl03.u = fixdiv(uvls[1].u - uvls[0].u,dist);
137 	duvl03.v = fixdiv(uvls[1].v - uvls[0].v,dist);
138 
139 	range_for (auto &v, uvls)
140 	{
141 		v.u -= duvl03.u;
142 		v.v -= duvl03.v;
143 	}
144 
145 	Update_flags |= UF_WORLD_CHANGED;
146 
147 	return	1;
148 }
149 
TexSlideUp()150 int TexSlideUp()
151 {
152 	return DoTexSlideUp(3);
153 }
154 
TexSlideUpBig()155 int TexSlideUpBig()
156 {
157 	return DoTexSlideUp(1);
158 }
159 
160 
161 //	-----------------------------------------------------------
DoTexSlideDown(int value)162 static int DoTexSlideDown(int value)
163 {
164 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
165 	auto &Vertices = LevelSharedVertexState.get_vertices();
166 	uvl	duvl03;
167 	fix	dist;
168 	auto &vp = Side_to_verts[Curside];
169 	auto &uvls = Cursegp->unique_segment::sides[Curside].uvls;
170 
171 	auto &vcvertptr = Vertices.vcptr;
172 	dist = vm_vec_dist(vcvertptr(Cursegp->verts[vp[1]]), vcvertptr(Cursegp->verts[vp[0]]));
173 	dist *= value;
174 	if (dist < F1_0/(64*value))
175 		dist = F1_0/(64*value);
176 
177 	duvl03.u = fixdiv(uvls[1].u - uvls[0].u,dist);
178 	duvl03.v = fixdiv(uvls[1].v - uvls[0].v,dist);
179 
180 	range_for (auto &v, uvls)
181 	{
182 		v.u += duvl03.u;
183 		v.v += duvl03.v;
184 	}
185 
186 	Update_flags |= UF_WORLD_CHANGED;
187 
188 	return	1;
189 }
190 
TexSlideDown()191 int TexSlideDown()
192 {
193 	return DoTexSlideDown(3);
194 }
195 
TexSlideDownBig()196 int TexSlideDownBig()
197 {
198 	return DoTexSlideDown(1);
199 }
200 
201 //	-----------------------------------------------------------
202 //	Compute the center of the side in u,v coordinates.
compute_uv_side_center(const unique_segment & segp,const sidenum_fast_t sidenum)203 static uvl compute_uv_side_center(const unique_segment &segp, const sidenum_fast_t sidenum)
204 {
205 	uvl uvcenter{};
206 	range_for (auto &v, segp.sides[sidenum].uvls)
207 	{
208 		uvcenter.u += v.u;
209 		uvcenter.v += v.v;
210 	}
211 	uvcenter.u /= 4;
212 	uvcenter.v /= 4;
213 	return uvcenter;
214 }
215 
216 //	-----------------------------------------------------------
217 //	rotate point *uv by matrix rotmat, return *uvrot
rotate_uv_point(const std::array<fix,4> & rotmat,const uvl & uv,const uvl & uvcenter)218 static uvl rotate_uv_point(const std::array<fix, 4> &rotmat, const uvl &uv, const uvl &uvcenter)
219 {
220 	const auto centered_u = uv.u - uvcenter.u;
221 	const auto centered_v = uv.v - uvcenter.v;
222 	return {
223 		fixmul(centered_u, rotmat[0]) + fixmul(centered_v, rotmat[1]) + uvcenter.u,
224 		fixmul(centered_u, rotmat[2]) + fixmul(centered_v, rotmat[3]) + uvcenter.v,
225 		0
226 	};
227 }
228 
229 //	-----------------------------------------------------------
230 //	Compute the center of the side in u,v coordinates.
rotate_uv_points_on_side(unique_segment & segp,const sidenum_fast_t sidenum,const std::array<fix,4> & rotmat,const uvl & uvcenter)231 static void rotate_uv_points_on_side(unique_segment &segp, const sidenum_fast_t sidenum, const std::array<fix, 4> &rotmat, const uvl &uvcenter)
232 {
233 	range_for (auto &v, segp.sides[sidenum].uvls)
234 	{
235 		v = rotate_uv_point(rotmat, v, uvcenter);
236 	}
237 }
238 
239 //	-----------------------------------------------------------
240 //	ang is in 0..ffff = 0..359.999 degrees
241 //	rotmat is filled in with 4 fixes
create_2d_rotation_matrix(const fixang ang)242 static std::array<fix, 4> create_2d_rotation_matrix(const fixang ang)
243 {
244 	const auto &&a = fix_sincos(ang);
245 	const auto &sinang = a.sin;
246 	const auto &cosang = a.cos;
247 	return {{
248 		cosang,
249 		sinang,
250 		-sinang,
251 		cosang
252 	}};
253 }
254 
255 
256 //	-----------------------------------------------------------
DoTexRotateLeft(int value)257 static int DoTexRotateLeft(int value)
258 {
259 	const auto uvcenter = compute_uv_side_center(Cursegp, Curside);
260 	//	Create a rotation matrix
261 	const auto rotmat = create_2d_rotation_matrix(-F1_0/value);
262 
263 	rotate_uv_points_on_side(Cursegp, Curside, rotmat, uvcenter);
264 
265  	Update_flags |= UF_WORLD_CHANGED;
266 
267 	return	1;
268 }
269 
TexRotateLeft()270 int TexRotateLeft()
271 {
272 	return DoTexRotateLeft(192);
273 }
274 
TexRotateLeftBig()275 int TexRotateLeftBig()
276 {
277 	return DoTexRotateLeft(64);
278 }
279 
280 
281 //	-----------------------------------------------------------
DoTexSlideRight(int value)282 static int DoTexSlideRight(int value)
283 {
284 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
285 	auto &Vertices = LevelSharedVertexState.get_vertices();
286 	uvl	duvl03;
287 	fix	dist;
288 	auto &vp = Side_to_verts[Curside];
289 	auto &uvls = Cursegp->unique_segment::sides[Curside].uvls;
290 
291 	auto &vcvertptr = Vertices.vcptr;
292 	dist = vm_vec_dist(vcvertptr(Cursegp->verts[vp[3]]), vcvertptr(Cursegp->verts[vp[0]]));
293 	dist *= value;
294 	if (dist < F1_0/(64*value))
295 		dist = F1_0/(64*value);
296 
297 	duvl03.u = fixdiv(uvls[3].u - uvls[0].u,dist);
298 	duvl03.v = fixdiv(uvls[3].v - uvls[0].v,dist);
299 
300 	range_for (auto &v, uvls)
301 	{
302 		v.u += duvl03.u;
303 		v.v += duvl03.v;
304 	}
305 
306 	Update_flags |= UF_WORLD_CHANGED;
307 
308 	return	1;
309 }
310 
TexSlideRight()311 int TexSlideRight()
312 {
313 	return DoTexSlideRight(3);
314 }
315 
TexSlideRightBig()316 int TexSlideRightBig()
317 {
318 	return DoTexSlideRight(1);
319 }
320 
321 //	-----------------------------------------------------------
DoTexRotateRight(int value)322 static int DoTexRotateRight(int value)
323 {
324 	const auto uvcenter = compute_uv_side_center(Cursegp, Curside);
325 
326 	//	Create a rotation matrix
327 	const auto rotmat = create_2d_rotation_matrix(F1_0/value);
328 
329 	rotate_uv_points_on_side(Cursegp, Curside, rotmat, uvcenter);
330 
331  	Update_flags |= UF_WORLD_CHANGED;
332 
333 	return	1;
334 }
335 
TexRotateRight()336 int TexRotateRight()
337 {
338 	return DoTexRotateRight(192);
339 }
340 
TexRotateRightBig()341 int TexRotateRightBig()
342 {
343 	return DoTexRotateRight(64);
344 }
345 
346 //	-----------------------------------------------------------
TexSelectActiveEdge()347 int	TexSelectActiveEdge()
348 {
349 	return	1;
350 }
351 
352 //	-----------------------------------------------------------
TexRotate90Degrees()353 int	TexRotate90Degrees()
354 {
355 	const auto uvcenter = compute_uv_side_center(Cursegp, Curside);
356 
357 	//	Create a rotation matrix
358 	const auto rotmat = create_2d_rotation_matrix(F1_0/4);
359 
360 	rotate_uv_points_on_side(Cursegp, Curside, rotmat, uvcenter);
361 
362  	Update_flags |= UF_WORLD_CHANGED;
363 
364 	return	1;
365 }
366 
367 //	-----------------------------------------------------------
TexSetDefault()368 int	TexSetDefault()
369 {
370 	Num_tilings = 1;
371 
372 	Stretch_scale_x = F1_0;
373 	Stretch_scale_y = F1_0;
374 
375 	assign_default_uvs_to_side(Cursegp,Curside);
376 
377 	Update_flags |= UF_GAME_VIEW_CHANGED;
378 	return	1;
379 }
380 
381 //	-----------------------------------------------------------
TexIncreaseTiling()382 int	TexIncreaseTiling()
383 {
384 
385 	Num_tilings++;
386 	assign_default_uvs_to_side(Cursegp, Curside);
387 	Update_flags |= UF_GAME_VIEW_CHANGED;
388 
389 	return	1;
390 }
391 
392 //	-----------------------------------------------------------
TexDecreaseTiling()393 int	TexDecreaseTiling()
394 {
395 
396 	if (--Num_tilings < 1)
397 		Num_tilings = 1;
398 
399 	assign_default_uvs_to_side(Cursegp, Curside);
400 	Update_flags |= UF_GAME_VIEW_CHANGED;
401 
402 	return	1;
403 }
404 
405 
406 //	direction = -1 or 1 depending on direction
TexStretchCommon(int direction)407 static int	TexStretchCommon(int direction)
408 {
409 	fix	*sptr;
410 
411 	if ((Curedge == 0) || (Curedge == 2))
412 		sptr = &Stretch_scale_x;
413 	else
414 		sptr = &Stretch_scale_y;
415 
416 	*sptr += direction*F1_0/64;
417 
418 	if (*sptr < F1_0/16)
419 		*sptr = F1_0/16;
420 
421 	if (*sptr > 2*F1_0)
422 		*sptr = 2*F1_0;
423 
424 	stretch_uvs_from_curedge(Cursegp, Curside);
425 
426 	editor_status_fmt("Stretch scale = %7.4f, use Set Default to return to 1.0", f2fl(*sptr));
427 
428 	Update_flags |= UF_GAME_VIEW_CHANGED;
429 	return	1;
430 
431 }
432 
TexStretchDown(void)433 int	TexStretchDown(void)
434 {
435 	return TexStretchCommon(-1);
436 
437 }
438 
TexStretchUp(void)439 int	TexStretchUp(void)
440 {
441 	return TexStretchCommon(1);
442 
443 }
444 
445