1use std::io::{self, Cursor, Write};
2
3pub use azalea_buf::AzBuf;
4use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
5
6use crate::{math, position::Vec3};
7
8pub trait PositionDeltaTrait {
9 fn x(&self) -> f64;
10 fn y(&self) -> f64;
11 fn z(&self) -> f64;
12}
13
14#[derive(Clone, Debug, AzBuf, Default, PartialEq)]
16pub struct PositionDelta8 {
17 pub xa: i16,
18 pub ya: i16,
19 pub za: i16,
20}
21
22impl PositionDeltaTrait for PositionDelta8 {
23 fn x(&self) -> f64 {
24 (self.xa as f64) / 4096.0
25 }
26 fn y(&self) -> f64 {
27 (self.ya as f64) / 4096.0
28 }
29 fn z(&self) -> f64 {
30 (self.za as f64) / 4096.0
31 }
32}
33impl<T: PositionDeltaTrait> From<T> for Vec3 {
34 fn from(value: T) -> Self {
35 Vec3::new(value.x(), value.y(), value.z())
36 }
37}
38
39impl Vec3 {
40 #[must_use]
41 pub fn with_delta(&self, delta: &impl PositionDeltaTrait) -> Vec3 {
42 Vec3 {
43 x: self.x + delta.x(),
44 y: self.y + delta.y(),
45 z: self.z + delta.z(),
46 }
47 }
48
49 pub fn normalize(&self) -> Vec3 {
50 let length = f64::sqrt(self.x * self.x + self.y * self.y + self.z * self.z);
51 if length < 1e-5 {
52 return Vec3::ZERO;
53 }
54 Vec3 {
55 x: self.x / length,
56 y: self.y / length,
57 z: self.z / length,
58 }
59 }
60
61 pub fn multiply(&self, x: f64, y: f64, z: f64) -> Vec3 {
62 Vec3 {
63 x: self.x * x,
64 y: self.y * y,
65 z: self.z * z,
66 }
67 }
68 pub fn scale(&self, amount: f64) -> Vec3 {
69 self.multiply(amount, amount, amount)
70 }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
78pub enum LpVec3 {
79 #[default]
80 Zero,
81 Normal {
82 a: u8,
83 b: u8,
84 c: u32,
85 },
86 Extended {
87 a: u8,
88 b: u8,
89 c: u32,
90 d: u32,
91 },
92}
93
94impl AzaleaRead for LpVec3 {
95 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
96 let a = u8::azalea_read(buf)?;
97 if a == 0 {
98 return Ok(LpVec3::Zero);
99 }
100 let b = u8::azalea_read(buf)?;
101 let c = u32::azalea_read(buf)?;
102 if a & 4 == 4 {
103 let d = u32::azalea_read_var(buf)?;
104 Ok(LpVec3::Extended { a, b, c, d })
105 } else {
106 Ok(LpVec3::Normal { a, b, c })
107 }
108 }
109}
110impl AzaleaWrite for LpVec3 {
111 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
112 match self {
113 LpVec3::Zero => {
114 0u8.azalea_write(buf)?;
115 }
116 LpVec3::Normal { a, b, c } => {
117 a.azalea_write(buf)?;
118 b.azalea_write(buf)?;
119 c.azalea_write(buf)?;
120 }
121 LpVec3::Extended { a, b, c, d } => {
122 a.azalea_write(buf)?;
123 b.azalea_write(buf)?;
124 c.azalea_write(buf)?;
125 d.azalea_write_var(buf)?;
126 }
127 }
128 Ok(())
129 }
130}
131impl LpVec3 {
132 pub fn from_vec3(vec3: Vec3) -> Self {
133 let x = Self::sanitize(vec3.x);
134 let y = Self::sanitize(vec3.y);
135 let z = Self::sanitize(vec3.z);
136 let max = x.abs().max(y.abs()).max(z.abs());
137 if max < 3.051944088384301E-5 {
138 return LpVec3::Zero;
139 }
140
141 let divisor = math::ceil_long(max);
142 let is_extended = divisor & 3 != divisor;
143 let packed_divisor = if is_extended {
144 (divisor as u64 & 3) | 4
145 } else {
146 divisor as u64
147 };
148 let packed_x = Self::pack(x / (divisor as f64)) << 3;
149 let packed_y = Self::pack(y / (divisor as f64)) << 18;
150 let packed_z = Self::pack(z / (divisor as f64)) << 33;
151 let packed = packed_divisor | packed_x | packed_y | packed_z;
152
153 let a = packed as u8;
154 let b = (packed >> 8) as u8;
155 let c = (packed >> 16) as u32;
156
157 if is_extended {
158 let d = ((divisor as u64) >> 2) as u32;
159 Self::Extended { a, b, c, d }
160 } else {
161 Self::Normal { a, b, c }
162 }
163 }
164
165 pub fn to_vec3(self) -> Vec3 {
166 match self {
167 LpVec3::Zero => Vec3::ZERO,
168 LpVec3::Normal { a, b, c } => {
169 let packed: u64 = (c as u64) << 16 | (b as u64) << 8 | (a as u64);
170 let multiplier = (a & 3) as u64 as f64;
171
172 Vec3 {
173 x: Self::unpack(packed >> 3) * multiplier,
174 y: Self::unpack(packed >> 18) * multiplier,
175 z: Self::unpack(packed >> 33) * multiplier,
176 }
177 }
178 LpVec3::Extended { a, b, c, d } => {
179 let packed: u64 = (c as u64) << 16 | (b as u64) << 8 | (a as u64);
180 let multiplier = (a & 3) as u64;
181 let multiplier = multiplier | ((d as u64) << 2);
182 let multiplier = multiplier as f64;
183
184 Vec3 {
185 x: Self::unpack(packed >> 3) * multiplier,
186 y: Self::unpack(packed >> 18) * multiplier,
187 z: Self::unpack(packed >> 33) * multiplier,
188 }
189 }
190 }
191 }
192
193 fn unpack(value: u64) -> f64 {
194 f64::min((value & 32767) as f64, 32766.) * 2. / 32766. - 1.
195 }
196
197 fn pack(value: f64) -> u64 {
198 f64::round((value * 0.5 + 0.5) * 32766.) as u64
199 }
200
201 fn sanitize(value: f64) -> f64 {
202 if value.is_nan() {
203 0.
204 } else {
205 f64::clamp(value, -1.7179869183E10, 1.7179869183E10)
206 }
207 }
208}
209impl From<LpVec3> for Vec3 {
210 fn from(value: LpVec3) -> Self {
211 value.to_vec3()
212 }
213}
214impl From<Vec3> for LpVec3 {
215 fn from(value: Vec3) -> Self {
216 LpVec3::from_vec3(value)
217 }
218}
219#[cfg(test)]
220mod tests {
221 use azalea_buf::AzaleaWrite;
222
223 use super::*;
224
225 static TEST_VALUES: [Vec3; 3] = [
226 Vec3::ZERO,
227 Vec3 {
228 x: 1.234,
229 y: -5.678,
230 z: 9.876,
231 },
232 Vec3 {
233 x: 10000000.,
234 y: -5000000.,
235 z: 9876543.,
236 },
237 ];
238
239 #[test]
240 fn test_lpvec3_roundtrip() {
241 fn close_enough(a: f64, b: f64) -> bool {
242 a == b || (a / b - 1.).abs() < 0.01
243 }
244
245 for v in TEST_VALUES {
246 let lp = LpVec3::from_vec3(v);
247 let v2 = lp.to_vec3();
248 assert!(
249 close_enough(v.x, v2.x) && close_enough(v.y, v2.y) && close_enough(v.z, v2.z),
250 "Original: {:?}, Roundtrip: {:?}",
251 v,
252 v2
253 );
254 }
255 }
256
257 #[test]
258 fn test_encode_decode_lpvec3() {
259 for v in TEST_VALUES {
260 let v: LpVec3 = LpVec3::from(v);
261 let mut first_buf = Vec::new();
262 v.azalea_write(&mut first_buf).unwrap();
263 let decoded = LpVec3::azalea_read(&mut Cursor::new(&first_buf)).unwrap();
264 assert_eq!(v, decoded);
265
266 let mut second_buf = Vec::new();
267 LpVec3::from(Vec3::from(decoded))
268 .azalea_write(&mut second_buf)
269 .unwrap();
270
271 assert_eq!(first_buf, second_buf);
272 }
273 }
274}