1use crate::native::{NativeCallContext, NativeCallResult, NativeFunction};
2use crate::thread::Thread;
3
4static VECTOR_LIB: [NativeFunction; 14] = [
5 NativeFunction {
6 name: "create",
7 function: vector_create,
8 },
9 NativeFunction {
10 name: "magnitude",
11 function: vector_magnitude,
12 },
13 NativeFunction {
14 name: "normalize",
15 function: vector_normalize,
16 },
17 NativeFunction {
18 name: "cross",
19 function: vector_cross,
20 },
21 NativeFunction {
22 name: "dot",
23 function: vector_dot,
24 },
25 NativeFunction {
26 name: "angle",
27 function: vector_angle,
28 },
29 NativeFunction {
30 name: "floor",
31 function: vector_floor,
32 },
33 NativeFunction {
34 name: "ceil",
35 function: vector_ceil,
36 },
37 NativeFunction {
38 name: "abs",
39 function: vector_abs,
40 },
41 NativeFunction {
42 name: "sign",
43 function: vector_sign,
44 },
45 NativeFunction {
46 name: "clamp",
47 function: vector_clamp,
48 },
49 NativeFunction {
50 name: "max",
51 function: vector_max,
52 },
53 NativeFunction {
54 name: "min",
55 function: vector_min,
56 },
57 NativeFunction {
58 name: "lerp",
59 function: vector_lerp,
60 },
61];
62
63fn vector_create(ctx: NativeCallContext) -> NativeCallResult {
65 let count = ctx.arg_count();
66 let x = ctx.arg(1).number()? as f32;
67 let y = ctx.arg(2).number()? as f32;
68 let z = if count >= 3 {
69 ctx.arg(3).number()? as f32
70 } else {
71 0.0
72 };
73
74 #[cfg(feature = "vector4")]
75 {
76 let w = if count >= 4 {
77 ctx.arg(4).number()? as f32
78 } else {
79 0.0
80 };
81 ctx.push_vector([x, y, z, w])?;
82 Ok(1)
83 }
84
85 #[cfg(not(feature = "vector4"))]
86 {
87 ctx.push_vector([x, y, z])?;
88 Ok(1)
89 }
90}
91
92fn vector_magnitude(ctx: NativeCallContext) -> NativeCallResult {
94 let vector = ctx.arg(1).vector()?;
95 ctx.push_number(
96 vector
97 .iter()
98 .map(|component| component * component)
99 .sum::<f32>()
100 .sqrt() as f64,
101 )?;
102 Ok(1)
103}
104
105fn vector_normalize(ctx: NativeCallContext) -> NativeCallResult {
107 let mut vector = ctx.arg(1).vector()?;
108 let inv_sqrt = 1.0f32
109 / vector
110 .iter()
111 .map(|component| component * component)
112 .sum::<f32>()
113 .sqrt();
114 for component in &mut vector {
115 *component *= inv_sqrt;
116 }
117 ctx.push_vector(vector)?;
118 Ok(1)
119}
120
121fn vector_cross(ctx: NativeCallContext) -> NativeCallResult {
123 let left = ctx.arg(1).vector()?;
124 let right = ctx.arg(2).vector()?;
125
126 #[cfg(feature = "vector4")]
127 {
128 ctx.push_vector([
129 left[1] * right[2] - left[2] * right[1],
130 left[2] * right[0] - left[0] * right[2],
131 left[0] * right[1] - left[1] * right[0],
132 0.0,
133 ])?;
134 Ok(1)
135 }
136
137 #[cfg(not(feature = "vector4"))]
138 {
139 ctx.push_vector([
140 left[1] * right[2] - left[2] * right[1],
141 left[2] * right[0] - left[0] * right[2],
142 left[0] * right[1] - left[1] * right[0],
143 ])?;
144 Ok(1)
145 }
146}
147
148fn vector_dot(ctx: NativeCallContext) -> NativeCallResult {
150 let left = ctx.arg(1).vector()?;
151 let right = ctx.arg(2).vector()?;
152 ctx.push_number(
153 left.iter()
154 .zip(right.iter())
155 .map(|(left, right)| left * right)
156 .sum::<f32>() as f64,
157 )?;
158 Ok(1)
159}
160
161fn vector_angle(ctx: NativeCallContext) -> NativeCallResult {
163 let thread = ctx.raw_thread();
164 let angle = unsafe {
165 let left = ctx.arg(1).vector()?;
166 let right = ctx.arg(2).vector()?;
167 let axis = thread.opt_vector(3)?;
168
169 let cross = [
170 left[1] * right[2] - left[2] * right[1],
171 left[2] * right[0] - left[0] * right[2],
172 left[0] * right[1] - left[1] * right[0],
173 ];
174 let sin = (cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]).sqrt();
175 let cos = left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
176 let mut angle = (sin as f64).atan2(cos as f64);
177
178 if let Some(axis) = axis
179 && cross[0] * axis[0] + cross[1] * axis[1] + cross[2] * axis[2] < 0.0
180 {
181 angle = -angle;
182 }
183
184 angle
185 };
186 ctx.push_number(angle)?;
187 Ok(1)
188}
189
190fn vector_floor(ctx: NativeCallContext) -> NativeCallResult {
192 let mut vector = ctx.arg(1).vector()?;
193 for component in &mut vector {
194 *component = component.floor();
195 }
196 ctx.push_vector(vector)?;
197 Ok(1)
198}
199
200fn vector_ceil(ctx: NativeCallContext) -> NativeCallResult {
202 let mut vector = ctx.arg(1).vector()?;
203 for component in &mut vector {
204 *component = component.ceil();
205 }
206 ctx.push_vector(vector)?;
207 Ok(1)
208}
209
210fn vector_abs(ctx: NativeCallContext) -> NativeCallResult {
212 let mut vector = ctx.arg(1).vector()?;
213 for component in &mut vector {
214 *component = component.abs();
215 }
216 ctx.push_vector(vector)?;
217 Ok(1)
218}
219
220fn vector_sign(ctx: NativeCallContext) -> NativeCallResult {
222 let mut vector = ctx.arg(1).vector()?;
223 for component in &mut vector {
224 *component = crate::number::sign_f(*component);
225 }
226 ctx.push_vector(vector)?;
227 Ok(1)
228}
229
230fn vector_clamp(ctx: NativeCallContext) -> NativeCallResult {
232 let vector = ctx.arg(1).vector()?;
233 let min = ctx.arg(2).vector()?;
234 let max = ctx.arg(3).vector()?;
235
236 if min[0] > max[0] {
237 return ctx
238 .arg(3)
239 .error("max.x must be greater than or equal to min.x")
240 .map_err(Into::into);
241 }
242 if min[1] > max[1] {
243 return ctx
244 .arg(3)
245 .error("max.y must be greater than or equal to min.y")
246 .map_err(Into::into);
247 }
248 if min[2] > max[2] {
249 return ctx
250 .arg(3)
251 .error("max.z must be greater than or equal to min.z")
252 .map_err(Into::into);
253 }
254
255 let mut out = vector;
256 for index in 0..crate::types::LUA_VECTOR_SIZE {
257 out[index] = crate::number::clamp_f(vector[index], min[index], max[index]);
258 }
259 ctx.push_vector(out)?;
260 Ok(1)
261}
262
263fn vector_min(ctx: NativeCallContext) -> NativeCallResult {
265 let mut result = ctx.arg(1).vector()?;
266 for argument in ctx.args().skip(1) {
267 let vector = argument.vector()?;
268 for component in 0..crate::types::LUA_VECTOR_SIZE {
269 if vector[component] < result[component] {
270 result[component] = vector[component];
271 }
272 }
273 }
274 ctx.push_vector(result)?;
275 Ok(1)
276}
277
278fn vector_max(ctx: NativeCallContext) -> NativeCallResult {
280 let mut result = ctx.arg(1).vector()?;
281 for argument in ctx.args().skip(1) {
282 let vector = argument.vector()?;
283 for component in 0..crate::types::LUA_VECTOR_SIZE {
284 if vector[component] > result[component] {
285 result[component] = vector[component];
286 }
287 }
288 }
289 ctx.push_vector(result)?;
290 Ok(1)
291}
292
293fn vector_index(ctx: NativeCallContext) -> NativeCallResult {
295 let thread = ctx.raw_thread();
296 unsafe {
297 let vector = ctx.arg(1).vector()?;
298 let name = ctx.arg(2).string()?;
299
300 if name.len() == 1 {
301 let component_index = ((name[0] | b' ') as i32) - ('x' as i32);
302
303 #[cfg(feature = "vector4")]
304 let component_index = if component_index == -1 {
305 3
306 } else {
307 component_index
308 };
309
310 if (0..crate::types::LUA_VECTOR_SIZE as i32).contains(&component_index) {
311 ctx.push_number(vector[component_index as usize] as f64)?;
312 return Ok(1);
313 }
314 }
315
316 crate::error!(thread, "attempt to index vector with '%s'", name).map_err(Into::into)
317 }
318}
319
320fn vector_lerp(ctx: NativeCallContext) -> NativeCallResult {
322 let left = ctx.arg(1).vector()?;
323 let right = ctx.arg(2).vector()?;
324 let t = ctx.arg(3).number()? as f32;
325
326 let mut out = left;
327 for index in 0..crate::types::LUA_VECTOR_SIZE {
328 out[index] = crate::number::lerp_f(left[index], right[index], t);
329 }
330 ctx.push_vector(out)?;
331 Ok(1)
332}
333
334fn create_metatable(thread: &Thread) -> NativeCallResult {
336 unsafe { thread.create_table(0, 1)? };
337
338 #[cfg(feature = "vector4")]
339 unsafe {
340 thread.push_vector([0.0, 0.0, 0.0, 0.0])?;
341 }
342 #[cfg(not(feature = "vector4"))]
343 unsafe {
344 thread.push_vector([0.0, 0.0, 0.0])?;
345 }
346
347 unsafe {
348 thread.push_value(-2)?;
349 thread.set_metatable(-2)?;
350 thread.pop(1);
351 thread.push_native_closure_k(vector_index, None, 0, None)?;
352 thread.raw_set_field(-2, "__index")?;
353 thread.set_readonly(-1, 1);
354 thread.pop(1);
355 }
356 Ok(0)
357}
358
359impl Thread {
360 pub unsafe fn open_vector(&self) -> NativeCallResult {
362 unsafe {
363 self.register(Some(super::LUA_VECLIB_NAME), &VECTOR_LIB[..])?;
364
365 #[cfg(feature = "vector4")]
366 {
367 self.push_vector([0.0, 0.0, 0.0, 0.0])?;
368 self.raw_set_field(-2, "zero")?;
369 self.push_vector([1.0, 1.0, 1.0, 1.0])?;
370 self.raw_set_field(-2, "one")?;
371 }
372 #[cfg(not(feature = "vector4"))]
373 {
374 self.push_vector([0.0, 0.0, 0.0])?;
375 self.raw_set_field(-2, "zero")?;
376 self.push_vector([1.0, 1.0, 1.0])?;
377 self.raw_set_field(-2, "one")?;
378 }
379
380 create_metatable(self)?;
381 Ok(1)
382 }
383 }
384}