Skip to main content

eldiron_shared/
lib.rs

1pub mod asset;
2pub mod character;
3pub mod context;
4pub mod interaction;
5pub mod item;
6pub mod project;
7pub mod region;
8#[cfg(feature = "graphics")]
9pub mod rusterix_utils;
10pub mod screen;
11pub mod text_game;
12pub mod text_session;
13pub mod tilemap;
14pub mod treasury;
15pub use buildergraph;
16pub use tilegraph;
17
18pub mod prelude {
19    pub use serde::{Deserialize, Serialize};
20
21    pub use crate::asset::*;
22    pub use crate::buildergraph::{
23        BuilderAnchor, BuilderAssembly, BuilderGraph, BuilderNode, BuilderNodeKind,
24        BuilderOutputTarget, BuilderPrimitive, BuilderTransform,
25    };
26    pub use crate::character::Character;
27    pub use crate::context::*;
28    pub use crate::interaction::*;
29    pub use crate::item::Item;
30    pub use crate::project::{
31        BuilderGraphAsset, NodeGroupAsset, Project, TileCollectionAsset, TileCollectionEntry,
32    };
33    pub use crate::region::Region;
34    pub use crate::screen::*;
35    pub use crate::text_game::*;
36    pub use crate::text_session::*;
37    pub use crate::tilegraph::{
38        RenderedTileGraph, TileEvalContext, TileGraphRenderer, TileNodeGraphExchange,
39        TileNodeGraphState, TileNodeKind, TileNodeState, TileParticleOutput,
40    };
41    pub use crate::tilemap::{Tile, Tilemap};
42    pub use crate::treasury::{
43        TreasuryIndex, TreasuryIndexCategories, TreasuryPackageManifest, TreasuryPackageMetadata,
44        TreasuryPackageSummary, TreasuryTileCollectionPackage,
45    };
46    pub use indexmap::IndexMap;
47    pub use rusterix::{
48        Avatar, AvatarAnimation, AvatarAnimationFrame, AvatarDirection, AvatarPerspective,
49        AvatarPerspectiveCount, TileGroup, TileGroupMemberRef, TileSource,
50    };
51}
52
53use theframework::prelude::*;
54
55/// Messages to the clients. The first argument is always the client id.
56#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
57pub enum ServerMessage {
58    /// The given player instance has joined the server in the given region.
59    /// The client will use this to know what player / region to draw / focus on.
60    PlayerJoined(Uuid, Uuid, Uuid),
61}
62
63/// Ray
64#[derive(Serialize, Deserialize, PartialEq, Debug, Copy, Clone)]
65pub struct Ray {
66    pub o: Vec3<f32>,
67    pub d: Vec3<f32>,
68}
69
70impl Ray {
71    pub fn new(o: Vec3<f32>, d: Vec3<f32>) -> Self {
72        Self { o, d }
73    }
74
75    /// Returns the position on the ray at the given distance
76    pub fn at(&self, d: f32) -> Vec3<f32> {
77        self.o + self.d * d
78    }
79}
80
81#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
82pub enum HitMode {
83    Albedo,
84    Bump,
85}
86
87#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
88pub enum HitFace {
89    XFace,
90    YFace,
91    ZFace,
92}
93
94#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
95pub enum MaterialType {
96    Off,
97    PBR,
98}
99
100#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
101pub struct Material {
102    pub base_color: Vec3<f32>,
103
104    pub roughness: f32,
105    pub metallic: f32,
106    pub ior: f32,
107
108    pub mat_type: MaterialType,
109}
110
111impl Default for Material {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117impl Material {
118    pub fn new() -> Self {
119        Self {
120            base_color: Vec3::new(0.5, 0.5, 0.5),
121            roughness: 0.5,
122            metallic: 0.0,
123            ior: 1.45,
124
125            mat_type: MaterialType::Off,
126        }
127    }
128
129    /// Mixes two materials.
130    pub fn mix(&mut self, mat1: &Material, mat2: &Material, t: f32) {
131        self.base_color = mat1
132            .base_color
133            .map2(mat2.base_color, |a, b| a + t * (b - a));
134
135        self.metallic = f32::lerp(mat1.metallic, mat2.metallic, t);
136        self.roughness = f32::lerp(mat1.roughness, mat2.roughness, t);
137        self.ior = f32::lerp(mat1.ior, mat2.ior, t);
138    }
139}
140
141#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
142pub struct Hit {
143    pub is_valid: bool,
144
145    pub mode: HitMode,
146
147    pub node: usize,
148
149    pub eps: f32,
150
151    pub key: Vec3<f32>,
152    pub hash: f32,
153
154    pub bump: f32,
155
156    pub distance: f32,
157    pub interior_distance: f32,
158
159    pub hit_point: Vec3<f32>,
160    pub normal: Vec3<f32>,
161    pub uv: Vec2<f32>,
162    pub global_uv: Vec2<f32>,
163    pub face: HitFace,
164
165    pub pattern_pos: Vec2<f32>,
166
167    pub color: Vec4<f32>,
168
169    pub mat: Material,
170    pub noise: Option<f32>,
171    pub noise_scale: f32,
172
173    pub value: f32,
174
175    pub two_d: bool,
176}
177
178impl Default for Hit {
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184impl Hit {
185    pub fn new() -> Self {
186        Self {
187            is_valid: true,
188
189            mode: HitMode::Albedo,
190
191            node: 0,
192
193            eps: 0.001, //0.0003,
194
195            key: Vec3::zero(),
196            hash: 0.0,
197
198            bump: 0.0,
199
200            distance: f32::MAX,
201            interior_distance: f32::MAX,
202
203            hit_point: Vec3::zero(),
204            normal: Vec3::zero(),
205            uv: Vec2::zero(),
206            global_uv: Vec2::zero(),
207            face: HitFace::XFace,
208
209            pattern_pos: Vec2::zero(),
210
211            color: Vec4::zero(),
212
213            mat: Material::default(),
214            noise: None,
215            noise_scale: 1.0,
216
217            value: 1.0,
218
219            two_d: false,
220        }
221    }
222}
223
224#[derive(Debug, Clone, Copy)]
225pub struct AABB2D {
226    min: Vec2<f32>,
227    max: Vec2<f32>,
228}
229
230impl Default for AABB2D {
231    fn default() -> Self {
232        Self::zero()
233    }
234}
235
236impl AABB2D {
237    pub fn new(min: Vec2<f32>, max: Vec2<f32>) -> Self {
238        AABB2D { min, max }
239    }
240
241    pub fn zero() -> Self {
242        AABB2D {
243            min: Vec2::new(f32::MAX, f32::MAX),
244            max: Vec2::new(f32::MIN, f32::MIN),
245        }
246    }
247
248    pub fn is_empty(&self) -> bool {
249        self.min.x > self.max.x || self.min.y > self.max.y
250    }
251
252    pub fn grow(&mut self, other: AABB2D) {
253        self.min.x = self.min.x.min(other.min.x);
254        self.min.y = self.min.y.min(other.min.y);
255        self.max.x = self.max.x.max(other.max.x);
256        self.max.y = self.max.y.max(other.max.y);
257    }
258
259    pub fn to_int(&self) -> (Vec2<i32>, Vec2<i32>) {
260        let min_int = Vec2::new(self.min.x.floor() as i32, self.min.y.floor() as i32);
261        let max_int = Vec2::new(self.max.x.ceil() as i32, self.max.y.ceil() as i32);
262        (min_int, max_int)
263    }
264
265    pub fn to_tiles(&self) -> Vec<Vec2<i32>> {
266        let (min_int, max_int) = self.to_int();
267        let mut tiles = Vec::new();
268
269        for x in min_int.x..=max_int.x {
270            for y in min_int.y..=max_int.y {
271                tiles.push(Vec2::new(x, y));
272            }
273        }
274
275        tiles
276    }
277}
278
279// https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
280pub fn do_intersect(p1: (i32, i32), q1: (i32, i32), p2: (i32, i32), q2: (i32, i32)) -> bool {
281    // Given three collinear points p, q, r, the function checks if
282    // point q lies on line segment 'pr'
283    fn on_segment(p: (i32, i32), q: (i32, i32), r: (i32, i32)) -> bool {
284        q.0 <= std::cmp::max(p.0, r.0)
285            && q.0 >= std::cmp::min(p.0, r.0)
286            && q.1 <= std::cmp::max(p.1, r.1)
287            && q.1 >= std::cmp::min(p.1, r.1)
288    }
289
290    // To find orientation of ordered triplet (p, q, r).
291    // The function returns following values
292    // 0 --> p, q and r are collinear
293    // 1 --> Clockwise
294    // 2 --> Counterclockwise
295    fn orientation(p: (i32, i32), q: (i32, i32), r: (i32, i32)) -> i32 {
296        let val = (q.1 - p.1) * (r.0 - q.0) - (q.0 - p.0) * (r.1 - q.1);
297        if val == 0 {
298            return 0;
299        } // collinear
300        if val > 0 { 1 } else { 2 } // clock or counterclock wise
301    }
302
303    // Check if line segments 'p1q1' and 'p2q2' intersect.
304    let o1 = orientation(p1, q1, p2);
305    let o2 = orientation(p1, q1, q2);
306    let o3 = orientation(p2, q2, p1);
307    let o4 = orientation(p2, q2, q1);
308
309    // General case
310    if o1 != o2 && o3 != o4 {
311        return true;
312    }
313
314    // Special Cases
315    // p1, q1 and p2 are collinear and p2 lies on segment p1q1
316    if o1 == 0 && on_segment(p1, p2, q1) {
317        return true;
318    }
319
320    // p1, q1 and q2 are collinear and q2 lies on segment p1q1
321    if o2 == 0 && on_segment(p1, q2, q1) {
322        return true;
323    }
324
325    // p2, q2 and p1 are collinear and p1 lies on segment p2q2
326    if o3 == 0 && on_segment(p2, p1, q2) {
327        return true;
328    }
329
330    // p2, q2 and q1 are collinear and q1 lies on segment p2q2
331    if o4 == 0 && on_segment(p2, q1, q2) {
332        return true;
333    }
334
335    // Doesn't fall in any of the above cases
336    false
337}
338
339#[derive(Serialize, Deserialize, Clone, Debug)]
340pub struct RenderTile {
341    pub x: usize,
342    pub y: usize,
343    pub width: usize,
344    pub height: usize,
345}
346
347impl RenderTile {
348    pub fn new(x: usize, y: usize, width: usize, height: usize) -> Self {
349        Self {
350            x,
351            y,
352            width,
353            height,
354        }
355    }
356
357    pub fn create_tiles(
358        image_width: usize,
359        image_height: usize,
360        tile_width: usize,
361        tile_height: usize,
362    ) -> Vec<Self> {
363        // TODO: Generate the tiles in a nice spiral pattern
364
365        let mut tiles = Vec::new();
366        let mut x = 0;
367        let mut y = 0;
368        while x < image_width && y < image_height {
369            let tile = Self {
370                x,
371                y,
372                width: tile_width,
373                height: tile_height,
374            };
375            tiles.push(tile);
376            x += tile_width;
377            if x >= image_width {
378                x = 0;
379                y += tile_height;
380            }
381        }
382
383        tiles
384    }
385}