├── .gitignore ├── etc ├── city.wasm ├── img │ └── flecs_logo.png ├── sokol │ └── shaders │ │ ├── constants.glsl │ │ ├── fx_fog_header.glsl │ │ ├── fx_ssao_blend.glsl │ │ ├── fx_hdr.glsl │ │ ├── fx_hdr_threshold.glsl │ │ ├── scene_vert.glsl │ │ ├── fx_ssao_main.glsl │ │ ├── scene_atmos_sun.frag │ │ ├── fx_fog_main.glsl │ │ ├── fx_hdr_blur.glsl │ │ ├── common.glsl │ │ ├── atmosphere_frag.glsl │ │ ├── fx_ssao_header.glsl │ │ ├── atmosphere.glsl │ │ └── scene_frag.glsl ├── assets │ ├── scene.flecs │ ├── app.flecs │ └── city.flecs └── index.html ├── .gitattributes ├── deps ├── flecs_systems_sokol.c ├── flecs_systems_sokol_objc.m ├── flecs_components_cglm.c ├── flecs_components_physics.c ├── flecs_components_gui.c ├── dependee.json ├── flecs_components_cglm.h ├── flecs_components_geometry.c ├── flecs_systems_transform.h ├── flecs_components_transform.c ├── flecs_systems_sokol.h ├── flecs_components_graphics.c ├── flecs_game.h ├── flecs_components_physics.h ├── flecs_components_geometry.h ├── flecs_components_gui.h ├── flecs_systems_transform.c ├── flecs_components_transform.h ├── flecs_systems_physics.h ├── flecs_components_graphics.h ├── flecs_components_input.c ├── flecs_components_input.h ├── flecs_systems_physics.c └── flecs_game.c ├── project.json ├── include ├── city │ ├── game.h │ └── bake_config.h └── city.h ├── README.md ├── LICENSE └── src ├── main.c └── module.c /.gitignore: -------------------------------------------------------------------------------- 1 | .bake_cache 2 | .DS_Store 3 | .vscode 4 | gcov 5 | bin 6 | -------------------------------------------------------------------------------- /etc/city.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flecs-hub/city/HEAD/etc/city.wasm -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /etc/img/flecs_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flecs-hub/city/HEAD/etc/img/flecs_logo.png -------------------------------------------------------------------------------- /deps/flecs_systems_sokol.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flecs-hub/city/HEAD/deps/flecs_systems_sokol.c -------------------------------------------------------------------------------- /deps/flecs_systems_sokol_objc.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flecs-hub/city/HEAD/deps/flecs_systems_sokol_objc.m -------------------------------------------------------------------------------- /etc/sokol/shaders/constants.glsl: -------------------------------------------------------------------------------- 1 | #define PI 3.1415926535897932384626433832795 2 | #define PI2 (2.0 * PI) 3 | #define EPSILON 1e-6f 4 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_fog_header.glsl: -------------------------------------------------------------------------------- 1 | #include "etc/sokol/shaders/common.glsl" 2 | #include "etc/sokol/shaders/atmosphere.glsl" 3 | 4 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_ssao_blend.glsl: -------------------------------------------------------------------------------- 1 | float ambientOcclusion = rgba_to_float(texture(t_occlusion, uv)); 2 | frag_color = (1.0 - ambientOcclusion) * texture(t_scene, uv); 3 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_hdr.glsl: -------------------------------------------------------------------------------- 1 | 2 | vec3 c = texture(hdr, uv).rgb; 3 | vec3 b = texture(bloom, uv).rgb; 4 | float b_clip = dot(vec3(0.333), b); 5 | b = b + pow(b_clip, 3.0); 6 | c = c + b; 7 | c = pow(c, vec3(1.0 / gamma)); 8 | frag_color = vec4(c, 1.0); 9 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_hdr_threshold.glsl: -------------------------------------------------------------------------------- 1 | 2 | const vec3 channel_lum = vec3(0.299, 0.587, 0.114); 3 | const float lmax = 0.2 / dot(vec3(1.0, 1.0, 1.0), channel_lum); 4 | 5 | vec4 c = texture(hdr, uv, mipmap); 6 | float l = dot(c.rgb, channel_lum); 7 | l = l * lmax; 8 | frag_color = c * l; 9 | -------------------------------------------------------------------------------- /etc/sokol/shaders/scene_vert.glsl: -------------------------------------------------------------------------------- 1 | out vec4 position; 2 | out vec4 light_position; 3 | out vec3 normal; 4 | out vec4 color; 5 | out vec3 material; 6 | 7 | void main() { 8 | vec4 pos4 = vec4(v_position, 1.0); 9 | gl_Position = u_mat_vp * i_mat_m * pos4; 10 | light_position = u_light_vp * i_mat_m * pos4; 11 | position = (i_mat_m * pos4); 12 | normal = (i_mat_m * vec4(v_normal, 0.0)).xyz; 13 | color = vec4(i_color, 0.0); 14 | material = i_material; 15 | } 16 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_ssao_main.glsl: -------------------------------------------------------------------------------- 1 | // Depth (range = 0 .. u_far) 2 | float centerDepth = getDepth( uv ); 3 | 4 | // Depth (range = 0 .. 1) 5 | float centerDepthNorm = centerDepth / u_far; 6 | float centerViewZ = getViewZ( centerDepth ); 7 | vec3 viewPosition = getViewPosition( uv, centerDepthNorm, centerViewZ ); 8 | float ambientOcclusion = getAmbientOcclusion( viewPosition, centerDepth ); 9 | 10 | // Store value as rgba to increase precision 11 | float max_dist = 0.1; 12 | float mult = 1.0 / max_dist; 13 | frag_color = float_to_rgba(ambientOcclusion) * max(0.0, max_dist - centerDepthNorm) * mult; 14 | -------------------------------------------------------------------------------- /etc/sokol/shaders/scene_atmos_sun.frag: -------------------------------------------------------------------------------- 1 | uniform sampler2D atmos; 2 | uniform vec3 u_sun_screen_pos; 3 | uniform vec3 u_sun_color; 4 | uniform float u_aspect; 5 | uniform float u_sun_intensity; 6 | 7 | in vec2 uv; 8 | out vec4 frag_color; 9 | 10 | void main() { 11 | vec2 sun_dist = uv.xy - u_sun_screen_pos.xy; 12 | sun_dist.x *= u_aspect; 13 | 14 | float sun_dist_len = length(sun_dist); 15 | float intensity = u_sun_intensity * float(u_sun_screen_pos.z >= 0.0); 16 | vec3 sunDisc = intensity * u_sun_color * ((sun_dist_len < 0.025) ? 1.0 : 0.0); 17 | 18 | frag_color = texture(atmos, uv) + vec4(sunDisc, 1.0); 19 | } 20 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_fog_main.glsl: -------------------------------------------------------------------------------- 1 | #define LOG2 1.442695 2 | 3 | const float transition = 0.025; 4 | const float inv_transition = 1.0 / transition; 5 | const float sample_height = 0.01; 6 | 7 | vec4 c = texture(hdr, uv); 8 | float d = (rgba_to_depth(texture(depth, uv)) / u_far); 9 | vec4 fog_color = texture(atmos, vec2(uv.x, u_horizon + sample_height)); 10 | float intensity; 11 | if (d > 1.0) { 12 | intensity = (max((u_horizon + transition) - uv.y, 0.0) * inv_transition); 13 | intensity = min(intensity, 1.0); 14 | } else { 15 | intensity = 1.0 - exp2(-(d * d) * u_density * u_density * LOG2); 16 | } 17 | frag_color = mix(c, fog_color, intensity); 18 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "city", 3 | "type": "application", 4 | "value": { 5 | "use": [ 6 | "flecs", 7 | "flecs.components.transform", 8 | "flecs.components.graphics", 9 | "flecs.components.geometry", 10 | "flecs.components.physics", 11 | "flecs.components.gui", 12 | "flecs.systems.physics", 13 | "flecs.systems.transform", 14 | "flecs.systems.sokol", 15 | "flecs.game" 16 | ], 17 | "standalone": true 18 | }, 19 | "lang.c": { 20 | "${target em}": { 21 | "ldflags": ["-sSTACK_SIZE=1000000", "-Wl,-u,ntohs"], 22 | "embed": ["etc/assets"] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /include/city/game.h: -------------------------------------------------------------------------------- 1 | #ifndef FLECS_CITY_H 2 | #define FLECS_CITY_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | ECS_STRUCT(CityBlock, { 9 | float x; 10 | float y; 11 | ecs_entity_t city; 12 | int16_t size; 13 | int16_t building_size; 14 | int16_t building_height; 15 | float park_chance; 16 | float tree_chance; 17 | }); 18 | 19 | ECS_STRUCT(City, { 20 | int16_t blocks_x; 21 | int16_t blocks_y; 22 | int16_t block_size; 23 | int16_t road_width; 24 | int16_t building_size; 25 | int16_t min_building_height; 26 | int16_t max_building_height; 27 | float park_chance; 28 | float tree_chance; 29 | }); 30 | 31 | void FlecsCityImport( 32 | ecs_world_t *world); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_hdr_blur.glsl: -------------------------------------------------------------------------------- 1 | const float gauss[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); 2 | 3 | float offset = 0.0; 4 | vec4 result = gauss[0] * texture(tex, vec2(uv.x, uv.y)); 5 | ivec2 ts = textureSize(tex, 0); 6 | float px = 1.0 / float(ts.x); 7 | float py = 1.0 / float(ts.y); 8 | if (horizontal == 0.0) { 9 | for (int i = 1; i < 5; i ++) { 10 | offset += px / u_aspect; 11 | result += gauss[i] * texture(tex, vec2(uv.x + offset, uv.y)); 12 | result += gauss[i] * texture(tex, vec2(uv.x - offset, uv.y)); 13 | } 14 | } else { 15 | for (int i = 1; i < 5; i ++) { 16 | offset += py; 17 | result += gauss[i] * texture(tex, vec2(uv.x, uv.y + offset)); 18 | result += gauss[i] * texture(tex, vec2(uv.x, uv.y - offset)); 19 | } 20 | } 21 | 22 | frag_color = result; 23 | -------------------------------------------------------------------------------- /deps/flecs_components_cglm.c: -------------------------------------------------------------------------------- 1 | #include "flecs_components_cglm.h" 2 | 3 | ECS_COMPONENT_DECLARE(vec3); 4 | ECS_COMPONENT_DECLARE(vec4); 5 | 6 | void FlecsComponentsCglmImport( 7 | ecs_world_t *world) 8 | { 9 | ECS_MODULE(world, FlecsComponentsCglm); 10 | 11 | ecs_id(vec3) = ecs_array(world, { 12 | .entity = ecs_entity(world, { 13 | .name = "vec3", 14 | .symbol = "vec3", 15 | .id = ecs_id(vec3) 16 | }), 17 | .type = ecs_id(ecs_f32_t), 18 | .count = 3 19 | }); 20 | 21 | ecs_id(vec4) = ecs_array(world, { 22 | .entity = ecs_entity(world, { 23 | .name = "vec4", 24 | .symbol = "vec4", 25 | .id = ecs_id(vec4) 26 | }), 27 | .type = ecs_id(ecs_f32_t), 28 | .count = 4 29 | }); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # city 2 | Simple procedural city generator. Standalone Flecs project with embedded graphics modules ([live webasm demo](https://flecs.dev/city)) 3 | 4 | Screen Shot 2021-12-21 at 2 17 27 PM 5 | 6 | ## How to run 7 | Use the following commands to run the demo: 8 | 9 | Install bake on macOS/Linux: 10 | ``` 11 | git clone https://github.com/SanderMertens/bake 12 | bake/setup.sh 13 | ``` 14 | 15 | Install bake on Windows 16 | ``` 17 | git clone https://github.com/SanderMertens/bake 18 | cd bake 19 | setup 20 | ``` 21 | 22 | or if you already have a bake installation: 23 | ``` 24 | bake upgrade 25 | ``` 26 | 27 | Then run: 28 | ``` 29 | bake run flecs-hub/city 30 | ``` 31 | 32 | Have fun! 33 | 34 | ## How to use 35 | To customize the city generation parameters, change the settings in the etc/assets/scene.plecs file. 36 | -------------------------------------------------------------------------------- /etc/assets/scene.flecs: -------------------------------------------------------------------------------- 1 | using flecs.components.* 2 | using flecs.city 3 | using flecs.game 4 | 5 | city { 6 | City: { 7 | blocks_x: 25 8 | blocks_y: 18 9 | block_width: 15 10 | block_height: 25 11 | road_width: 6 12 | 13 | buildings: { 14 | min_height: 3 15 | max_height: 160 16 | x_variation: 0.5 17 | y_variation: 2.0 18 | small_height: 50 19 | 20 | modern_chance: 0.3 21 | skyscraper_chance: 0.2 22 | backyard_chance: 1.0 23 | } 24 | 25 | parks: { 26 | chance: 0.3, 27 | tree_chance: 0.6 28 | plaza_chance: 0.1 29 | tree_count: 4 30 | } 31 | 32 | props: { 33 | chance: 0.7 34 | tree_chance: 0.5 35 | bin_chance: 0.09 36 | hydrant_chance: 0.03 37 | bench_chance: 0.08 38 | } 39 | 40 | traffic: { 41 | frequency: 5 42 | speed: 10 43 | chance: 0.02 44 | } 45 | 46 | lanterns: true 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /etc/assets/app.flecs: -------------------------------------------------------------------------------- 1 | using flecs.components.* 2 | using flecs.game 3 | 4 | light { 5 | Sun 6 | Rotation3:{y: 0.5} 7 | } 8 | 9 | $ { 10 | TimeOfDay: { 11 | t: 0.8 12 | speed: 0.0005 13 | } 14 | } 15 | 16 | camera { 17 | CameraController 18 | Position3: {-115, 10.0, -287} 19 | Rotation3: {-0.15} 20 | Camera: { 21 | fov: 20 22 | up: [0, 1, 0] 23 | near_: 2 24 | far_: 1000 25 | } 26 | } 27 | 28 | canvas { 29 | Atmosphere: { 30 | night_color: {0.0001, 0.002, 0.004} 31 | } 32 | Canvas: { 33 | title: "Flecs City" 34 | width: 1200 35 | height: 900 36 | background_color: {0.3, 0.6, 0.9} 37 | ambient_light: {0.03, 0.06, 0.09} 38 | ambient_light_ground: {0.007, 0.00625, 0.00312} 39 | ambient_light_ground_falloff: 4.0 40 | directional_light: light 41 | camera: camera 42 | fog_density: 1.8 43 | shadow_far: 300 44 | } 45 | } 46 | 47 | ground_plane { 48 | Position3: {0, -1.0, 0} 49 | Box: {5000, 1.0, 5000} 50 | Rgb: {0.11, 0.15, 0.1} 51 | } 52 | -------------------------------------------------------------------------------- /etc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Flecs city 4 | 5 | 6 | 7 | 8 | 9 | [ 12 | Source 13 | ] 14 | [ 17 | Open in Explorer 18 | ] 19 |  -  20 | Use WASD + QE to move camera, arrow keys to look around 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Flecs Hub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) { 4 | ecs_world_t *world = ecs_init(); 5 | 6 | ECS_IMPORT(world, FlecsStats); 7 | 8 | ECS_IMPORT(world, FlecsUnits); 9 | ECS_IMPORT(world, FlecsScript); 10 | ECS_IMPORT(world, FlecsComponentsTransform); 11 | ECS_IMPORT(world, FlecsComponentsGeometry); 12 | ECS_IMPORT(world, FlecsComponentsGui); 13 | ECS_IMPORT(world, FlecsComponentsGraphics); 14 | 15 | ECS_IMPORT(world, FlecsGame); 16 | ECS_IMPORT(world, FlecsCity); 17 | 18 | ecs_time_t t = {0}; ecs_time_measure(&t); 19 | ecs_script_run_file(world, "etc/assets/scene.flecs"); 20 | printf("scene loaded in %fs\n", ecs_time_measure(&t)); 21 | 22 | /* Prewarm simulation so there's cars everywhere */ 23 | for (int i = 0; i < 5000; i ++) { 24 | ecs_progress(world, 0.16); 25 | } 26 | 27 | ECS_IMPORT(world, FlecsSystemsTransform); 28 | ECS_IMPORT(world, FlecsSystemsSokol); 29 | ecs_script_run_file(world, "etc/assets/app.flecs"); 30 | 31 | return ecs_app_run(world, &(ecs_app_desc_t){ 32 | .enable_rest = true, 33 | .enable_stats = true, 34 | .target_fps = 60 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /include/city/bake_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | ) 3 | (.) 4 | .|. 5 | | | 6 | _.--| |--._ 7 | .-'; ;`-'& ; `&. 8 | \ & ; & &_/ 9 | |"""---...---"""| 10 | \ | | | | | | | / 11 | `---.|.|.|.---' 12 | 13 | * This file is generated by bake.lang.c for your convenience. Headers of 14 | * dependencies will automatically show up in this file. Include bake_config.h 15 | * in your main project file. Do not edit! */ 16 | 17 | #ifndef CITY_BAKE_CONFIG_H 18 | #define CITY_BAKE_CONFIG_H 19 | 20 | /* Headers of public dependencies */ 21 | #include "../../deps/flecs.h" 22 | #include "../../deps/flecs_components_transform.h" 23 | #include "../../deps/flecs_components_graphics.h" 24 | #include "../../deps/flecs_components_geometry.h" 25 | #include "../../deps/flecs_components_physics.h" 26 | #include "../../deps/flecs_components_gui.h" 27 | #include "../../deps/flecs_systems_physics.h" 28 | #include "../../deps/flecs_systems_transform.h" 29 | #include "../../deps/flecs_systems_sokol.h" 30 | #include "../../deps/flecs_game.h" 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /etc/sokol/shaders/common.glsl: -------------------------------------------------------------------------------- 1 | #include "etc/sokol/shaders/constants.glsl" 2 | 3 | vec4 float_to_rgba(const in float v) { 4 | vec4 enc = vec4(1.0, 255.0, 65025.0, 160581375.0) * v; 5 | enc = fract(enc); 6 | enc -= enc.yzww * vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0); 7 | return enc; 8 | } 9 | 10 | float rgba_to_float(const in vec4 rgba) { 11 | return dot(rgba, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 160581375.0)); 12 | } 13 | 14 | float pow2(const in float v) { 15 | return v * v; 16 | } 17 | 18 | #ifdef FX 19 | float rgba_to_depth(vec4 rgba) { 20 | float d = rgba_to_float(rgba); 21 | d *= log(0.05 * u_far + 1.0); 22 | d = exp(d); 23 | d -= 1.0; 24 | d /= 0.05; 25 | return d; 26 | } 27 | #endif 28 | 29 | float rgba_to_depth_log(vec4 rgba) { 30 | return rgba_to_float(rgba); 31 | } 32 | 33 | highp float rand( const in vec2 uv ) { 34 | const highp float a = 12.9898, b = 78.233, c = 43758.5453; 35 | highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); 36 | return fract( sin( sn ) * c ); 37 | } 38 | 39 | vec4 unpack_rgba(uint packedColor) { 40 | float r = float((packedColor >> 24) & 0xFFu) / 255.0; 41 | float g = float((packedColor >> 16) & 0xFFu) / 255.0; 42 | float b = float((packedColor >> 8) & 0xFFu) / 255.0; 43 | float a = float(packedColor & 0xFFu) / 255.0; 44 | return vec4(a, g, b, a); 45 | } 46 | -------------------------------------------------------------------------------- /deps/flecs_components_physics.c: -------------------------------------------------------------------------------- 1 | #define FLECS_COMPONENTS_PHYSICS_IMPL 2 | #include "flecs_components_physics.h" 3 | 4 | ECS_DECLARE(EcsCollider); 5 | ECS_DECLARE(EcsRigidBody); 6 | 7 | void FlecsComponentsPhysicsImport( 8 | ecs_world_t *world) 9 | { 10 | ECS_MODULE(world, FlecsComponentsPhysics); 11 | 12 | ecs_set_name_prefix(world, "Ecs"); 13 | 14 | ECS_TAG_DEFINE(world, EcsCollider); 15 | ECS_TAG_DEFINE(world, EcsRigidBody); 16 | ECS_META_COMPONENT(world, EcsVelocity2); 17 | ECS_META_COMPONENT(world, EcsVelocity3); 18 | ECS_META_COMPONENT(world, EcsAngularSpeed); 19 | ECS_META_COMPONENT(world, EcsAngularVelocity); 20 | ECS_META_COMPONENT(world, EcsBounciness); 21 | ECS_META_COMPONENT(world, EcsFriction); 22 | 23 | ecs_set_hooks(world, EcsVelocity2, { 24 | .ctor = flecs_default_ctor 25 | }); 26 | 27 | ecs_set_hooks(world, EcsVelocity3, { 28 | .ctor = flecs_default_ctor 29 | }); 30 | 31 | ecs_set_hooks(world, EcsAngularSpeed, { 32 | .ctor = flecs_default_ctor 33 | }); 34 | 35 | ecs_set_hooks(world, EcsAngularVelocity, { 36 | .ctor = flecs_default_ctor 37 | }); 38 | 39 | ecs_set_hooks(world, EcsBounciness, { 40 | .ctor = flecs_default_ctor 41 | }); 42 | 43 | ecs_set_hooks(world, EcsFriction, { 44 | .ctor = flecs_default_ctor 45 | }); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /deps/flecs_components_gui.c: -------------------------------------------------------------------------------- 1 | #define FLECS_COMPONENTS_GUI_IMPL 2 | 3 | #include "flecs_components_gui.h" 4 | 5 | ECS_CTOR(EcsText, ptr, { 6 | ptr->value = NULL; 7 | }) 8 | 9 | ECS_DTOR(EcsText, ptr, { 10 | ecs_os_free(ptr->value); 11 | ptr->value = NULL; 12 | }) 13 | 14 | ECS_COPY(EcsText, dst, src, { 15 | ecs_os_free(dst->value); 16 | dst->value = ecs_os_strdup(src->value); 17 | }) 18 | 19 | ECS_MOVE(EcsText, dst, src, { 20 | ecs_os_free(dst->value); 21 | dst->value = src->value; 22 | src->value = NULL; 23 | }) 24 | 25 | ECS_CTOR(EcsCanvas, ptr, { 26 | ecs_os_zeromem(ptr); 27 | ptr->ambient_light_ground_intensity = 1.0; 28 | }) 29 | 30 | void FlecsComponentsGuiImport( 31 | ecs_world_t *world) 32 | { 33 | ECS_MODULE(world, FlecsComponentsGui); 34 | ECS_IMPORT(world, FlecsComponentsGraphics); 35 | ECS_IMPORT(world, FlecsComponentsCglm); 36 | 37 | ecs_set_name_prefix(world, "Ecs"); 38 | 39 | ECS_META_COMPONENT(world, EcsCanvas); 40 | ECS_META_COMPONENT(world, EcsText); 41 | ECS_META_COMPONENT(world, EcsFontSize); 42 | ECS_META_COMPONENT(world, EcsFontStyle); 43 | ECS_META_COMPONENT(world, EcsAlign); 44 | ECS_META_COMPONENT(world, EcsPadding); 45 | 46 | ecs_set_hooks(world, EcsCanvas, { 47 | .ctor = ecs_ctor(EcsCanvas), 48 | }); 49 | 50 | ecs_set_hooks(world, EcsText, { 51 | .ctor = ecs_ctor(EcsText), 52 | .dtor = ecs_dtor(EcsText), 53 | .copy = ecs_copy(EcsText), 54 | .move = ecs_move(EcsText) 55 | }); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /include/city.h: -------------------------------------------------------------------------------- 1 | #ifndef CITY_H 2 | #define CITY_H 3 | 4 | /* This generated file contains includes for project dependencies */ 5 | #include "city/bake_config.h" 6 | 7 | #undef ECS_META_IMPL 8 | #ifndef FLECS_CITY_IMPL 9 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 10 | #endif 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | ECS_STRUCT(CityBuildings, { 17 | int16_t min_height; 18 | int16_t max_height; 19 | int16_t min_width; 20 | float x_variation; 21 | float y_variation; 22 | float small_height; 23 | float skyscraper_chance; 24 | float modern_chance; 25 | float backyard_chance; 26 | }); 27 | 28 | ECS_STRUCT(CityParks, { 29 | float chance; 30 | float tree_chance; 31 | float plaza_chance; 32 | int32_t tree_count; 33 | }); 34 | 35 | ECS_STRUCT(CityProps, { 36 | float chance; 37 | float tree_chance; 38 | float bin_chance; 39 | float hydrant_chance; 40 | float bench_chance; 41 | }); 42 | 43 | ECS_STRUCT(CityTraffic, { 44 | float frequency; 45 | float speed; 46 | float chance; 47 | }); 48 | 49 | ECS_STRUCT(City, { 50 | int16_t blocks_x; 51 | int16_t blocks_y; 52 | int16_t block_width; 53 | int16_t block_height; 54 | float road_width; 55 | float pavement_width; 56 | 57 | bool lanterns; 58 | bool street_signs; 59 | 60 | CityBuildings buildings; 61 | CityParks parks; 62 | CityProps props; 63 | CityTraffic traffic; 64 | 65 | ecs_entity_t cars_scope; 66 | }); 67 | 68 | void FlecsCityImport( 69 | ecs_world_t *world); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /etc/sokol/shaders/atmosphere_frag.glsl: -------------------------------------------------------------------------------- 1 | #include "etc/sokol/shaders/atmosphere.glsl" 2 | 3 | uniform mat4 u_mat_v; 4 | uniform vec3 u_eye_pos; 5 | uniform vec3 u_light_pos; 6 | uniform vec3 u_night_color; 7 | uniform float u_aspect; 8 | uniform float u_offset; 9 | 10 | uniform float intensity; 11 | uniform float planet_radius; 12 | uniform float atmosphere_radius; 13 | uniform vec3 rayleigh_coef; 14 | uniform float mie_coef; 15 | uniform float rayleigh_scale_height; 16 | uniform float mie_scale_height; 17 | uniform float mie_scatter_dir; 18 | 19 | in vec2 uv; 20 | out vec4 frag_color; 21 | 22 | void main() { 23 | vec2 uv_scaled = uv * 1.3 - 0.66; 24 | uv_scaled.x *= u_aspect; 25 | uv_scaled.y += u_offset; 26 | 27 | vec4 coord = vec4(uv_scaled, -1, 0); 28 | vec3 ray = vec3(u_mat_v * coord); 29 | vec3 orig = vec3(u_eye_pos.x, 6372e3 + u_eye_pos.y, u_eye_pos.z); 30 | vec3 atmos = atmosphere( 31 | ray, // normalized ray direction 32 | orig, // ray origin 33 | -u_light_pos, // position of the sun 34 | intensity, // intensity of the sun 35 | planet_radius, // radius of the planet in meters 36 | atmosphere_radius, // radius of the atmosphere in meters 37 | rayleigh_coef, // Rayleigh scattering coefficient 38 | mie_coef, // Mie scattering coefficient 39 | rayleigh_scale_height, // Rayleigh scale height 40 | mie_scale_height, // Mie scale height 41 | mie_scatter_dir // Mie preferred scattering direction 42 | ); 43 | 44 | frag_color = vec4(atmos, 1.0) + vec4(u_night_color, 1.0); 45 | } 46 | -------------------------------------------------------------------------------- /deps/dependee.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependee": { 3 | "lang.c": { 4 | "${cfg sanitize}": { 5 | "defines": [ 6 | "FLECS_SANITIZE" 7 | ] 8 | } 9 | }, 10 | "lang.cpp": { 11 | "${cfg sanitize}": { 12 | "defines": [ 13 | "FLECS_SANITIZE" 14 | ] 15 | } 16 | } 17 | }, 18 | "lang.c": { 19 | "${os linux}": { 20 | "lib": [ 21 | "rt", 22 | "pthread", 23 | "m", 24 | "GL", 25 | "X11", 26 | "Xi", 27 | "Xcursor", 28 | "dl" 29 | ], 30 | "${cfg debug}": { 31 | "export-symbols": true 32 | }, 33 | "${cfg sanitize}": { 34 | "export-symbols": true 35 | } 36 | }, 37 | "${os windows}": { 38 | "lib": [ 39 | "ws2_32", 40 | "dbgHelp" 41 | ], 42 | "defines": [ 43 | "_WINDOWS", 44 | "_USRDLL", 45 | "CGLM_EXPORTS", 46 | "CGLM_DLL" 47 | ] 48 | }, 49 | "${cfg sanitize}": { 50 | "defines": [ 51 | "FLECS_SANITIZE" 52 | ] 53 | }, 54 | "${os darwin}": { 55 | "ldflags": [ 56 | "-framework Cocoa", 57 | "-framework QuartzCore", 58 | "-framework OpenGL" 59 | ] 60 | }, 61 | "${target em}": { 62 | "ldflags": [ 63 | "-s USE_WEBGL2=1" 64 | ], 65 | "${cfg debug}": { 66 | "ldflags": [ 67 | "-s GL_DEBUG=1" 68 | ] 69 | }, 70 | "embed": [ 71 | "etc\/sokol\/shaders" 72 | ] 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /deps/flecs_components_cglm.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_cglm_STATIC 3 | #ifndef FLECS_COMPONENTS_CGLM_H 4 | #define FLECS_COMPONENTS_CGLM_H 5 | 6 | /* This generated file contains includes for project dependencies */ 7 | /* 8 | ) 9 | (.) 10 | .|. 11 | | | 12 | _.--| |--._ 13 | .-'; ;`-'& ; `&. 14 | \ & ; & &_/ 15 | |"""---...---"""| 16 | \ | | | | | | | / 17 | `---.|.|.|.---' 18 | 19 | * This file is generated by bake.lang.c for your convenience. Headers of 20 | * dependencies will automatically show up in this file. Include bake_config.h 21 | * in your main project file. Do not edit! */ 22 | 23 | #ifndef FLECS_COMPONENTS_CGLM_BAKE_CONFIG_H 24 | #define FLECS_COMPONENTS_CGLM_BAKE_CONFIG_H 25 | 26 | /* Headers of public dependencies */ 27 | #include "flecs.h" 28 | #include "cglm.h" 29 | 30 | /* Convenience macro for exporting symbols */ 31 | #ifndef flecs_components_cglm_STATIC 32 | #if defined(flecs_components_cglm_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 33 | #define FLECS_COMPONENTS_CGLM_API __declspec(dllexport) 34 | #elif defined(flecs_components_cglm_EXPORTS) 35 | #define FLECS_COMPONENTS_CGLM_API __attribute__((__visibility__("default"))) 36 | #elif defined(_MSC_VER) 37 | #define FLECS_COMPONENTS_CGLM_API __declspec(dllimport) 38 | #else 39 | #define FLECS_COMPONENTS_CGLM_API 40 | #endif 41 | #else 42 | #define FLECS_COMPONENTS_CGLM_API 43 | #endif 44 | 45 | #endif 46 | 47 | 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | FLECS_COMPONENTS_CGLM_API 54 | extern ECS_COMPONENT_DECLARE(vec3); 55 | 56 | FLECS_COMPONENTS_CGLM_API 57 | extern ECS_COMPONENT_DECLARE(vec4); 58 | 59 | FLECS_COMPONENTS_CGLM_API 60 | void FlecsComponentsCglmImport( 61 | ecs_world_t *world); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif 68 | 69 | -------------------------------------------------------------------------------- /deps/flecs_components_geometry.c: -------------------------------------------------------------------------------- 1 | #define FLECS_COMPONENTS_GEOMETRY_IMPL 2 | #include "flecs_components_geometry.h" 3 | 4 | ECS_DECLARE(EcsGeometry); 5 | ECS_COMPONENT_DECLARE(EcsStrokeColor); 6 | 7 | ECS_COPY(EcsMesh, dst, src, { 8 | if (dst->vertices) { 9 | ecs_os_free(dst->vertices); 10 | } 11 | 12 | if (src->vertices) { 13 | size_t size = sizeof(vec3) * src->vertex_count; 14 | dst->vertices = ecs_os_malloc(size); 15 | dst->vertex_count = src->vertex_count; 16 | memcpy(dst->vertices, src->vertices, size); 17 | } else { 18 | dst->vertices = NULL; 19 | dst->vertex_count = 0; 20 | } 21 | }) 22 | 23 | ECS_MOVE(EcsMesh, dst, src, { 24 | if (dst->vertices) { 25 | ecs_os_free(dst->vertices); 26 | } 27 | 28 | dst->vertices = src->vertices; 29 | dst->vertex_count = src->vertex_count; 30 | src->vertices = NULL; 31 | src->vertex_count = 0; 32 | }) 33 | 34 | ECS_DTOR(EcsMesh, ptr, { 35 | ecs_os_free(ptr->vertices); 36 | }) 37 | 38 | void FlecsComponentsGeometryImport( 39 | ecs_world_t *world) 40 | { 41 | ECS_MODULE(world, FlecsComponentsGeometry); 42 | ECS_IMPORT(world, FlecsComponentsGraphics); 43 | 44 | ecs_set_name_prefix(world, "Ecs"); 45 | 46 | ECS_META_COMPONENT(world, EcsDrawDistance); 47 | ECS_META_COMPONENT(world, EcsLine2); 48 | ECS_META_COMPONENT(world, EcsLine3); 49 | ECS_META_COMPONENT(world, EcsRectangle); 50 | ECS_META_COMPONENT(world, EcsStrokeWidth); 51 | ECS_COMPONENT_DEFINE(world, EcsStrokeColor); 52 | ECS_META_COMPONENT(world, EcsCornerRadius); 53 | ECS_META_COMPONENT(world, EcsBox); 54 | ECS_META_COMPONENT(world, EcsCircle); 55 | 56 | ECS_TAG_DEFINE(world, EcsGeometry); 57 | 58 | ecs_add_pair(world, ecs_id(EcsRectangle), EcsWith, EcsGeometry); 59 | ecs_add_pair(world, ecs_id(EcsBox), EcsWith, EcsGeometry); 60 | 61 | ecs_add_pair(world, ecs_id(EcsRectangle), EcsOnInstantiate, EcsInherit); 62 | ecs_add_pair(world, ecs_id(EcsBox), EcsOnInstantiate, EcsInherit); 63 | 64 | ecs_struct(world, { 65 | .entity = ecs_id(EcsStrokeColor), 66 | .members = { 67 | { "r", ecs_id(ecs_f32_t) }, 68 | { "g", ecs_id(ecs_f32_t) }, 69 | { "b", ecs_id(ecs_f32_t) } 70 | } 71 | }); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /deps/flecs_systems_transform.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_systems_transform_STATIC 3 | #ifndef FLECS_SYSTEMS_TRANSFORM_H 4 | #define FLECS_SYSTEMS_TRANSFORM_H 5 | 6 | /* 7 | ) 8 | (.) 9 | .|. 10 | | | 11 | _.--| |--._ 12 | .-'; ;`-'& ; `&. 13 | \ & ; & &_/ 14 | |"""---...---"""| 15 | \ | | | | | | | / 16 | `---.|.|.|.---' 17 | 18 | * This file is generated by bake.lang.c for your convenience. Headers of 19 | * dependencies will automatically show up in this file. Include bake_config.h 20 | * in your main project file. Do not edit! */ 21 | 22 | #ifndef FLECS_SYSTEMS_TRANSFORM_BAKE_CONFIG_H 23 | #define FLECS_SYSTEMS_TRANSFORM_BAKE_CONFIG_H 24 | 25 | /* Headers of public dependencies */ 26 | #include "flecs.h" 27 | #include "cglm.h" 28 | #include "flecs_components_transform.h" 29 | 30 | /* Convenience macro for exporting symbols */ 31 | #ifndef flecs_systems_transform_STATIC 32 | #if defined(flecs_systems_transform_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 33 | #define FLECS_SYSTEMS_TRANSFORM_API __declspec(dllexport) 34 | #elif defined(flecs_systems_transform_EXPORTS) 35 | #define FLECS_SYSTEMS_TRANSFORM_API __attribute__((__visibility__("default"))) 36 | #elif defined(_MSC_VER) 37 | #define FLECS_SYSTEMS_TRANSFORM_API __declspec(dllimport) 38 | #else 39 | #define FLECS_SYSTEMS_TRANSFORM_API 40 | #endif 41 | #else 42 | #define FLECS_SYSTEMS_TRANSFORM_API 43 | #endif 44 | 45 | #endif 46 | 47 | 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | FLECS_SYSTEMS_TRANSFORM_API 54 | void FlecsSystemsTransformImport( 55 | ecs_world_t *world); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #ifdef __cplusplus 62 | #ifndef FLECS_NO_CPP 63 | 64 | namespace flecs { 65 | namespace systems { 66 | 67 | class transform { 68 | public: 69 | transform(flecs::world& ecs) { 70 | // Load module contents 71 | FlecsSystemsTransformImport(ecs); 72 | 73 | // Bind module contents with C++ types 74 | ecs.module(); 75 | } 76 | }; 77 | 78 | } 79 | } 80 | 81 | #endif // FLECS_NO_CPP 82 | #endif // __cplusplus 83 | 84 | #endif 85 | 86 | -------------------------------------------------------------------------------- /deps/flecs_components_transform.c: -------------------------------------------------------------------------------- 1 | #define FLECS_COMPONENTS_TRANSFORM_IMPL 2 | 3 | #include "flecs_components_transform.h" 4 | 5 | ECS_TAG_DECLARE(EcsTransformManually); 6 | ECS_TAG_DECLARE(EcsTransformOnce); 7 | ECS_TAG_DECLARE(EcsTransformNeeded); 8 | 9 | ECS_COMPONENT_DECLARE(EcsTransform2); 10 | ECS_COMPONENT_DECLARE(EcsTransform3); 11 | ECS_COMPONENT_DECLARE(EcsProject2); 12 | ECS_COMPONENT_DECLARE(EcsProject3); 13 | 14 | void FlecsComponentsTransformImport( 15 | ecs_world_t *world) 16 | { 17 | ECS_MODULE(world, FlecsComponentsTransform); 18 | ECS_IMPORT(world, FlecsComponentsCglm); 19 | 20 | ecs_set_name_prefix(world, "Ecs"); 21 | 22 | ECS_META_COMPONENT(world, EcsPosition2); 23 | ECS_META_COMPONENT(world, EcsPosition3); 24 | ECS_META_COMPONENT(world, EcsScale2); 25 | ECS_META_COMPONENT(world, EcsScale3); 26 | ECS_META_COMPONENT(world, EcsRotation2); 27 | ECS_META_COMPONENT(world, EcsRotation3); 28 | ECS_META_COMPONENT(world, EcsQuaternion); 29 | 30 | ECS_COMPONENT_DEFINE(world, EcsTransform2); 31 | ECS_COMPONENT_DEFINE(world, EcsTransform3); 32 | ECS_COMPONENT_DEFINE(world, EcsProject2); 33 | ECS_COMPONENT_DEFINE(world, EcsProject3); 34 | 35 | ECS_TAG_DEFINE(world, EcsTransformManually); 36 | ECS_TAG_DEFINE(world, EcsTransformOnce); 37 | ECS_TAG_DEFINE(world, EcsTransformNeeded); 38 | 39 | ecs_add_pair(world, EcsTransformOnce, EcsWith, EcsTransformNeeded); 40 | 41 | ecs_set_hooks(world, EcsPosition2, { 42 | .ctor = flecs_default_ctor 43 | }); 44 | 45 | ecs_set_hooks(world, EcsPosition3, { 46 | .ctor = flecs_default_ctor 47 | }); 48 | 49 | ecs_set_hooks(world, EcsScale2, { 50 | .ctor = flecs_default_ctor 51 | }); 52 | 53 | ecs_set_hooks(world, EcsScale3, { 54 | .ctor = flecs_default_ctor 55 | }); 56 | 57 | ecs_set_hooks(world, EcsRotation2, { 58 | .ctor = flecs_default_ctor 59 | }); 60 | 61 | ecs_set_hooks(world, EcsRotation3, { 62 | .ctor = flecs_default_ctor 63 | }); 64 | 65 | ecs_set_hooks(world, EcsTransform2, { 66 | .ctor = flecs_default_ctor 67 | }); 68 | 69 | ecs_set_hooks(world, EcsTransform3, { 70 | .ctor = flecs_default_ctor 71 | }); 72 | 73 | ecs_add_pair(world, ecs_id(EcsPosition3), EcsWith, ecs_id(EcsTransform3)); 74 | ecs_add_pair(world, ecs_id(EcsRotation3), EcsWith, ecs_id(EcsTransform3)); 75 | ecs_add_pair(world, ecs_id(EcsScale3), EcsWith, ecs_id(EcsTransform3)); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /deps/flecs_systems_sokol.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_systems_sokol_STATIC 3 | #ifndef FLECS_SYSTEMS_SOKOL_H 4 | #define FLECS_SYSTEMS_SOKOL_H 5 | 6 | /* This generated file contains includes for project dependencies */ 7 | /* 8 | ) 9 | (.) 10 | .|. 11 | | | 12 | _.--| |--._ 13 | .-'; ;`-'& ; `&. 14 | \ & ; & &_/ 15 | |"""---...---"""| 16 | \ | | | | | | | / 17 | `---.|.|.|.---' 18 | 19 | * This file is generated by bake.lang.c for your convenience. Headers of 20 | * dependencies will automatically show up in this file. Include bake_config.h 21 | * in your main project file. Do not edit! */ 22 | 23 | #ifndef FLECS_SYSTEMS_SOKOL_BAKE_CONFIG_H 24 | #define FLECS_SYSTEMS_SOKOL_BAKE_CONFIG_H 25 | 26 | /* Headers of public dependencies */ 27 | #include "flecs.h" 28 | #include "flecs_components_gui.h" 29 | #include "flecs_components_input.h" 30 | #include "flecs_components_graphics.h" 31 | #include "flecs_components_transform.h" 32 | #include "flecs_components_geometry.h" 33 | #include "flecs_systems_transform.h" 34 | #include "flecs_game.h" 35 | 36 | /* Convenience macro for exporting symbols */ 37 | #ifndef flecs_systems_sokol_STATIC 38 | #if defined(flecs_systems_sokol_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 39 | #define FLECS_SYSTEMS_SOKOL_API __declspec(dllexport) 40 | #elif defined(flecs_systems_sokol_EXPORTS) 41 | #define FLECS_SYSTEMS_SOKOL_API __attribute__((__visibility__("default"))) 42 | #elif defined(_MSC_VER) 43 | #define FLECS_SYSTEMS_SOKOL_API __declspec(dllimport) 44 | #else 45 | #define FLECS_SYSTEMS_SOKOL_API 46 | #endif 47 | #else 48 | #define FLECS_SYSTEMS_SOKOL_API 49 | #endif 50 | 51 | #endif 52 | 53 | 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif 58 | 59 | FLECS_SYSTEMS_SOKOL_API 60 | void FlecsSystemsSokolImport( 61 | ecs_world_t *world); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #ifdef __cplusplus 68 | #ifndef FLECS_NO_CPP 69 | 70 | namespace flecs { 71 | namespace systems { 72 | 73 | class sokol { 74 | public: 75 | sokol(flecs::world& ecs) { 76 | // Load module contents 77 | FlecsSystemsSokolImport(ecs); 78 | 79 | // Bind C++ types with module contents 80 | ecs.module(); 81 | } 82 | }; 83 | 84 | } 85 | } 86 | 87 | #endif 88 | #endif 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /deps/flecs_components_graphics.c: -------------------------------------------------------------------------------- 1 | #define FLECS_COMPONENTS_GRAPHICS_IMPL 2 | 3 | #include "flecs_components_graphics.h" 4 | 5 | ECS_TAG_DECLARE(EcsSun); 6 | 7 | ECS_CTOR(EcsCamera, ptr, { 8 | ptr->position[0] = 0.0f; 9 | ptr->position[1] = 0.0f; 10 | ptr->position[2] = 0.0f; 11 | 12 | ptr->lookat[0] = 0.0f; 13 | ptr->lookat[1] = 1.0f; 14 | ptr->lookat[2] = 1.0f; 15 | 16 | ptr->up[0] = 0.0f; 17 | ptr->up[1] = -1.0f; 18 | ptr->up[2] = 0.0f; 19 | 20 | ptr->fov = 30; 21 | ptr->near_ = 0.1; 22 | ptr->far_ = 1000; 23 | }) 24 | 25 | ECS_CTOR(EcsAtmosphere, ptr, { 26 | ptr->intensity = 7.0; 27 | ptr->planet_radius = 6371e3; 28 | ptr->atmosphere_radius = 6471e3; 29 | ptr->rayleigh_coef[0] = 5.5e-6; 30 | ptr->rayleigh_coef[1] = 13.0e-6; 31 | ptr->rayleigh_coef[2] = 22.4e-6; 32 | ptr->mie_coef = 21e-6; 33 | ptr->rayleigh_scale_height = 8e3; 34 | ptr->mie_scale_height = 1.2e3; 35 | ptr->mie_scatter_dir = 0.758; 36 | }) 37 | 38 | static void UpdateSelfLights(ecs_iter_t *it) { 39 | EcsSelfLight *sl = ecs_field(it, EcsSelfLight, 0); 40 | EcsRgb *color = ecs_field(it, EcsRgb, 1); 41 | EcsEmissive *emissive = ecs_field(it, EcsEmissive, 2); 42 | EcsPointLight *out = ecs_field(it, EcsPointLight, 3); 43 | 44 | for (int i = 0; i < it->count; i ++) { 45 | out[i].distance = sl[i].distance; 46 | out[i].color[0] = color[i].r; 47 | out[i].color[1] = color[i].g; 48 | out[i].color[2] = color[i].b; 49 | if (emissive) { 50 | out[i].intensity = emissive[i].value; 51 | } else { 52 | out[i].intensity = 1.0; 53 | } 54 | } 55 | } 56 | 57 | void FlecsComponentsGraphicsImport( 58 | ecs_world_t *world) 59 | { 60 | ECS_MODULE(world, FlecsComponentsGraphics); 61 | ECS_IMPORT(world, FlecsComponentsCglm); 62 | 63 | ecs_set_name_prefix(world, "Ecs"); 64 | ECS_META_COMPONENT(world, EcsRgb); 65 | 66 | ecs_set_name_prefix(world, "Ecs"); 67 | ECS_META_COMPONENT(world, EcsCamera); 68 | ECS_META_COMPONENT(world, EcsLookAt); 69 | ECS_META_COMPONENT(world, EcsDirectionalLight); 70 | ECS_META_COMPONENT(world, EcsPointLight); 71 | ECS_META_COMPONENT(world, EcsSelfLight); 72 | ECS_META_COMPONENT(world, EcsSpecular); 73 | ECS_META_COMPONENT(world, EcsEmissive); 74 | ECS_META_COMPONENT(world, EcsLightIntensity); 75 | ECS_META_COMPONENT(world, EcsAtmosphere); 76 | ECS_TAG_DEFINE(world, EcsSun); 77 | 78 | ecs_add_pair(world, ecs_id(EcsRgb), EcsOnInstantiate, EcsInherit); 79 | ecs_add_pair(world, ecs_id(EcsSpecular), EcsOnInstantiate, EcsInherit); 80 | ecs_add_pair(world, ecs_id(EcsEmissive), EcsOnInstantiate, EcsInherit); 81 | 82 | ecs_add_pair(world, ecs_id(EcsDirectionalLight), EcsOnInstantiate, EcsInherit); 83 | ecs_add_pair(world, ecs_id(EcsPointLight), EcsOnInstantiate, EcsInherit); 84 | ecs_add_pair(world, ecs_id(EcsSelfLight), EcsOnInstantiate, EcsInherit); 85 | 86 | ecs_add_pair(world, ecs_id(EcsSelfLight), EcsWith, ecs_id(EcsPointLight)); 87 | 88 | ecs_struct(world, { 89 | .entity = ecs_entity(world, { 90 | .name = "ecs_rgb_t", 91 | .symbol = "ecs_rgb_t", 92 | }), 93 | .members = { 94 | { .name = "r", .type = ecs_id(ecs_f32_t) }, 95 | { .name = "g", .type = ecs_id(ecs_f32_t) }, 96 | { .name = "b", .type = ecs_id(ecs_f32_t) } 97 | } 98 | }); 99 | 100 | ecs_set_hooks(world, EcsAtmosphere, { 101 | .ctor = ecs_ctor(EcsAtmosphere) 102 | }); 103 | 104 | ECS_SYSTEM(world, UpdateSelfLights, EcsPostUpdate, 105 | [in] SelfLight, 106 | [in] Rgb, 107 | [in] ?Emissive, 108 | [out] PointLight); 109 | } 110 | 111 | -------------------------------------------------------------------------------- /deps/flecs_game.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_game_STATIC 3 | #ifndef GAME_H 4 | #define GAME_H 5 | 6 | /* This generated file contains includes for project dependencies */ 7 | /* 8 | ) 9 | (.) 10 | .|. 11 | | | 12 | _.--| |--._ 13 | .-'; ;`-'& ; `&. 14 | \ & ; & &_/ 15 | |"""---...---"""| 16 | \ | | | | | | | / 17 | `---.|.|.|.---' 18 | 19 | * This file is generated by bake.lang.c for your convenience. Headers of 20 | * dependencies will automatically show up in this file. Include bake_config.h 21 | * in your main project file. Do not edit! */ 22 | 23 | #ifndef FLECS_GAME_BAKE_CONFIG_H 24 | #define FLECS_GAME_BAKE_CONFIG_H 25 | 26 | /* Headers of public dependencies */ 27 | #include "cglm.h" 28 | #include "flecs.h" 29 | #include "flecs_components_input.h" 30 | #include "flecs_components_graphics.h" 31 | #include "flecs_components_gui.h" 32 | #include "flecs_components_transform.h" 33 | #include "flecs_components_geometry.h" 34 | #include "flecs_components_physics.h" 35 | #include "flecs_systems_physics.h" 36 | 37 | /* Convenience macro for exporting symbols */ 38 | #ifndef flecs_game_STATIC 39 | #if defined(flecs_game_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 40 | #define FLECS_GAME_API __declspec(dllexport) 41 | #elif defined(flecs_game_EXPORTS) 42 | #define FLECS_GAME_API __attribute__((__visibility__("default"))) 43 | #elif defined(_MSC_VER) 44 | #define FLECS_GAME_API __declspec(dllimport) 45 | #else 46 | #define FLECS_GAME_API 47 | #endif 48 | #else 49 | #define FLECS_GAME_API 50 | #endif 51 | 52 | #endif 53 | 54 | 55 | 56 | // Reflection system boilerplate 57 | #undef ECS_META_IMPL 58 | #ifndef FLECS_GAME_IMPL 59 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 60 | #endif 61 | 62 | #ifdef __cplusplus 63 | extern "C" { 64 | #endif 65 | 66 | FLECS_GAME_API 67 | extern ECS_DECLARE(EcsCameraController); 68 | 69 | FLECS_GAME_API 70 | ECS_STRUCT(EcsCameraAutoMove, { 71 | float after; 72 | float speed; 73 | float t; 74 | }); 75 | 76 | FLECS_GAME_API 77 | ECS_STRUCT(EcsTimeOfDay, { 78 | float t; 79 | float speed; 80 | }); 81 | 82 | FLECS_GAME_API 83 | ECS_STRUCT(ecs_grid_slot_t, { 84 | ecs_entity_t prefab; 85 | float chance; 86 | }); 87 | 88 | FLECS_GAME_API 89 | ECS_STRUCT(ecs_grid_coord_t, { 90 | int32_t count; 91 | float spacing; 92 | float variation; 93 | }); 94 | 95 | FLECS_GAME_API 96 | ECS_STRUCT(EcsGrid, { 97 | ecs_grid_coord_t x; 98 | ecs_grid_coord_t y; 99 | ecs_grid_coord_t z; 100 | 101 | EcsPosition3 border; 102 | EcsPosition3 border_offset; 103 | 104 | ecs_entity_t prefab; 105 | ecs_grid_slot_t variations[20]; 106 | }); 107 | 108 | FLECS_GAME_API 109 | ECS_STRUCT(EcsParticleEmitter, { 110 | ecs_entity_t particle; 111 | float spawn_interval; 112 | float lifespan; 113 | float size_decay; 114 | float color_decay; 115 | float velocity_decay; 116 | float t; 117 | }); 118 | 119 | FLECS_GAME_API 120 | ECS_STRUCT(EcsParticle, { 121 | float t; 122 | }); 123 | 124 | FLECS_GAME_API 125 | void FlecsGameImport(ecs_world_t *world); 126 | 127 | #ifdef __cplusplus 128 | } 129 | #endif 130 | 131 | #ifdef __cplusplus 132 | #ifndef FLECS_NO_CPP 133 | #include 134 | 135 | namespace flecs { 136 | 137 | struct game { 138 | 139 | game(flecs::world& ecs) { 140 | // Load module contents 141 | FlecsGameImport(ecs); 142 | 143 | // Bind C++ types with module contents 144 | ecs.module(); 145 | } 146 | }; 147 | 148 | } 149 | 150 | #endif 151 | #endif 152 | 153 | #endif 154 | 155 | -------------------------------------------------------------------------------- /etc/sokol/shaders/fx_ssao_header.glsl: -------------------------------------------------------------------------------- 1 | #include "etc/sokol/shaders/common.glsl" 2 | 3 | // Based on https://threejs.org/examples/webgl_postprocessing_sao.html 4 | 5 | // Increase/decrease to trade quality for performance 6 | #define NUM_SAMPLES 1 7 | // The sample kernel uses a spiral pattern so most samples are concentrated 8 | // close to the center. 9 | #define NUM_RINGS 3 10 | #define KERNEL_RADIUS 15.0 11 | // Misc params, tweaked to match the renderer 12 | #define BIAS 0.2 13 | #define SCALE 0.1 14 | // Derived constants 15 | #define ANGLE_STEP ((PI2 * float(NUM_RINGS)) / float(NUM_SAMPLES)) 16 | #define INV_NUM_SAMPLES (1.0 / float(NUM_SAMPLES)) 17 | 18 | float getDepth(const in vec2 t_uv) { 19 | return rgba_to_depth(texture(t_depth, t_uv)); 20 | } 21 | 22 | float getViewZ(const in float depth) { 23 | return (u_near * u_far) / (depth - u_far); 24 | } 25 | 26 | // Compute position in world space from depth & projection matrix 27 | vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) { 28 | float clipW = u_mat_p[2][3] * viewZ + u_mat_p[3][3]; 29 | vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 ); 30 | clipPosition *= clipW; // unprojection. 31 | return ( u_inv_mat_p * clipPosition ).xyz; 32 | } 33 | 34 | // Compute normal from derived position. Should at some point replace it 35 | // with reading from a normal buffer so it works correctly with smooth 36 | // shading / normal maps. 37 | vec3 getViewNormal( const in vec3 viewPosition, const in vec2 t_uv ) { 38 | return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) ); 39 | } 40 | 41 | float scaleDividedByCameraFar; 42 | 43 | // Compute occlusion of single sample 44 | float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) { 45 | vec3 viewDelta = sampleViewPosition - centerViewPosition; 46 | float viewDistance = length( viewDelta ); 47 | float scaledScreenDistance = scaleDividedByCameraFar * viewDistance; 48 | float n_dot_d = dot(centerViewNormal, viewDelta); 49 | float scaled_n_dot_d = max(0.0, n_dot_d / scaledScreenDistance - BIAS); 50 | float result = scaled_n_dot_d / (1.0 + pow2(scaledScreenDistance)); 51 | 52 | // Strip off values that are too large which eliminates shadowing objects 53 | // that are far away. 54 | if (result > 220.0) { 55 | result = 0.0; 56 | } 57 | 58 | // Squash the range and offset noise. 59 | return max(0.0, clamp(result, 1.1, 20.0) / 13.0 - 0.2); 60 | } 61 | 62 | float getAmbientOcclusion( const in vec3 centerViewPosition, float centerDepth ) { 63 | scaleDividedByCameraFar = SCALE / u_far; 64 | vec3 centerViewNormal = getViewNormal( centerViewPosition, uv ); 65 | 66 | float angle = rand( uv ) * PI2; 67 | vec2 radius = vec2( KERNEL_RADIUS * INV_NUM_SAMPLES ); 68 | 69 | // Use smaller kernels for objects farther away from the camera 70 | radius /= u_target_size * centerDepth * 0.05; 71 | // Make sure tha the sample radius isn't less than a single texel, as this 72 | // introduces noise 73 | radius = max(radius, 5.0 / u_target_size); 74 | 75 | vec2 radiusStep = radius; 76 | float occlusionSum = 0.0; 77 | 78 | // Collect occlusion samples 79 | for( int i = 0; i < NUM_SAMPLES; i ++ ) { 80 | vec2 sampleUv = uv + vec2( cos( angle ), sin( angle ) ) * radius; 81 | 82 | // Don't sample outside of texture coords to prevent edge artifacts 83 | sampleUv = clamp(sampleUv, EPSILON, 1.0 - EPSILON); 84 | 85 | radius += radiusStep; 86 | angle += ANGLE_STEP; 87 | 88 | float sampleDepth = getDepth( sampleUv ); 89 | float sampleDepthNorm = sampleDepth / u_far; 90 | 91 | float sampleViewZ = getViewZ( sampleDepth ); 92 | vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepthNorm, sampleViewZ ); 93 | occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition ); 94 | } 95 | 96 | return occlusionSum * (1.0 / (float(NUM_SAMPLES))); 97 | } 98 | -------------------------------------------------------------------------------- /deps/flecs_components_physics.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_physics_STATIC 3 | #ifndef FLECS_COMPONENTS_PHYSICS_H 4 | #define FLECS_COMPONENTS_PHYSICS_H 5 | 6 | /* 7 | ) 8 | (.) 9 | .|. 10 | | | 11 | _.--| |--._ 12 | .-'; ;`-'& ; `&. 13 | \ & ; & &_/ 14 | |"""---...---"""| 15 | \ | | | | | | | / 16 | `---.|.|.|.---' 17 | 18 | * This file is generated by bake.lang.c for your convenience. Headers of 19 | * dependencies will automatically show up in this file. Include bake_config.h 20 | * in your main project file. Do not edit! */ 21 | 22 | #ifndef FLECS_COMPONENTS_PHYSICS_BAKE_CONFIG_H 23 | #define FLECS_COMPONENTS_PHYSICS_BAKE_CONFIG_H 24 | 25 | /* Headers of public dependencies */ 26 | #include "flecs.h" 27 | #include "flecs_components_graphics.h" 28 | 29 | /* Convenience macro for exporting symbols */ 30 | #ifndef flecs_components_physics_STATIC 31 | #if defined(flecs_components_physics_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 32 | #define FLECS_COMPONENTS_PHYSICS_API __declspec(dllexport) 33 | #elif defined(flecs_components_physics_EXPORTS) 34 | #define FLECS_COMPONENTS_PHYSICS_API __attribute__((__visibility__("default"))) 35 | #elif defined(_MSC_VER) 36 | #define FLECS_COMPONENTS_PHYSICS_API __declspec(dllimport) 37 | #else 38 | #define FLECS_COMPONENTS_PHYSICS_API 39 | #endif 40 | #else 41 | #define FLECS_COMPONENTS_PHYSICS_API 42 | #endif 43 | 44 | #endif 45 | 46 | 47 | 48 | // Reflection system boilerplate 49 | #undef ECS_META_IMPL 50 | #ifndef FLECS_COMPONENTS_PHYSICS_IMPL 51 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 52 | #endif 53 | 54 | FLECS_COMPONENTS_PHYSICS_API 55 | extern ECS_DECLARE(EcsCollider); 56 | 57 | FLECS_COMPONENTS_PHYSICS_API 58 | extern ECS_DECLARE(EcsRigidBody); 59 | 60 | FLECS_COMPONENTS_PHYSICS_API 61 | ECS_STRUCT(EcsVelocity2, { 62 | float x; 63 | float y; 64 | }); 65 | 66 | FLECS_COMPONENTS_PHYSICS_API 67 | ECS_STRUCT(EcsVelocity3, { 68 | float x; 69 | float y; 70 | float z; 71 | }); 72 | 73 | FLECS_COMPONENTS_PHYSICS_API 74 | ECS_STRUCT(EcsAngularSpeed, { 75 | float value; 76 | }); 77 | 78 | FLECS_COMPONENTS_PHYSICS_API 79 | ECS_STRUCT(EcsAngularVelocity, { 80 | float x; 81 | float y; 82 | float z; 83 | }); 84 | 85 | FLECS_COMPONENTS_PHYSICS_API 86 | ECS_STRUCT(EcsBounciness, { 87 | float value; 88 | }); 89 | 90 | FLECS_COMPONENTS_PHYSICS_API 91 | ECS_STRUCT(EcsFriction, { 92 | float value; 93 | }); 94 | 95 | #ifdef __cplusplus 96 | extern "C" { 97 | #endif 98 | 99 | FLECS_COMPONENTS_PHYSICS_API 100 | void FlecsComponentsPhysicsImport( 101 | ecs_world_t *world); 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | #ifdef __cplusplus 108 | #ifndef FLECS_NO_CPP 109 | 110 | namespace flecs { 111 | namespace components { 112 | 113 | class physics { 114 | public: 115 | using Velocity2 = EcsVelocity2; 116 | using Velocity3 = EcsVelocity3; 117 | using AngularSpeed = EcsAngularSpeed; 118 | using AngularVelocity = EcsAngularVelocity; 119 | using Bounciness = EcsBounciness; 120 | using Friction = EcsFriction; 121 | 122 | physics(flecs::world& ecs) { 123 | // Load module contents 124 | FlecsComponentsPhysicsImport(ecs); 125 | 126 | // Bind C++ types with module contents 127 | ecs.module(); 128 | ecs.component(); 129 | ecs.component(); 130 | ecs.component(); 131 | ecs.component(); 132 | ecs.component(); 133 | ecs.component(); 134 | } 135 | }; 136 | 137 | } 138 | } 139 | 140 | #endif 141 | #endif 142 | 143 | #endif 144 | 145 | -------------------------------------------------------------------------------- /etc/sokol/shaders/atmosphere.glsl: -------------------------------------------------------------------------------- 1 | // From https://github.com/wwwtyro/glsl-atmosphere 2 | #include "etc/sokol/shaders/constants.glsl" 3 | 4 | #define iSteps 16 5 | #define jSteps 8 6 | 7 | vec2 rsi(vec3 r0, vec3 rd, float sr) { 8 | // ray-sphere intersection that assumes 9 | // the sphere is centered at the origin. 10 | // No intersection when result.x > result.y 11 | float a = dot(rd, rd); 12 | float b = 2.0 * dot(rd, r0); 13 | float c = dot(r0, r0) - (sr * sr); 14 | float d = (b*b) - 4.0*a*c; 15 | if (d < 0.0) return vec2(1e5,-1e5); 16 | return vec2( 17 | (-b - sqrt(d))/(2.0*a), 18 | (-b + sqrt(d))/(2.0*a) 19 | ); 20 | } 21 | 22 | vec3 atmosphere( 23 | vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, 24 | vec3 kRlh, float kMie, float shRlh, float shMie, float g) 25 | { 26 | // Normalize the sun and view directions. 27 | pSun = normalize(pSun); 28 | r = normalize(r); 29 | 30 | // Calculate the step size of the primary ray. 31 | vec2 p = rsi(r0, r, rAtmos); 32 | if (p.x > p.y) return vec3(0,0,0); 33 | p.y = min(p.y, rsi(r0, r, rPlanet).x); 34 | float iStepSize = (p.y - p.x) / float(iSteps); 35 | 36 | // Initialize the primary ray time. 37 | float iTime = 0.0; 38 | 39 | // Initialize accumulators for Rayleigh and Mie scattering. 40 | vec3 totalRlh = vec3(0,0,0); 41 | vec3 totalMie = vec3(0,0,0); 42 | 43 | // Initialize optical depth accumulators for the primary ray. 44 | float iOdRlh = 0.0; 45 | float iOdMie = 0.0; 46 | 47 | // Calculate the Rayleigh and Mie phases. 48 | float mu = dot(r, pSun); 49 | float mumu = mu * mu; 50 | float gg = g * g; 51 | float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu); 52 | float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg)); 53 | 54 | // Sample the primary ray. 55 | for (int i = 0; i < iSteps; i++) { 56 | 57 | // Calculate the primary ray sample position. 58 | vec3 iPos = r0 + r * (iTime + iStepSize * 0.5); 59 | 60 | // Calculate the height of the sample. 61 | float iHeight = length(iPos) - rPlanet; 62 | 63 | // Calculate the optical depth of the Rayleigh and Mie scattering for this step. 64 | float odStepRlh = exp(-iHeight / shRlh) * iStepSize; 65 | float odStepMie = exp(-iHeight / shMie) * iStepSize; 66 | 67 | // Accumulate optical depth. 68 | iOdRlh += odStepRlh; 69 | iOdMie += odStepMie; 70 | 71 | // Calculate the step size of the secondary ray. 72 | float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps); 73 | 74 | // Initialize the secondary ray time. 75 | float jTime = 0.0; 76 | 77 | // Initialize optical depth accumulators for the secondary ray. 78 | float jOdRlh = 0.0; 79 | float jOdMie = 0.0; 80 | 81 | // Sample the secondary ray. 82 | for (int j = 0; j < jSteps; j++) { 83 | 84 | // Calculate the secondary ray sample position. 85 | vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5); 86 | 87 | // Calculate the height of the sample. 88 | float jHeight = length(jPos) - rPlanet; 89 | 90 | // Accumulate the optical depth. 91 | jOdRlh += exp(-jHeight / shRlh) * jStepSize; 92 | jOdMie += exp(-jHeight / shMie) * jStepSize; 93 | 94 | // Increment the secondary ray time. 95 | jTime += jStepSize; 96 | } 97 | 98 | // Calculate attenuation. 99 | vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh))); 100 | 101 | // Accumulate scattering. 102 | totalRlh += odStepRlh * attn; 103 | totalMie += odStepMie * attn; 104 | 105 | // Increment the primary ray time. 106 | iTime += iStepSize; 107 | 108 | } 109 | 110 | // Calculate and return the final color. 111 | return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie); 112 | } 113 | 114 | // #pragma glslify: export(atmosphere) 115 | -------------------------------------------------------------------------------- /deps/flecs_components_geometry.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_geometry_STATIC 3 | #ifndef FLECS_COMPONENTS_GEOMETRY_H 4 | #define FLECS_COMPONENTS_GEOMETRY_H 5 | 6 | /* 7 | ) 8 | (.) 9 | .|. 10 | | | 11 | _.--| |--._ 12 | .-'; ;`-'& ; `&. 13 | \ & ; & &_/ 14 | |"""---...---"""| 15 | \ | | | | | | | / 16 | `---.|.|.|.---' 17 | 18 | * This file is generated by bake.lang.c for your convenience. Headers of 19 | * dependencies will automatically show up in this file. Include bake_config.h 20 | * in your main project file. Do not edit! */ 21 | 22 | #ifndef FLECS_COMPONENTS_GEOMETRY_BAKE_CONFIG_H 23 | #define FLECS_COMPONENTS_GEOMETRY_BAKE_CONFIG_H 24 | 25 | /* Headers of public dependencies */ 26 | #include "flecs.h" 27 | #include "flecs_components_graphics.h" 28 | 29 | /* Convenience macro for exporting symbols */ 30 | #ifndef flecs_components_geometry_STATIC 31 | #if defined(flecs_components_geometry_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 32 | #define FLECS_COMPONENTS_GEOMETRY_API __declspec(dllexport) 33 | #elif defined(flecs_components_geometry_EXPORTS) 34 | #define FLECS_COMPONENTS_GEOMETRY_API __attribute__((__visibility__("default"))) 35 | #elif defined(_MSC_VER) 36 | #define FLECS_COMPONENTS_GEOMETRY_API __declspec(dllimport) 37 | #else 38 | #define FLECS_COMPONENTS_GEOMETRY_API 39 | #endif 40 | #else 41 | #define FLECS_COMPONENTS_GEOMETRY_API 42 | #endif 43 | 44 | #endif 45 | 46 | 47 | 48 | // Reflection system boilerplate 49 | #undef ECS_META_IMPL 50 | #ifndef FLECS_COMPONENTS_GEOMETRY_IMPL 51 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 52 | #endif 53 | 54 | FLECS_COMPONENTS_GEOMETRY_API 55 | ECS_STRUCT(EcsDrawDistance, { 56 | float far_; 57 | }); 58 | 59 | FLECS_COMPONENTS_GEOMETRY_API 60 | ECS_STRUCT(EcsLine2, { 61 | vec3 start; 62 | vec3 stop; 63 | }); 64 | 65 | FLECS_COMPONENTS_GEOMETRY_API 66 | ECS_STRUCT(EcsLine3, { 67 | vec3 start; 68 | vec3 stop; 69 | }); 70 | 71 | FLECS_COMPONENTS_GEOMETRY_API 72 | ECS_STRUCT(EcsRectangle, { 73 | float width; 74 | float height; 75 | }); 76 | 77 | FLECS_COMPONENTS_GEOMETRY_API 78 | ECS_STRUCT(EcsStrokeWidth, { 79 | float value; 80 | float left; 81 | float right; 82 | float top; 83 | float bottom; 84 | }); 85 | 86 | FLECS_COMPONENTS_GEOMETRY_API 87 | extern ECS_COMPONENT_DECLARE(EcsStrokeColor); 88 | typedef EcsRgb EcsStrokeColor; 89 | 90 | FLECS_COMPONENTS_GEOMETRY_API 91 | ECS_STRUCT(EcsCornerRadius, { 92 | float value; 93 | float top_left; 94 | float top_right; 95 | float bottom_left; 96 | float bottom_right; 97 | }); 98 | 99 | typedef EcsRectangle ecs_rect_t; 100 | 101 | FLECS_COMPONENTS_GEOMETRY_API 102 | ECS_STRUCT(EcsCircle, { 103 | float radius; 104 | }); 105 | 106 | FLECS_COMPONENTS_GEOMETRY_API 107 | ECS_STRUCT(EcsBox, { 108 | float width; 109 | float height; 110 | float depth; 111 | }); 112 | 113 | FLECS_COMPONENTS_GEOMETRY_API 114 | extern ECS_DECLARE(EcsGeometry); 115 | 116 | // Not yet supported 117 | typedef struct EcsMesh { 118 | vec3 *vertices; 119 | int32_t vertex_count; 120 | } EcsMesh; 121 | 122 | #ifdef __cplusplus 123 | extern "C" { 124 | #endif 125 | 126 | FLECS_COMPONENTS_GEOMETRY_API 127 | void FlecsComponentsGeometryImport( 128 | ecs_world_t *world); 129 | 130 | #ifdef __cplusplus 131 | } 132 | #endif 133 | 134 | #ifdef __cplusplus 135 | #ifndef FLECS_NO_CPP 136 | 137 | namespace flecs { 138 | namespace components { 139 | 140 | class geometry { 141 | public: 142 | using Line2 = EcsLine2; 143 | using Line3 = EcsLine3; 144 | using Rectangle = EcsRectangle; 145 | using StrokeWidth = EcsStrokeWidth; 146 | using CornerRadius = EcsCornerRadius; 147 | using Circle = EcsCircle; 148 | using Box = EcsBox; 149 | 150 | struct StrokeColor { 151 | float r; 152 | float g; 153 | float b; 154 | }; 155 | 156 | geometry(flecs::world& ecs) { 157 | // Load module contents 158 | FlecsComponentsGeometryImport(ecs); 159 | 160 | // Bind C++ types with module contents 161 | ecs.module(); 162 | ecs.component(); 163 | ecs.component(); 164 | ecs.component(); 165 | ecs.component(); 166 | ecs.component(); 167 | ecs.component(); 168 | ecs.component(); 169 | } 170 | }; 171 | 172 | } 173 | } 174 | #endif 175 | #endif 176 | 177 | #endif 178 | 179 | -------------------------------------------------------------------------------- /deps/flecs_components_gui.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_gui_STATIC 3 | #ifndef FLECS_COMPONENTS_GUI_H 4 | #define FLECS_COMPONENTS_GUI_H 5 | 6 | /* This generated file contains includes for project dependencies */ 7 | /* 8 | ) 9 | (.) 10 | .|. 11 | | | 12 | _.--| |--._ 13 | .-'; ;`-'& ; `&. 14 | \ & ; & &_/ 15 | |"""---...---"""| 16 | \ | | | | | | | / 17 | `---.|.|.|.---' 18 | 19 | * This file is generated by bake.lang.c for your convenience. Headers of 20 | * dependencies will automatically show up in this file. Include bake_config.h 21 | * in your main project file. Do not edit! */ 22 | 23 | #ifndef FLECS_COMPONENTS_GUI_BAKE_CONFIG_H 24 | #define FLECS_COMPONENTS_GUI_BAKE_CONFIG_H 25 | 26 | /* Headers of public dependencies */ 27 | #include "flecs.h" 28 | #include "flecs_components_graphics.h" 29 | #include "flecs_components_cglm.h" 30 | 31 | /* Convenience macro for exporting symbols */ 32 | #ifndef flecs_components_gui_STATIC 33 | #if defined(flecs_components_gui_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 34 | #define FLECS_COMPONENTS_GUI_API __declspec(dllexport) 35 | #elif defined(flecs_components_gui_EXPORTS) 36 | #define FLECS_COMPONENTS_GUI_API __attribute__((__visibility__("default"))) 37 | #elif defined(_MSC_VER) 38 | #define FLECS_COMPONENTS_GUI_API __declspec(dllimport) 39 | #else 40 | #define FLECS_COMPONENTS_GUI_API 41 | #endif 42 | #else 43 | #define FLECS_COMPONENTS_GUI_API 44 | #endif 45 | 46 | #endif 47 | 48 | 49 | 50 | // Reflection system boilerplate 51 | #undef ECS_META_IMPL 52 | #ifndef FLECS_COMPONENTS_GUI_IMPL 53 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 54 | #endif 55 | 56 | /* Canvas */ 57 | 58 | FLECS_COMPONENTS_GUI_API 59 | ECS_STRUCT(EcsCanvas, { 60 | char *title; 61 | int32_t width; 62 | int32_t height; 63 | int32_t left; 64 | int32_t right; 65 | int32_t top; 66 | int32_t bottom; 67 | ecs_entity_t camera; 68 | ecs_entity_t directional_light; 69 | EcsRgb background_color; 70 | EcsRgb ambient_light; 71 | EcsRgb ambient_light_ground; 72 | float ambient_light_ground_falloff; 73 | float ambient_light_ground_offset; 74 | float ambient_light_ground_intensity; 75 | float fog_density; 76 | float shadow_far; 77 | }); 78 | 79 | /* Text & fonts */ 80 | 81 | FLECS_COMPONENTS_GUI_API 82 | ECS_STRUCT(EcsText, { 83 | char *value; 84 | }); 85 | 86 | FLECS_COMPONENTS_GUI_API 87 | ECS_STRUCT(EcsFontSize, { 88 | int32_t value; 89 | }); 90 | 91 | FLECS_COMPONENTS_GUI_API 92 | ECS_ENUM(EcsFontStyle, { 93 | EcsFontStyleRegular, 94 | EcsFontStyleItalic, 95 | EcsFontStyleBold 96 | }); 97 | 98 | /* Alignment */ 99 | 100 | FLECS_COMPONENTS_GUI_API 101 | ECS_BITMASK(EcsAlign, { 102 | EcsAlignLeft = 1, 103 | EcsAlignCenter = 2, 104 | EcsAlignRight = 4, 105 | EcsAlignTop = 8, 106 | EcsAlignMiddle = 16, 107 | EcsAlignBottom = 32 108 | }); 109 | 110 | FLECS_COMPONENTS_GUI_API 111 | ECS_STRUCT(EcsPadding, { 112 | float value; 113 | }); 114 | 115 | 116 | #ifdef __cplusplus 117 | extern "C" { 118 | #endif 119 | 120 | FLECS_COMPONENTS_GUI_API 121 | void FlecsComponentsGuiImport( 122 | ecs_world_t *world); 123 | 124 | #ifdef __cplusplus 125 | } 126 | #endif 127 | 128 | #ifdef __cplusplus 129 | #ifndef FLECS_NO_CPP 130 | 131 | namespace flecs { 132 | namespace components { 133 | 134 | class gui { 135 | public: 136 | using Align = EcsAlign; 137 | 138 | struct Canvas : EcsCanvas { 139 | Canvas() { 140 | this->title = nullptr; 141 | 142 | this->width = 0; 143 | this->height = 0; 144 | 145 | this->left = 0; 146 | this->right = 0; 147 | this->top = 0; 148 | this->bottom = 0; 149 | 150 | this->ambient_light.r = 1.0; 151 | this->ambient_light.g = 1.0; 152 | this->ambient_light.b = 1.0; 153 | 154 | this->background_color.r = 0.0; 155 | this->background_color.g = 0.0; 156 | this->background_color.b = 0.0; 157 | 158 | this->camera = 0; 159 | this->directional_light = 0; 160 | } 161 | }; 162 | 163 | gui(flecs::world& ecs) { 164 | // Load module contents 165 | FlecsComponentsGuiImport(ecs); 166 | 167 | // Bind C++ types with module contents 168 | ecs.module(); 169 | ecs.component(); 170 | } 171 | }; 172 | 173 | } 174 | } 175 | 176 | #endif 177 | #endif 178 | 179 | #endif 180 | 181 | -------------------------------------------------------------------------------- /deps/flecs_systems_transform.c: -------------------------------------------------------------------------------- 1 | #include "flecs_systems_transform.h" 2 | 3 | static void transform(ecs_iter_t *it) { 4 | while (ecs_query_next(it)) { 5 | EcsTransform3 *m = ecs_field(it, EcsTransform3, 0); 6 | EcsTransform3 *m_parent = ecs_field(it, EcsTransform3, 1); 7 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 2); 8 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 3); 9 | EcsScale3 *s = ecs_field(it, EcsScale3, 4); 10 | int i; 11 | 12 | if (!m_parent) { 13 | if (ecs_field_is_self(it, 2)) { 14 | for (i = 0; i < it->count; i ++) { 15 | glm_translate_make(m[i].value, *(vec3*)&p[i]); 16 | } 17 | } else { 18 | for (i = 0; i < it->count; i ++) { 19 | glm_translate_make(m[i].value, *(vec3*)p); 20 | } 21 | } 22 | } else { 23 | if (ecs_field_is_self(it, 2)) { 24 | for (i = 0; i < it->count; i ++) { 25 | glm_translate_to(m_parent[0].value, *(vec3*)&p[i], m[i].value); 26 | } 27 | } else { 28 | for (i = 0; i < it->count; i ++) { 29 | glm_translate_to(m_parent[0].value, *(vec3*)p, m[i].value); 30 | } 31 | } 32 | } 33 | 34 | if (r) { 35 | if (ecs_field_is_self(it, 3)) { 36 | for (i = 0; i < it->count; i ++) { 37 | glm_rotate(m[i].value, r[i].x, (vec3){1.0, 0.0, 0.0}); 38 | glm_rotate(m[i].value, r[i].y, (vec3){0.0, 1.0, 0.0}); 39 | glm_rotate(m[i].value, r[i].z, (vec3){0.0, 0.0, 1.0}); 40 | } 41 | } else { 42 | for (i = 0; i < it->count; i ++) { 43 | glm_rotate(m[i].value, r->x, (vec3){1.0, 0.0, 0.0}); 44 | glm_rotate(m[i].value, r->y, (vec3){0.0, 1.0, 0.0}); 45 | glm_rotate(m[i].value, r->z, (vec3){0.0, 0.0, 1.0}); 46 | } 47 | } 48 | } 49 | 50 | if (s) { 51 | for (i = 0; i < it->count; i ++) { 52 | glm_scale(m[i].value, *(vec3*)&s[i]); 53 | } 54 | } 55 | } 56 | } 57 | 58 | void EcsApplyTransform3(ecs_iter_t *it) { 59 | transform(it); 60 | } 61 | 62 | void EcsApplyTransformOnce3(ecs_iter_t *it) { 63 | transform(it); 64 | ecs_remove_all(it->world, EcsTransformNeeded); 65 | } 66 | 67 | void FlecsSystemsTransformImport( 68 | ecs_world_t *world) 69 | { 70 | ECS_MODULE(world, FlecsSystemsTransform); 71 | ECS_IMPORT(world, FlecsComponentsTransform); 72 | 73 | ecs_set_name_prefix(world, "Ecs"); 74 | 75 | ecs_add_pair(world, ecs_id(EcsPosition3), EcsWith, ecs_id(EcsTransform3)); 76 | ecs_add_pair(world, ecs_id(EcsRotation3), EcsWith, ecs_id(EcsTransform3)); 77 | ecs_add_pair(world, ecs_id(EcsScale3), EcsWith, ecs_id(EcsTransform3)); 78 | 79 | ecs_system(world, { 80 | .entity = ecs_entity(world, { 81 | .name = "EcsApplyTransform3", 82 | .add = ecs_ids( ecs_dependson(EcsOnValidate) ) 83 | }), 84 | .query = { 85 | .terms = {{ 86 | .id = ecs_id(EcsTransform3), 87 | .inout = EcsOut, 88 | }, 89 | { 90 | .id = ecs_id(EcsTransform3), 91 | .inout = EcsIn, 92 | .oper = EcsOptional, 93 | .src.id = EcsCascade 94 | }, 95 | { 96 | .id = ecs_id(EcsPosition3), 97 | .inout = EcsIn 98 | }, 99 | { 100 | .id = ecs_id(EcsRotation3), 101 | .inout = EcsIn, 102 | .oper = EcsOptional 103 | }, 104 | { 105 | .id = ecs_id(EcsScale3), 106 | .inout = EcsIn, 107 | .oper = EcsOptional 108 | }, 109 | { 110 | .id = EcsTransformManually, 111 | .oper = EcsNot 112 | }, 113 | { 114 | .id = EcsTransformOnce, 115 | .oper = EcsNot 116 | }} 117 | }, 118 | .run = EcsApplyTransform3 119 | }); 120 | 121 | ecs_system(world, { 122 | .entity = ecs_entity(world, { 123 | .name = "EcsApplyTransformOnce3", 124 | .add = ecs_ids( ecs_dependson(EcsOnValidate) ) 125 | }), 126 | .query = { 127 | .terms = {{ 128 | .id = ecs_id(EcsTransform3), 129 | .inout = EcsOut, 130 | }, { 131 | .id = ecs_id(EcsTransform3), 132 | .inout = EcsIn, 133 | .oper = EcsOptional, 134 | .src.id = EcsCascade 135 | }, { 136 | .id = ecs_id(EcsPosition3), 137 | .inout = EcsIn 138 | }, { 139 | .id = ecs_id(EcsRotation3), 140 | .inout = EcsIn, 141 | .oper = EcsOptional 142 | }, { 143 | .id = ecs_id(EcsScale3), 144 | .inout = EcsIn, 145 | .oper = EcsOptional 146 | }, { 147 | .id = EcsTransformOnce 148 | }, { 149 | .id = EcsTransformNeeded 150 | }, { 151 | .id = EcsTransformManually, 152 | .oper = EcsNot 153 | }} 154 | }, 155 | .run = EcsApplyTransformOnce3 156 | }); 157 | } 158 | 159 | -------------------------------------------------------------------------------- /etc/sokol/shaders/scene_frag.glsl: -------------------------------------------------------------------------------- 1 | #include "etc/sokol/shaders/common.glsl" 2 | 3 | uniform vec3 u_light_ambient; 4 | uniform vec3 u_light_ambient_ground; 5 | uniform vec3 u_light_direction; 6 | uniform vec3 u_light_color; 7 | uniform vec3 u_eye_pos; 8 | uniform float u_light_ambient_ground_falloff; 9 | uniform float u_light_ambient_ground_offset; 10 | uniform float u_light_ambient_ground_intensity; 11 | uniform float u_shadow_map_size; 12 | uniform sampler2D shadow_map; 13 | uniform float u_shadow_far; 14 | 15 | #define MAX_LIGHT_COUNT 64 16 | uniform vec3 u_point_light_color[MAX_LIGHT_COUNT]; 17 | uniform vec3 u_point_light_position[MAX_LIGHT_COUNT]; 18 | uniform float u_point_light_distance[MAX_LIGHT_COUNT]; 19 | 20 | uniform int u_light_count; 21 | 22 | in vec4 position; 23 | in vec4 light_position; 24 | in vec3 normal; 25 | in vec4 color; 26 | in vec3 material; 27 | out vec4 frag_color; 28 | 29 | const int pcf_count = 1; 30 | const int pcf_samples = (2 * pcf_count + 1) * (2 * pcf_count + 1); 31 | const float texel_c = 1.0; 32 | 33 | float sampleShadow(sampler2D shadowMap, vec2 uv, float compare, float bias) { 34 | float depth = rgba_to_float(texture(shadowMap, vec2(uv.x, uv.y))); 35 | depth += bias; 36 | return step(compare, depth); 37 | } 38 | 39 | float sampleShadowPCF(sampler2D shadowMap, vec2 uv, float texel_size, float compare, float n_dot_l, float d) { 40 | float result = 0.0; 41 | float cos_theta = clamp(n_dot_l, 0.0, 1.0); 42 | float bias = 0.001; 43 | 44 | if (uv.x < 0. || uv.x > 1.) { 45 | return 1.0; 46 | } 47 | if (uv.y < 0. || uv.y > 1.) { 48 | return 1.0; 49 | } 50 | 51 | float tx = texel_size * texel_c; 52 | 53 | result += 0.075 * sampleShadow(shadowMap, uv + vec2(-1, -1) * tx, compare, bias); 54 | result += 0.124 * sampleShadow(shadowMap, uv + vec2(0, -1) * tx, compare, bias); 55 | result += 0.075 * sampleShadow(shadowMap, uv + vec2(1, -1) * tx, compare, bias); 56 | 57 | result += 0.124 * sampleShadow(shadowMap, uv + vec2(-1, 0) * tx, compare, bias); 58 | result += 0.204 * sampleShadow(shadowMap, uv + vec2(0, 0) * tx, compare, bias); 59 | result += 0.124 * sampleShadow(shadowMap, uv + vec2(1, 0) * tx, compare, bias); 60 | 61 | result += 0.075 * sampleShadow(shadowMap, uv + vec2(-1, 1) * tx, compare, bias); 62 | result += 0.124 * sampleShadow(shadowMap, uv + vec2(0, 1) * tx, compare, bias); 63 | result += 0.075 * sampleShadow(shadowMap, uv + vec2(1, 1) * tx, compare, bias); 64 | 65 | return result; 66 | } 67 | 68 | vec3 applyAmbientGround(vec3 n) { 69 | if (u_light_ambient_ground_falloff > 0.0) { 70 | vec3 l = vec3(0.0, -1.0, 0.0); 71 | float n_dot_l = dot(n, l); 72 | float n_dot_l_inv = 1.0 - n_dot_l; 73 | float falloff = (u_light_ambient_ground_falloff - position.y) / u_light_ambient_ground_falloff; 74 | vec3 sideLight = exp2(falloff) * n_dot_l_inv * u_light_ambient_ground; 75 | vec3 floorLight = 0.1 * exp2(falloff) * n_dot_l * u_light_ambient_ground; 76 | if (position.y < u_light_ambient_ground_offset) { 77 | sideLight *= 0.1; 78 | } 79 | return (sideLight + floorLight) * u_light_ambient_ground_intensity; 80 | } else { 81 | return vec3(0.0, 0.0, 0.0); 82 | } 83 | } 84 | 85 | vec3 applyLight(vec3 n, vec3 v, float shininess, vec3 pos, vec3 color, float maxDistance) { 86 | vec3 lightPos = pos; 87 | vec3 eyePos = u_eye_pos; 88 | eyePos[0] *= -1.0; 89 | lightPos += u_eye_pos; 90 | vec3 l = position.xyz - lightPos; 91 | 92 | float n_dot_l = dot(n, l); 93 | if (n_dot_l >= 0.0) { 94 | float distance = length(position.xyz - lightPos); 95 | float attenuation = exp(-distance / maxDistance); 96 | 97 | // Specular 98 | vec3 r = reflect(normalize(l), n); 99 | float r_dot_v = max(dot(r, v), 0.0); 100 | float l_shiny = pow(r_dot_v * n_dot_l, shininess); 101 | vec3 l_specular = vec3(material.x * l_shiny * color); 102 | 103 | return color * attenuation * n_dot_l + l_specular * attenuation; 104 | } else { 105 | return vec3(0.0, 0.0, 0.0); 106 | } 107 | } 108 | 109 | vec3 applyLights(vec3 n, vec3 v, float shininess) { 110 | int i; 111 | 112 | vec3 result = vec3(0.0, 0.0, 0.0); 113 | for (i = 0; i < u_light_count; i ++) { 114 | result += applyLight(n, v, shininess, 115 | u_point_light_position[i], 116 | u_point_light_color[i], 117 | u_point_light_distance[i]); 118 | } 119 | 120 | return result; 121 | } 122 | 123 | void main() { 124 | float specular_power = material.x; 125 | float shininess = max(material.y, 1.0); 126 | float emissive = material.z; 127 | vec3 l = normalize(u_light_direction); 128 | vec3 n = normalize(normal); 129 | float n_dot_l = dot(n, l); 130 | 131 | vec3 vd = u_eye_pos - position.xyz; 132 | vec3 v = normalize(vd); 133 | 134 | if (n_dot_l >= 0.0) { 135 | vec3 r = reflect(l, n); 136 | 137 | // Shadows 138 | vec3 light_pos = light_position.xyz / light_position.w; 139 | vec2 sm_uv = (light_pos.xy + 1.0) * 0.5; 140 | float depth = light_position.z; 141 | float texel_size = 1.0 / u_shadow_map_size; 142 | float d = length(vd); 143 | float s = sampleShadowPCF(shadow_map, sm_uv, texel_size, depth, n_dot_l, d); 144 | s = max(s, emissive); 145 | 146 | float r_dot_v = max(dot(r, v), 0.0); 147 | float l_shiny = pow(r_dot_v * n_dot_l, shininess); 148 | vec3 l_specular = vec3(specular_power * l_shiny * u_light_color); 149 | vec3 l_diffuse = vec3(u_light_color) * n_dot_l; 150 | vec3 l_light = (u_light_ambient + s * l_diffuse); 151 | frag_color = vec4(max(vec3(emissive), l_light) * color.xyz + s * l_specular, 1.0); 152 | } else { 153 | vec3 light = emissive + clamp(1.0 - emissive, 0.0, 1.0) * (u_light_ambient); 154 | frag_color = vec4(light * color.xyz, 1.0); 155 | } 156 | 157 | frag_color += vec4(applyLights(n, v, shininess), 0.0); 158 | frag_color += vec4(applyAmbientGround(n).xyz, 0.0); 159 | } 160 | -------------------------------------------------------------------------------- /deps/flecs_components_transform.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_transform_STATIC 3 | #ifndef FLECS_COMPONENTS_TRANSFORM_H 4 | #define FLECS_COMPONENTS_TRANSFORM_H 5 | 6 | /* 7 | ) 8 | (.) 9 | .|. 10 | | | 11 | _.--| |--._ 12 | .-'; ;`-'& ; `&. 13 | \ & ; & &_/ 14 | |"""---...---"""| 15 | \ | | | | | | | / 16 | `---.|.|.|.---' 17 | 18 | * This file is generated by bake.lang.c for your convenience. Headers of 19 | * dependencies will automatically show up in this file. Include bake_config.h 20 | * in your main project file. Do not edit! */ 21 | 22 | #ifndef FLECS_COMPONENTS_TRANSFORM_BAKE_CONFIG_H 23 | #define FLECS_COMPONENTS_TRANSFORM_BAKE_CONFIG_H 24 | 25 | /* Headers of public dependencies */ 26 | #include "flecs.h" 27 | #include "flecs_components_cglm.h" 28 | 29 | /* Convenience macro for exporting symbols */ 30 | #ifndef flecs_components_transform_STATIC 31 | #if defined(flecs_components_transform_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 32 | #define FLECS_COMPONENTS_TRANSFORM_API __declspec(dllexport) 33 | #elif defined(flecs_components_transform_EXPORTS) 34 | #define FLECS_COMPONENTS_TRANSFORM_API __attribute__((__visibility__("default"))) 35 | #elif defined(_MSC_VER) 36 | #define FLECS_COMPONENTS_TRANSFORM_API __declspec(dllimport) 37 | #else 38 | #define FLECS_COMPONENTS_TRANSFORM_API 39 | #endif 40 | #else 41 | #define FLECS_COMPONENTS_TRANSFORM_API 42 | #endif 43 | 44 | #endif 45 | 46 | 47 | 48 | // Reflection system boilerplate 49 | #undef ECS_META_IMPL 50 | #ifndef FLECS_COMPONENTS_TRANSFORM_IMPL 51 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 52 | #endif 53 | 54 | #ifndef FLECS_LEGACY 55 | 56 | // Don't automatically transform entity 57 | FLECS_COMPONENTS_TRANSFORM_API 58 | extern ECS_TAG_DECLARE(EcsTransformManually); 59 | 60 | // Entity should be transformed once 61 | FLECS_COMPONENTS_TRANSFORM_API 62 | extern ECS_TAG_DECLARE(EcsTransformOnce); 63 | 64 | // Added with TransformOnce, removed after entity is transformed 65 | FLECS_COMPONENTS_TRANSFORM_API 66 | extern ECS_TAG_DECLARE(EcsTransformNeeded); 67 | 68 | FLECS_COMPONENTS_TRANSFORM_API 69 | ECS_STRUCT(EcsPosition2, { 70 | float x; 71 | float y; 72 | }); 73 | 74 | FLECS_COMPONENTS_TRANSFORM_API 75 | ECS_STRUCT(EcsPosition3, { 76 | float x; 77 | float y; 78 | float z; 79 | }); 80 | 81 | FLECS_COMPONENTS_TRANSFORM_API 82 | ECS_STRUCT(EcsScale2, { 83 | float x; 84 | float y; 85 | }); 86 | 87 | FLECS_COMPONENTS_TRANSFORM_API 88 | ECS_STRUCT(EcsScale3, { 89 | float x; 90 | float y; 91 | float z; 92 | }); 93 | 94 | FLECS_COMPONENTS_TRANSFORM_API 95 | ECS_STRUCT(EcsRotation2, { 96 | float angle; 97 | }); 98 | 99 | FLECS_COMPONENTS_TRANSFORM_API 100 | ECS_STRUCT(EcsRotation3, { 101 | float x; 102 | float y; 103 | float z; 104 | }); 105 | 106 | FLECS_COMPONENTS_TRANSFORM_API 107 | ECS_STRUCT(EcsQuaternion, { 108 | float x; 109 | float y; 110 | float z; 111 | float w; 112 | }); 113 | 114 | #endif 115 | 116 | FLECS_COMPONENTS_TRANSFORM_API 117 | extern ECS_COMPONENT_DECLARE(EcsTransform2); 118 | 119 | typedef struct EcsTransform2 { 120 | mat3 value; 121 | } EcsTransform2; 122 | 123 | FLECS_COMPONENTS_TRANSFORM_API 124 | extern ECS_COMPONENT_DECLARE(EcsTransform3); 125 | 126 | typedef struct EcsTransform3 { 127 | mat4 value; 128 | } EcsTransform3; 129 | 130 | FLECS_COMPONENTS_TRANSFORM_API 131 | extern ECS_COMPONENT_DECLARE(EcsProject2); 132 | 133 | typedef struct EcsProject2 { 134 | mat3 value; 135 | } EcsProject2; 136 | 137 | FLECS_COMPONENTS_TRANSFORM_API 138 | extern ECS_COMPONENT_DECLARE(EcsProject3); 139 | 140 | typedef struct EcsProject3 { 141 | mat4 value; 142 | } EcsProject3; 143 | 144 | #ifdef __cplusplus 145 | extern "C" { 146 | #endif 147 | 148 | FLECS_COMPONENTS_TRANSFORM_API 149 | void FlecsComponentsTransformImport( 150 | ecs_world_t *world); 151 | 152 | #ifdef __cplusplus 153 | } 154 | #endif 155 | 156 | #ifdef __cplusplus 157 | #ifndef FLECS_NO_CPP 158 | 159 | namespace flecs { 160 | namespace components { 161 | 162 | class transform { 163 | public: 164 | using Position2 = EcsPosition2; 165 | 166 | struct Position3 : EcsPosition3 { 167 | Position3() { } 168 | 169 | Position3(float x, float y, float z) { 170 | this->x = x; 171 | this->y = y; 172 | this->z = z; 173 | } 174 | 175 | operator float*() { 176 | return reinterpret_cast(this); 177 | } 178 | }; 179 | 180 | struct TransformManually { }; 181 | struct TransformOnce { }; 182 | 183 | using Scale2 = EcsScale2; 184 | using Scale3 = EcsScale3; 185 | 186 | using Rotation2 = EcsRotation2; 187 | using Rotation3 = EcsRotation3; 188 | 189 | using Quaternion = EcsQuaternion; 190 | 191 | using Transform2 = EcsTransform2; 192 | using Transform3 = EcsTransform3; 193 | 194 | using Project2 = EcsProject2; 195 | using Project3 = EcsProject3; 196 | 197 | transform(flecs::world& ecs) { 198 | // Load module contents 199 | FlecsComponentsTransformImport(ecs); 200 | 201 | // Bind C++ types with module contents 202 | ecs.module(); 203 | ecs.component(); 204 | ecs.component(); 205 | ecs.component(); 206 | ecs.component(); 207 | ecs.component(); 208 | ecs.component(); 209 | ecs.component(); 210 | ecs.component(); 211 | ecs.component(); 212 | ecs.component(); 213 | ecs.component(); 214 | ecs.component(); 215 | ecs.component(); 216 | } 217 | }; 218 | 219 | } 220 | } 221 | 222 | #endif // FLECS_NO_CPP 223 | #endif // __cplusplus 224 | 225 | #endif // FLECS_COMPONENTS_TRANSFORM_H 226 | 227 | -------------------------------------------------------------------------------- /deps/flecs_systems_physics.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_systems_physics_STATIC 3 | #ifndef FLECS_SYSTEMS_PHYSICS_H 4 | #define FLECS_SYSTEMS_PHYSICS_H 5 | 6 | /* This generated file contains includes for project dependencies */ 7 | /* 8 | ) 9 | (.) 10 | .|. 11 | | | 12 | _.--| |--._ 13 | .-'; ;`-'& ; `&. 14 | \ & ; & &_/ 15 | |"""---...---"""| 16 | \ | | | | | | | / 17 | `---.|.|.|.---' 18 | 19 | * This file is generated by bake.lang.c for your convenience. Headers of 20 | * dependencies will automatically show up in this file. Include bake_config.h 21 | * in your main project file. Do not edit! */ 22 | 23 | #ifndef FLECS_SYSTEMS_PHYSICS_BAKE_CONFIG_H 24 | #define FLECS_SYSTEMS_PHYSICS_BAKE_CONFIG_H 25 | 26 | /* Headers of public dependencies */ 27 | #include "flecs.h" 28 | #include "cglm.h" 29 | #include "flecs_components_cglm.h" 30 | #include "flecs_components_transform.h" 31 | #include "flecs_components_geometry.h" 32 | #include "flecs_components_physics.h" 33 | 34 | /* Convenience macro for exporting symbols */ 35 | #ifndef flecs_systems_physics_STATIC 36 | #if defined(flecs_systems_physics_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 37 | #define FLECS_SYSTEMS_PHYSICS_API __declspec(dllexport) 38 | #elif defined(flecs_systems_physics_EXPORTS) 39 | #define FLECS_SYSTEMS_PHYSICS_API __attribute__((__visibility__("default"))) 40 | #elif defined(_MSC_VER) 41 | #define FLECS_SYSTEMS_PHYSICS_API __declspec(dllimport) 42 | #else 43 | #define FLECS_SYSTEMS_PHYSICS_API 44 | #endif 45 | #else 46 | #define FLECS_SYSTEMS_PHYSICS_API 47 | #endif 48 | 49 | #endif 50 | 51 | 52 | #ifndef FLECS_SYSTEMS_PHYSICS_OCTREE_H 53 | #define FLECS_SYSTEMS_PHYSICS_OCTREE_H 54 | 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | 60 | typedef struct ecs_octree_t ecs_octree_t; 61 | 62 | typedef struct ecs_oct_entity_t { 63 | ecs_entity_t id; 64 | vec3 pos; 65 | vec3 size; 66 | } ecs_oct_entity_t; 67 | 68 | FLECS_SYSTEMS_PHYSICS_API 69 | ecs_octree_t* ecs_octree_new( 70 | vec3 center, 71 | float size); 72 | 73 | FLECS_SYSTEMS_PHYSICS_API 74 | void ecs_octree_free( 75 | ecs_octree_t *ot); 76 | 77 | FLECS_SYSTEMS_PHYSICS_API 78 | void ecs_octree_clear( 79 | ecs_octree_t *ot); 80 | 81 | FLECS_SYSTEMS_PHYSICS_API 82 | int32_t ecs_octree_insert( 83 | ecs_octree_t *ot, 84 | ecs_entity_t e, 85 | vec3 pos, 86 | vec3 size); 87 | 88 | FLECS_SYSTEMS_PHYSICS_API 89 | void ecs_octree_findn( 90 | ecs_octree_t *ot, 91 | vec3 pos, 92 | float range, 93 | ecs_vec_t *result); 94 | 95 | FLECS_SYSTEMS_PHYSICS_API 96 | int32_t ecs_octree_dump( 97 | ecs_octree_t *ot); 98 | 99 | #ifdef __cplusplus 100 | } 101 | #endif 102 | 103 | #endif 104 | 105 | #ifndef FLECS_SYSTEMS_PHYSICS_SQUERY_H 106 | #define FLECS_SYSTEMS_PHYSICS_SQUERY_H 107 | 108 | 109 | #ifdef __cplusplus 110 | extern "C" { 111 | #endif 112 | 113 | typedef struct ecs_squery_t ecs_squery_t; 114 | 115 | FLECS_SYSTEMS_PHYSICS_API 116 | ecs_squery_t* ecs_squery_new( 117 | ecs_world_t *world, 118 | ecs_id_t filter, 119 | vec3 center, 120 | float size); 121 | 122 | FLECS_SYSTEMS_PHYSICS_API 123 | void ecs_squery_free( 124 | ecs_squery_t *sq); 125 | 126 | FLECS_SYSTEMS_PHYSICS_API 127 | void ecs_squery_update( 128 | ecs_squery_t *sq); 129 | 130 | FLECS_SYSTEMS_PHYSICS_API 131 | void ecs_squery_findn( 132 | const ecs_squery_t *sq, 133 | vec3 position, 134 | float range, 135 | ecs_vec_t *result); 136 | 137 | #ifdef __cplusplus 138 | } 139 | #endif 140 | #endif 141 | 142 | 143 | // Don't use reflection, but use utility macro's for auto-exporting variables 144 | #undef ECS_META_IMPL 145 | #ifndef FLECS_SYSTEMS_PHYSICS_IMPL 146 | #define ECS_META_IMPL EXTERN 147 | #else 148 | #define ECS_META_IMPL DECLARE 149 | #endif 150 | 151 | #ifdef __cplusplus 152 | extern "C" { 153 | #endif 154 | 155 | FLECS_SYSTEMS_PHYSICS_API 156 | ECS_STRUCT(EcsSpatialQuery, { 157 | vec3 center; 158 | float size; 159 | ecs_squery_t *query; 160 | }); 161 | 162 | FLECS_SYSTEMS_PHYSICS_API 163 | ECS_STRUCT(EcsSpatialQueryResult, { 164 | ecs_vec_t results; 165 | }); 166 | 167 | FLECS_SYSTEMS_PHYSICS_API 168 | void FlecsSystemsPhysicsImport( 169 | ecs_world_t *world); 170 | 171 | #ifdef __cplusplus 172 | } 173 | #endif 174 | 175 | #ifdef __cplusplus 176 | #ifndef FLECS_NO_CPP 177 | 178 | namespace flecs { 179 | namespace systems { 180 | 181 | class physics { 182 | public: 183 | using oct_entity_t = ecs_oct_entity_t; 184 | 185 | struct SpatialQuery : EcsSpatialQuery { 186 | SpatialQuery() { 187 | ecs_os_zeromem(center); 188 | size = 0; 189 | query = nullptr; 190 | } 191 | 192 | SpatialQuery(ecs_squery_t *q) { 193 | query = q; 194 | } 195 | 196 | SpatialQuery(vec3 c, float s, ecs_squery_t *q = nullptr) { 197 | ecs_os_memcpy_t(center, c, vec3); 198 | size = s; 199 | query = q; 200 | } 201 | 202 | void update() { 203 | ecs_squery_update(query); 204 | } 205 | 206 | void findn(vec3 pos, float range, EcsSpatialQueryResult& qr) const { 207 | ecs_squery_findn(query, pos, range, &qr.results); 208 | } 209 | }; 210 | 211 | struct SpatialQueryResult : EcsSpatialQueryResult { 212 | oct_entity_t* begin() { 213 | return static_cast(results.array); 214 | } 215 | 216 | oct_entity_t* end() { 217 | return &static_cast(results.array)[results.count]; 218 | } 219 | }; 220 | 221 | physics(flecs::world& ecs) { 222 | // Load module contents 223 | FlecsSystemsPhysicsImport(ecs); 224 | 225 | // Bind C++ types with module contents 226 | ecs.module(); 227 | ecs.component(); 228 | ecs.component(); 229 | } 230 | }; 231 | 232 | } 233 | } 234 | 235 | #endif 236 | #endif 237 | 238 | #endif 239 | 240 | -------------------------------------------------------------------------------- /deps/flecs_components_graphics.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_graphics_STATIC 3 | #ifndef FLECS_COMPONENTS_GRAPHICS_H 4 | #define FLECS_COMPONENTS_GRAPHICS_H 5 | 6 | /* 7 | ) 8 | (.) 9 | .|. 10 | | | 11 | _.--| |--._ 12 | .-'; ;`-'& ; `&. 13 | \ & ; & &_/ 14 | |"""---...---"""| 15 | \ | | | | | | | / 16 | `---.|.|.|.---' 17 | 18 | * This file is generated by bake.lang.c for your convenience. Headers of 19 | * dependencies will automatically show up in this file. Include bake_config.h 20 | * in your main project file. Do not edit! */ 21 | 22 | #ifndef FLECS_COMPONENTS_GRAPHICS_BAKE_CONFIG_H 23 | #define FLECS_COMPONENTS_GRAPHICS_BAKE_CONFIG_H 24 | 25 | /* Headers of public dependencies */ 26 | #include "flecs.h" 27 | #include "flecs_components_cglm.h" 28 | 29 | /* Convenience macro for exporting symbols */ 30 | #ifndef flecs_components_graphics_STATIC 31 | #if defined(flecs_components_graphics_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 32 | #define FLECS_COMPONENTS_GRAPHICS_API __declspec(dllexport) 33 | #elif defined(flecs_components_graphics_EXPORTS) 34 | #define FLECS_COMPONENTS_GRAPHICS_API __attribute__((__visibility__("default"))) 35 | #elif defined(_MSC_VER) 36 | #define FLECS_COMPONENTS_GRAPHICS_API __declspec(dllimport) 37 | #else 38 | #define FLECS_COMPONENTS_GRAPHICS_API 39 | #endif 40 | #else 41 | #define FLECS_COMPONENTS_GRAPHICS_API 42 | #endif 43 | 44 | #endif 45 | 46 | 47 | 48 | // Reflection system boilerplate 49 | #undef ECS_META_IMPL 50 | #ifndef FLECS_COMPONENTS_GRAPHICS_IMPL 51 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 52 | #endif 53 | 54 | #ifdef __cplusplus 55 | extern "C" { 56 | #endif 57 | 58 | FLECS_COMPONENTS_GRAPHICS_API 59 | ECS_STRUCT(EcsCamera, { 60 | vec3 position; 61 | vec3 lookat; 62 | vec3 up; 63 | float fov; 64 | float near_; 65 | float far_; 66 | bool ortho; 67 | }); 68 | 69 | FLECS_COMPONENTS_GRAPHICS_API 70 | ECS_STRUCT(EcsDirectionalLight, { 71 | vec3 position; 72 | vec3 direction; 73 | vec3 color; 74 | float intensity; 75 | }); 76 | 77 | FLECS_COMPONENTS_GRAPHICS_API 78 | ECS_STRUCT(EcsPointLight, { 79 | vec3 color; 80 | float distance; 81 | float intensity; 82 | }); 83 | 84 | FLECS_COMPONENTS_GRAPHICS_API 85 | ECS_STRUCT(EcsSelfLight, { 86 | float distance; 87 | }); 88 | 89 | FLECS_COMPONENTS_GRAPHICS_API 90 | ECS_STRUCT(EcsLookAt, { 91 | float x; 92 | float y; 93 | float z; 94 | }); 95 | 96 | FLECS_COMPONENTS_GRAPHICS_API 97 | ECS_STRUCT(EcsRgb, { 98 | float r; 99 | float g; 100 | float b; 101 | }); 102 | 103 | typedef EcsRgb ecs_rgb_t; 104 | 105 | FLECS_COMPONENTS_GRAPHICS_API 106 | ECS_STRUCT(EcsOpacity, { 107 | float value; 108 | }); 109 | 110 | FLECS_COMPONENTS_GRAPHICS_API 111 | ECS_STRUCT(EcsSpecular, { 112 | float specular_power; 113 | float shininess; 114 | }); 115 | 116 | FLECS_COMPONENTS_GRAPHICS_API 117 | ECS_STRUCT(EcsEmissive, { 118 | float value; 119 | }); 120 | 121 | FLECS_COMPONENTS_GRAPHICS_API 122 | ECS_STRUCT(EcsLightIntensity, { 123 | float value; 124 | }); 125 | 126 | FLECS_COMPONENTS_GRAPHICS_API 127 | ECS_STRUCT(EcsAtmosphere, { 128 | float intensity; 129 | float planet_radius; 130 | float atmosphere_radius; 131 | vec3 rayleigh_coef; 132 | float mie_coef; 133 | float rayleigh_scale_height; 134 | float mie_scale_height; 135 | float mie_scatter_dir; 136 | EcsRgb night_color; 137 | }); 138 | 139 | FLECS_COMPONENTS_GRAPHICS_API 140 | extern ECS_TAG_DECLARE(EcsSun); 141 | 142 | FLECS_COMPONENTS_GRAPHICS_API 143 | void FlecsComponentsGraphicsImport( 144 | ecs_world_t *world); 145 | 146 | #ifdef __cplusplus 147 | } 148 | #endif 149 | 150 | #ifdef __cplusplus 151 | #ifndef FLECS_NO_CPP 152 | 153 | namespace flecs { 154 | namespace components { 155 | 156 | class graphics { 157 | public: 158 | struct rgb_t : ecs_rgb_t { 159 | operator float*() { 160 | return reinterpret_cast(this); 161 | } 162 | }; 163 | 164 | struct Camera : EcsCamera { 165 | Camera() { 166 | this->set_position(0, 0, 0); 167 | this->set_lookat(0, 1, 1); 168 | this->set_up(0, -1, 0); 169 | this->set_fov(30); 170 | this->near_ = 0.1f; 171 | this->far_ = 100; 172 | this->ortho = false; 173 | } 174 | 175 | void set_position(float x, float y, float z) { 176 | this->position[0] = x; 177 | this->position[1] = y; 178 | this->position[2] = z; 179 | } 180 | 181 | void set_lookat(float x, float y, float z) { 182 | this->lookat[0] = x; 183 | this->lookat[1] = y; 184 | this->lookat[2] = z; 185 | } 186 | 187 | void set_up(float x, float y, float z) { 188 | this->up[0] = x; 189 | this->up[1] = y; 190 | this->up[2] = z; 191 | } 192 | 193 | void set_fov(float value) { 194 | this->fov = value; 195 | } 196 | }; 197 | 198 | struct DirectionalLight : EcsDirectionalLight { 199 | DirectionalLight() { 200 | this->set_position(0, 0, 0); 201 | this->set_direction(0, 1, 1); 202 | this->set_color(1, 1, 1); 203 | } 204 | 205 | void set_position(float x, float y, float z) { 206 | this->position[0] = x; 207 | this->position[1] = y; 208 | this->position[2] = z; 209 | } 210 | 211 | void set_direction(float x, float y, float z) { 212 | this->direction[0] = x; 213 | this->direction[1] = y; 214 | this->direction[2] = z; 215 | } 216 | 217 | void set_color(float r, float g, float b) { 218 | this->color[0] = r; 219 | this->color[1] = g; 220 | this->color[2] = b; 221 | } 222 | }; 223 | 224 | using Color = EcsRgb; 225 | using Opacity = EcsOpacity; 226 | using Specular = EcsSpecular; 227 | using Emissive = EcsEmissive; 228 | using Atmosphere = EcsAtmosphere; 229 | using PointLight = EcsPointLight; 230 | using SelfLight = EcsSelfLight; 231 | 232 | graphics(flecs::world& ecs) { 233 | // Load module contents 234 | FlecsComponentsGraphicsImport(ecs); 235 | 236 | // Bind C++ types with module contents 237 | ecs.module(); 238 | ecs.component(); 239 | ecs.component(); 240 | ecs.component(); 241 | ecs.component(); 242 | ecs.component(); 243 | ecs.component(); 244 | ecs.component(); 245 | ecs.component(); 246 | } 247 | }; 248 | 249 | } 250 | } 251 | 252 | #endif 253 | #endif 254 | 255 | #endif 256 | 257 | -------------------------------------------------------------------------------- /deps/flecs_components_input.c: -------------------------------------------------------------------------------- 1 | #define FLECS_COMPONENTS_INPUT_IMPL 2 | 3 | #include "flecs_components_input.h" 4 | #include 5 | 6 | ECS_COMPONENT_DECLARE(EcsEventListener); 7 | ECS_COMPONENT_DECLARE(EcsClassList); 8 | 9 | static 10 | ECS_MOVE(EcsEventListener, dst, src, { 11 | ecs_os_memcpy_t(dst, src, EcsEventListener); 12 | ecs_os_zeromem(src); 13 | }) 14 | 15 | static 16 | ECS_DTOR(EcsEventListener, ptr, { 17 | if (ptr->ctx && ptr->ctx_free) { 18 | ptr->ctx_free(ptr->ctx); 19 | } 20 | if (ptr->binding_ctx && ptr->binding_ctx_free) { 21 | ptr->binding_ctx_free(ptr->binding_ctx); 22 | } 23 | }) 24 | 25 | static 26 | ECS_CTOR(EcsClassList, ptr, { 27 | ecs_vec_init_t(NULL, &ptr->classes, ecs_entity_t, 0); 28 | ecs_vec_init_t(NULL, &ptr->added_by, ecs_classlist_added_by_t, 0); 29 | }) 30 | 31 | static 32 | ECS_MOVE(EcsClassList, dst, src, { 33 | ecs_vec_fini_t(NULL, &dst->classes, ecs_entity_t); 34 | ecs_vec_fini_t(NULL, &dst->added_by, ecs_classlist_added_by_t); 35 | ecs_os_memcpy_t(dst, src, EcsClassList); 36 | ecs_os_zeromem(src); 37 | ecs_vec_init_t(NULL, &src->classes, ecs_entity_t, 0); 38 | ecs_vec_init_t(NULL, &src->added_by, ecs_classlist_added_by_t, 0); 39 | }) 40 | 41 | static 42 | ECS_DTOR(EcsClassList, ptr, { 43 | ecs_vec_fini_t(NULL, &ptr->classes, ecs_entity_t); 44 | ecs_vec_fini_t(NULL, &ptr->added_by, ecs_classlist_added_by_t); 45 | }) 46 | 47 | static 48 | int32_t flecs_classlist_added_by_insert( 49 | ecs_classlist_added_by_t *arr, 50 | int32_t count, 51 | ecs_id_t added, 52 | ecs_entity_t by) 53 | { 54 | for (int32_t i = 0; i < count; i ++) { 55 | if (arr[i].added == added) { 56 | arr[i].by = by; 57 | return count; 58 | } 59 | if (arr[i].added > added) { 60 | ecs_assert(count < ECS_INPUT_MAX_CLASS_COUNT, 61 | ECS_INVALID_OPERATION, NULL); 62 | ecs_os_memmove_n( 63 | &arr[i + 1], &arr[i], ecs_classlist_added_by_t, count - i); 64 | arr[i].added = added; 65 | arr[i].by = by; 66 | return count + 1; 67 | } 68 | } 69 | 70 | ecs_assert(count < ECS_INPUT_MAX_CLASS_COUNT, 71 | ECS_INVALID_OPERATION, NULL); 72 | arr[count].added = added; 73 | arr[count].by = by; 74 | return count + 1; 75 | } 76 | 77 | static 78 | void ecs_on_set(EcsClassList)(ecs_iter_t *it) { 79 | ecs_world_t *world = it->world; 80 | EcsClassList *class_list = ecs_field(it, EcsClassList, 1); 81 | ecs_classlist_added_by_t local_added_by[64]; 82 | 83 | for (int32_t i = 0; i < it->count; i ++) { 84 | ecs_entity_t tgt = it->entities[i]; 85 | ecs_vec_t *classes = &class_list[i].classes; 86 | ecs_vec_t *added_by = &class_list[i].added_by; 87 | int32_t class_count = ecs_vec_count(classes); 88 | ecs_entity_t *arr = ecs_vec_first(classes); 89 | int32_t added_by_count = 0; 90 | 91 | for (int32_t c = 0; c < class_count; c ++) { 92 | ecs_entity_t clss = arr[c]; 93 | ecs_table_t *table = ecs_get_table(world, clss); 94 | const ecs_type_t *type = ecs_table_get_type(table); 95 | 96 | for (int32_t t = 0; t < type->count; t ++) { 97 | ecs_entity_t e = type->array[t]; 98 | if (ECS_IS_PAIR(e)) { 99 | e = ecs_pair_first(world, e); 100 | } 101 | 102 | if (ecs_has_pair(world, e, EcsOnInstantiate, EcsDontInherit)) { 103 | continue; 104 | } 105 | 106 | added_by_count = flecs_classlist_added_by_insert( 107 | local_added_by, added_by_count, type->array[t], clss); 108 | } 109 | } 110 | 111 | int32_t t_new = 0; 112 | ecs_classlist_added_by_t *ids_old = ecs_vec_first(added_by); 113 | for (int32_t t_old = 0; t_old < ecs_vec_count(added_by);) { 114 | ecs_id_t old_id = ids_old[t_old].added; 115 | ecs_id_t new_id = local_added_by[t_new].added; 116 | 117 | if (old_id < new_id) { 118 | ecs_remove_id(world, tgt, old_id); 119 | } 120 | 121 | t_old += old_id <= new_id; 122 | if (t_new < added_by_count) { 123 | t_new += new_id <= old_id; 124 | } 125 | } 126 | 127 | /* Copy new added ids to classlist */ 128 | ecs_vec_set_count_t(NULL, added_by, 129 | ecs_classlist_added_by_t, added_by_count); 130 | ecs_os_memcpy_n(ecs_vec_first(added_by), local_added_by, 131 | ecs_classlist_added_by_t, added_by_count); 132 | 133 | /* Actually add the ids/set the components */ 134 | for (int32_t t = 0; t < added_by_count; t ++) { 135 | ecs_id_t id = local_added_by[t].added; 136 | ecs_id_t by = local_added_by[t].by; 137 | 138 | const ecs_type_info_t *ti = ecs_get_type_info(world, id); 139 | if (!ti) { 140 | /* Not a component, just add the id */ 141 | ecs_add_id(world, tgt, id); 142 | } else { 143 | /* Component, copy value */ 144 | void *dst = ecs_ensure_id(world, tgt, id, (size_t)ti->size); 145 | void *src = ecs_ensure_id(world, by, id, (size_t)ti->size); 146 | if (ti->hooks.copy) { 147 | ti->hooks.copy(dst, src, 1, ti); 148 | } else { 149 | ecs_os_memcpy(dst, src, ti->size); 150 | } 151 | } 152 | } 153 | } 154 | } 155 | 156 | void FlecsComponentsInputImport( 157 | ecs_world_t *world) 158 | { 159 | ECS_MODULE(world, FlecsComponentsInput); 160 | ECS_IMPORT(world, FlecsComponentsGraphics); 161 | 162 | ecs_set_name_prefix(world, "ecs"); 163 | 164 | ECS_META_COMPONENT(world, ecs_key_state_t); 165 | ECS_META_COMPONENT(world, ecs_mouse_coord_t); 166 | ECS_META_COMPONENT(world, ecs_mouse_state_t); 167 | ECS_META_COMPONENT(world, ecs_classlist_added_by_t); 168 | 169 | ecs_set_name_prefix(world, "Ecs"); 170 | 171 | ECS_META_COMPONENT(world, EcsInput); 172 | ECS_META_COMPONENT(world, EcsInputState); 173 | ECS_COMPONENT_DEFINE(world, EcsEventListener); 174 | ECS_COMPONENT_DEFINE(world, EcsClassList); 175 | 176 | ecs_add_id(world, ecs_id(EcsInput), EcsSingleton); 177 | 178 | ecs_add_pair(world, ecs_id(EcsEventListener), EcsWith, 179 | ecs_id(EcsInputState)); 180 | 181 | ecs_set_hooks(world, EcsInputState, { 182 | .ctor = flecs_default_ctor 183 | }); 184 | 185 | ecs_set_hooks(world, EcsEventListener, { 186 | .ctor = flecs_default_ctor, 187 | .move = ecs_move(EcsEventListener), 188 | .dtor = ecs_dtor(EcsEventListener) 189 | }); 190 | 191 | ecs_set_hooks(world, EcsClassList, { 192 | .ctor = ecs_ctor(EcsClassList), 193 | .move = ecs_move(EcsClassList), 194 | .dtor = ecs_dtor(EcsClassList), 195 | .on_set = ecs_on_set(EcsClassList) 196 | }); 197 | 198 | ecs_entity_t classes_vec = ecs_vector(world, { 199 | .entity = ecs_entity(world, { 200 | .parent = ecs_id(FlecsComponentsInput), 201 | }), 202 | .type = ecs_id(ecs_entity_t) 203 | }); 204 | 205 | ecs_entity_t added_by_vec = ecs_vector(world, { 206 | .entity = ecs_entity(world, { 207 | .parent = ecs_id(FlecsComponentsInput), 208 | }), 209 | .type = ecs_id(ecs_classlist_added_by_t) 210 | }); 211 | 212 | ecs_struct(world, { 213 | .entity = ecs_id(EcsClassList), 214 | .members = { 215 | { "classes", classes_vec }, 216 | { "added_by", added_by_vec } 217 | } 218 | }); 219 | } 220 | 221 | -------------------------------------------------------------------------------- /etc/assets/city.flecs: -------------------------------------------------------------------------------- 1 | module flecs.city 2 | using flecs.components.* 3 | using flecs.meta 4 | using flecs.game 5 | 6 | with TransformOnce { 7 | 8 | prefab Tree { 9 | const ground_height: 0.3 10 | const ground_width: 1.0 11 | const trunk_height: 2.0 12 | const trunk_width: 0.5 13 | const canopy_height: 2.0 14 | const canopy_width: 1.5 15 | 16 | Ground { 17 | Position3: {0, ($ground_height / 2), 0} 18 | Box: {$ground_width, $ground_height, $ground_width} 19 | Rgb: {0.05, 0.05, 0.05} 20 | } 21 | Trunk { 22 | Position3: {0, ($trunk_height / 2), 0} 23 | Box: {$trunk_width, $trunk_height, $trunk_width} 24 | Rgb: {0.25, 0.2, 0.1} 25 | } 26 | Canopy { 27 | const canopy_y: $trunk_height + ($canopy_height / 2) 28 | const canopy_top_width: $canopy_width - 0.5 29 | 30 | _ { 31 | Position3: {0, $canopy_y, 0} 32 | Box: {$canopy_width, $canopy_height, $canopy_width} 33 | Rgb: {0.2, 0.3, 0.15} 34 | } 35 | _ { 36 | Position3: {0, $canopy_y, 0} 37 | Box: {$canopy_top_width, $canopy_height + 1, $canopy_top_width} 38 | Rgb: {0.2, 0.3, 0.15} 39 | } 40 | } 41 | } 42 | 43 | // Tree, but enclosed in pavement 44 | prefab SidewalkTree : Tree { 45 | GroundEnclosing { 46 | Position3: {0, 0.05, 0} 47 | Box: {1.3, 0.2, 1.3} 48 | Rgb: {0.3, 0.3, 0.3} 49 | } 50 | } 51 | 52 | // Bin prefab 53 | prefab BinMaterial { 54 | Rgb: {0.04, 0.045, 0.04} 55 | Specular: {specular_power: 0.5, shininess: 0.8} 56 | } 57 | 58 | prefab Bin { 59 | const bin_height: 0.8 60 | const frame_height: 0.2 61 | 62 | BinFrame : BinMaterial { 63 | Box: {0.07, $frame_height, 0.07} 64 | } 65 | 66 | Base : BinMaterial { 67 | Position3: {0, $bin_height / 2, 0} 68 | Box: {0.5, $bin_height, 0.5} 69 | } 70 | Cover : BinMaterial { 71 | Position3: {0, $bin_height + $frame_height, 0} 72 | Box: {0.5, 0.1, 0.5} 73 | } 74 | Frame { 75 | const frame_y: $bin_height + $frame_height / 2 76 | const frame_x: 0.15 77 | 78 | _ : BinFrame { Position3: {-$frame_x, $frame_y, -$frame_x} } 79 | _ : BinFrame { Position3: {$frame_x, $frame_y, -$frame_x} } 80 | _ : BinFrame { Position3: {-$frame_x, $frame_y, $frame_x } } 81 | _ : BinFrame { Position3: {$frame_x, $frame_y, $frame_x } } 82 | } 83 | } 84 | 85 | // Fire hydrant 86 | prefab HydrantMaterial { 87 | Rgb: {0.5, 0.03, 0.03} 88 | Specular: {specular_power: 0.1, shininess: 0.2} 89 | } 90 | 91 | prefab Hydrant { 92 | const width: 0.15 93 | const height: 0.5 94 | 95 | Base : HydrantMaterial { 96 | Position3: {0, $height / 2, 0} 97 | Box: {$width, $height, $width} 98 | } 99 | Ring : HydrantMaterial { 100 | Position3: {0, $height * 0.8, 0} 101 | Box: {$width * 1.3, 0.03, $width * 1.3} 102 | } 103 | Bar : HydrantMaterial { 104 | Position3: {0, $height * 0.6, 0} 105 | Box: {0.3, 0.06, 0.06} 106 | } 107 | Front : HydrantMaterial { 108 | Position3: {0, $height * 0.6, -0.1} 109 | Box: {0.1, 0.1, 0.1} 110 | } 111 | } 112 | 113 | // Bench 114 | prefab BenchMaterial { 115 | Rgb: {0.5, 0.4, 0.3} 116 | } 117 | 118 | prefab BenchFootMaterial { 119 | Rgb: {0.2, 0.2, 0.2} 120 | Specular: {specular_power: 0.1, shininess: 0.3} 121 | } 122 | 123 | prefab Bench { 124 | const base_width: 0.8 125 | const height: 0.3 126 | const top_height: 0.08 127 | 128 | BenchFoot : BenchFootMaterial { 129 | Box: {0.1, $height, 0.3} 130 | } 131 | 132 | LeftFoot : BenchFoot { 133 | Position3: {-$base_width, $height / 2, 0} 134 | } 135 | 136 | RightFoot : BenchFoot { 137 | Position3: {$base_width, $height / 2, 0} 138 | } 139 | 140 | Top : BenchMaterial { 141 | Position3: {0, $height + $top_height / 2, 0} 142 | Box: {2.0, $top_height, 0.5} 143 | } 144 | } 145 | 146 | // Grass floor for park 147 | prefab Park { 148 | Rgb: {0.2, 0.3, 0.15} 149 | } 150 | 151 | // Plaza 152 | prefab Plaza { 153 | Rgb: {0.11, 0.11, 0.1} 154 | Specular: {specular_power: 0.2, shininess: 1.5} 155 | } 156 | 157 | // Building materials 158 | prefab Building { } 159 | 160 | prefab ModernBuilding : Building { 161 | Specular: {specular_power: 0.5, shininess: 0.8} 162 | } 163 | 164 | // Streets & pavements 165 | prefab StreetMaterial { 166 | Rgb: {0.15, 0.15, 0.15} 167 | Specular: {specular_power: 0.1, shininess: 1.0} 168 | } 169 | 170 | prefab Street : StreetMaterial { } 171 | 172 | prefab Pavement { 173 | Rgb: {0.3, 0.3, 0.3} 174 | } 175 | 176 | // Lantern 177 | prefab LanternLight { 178 | // const lanternColor: Rgb: {1.2, 1.0, 0.5} 179 | // Rgb: $lanternColor 180 | Rgb: {1.2, 1.0, 0.5} 181 | Emissive: {1.0} 182 | } 183 | 184 | prefab Lantern { 185 | const height: 1.75 186 | const base_height: 0.1 187 | const light_height: 0.5 188 | const light_bottom: $height 189 | const light_top: $height + $light_height 190 | const cap_height: 0.1 191 | 192 | Base { 193 | Position3: {0, $base_height / 2, 0} 194 | Box: {0.5, $base_height, 0.5} 195 | Rgb: {0.03, 0.05, 0.03} 196 | } 197 | 198 | BaseCap { 199 | Position3: {0, $base_height + 0.05, 0} 200 | Box: {0.3, 0.1, 0.3} 201 | Rgb: {0.03, 0.05, 0.03} 202 | } 203 | 204 | Pole { 205 | Position3: {0, $height / 2, 0} 206 | Box: {0.15, $height, 0.15} 207 | Rgb: {0.03, 0.05, 0.03} 208 | } 209 | 210 | Light : LanternLight { 211 | Position3: {0, $height + ($light_height / 2), 0} 212 | Box: {0.45, $light_height, 0.45} 213 | 214 | PointLight: { 215 | color: [1.2, 1.0, 0.5] 216 | distance: 0.75 217 | intensity: 0.5 218 | } 219 | } 220 | 221 | Bottom { 222 | Position3: {0, $light_bottom - 0.05, 0} 223 | Box: {0.4, 0.1, 0.4} 224 | Rgb: {0.03, 0.05, 0.03} 225 | } 226 | 227 | Cap { 228 | Position3: {0, $light_top + 0.05, 0} 229 | Box: {0.4, $cap_height, 0.4} 230 | Rgb: {0.03, 0.05, 0.03} 231 | } 232 | 233 | Top { 234 | Position3: {0, $light_top + $cap_height - 0.05, 0} 235 | Box: {0.2, 0.1, 0.2} 236 | Rgb: {0.03, 0.05, 0.03} 237 | } 238 | } 239 | 240 | } 241 | 242 | prefab Car { 243 | const wheel_size: 0.45 244 | const wheel_y: ($wheel_size / 2) 245 | const bottom_y: $wheel_size 246 | const bottom_height: 0.5 247 | const bottom_depth: 3.0 248 | const bottom_width: 1.0 249 | const wheel_x: ($bottom_width / 2) - 0.1 250 | const wheel_z: ($bottom_depth / 2) - 0.5 251 | const light_depth: 0.05 252 | const light_size: 0.2 253 | const light_x: ($bottom_width / 2.3) - ($light_size / 2) 254 | const light_z: ($bottom_depth / 2) + ($light_depth / 2) 255 | 256 | prefab BackLight { 257 | Box: {$light_size, $light_size, $light_depth} 258 | Rgb: {5.0, 0.0, 0.0} 259 | Emissive: {1.0} 260 | } 261 | 262 | prefab FrontLight { 263 | Box: {$light_size, $light_size, $light_depth} 264 | Rgb: {2.1, 2.1, 2.0} 265 | Emissive: {1.0} 266 | } 267 | 268 | prefab Wheel { 269 | Box: {0.3, $wheel_size, $wheel_size} 270 | Rgb: {0.0, 0.0, 0.0} 271 | } 272 | 273 | prefab Top { 274 | Position3: {y: 0.75, z: -0.2} 275 | Rotation3: {x: -0.05} 276 | Box: {1.0, 0.5, 2.0} 277 | Rgb: {0.005, 0.05, 0.1} 278 | } 279 | 280 | prefab Bottom { 281 | Position3: {y: $bottom_y} 282 | Box: {$bottom_width, $bottom_height, $bottom_depth} 283 | Rgb: {0.005, 0.05, 0.1} 284 | } 285 | 286 | _ : Wheel { Position3: {x: -$wheel_x, y: $wheel_y, z: $wheel_z} } 287 | _ : Wheel { Position3: {x: $wheel_x, y: $wheel_y, z: $wheel_z} } 288 | _ : Wheel { Position3: {x: -$wheel_x, y: $wheel_y, z: -$wheel_z} } 289 | _ : Wheel { Position3: {x: $wheel_x, y: $wheel_y, z: -$wheel_z} } 290 | 291 | _ : FrontLight { Position3: {x: -$light_x, y: $bottom_y, z: $light_z} } 292 | _ : FrontLight { Position3: {x: $light_x, y: $bottom_y, z: $light_z} } 293 | 294 | _ : BackLight { Position3: {x: -$light_x, y: $bottom_y, z: -$light_z} } 295 | _ : BackLight { Position3: {x: $light_x, y: $bottom_y, z: -$light_z} } 296 | } 297 | -------------------------------------------------------------------------------- /deps/flecs_components_input.h: -------------------------------------------------------------------------------- 1 | // Comment out this line when using as DLL 2 | #define flecs_components_input_STATIC 3 | #ifndef FLECS_COMPONENTS_INPUT_H 4 | #define FLECS_COMPONENTS_INPUT_H 5 | 6 | /* This generated file contains includes for project dependencies */ 7 | /* 8 | ) 9 | (.) 10 | .|. 11 | | | 12 | _.--| |--._ 13 | .-'; ;`-'& ; `&. 14 | \ & ; & &_/ 15 | |"""---...---"""| 16 | \ | | | | | | | / 17 | `---.|.|.|.---' 18 | 19 | * This file is generated by bake.lang.c for your convenience. Headers of 20 | * dependencies will automatically show up in this file. Include bake_config.h 21 | * in your main project file. Do not edit! */ 22 | 23 | #ifndef FLECS_COMPONENTS_INPUT_BAKE_CONFIG_H 24 | #define FLECS_COMPONENTS_INPUT_BAKE_CONFIG_H 25 | 26 | /* Headers of public dependencies */ 27 | #include "flecs.h" 28 | #include "flecs_components_graphics.h" 29 | 30 | /* Convenience macro for exporting symbols */ 31 | #ifndef flecs_components_input_STATIC 32 | #if defined(flecs_components_input_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__)) 33 | #define FLECS_COMPONENTS_INPUT_API __declspec(dllexport) 34 | #elif defined(flecs_components_input_EXPORTS) 35 | #define FLECS_COMPONENTS_INPUT_API __attribute__((__visibility__("default"))) 36 | #elif defined(_MSC_VER) 37 | #define FLECS_COMPONENTS_INPUT_API __declspec(dllimport) 38 | #else 39 | #define FLECS_COMPONENTS_INPUT_API 40 | #endif 41 | #else 42 | #define FLECS_COMPONENTS_INPUT_API 43 | #endif 44 | 45 | #endif 46 | 47 | 48 | 49 | // Reflection system boilerplate 50 | #undef ECS_META_IMPL 51 | #ifndef FLECS_COMPONENTS_INPUT_IMPL 52 | #define ECS_META_IMPL EXTERN // Ensure meta symbols are only defined once 53 | #endif 54 | 55 | #define ECS_INPUT_MAX_CLASS_COUNT (64) 56 | 57 | FLECS_COMPONENTS_INPUT_API 58 | ECS_STRUCT(ecs_key_state_t, { 59 | bool up; 60 | bool down; 61 | bool state; 62 | bool current; 63 | }); 64 | 65 | FLECS_COMPONENTS_INPUT_API 66 | ECS_STRUCT(ecs_mouse_coord_t, { 67 | float x; 68 | float y; 69 | }); 70 | 71 | FLECS_COMPONENTS_INPUT_API 72 | ECS_STRUCT(ecs_mouse_state_t, { 73 | ecs_key_state_t left; 74 | ecs_key_state_t right; 75 | ecs_mouse_coord_t screen; 76 | ecs_mouse_coord_t window; 77 | ecs_mouse_coord_t view; 78 | ecs_mouse_coord_t scroll; 79 | bool moved; 80 | }); 81 | 82 | FLECS_COMPONENTS_INPUT_API 83 | ECS_STRUCT(EcsInput, { 84 | ecs_key_state_t keys[128]; 85 | ecs_mouse_state_t mouse; 86 | }); 87 | 88 | FLECS_COMPONENTS_INPUT_API 89 | ECS_STRUCT(EcsInputState, { 90 | bool hover; 91 | bool drag; 92 | bool lmb_down; 93 | bool rmb_down; 94 | float mouse_x; 95 | float mouse_y; 96 | float drag_anchor_x; 97 | float drag_anchor_y; 98 | }); 99 | 100 | typedef struct EcsEventListener { 101 | ecs_iter_action_t on_enter; 102 | ecs_iter_action_t on_leave; 103 | ecs_iter_action_t on_lmb_down; 104 | ecs_iter_action_t on_lmb_up; 105 | ecs_iter_action_t on_rmb_down; 106 | ecs_iter_action_t on_rmb_up; 107 | ecs_iter_action_t on_drag; 108 | 109 | void *ctx; 110 | void *binding_ctx; 111 | 112 | ecs_ctx_free_t ctx_free; 113 | ecs_ctx_free_t binding_ctx_free; 114 | } EcsEventListener; 115 | 116 | FLECS_COMPONENTS_INPUT_API 117 | extern ECS_COMPONENT_DECLARE(EcsEventListener); 118 | 119 | ECS_STRUCT(ecs_classlist_added_by_t, { 120 | ecs_id_t added; 121 | ecs_entity_t by; 122 | }); 123 | 124 | typedef struct EcsClassList { 125 | ecs_vec_t classes; 126 | ecs_vec_t added_by; 127 | } EcsClassList; 128 | 129 | FLECS_COMPONENTS_INPUT_API 130 | extern ECS_COMPONENT_DECLARE(EcsClassList); 131 | 132 | #ifdef __cplusplus 133 | extern "C" { 134 | #endif 135 | 136 | #define ECS_KEY_UNKNOWN ((int)0) 137 | #define ECS_KEY_RETURN ((int)'\r') 138 | #define ECS_KEY_ESCAPE ((int)'\033') 139 | #define ECS_KEY_BACKSPACE ((int)'\b') 140 | #define ECS_KEY_TAB ((int)'\t') 141 | #define ECS_KEY_SPACE ((int)' ') 142 | #define ECS_KEY_EXCLAIM ((int)'!') 143 | #define ECS_KEY_QUOTEDBL ((int)'"') 144 | #define ECS_KEY_HASH ((int)'#') 145 | #define ECS_KEY_PERCENT ((int)'%') 146 | #define ECS_KEY_DOLLAR ((int)'$') 147 | #define ECS_KEY_AMPERSAND ((int)'&') 148 | #define ECS_KEY_QUOTE ((int)'\'') 149 | #define ECS_KEY_LEFT_PAREN ((int)'(') 150 | #define ECS_KEY_RIGHT_PAREN ((int)')') 151 | #define ECS_KEY_ASTERISK ((int)'*') 152 | #define ECS_KEY_PLUS ((int)'+') 153 | #define ECS_KEY_COMMA ((int)',') 154 | #define ECS_KEY_MINUS ((int)'-') 155 | #define ECS_KEY_PERIOD ((int)'.') 156 | #define ECS_KEY_SLASH ((int)'/') 157 | 158 | #define ECS_KEY_0 ((int)'0') 159 | #define ECS_KEY_1 ((int)'1') 160 | #define ECS_KEY_2 ((int)'2') 161 | #define ECS_KEY_3 ((int)'3') 162 | #define ECS_KEY_4 ((int)'4') 163 | #define ECS_KEY_5 ((int)'5') 164 | #define ECS_KEY_6 ((int)'6') 165 | #define ECS_KEY_7 ((int)'7') 166 | #define ECS_KEY_8 ((int)'8') 167 | #define ECS_KEY_9 ((int)'9') 168 | 169 | #define ECS_KEY_COLON ((int)':') 170 | #define ECS_KEY_SEMICOLON ((int)';') 171 | #define ECS_KEY_LESS ((int)'<') 172 | #define ECS_KEY_EQUAL ((int)'=') 173 | #define ECS_KEY_GREATER ((int)'>') 174 | #define ECS_KEY_QUESTION ((int)'?') 175 | #define ECS_KEY_AT ((int)'@') 176 | #define ECS_KEY_LEFT_BRACKET ((int)'[') 177 | #define ECS_KEY_RIGHT_BRACKET ((int)']') 178 | #define ECS_KEY_BACKSLASH ((int)'\\') 179 | #define ECS_KEY_CARET ((int)'^') 180 | #define ECS_KEY_UNDERSCORE ((int)'_') 181 | #define ECS_KEY_GRAVE_ACCENT ((int)'`') 182 | #define ECS_KEY_APOSTROPHE ((int)'\'') 183 | 184 | #define ECS_KEY_A ((int)'a') 185 | #define ECS_KEY_B ((int)'b') 186 | #define ECS_KEY_C ((int)'c') 187 | #define ECS_KEY_D ((int)'d') 188 | #define ECS_KEY_E ((int)'e') 189 | #define ECS_KEY_F ((int)'f') 190 | #define ECS_KEY_G ((int)'g') 191 | #define ECS_KEY_H ((int)'h') 192 | #define ECS_KEY_I ((int)'i') 193 | #define ECS_KEY_J ((int)'j') 194 | #define ECS_KEY_K ((int)'k') 195 | #define ECS_KEY_L ((int)'l') 196 | #define ECS_KEY_M ((int)'m') 197 | #define ECS_KEY_N ((int)'n') 198 | #define ECS_KEY_O ((int)'o') 199 | #define ECS_KEY_P ((int)'p') 200 | #define ECS_KEY_Q ((int)'q') 201 | #define ECS_KEY_R ((int)'r') 202 | #define ECS_KEY_S ((int)'s') 203 | #define ECS_KEY_T ((int)'t') 204 | #define ECS_KEY_U ((int)'u') 205 | #define ECS_KEY_V ((int)'v') 206 | #define ECS_KEY_W ((int)'w') 207 | #define ECS_KEY_X ((int)'x') 208 | #define ECS_KEY_Y ((int)'y') 209 | #define ECS_KEY_Z ((int)'z') 210 | #define ECS_KEY_DELETE ((int)127) 211 | 212 | #define ECS_KEY_RIGHT ((int)'R') 213 | #define ECS_KEY_LEFT ((int)'L') 214 | #define ECS_KEY_DOWN ((int)'D') 215 | #define ECS_KEY_UP ((int)'U') 216 | #define ECS_KEY_LEFT_CTRL ((int)'C') 217 | #define ECS_KEY_LEFT_ALT ((int)'A') 218 | #define ECS_KEY_LEFT_SHIFT ((int)'S') 219 | #define ECS_KEY_RIGHT_CTRL ((int)'T') 220 | #define ECS_KEY_RIGHT_ALT ((int)'Z') 221 | #define ECS_KEY_RIGHT_SHIFT ((int)'H') 222 | #define ECS_KEY_INSERT ((int)'I') 223 | #define ECS_KEY_HOME ((int)'H') 224 | #define ECS_KEY_END ((int)'E') 225 | #define ECS_KEY_PAGE_UP ((int)'O') 226 | #define ECS_KEY_PAGE_DOWN ((int)'P') 227 | 228 | FLECS_COMPONENTS_INPUT_API 229 | void FlecsComponentsInputImport( 230 | ecs_world_t *world); 231 | 232 | #ifdef __cplusplus 233 | } 234 | #endif 235 | 236 | #ifdef __cplusplus 237 | #ifndef FLECS_NO_CPP 238 | 239 | namespace flecs { 240 | 241 | namespace components { 242 | 243 | class input { 244 | public: 245 | using Input = EcsInput; 246 | using InputState = EcsInputState; 247 | 248 | struct EventListener : EcsEventListener { 249 | private: 250 | struct binding_ctx_t { 251 | void *on_enter = nullptr; 252 | void *on_leave = nullptr; 253 | void *on_lmb_down = nullptr; 254 | void *on_lmb_up = nullptr; 255 | void *on_rmb_down = nullptr; 256 | void *on_rmb_up = nullptr; 257 | void *on_drag = nullptr; 258 | 259 | ecs_ctx_free_t free_on_enter; 260 | ecs_ctx_free_t free_on_leave; 261 | ecs_ctx_free_t free_on_lmb_down; 262 | ecs_ctx_free_t free_on_lmb_up; 263 | ecs_ctx_free_t free_on_rmb_down; 264 | ecs_ctx_free_t free_on_rmb_up; 265 | ecs_ctx_free_t free_on_drag; 266 | }; 267 | 268 | template 269 | static void run_on_enter(ecs_iter_t *it) { 270 | it->binding_ctx = static_cast(it->binding_ctx)->on_enter; 271 | if (it->binding_ctx) { 272 | Delegate::run(it); 273 | } 274 | } 275 | 276 | template 277 | static void run_on_leave(ecs_iter_t *it) { 278 | it->binding_ctx = static_cast(it->binding_ctx)->on_leave; 279 | if (it->binding_ctx) { 280 | Delegate::run(it); 281 | } 282 | } 283 | 284 | template 285 | static void run_on_lmb_down(ecs_iter_t *it) { 286 | it->binding_ctx = static_cast(it->binding_ctx)->on_lmb_down; 287 | if (it->binding_ctx) { 288 | Delegate::run(it); 289 | } 290 | } 291 | 292 | template 293 | static void run_on_lmb_up(ecs_iter_t *it) { 294 | it->binding_ctx = static_cast(it->binding_ctx)->on_lmb_up; 295 | if (it->binding_ctx) { 296 | Delegate::run(it); 297 | } 298 | } 299 | 300 | template 301 | static void run_on_rmb_down(ecs_iter_t *it) { 302 | it->binding_ctx = static_cast(it->binding_ctx)->on_rmb_down; 303 | if (it->binding_ctx) { 304 | Delegate::run(it); 305 | } 306 | } 307 | 308 | template 309 | static void run_on_rmb_up(ecs_iter_t *it) { 310 | it->binding_ctx = static_cast(it->binding_ctx)->on_rmb_up; 311 | if (it->binding_ctx) { 312 | Delegate::run(it); 313 | } 314 | } 315 | 316 | template 317 | static void run_on_drag(ecs_iter_t *it) { 318 | it->binding_ctx = static_cast(it->binding_ctx)->on_drag; 319 | if (it->binding_ctx) { 320 | Delegate::run(it); 321 | } 322 | } 323 | 324 | static void free_binding_ctx(void *arg) { 325 | binding_ctx_t *ctx = static_cast(arg); 326 | if (ctx->on_enter) ctx->free_on_enter(ctx->on_enter); 327 | if (ctx->on_leave) ctx->free_on_enter(ctx->on_leave); 328 | if (ctx->on_lmb_down) ctx->free_on_enter(ctx->on_lmb_down); 329 | if (ctx->on_lmb_up) ctx->free_on_enter(ctx->on_lmb_up); 330 | if (ctx->on_rmb_down) ctx->free_on_enter(ctx->on_rmb_down); 331 | if (ctx->on_rmb_up) ctx->free_on_enter(ctx->on_rmb_up); 332 | if (ctx->on_drag) ctx->free_on_enter(ctx->on_drag); 333 | } 334 | 335 | binding_ctx_t* get_binding_ctx() { 336 | if (this->binding_ctx) { 337 | return static_cast(this->binding_ctx); 338 | } else { 339 | this->binding_ctx_free = free_binding_ctx; 340 | return static_cast( 341 | this->binding_ctx = new binding_ctx_t{}); 342 | } 343 | } 344 | 345 | public: 346 | template > 347 | void on_enter(const Func& func) { 348 | this->EcsEventListener::on_enter = run_on_enter; 349 | binding_ctx_t *ctx = get_binding_ctx(); 350 | ctx->on_enter = Delegate::make(func); 351 | ctx->free_on_enter = Delegate::free; 352 | } 353 | 354 | template > 355 | void on_leave(const Func& func) { 356 | this->EcsEventListener::on_leave = run_on_leave; 357 | binding_ctx_t *ctx = get_binding_ctx(); 358 | ctx->on_leave = Delegate::make(func); 359 | ctx->free_on_leave = Delegate::free; 360 | } 361 | 362 | template > 363 | void on_lmb_down(const Func& func) { 364 | this->EcsEventListener::on_lmb_down = run_on_lmb_down; 365 | binding_ctx_t *ctx = get_binding_ctx(); 366 | ctx->on_lmb_down = Delegate::make(func); 367 | ctx->free_on_lmb_down = Delegate::free; 368 | } 369 | 370 | template > 371 | void on_lmb_up(const Func& func) { 372 | this->EcsEventListener::on_lmb_up = run_on_lmb_up; 373 | binding_ctx_t *ctx = get_binding_ctx(); 374 | ctx->on_lmb_up = Delegate::make(func); 375 | ctx->free_on_lmb_up = Delegate::free; 376 | } 377 | 378 | template > 379 | void on_rmb_down(const Func& func) { 380 | this->EcsEventListener::on_rmb_down = run_on_rmb_down; 381 | binding_ctx_t *ctx = get_binding_ctx(); 382 | ctx->on_rmb_down = Delegate::make(func); 383 | ctx->free_on_rmb_down = Delegate::free; 384 | } 385 | 386 | template > 387 | void on_rmb_up(const Func& func) { 388 | this->EcsEventListener::on_rmb_up = run_on_rmb_up; 389 | binding_ctx_t *ctx = get_binding_ctx(); 390 | ctx->on_rmb_up = Delegate::make(func); 391 | ctx->free_on_rmb_up = Delegate::free; 392 | } 393 | 394 | template > 395 | void on_drag(const Func& func) { 396 | this->EcsEventListener::on_drag = run_on_drag; 397 | binding_ctx_t *ctx = get_binding_ctx(); 398 | ctx->on_drag = Delegate::make(func); 399 | ctx->free_on_drag = Delegate::free; 400 | } 401 | }; 402 | 403 | struct ClassList : EcsClassList { 404 | void add(flecs::entity_t clss) { 405 | ecs_vec_t *vec = &this->classes; 406 | flecs::entity_t *arr = ecs_vec_first_t(vec, flecs::entity_t); 407 | int32_t count = ecs_vec_count(vec); 408 | 409 | // Ensure there are no duplicates 410 | for (int32_t i = 0; i < count; i ++) { 411 | if (arr[i] == clss) { 412 | return; 413 | } 414 | } 415 | 416 | ecs_vec_append_t(NULL, vec, flecs::entity_t)[0] = clss; 417 | } 418 | 419 | void remove(flecs::entity_t clss) { 420 | ecs_vec_t *vec = &this->classes; 421 | flecs::entity_t *arr = ecs_vec_first_t(vec, flecs::entity_t); 422 | int32_t count = ecs_vec_count(vec); 423 | 424 | for (int32_t i = 0; i < count; i ++) { 425 | if (arr[i] == clss) { 426 | /* Preserve order (vs. moving last element) */ 427 | int32_t to_move = count - i - 1; 428 | if (to_move) { 429 | ecs_os_memmove_n( 430 | &arr[i], &arr[i + 1], flecs::entity_t, to_move); 431 | } 432 | 433 | /* Reduce length of vector by one */ 434 | ecs_vec_remove_last(vec); 435 | break; 436 | } 437 | } 438 | } 439 | 440 | template 441 | void add(flecs::world& world) { 442 | add(world.id()); 443 | } 444 | 445 | template 446 | void remove(flecs::world& world) { 447 | remove(world.id()); 448 | } 449 | }; 450 | 451 | input(flecs::world& ecs) { 452 | // Load module contents 453 | FlecsComponentsInputImport(ecs); 454 | 455 | // Bind C++ types with module contents 456 | ecs.module(); 457 | ecs.component(); 458 | ecs.component(); 459 | ecs.component(); 460 | ecs.component(); 461 | } 462 | }; 463 | 464 | } 465 | } 466 | 467 | #endif 468 | #endif 469 | 470 | #endif 471 | 472 | -------------------------------------------------------------------------------- /deps/flecs_systems_physics.c: -------------------------------------------------------------------------------- 1 | #define FLECS_SYSTEMS_PHYSICS_IMPL 2 | #include "flecs_systems_physics.h" 3 | 4 | ECS_CTOR(EcsSpatialQuery, ptr, { 5 | ptr->query = NULL; 6 | }) 7 | 8 | ECS_MOVE(EcsSpatialQuery, dst, src, { 9 | if (dst->query) { 10 | ecs_squery_free(dst->query); 11 | } 12 | 13 | ecs_os_memcpy_t(dst->center, src->center, vec3); 14 | dst->size = src->size; 15 | dst->query = src->query; 16 | src->query = NULL; 17 | }) 18 | 19 | ECS_DTOR(EcsSpatialQuery, ptr, { 20 | if (ptr->query) { 21 | ecs_squery_free(ptr->query); 22 | } 23 | }) 24 | 25 | ECS_CTOR(EcsSpatialQueryResult, ptr, { 26 | ptr->results = (ecs_vec_t){0}; 27 | }) 28 | 29 | ECS_MOVE(EcsSpatialQueryResult, dst, src, { 30 | if (dst->results.array) { 31 | ecs_vec_fini_t(NULL, &dst->results, ecs_oct_entity_t); 32 | } 33 | 34 | dst->results = src->results; 35 | src->results = (ecs_vec_t){0}; 36 | }) 37 | 38 | ECS_DTOR(EcsSpatialQueryResult, ptr, { 39 | if (ptr->results.array) { 40 | ecs_vec_fini_t(NULL, &ptr->results, ecs_oct_entity_t); 41 | } 42 | }) 43 | 44 | static 45 | void EcsMove2(ecs_iter_t *it) { 46 | EcsPosition2 *p = ecs_field(it, EcsPosition2, 0); 47 | EcsVelocity2 *v = ecs_field(it, EcsVelocity2, 1); 48 | 49 | int i; 50 | for (i = 0; i < it->count; i ++) { 51 | p[i].x += v[i].x * it->delta_time; 52 | p[i].y += v[i].y * it->delta_time; 53 | } 54 | } 55 | 56 | static 57 | void EcsMove3(ecs_iter_t *it) { 58 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 0); 59 | EcsVelocity3 *v = ecs_field(it, EcsVelocity3, 1); 60 | 61 | int i; 62 | for (i = 0; i < it->count; i ++) { 63 | p[i].x += v[i].x * it->delta_time; 64 | p[i].y += v[i].y * it->delta_time; 65 | p[i].z += v[i].z * it->delta_time; 66 | } 67 | } 68 | 69 | static 70 | void EcsRotate2(ecs_iter_t *it) { 71 | EcsRotation2 *r = ecs_field(it, EcsRotation2, 0); 72 | EcsAngularSpeed *a = ecs_field(it, EcsAngularSpeed, 1); 73 | 74 | int i; 75 | for (i = 0; i < it->count; i ++) { 76 | r[i].angle += a[i].value * it->delta_time; 77 | } 78 | } 79 | 80 | static 81 | void EcsRotate3(ecs_iter_t *it) { 82 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 0); 83 | EcsAngularVelocity *a = ecs_field(it, EcsAngularVelocity, 1); 84 | 85 | int i; 86 | for (i = 0; i < it->count; i ++) { 87 | r[i].x += a[i].x * it->delta_time; 88 | r[i].y += a[i].y * it->delta_time; 89 | r[i].z += a[i].z * it->delta_time; 90 | } 91 | } 92 | 93 | static 94 | void EcsAddBoxCollider(ecs_iter_t *it) { 95 | EcsBox *box = ecs_field(it, EcsBox, 1); 96 | ecs_entity_t C = ecs_field_id(it, 0); 97 | ecs_entity_t B = ecs_field_id(it, 1); 98 | 99 | int i; 100 | if (ecs_field_is_self(it, 2)) { 101 | for (i = 0; i < it->count; i ++) { 102 | ecs_entity_t pair = ecs_pair(C, B); 103 | EcsBox *collider = ecs_ensure_id( 104 | it->world, it->entities[i], pair, sizeof(EcsBox)); 105 | ecs_os_memcpy_t(collider, &box[i], EcsBox); 106 | } 107 | } else { 108 | for (i = 0; i < it->count; i ++) { 109 | ecs_entity_t pair = ecs_pair(C, B); 110 | EcsBox *collider = ecs_ensure_id( 111 | it->world, it->entities[i], pair, sizeof(EcsBox)); 112 | ecs_os_memcpy_t(collider, box, EcsBox); 113 | } 114 | } 115 | } 116 | 117 | static 118 | void EcsOnSetSpatialQuery(ecs_iter_t *it) { 119 | EcsSpatialQuery *q = ecs_field(it, EcsSpatialQuery, 0); 120 | ecs_id_t id = ecs_field_id(it, 0); 121 | ecs_id_t filter = ecs_pair_second(it->world, id); 122 | 123 | for (int i = 0; i < it->count; i ++) { 124 | q[i].query = ecs_squery_new(it->world, filter, q[i].center, q[i].size); 125 | if (!q[i].query) { 126 | char *filter_str = ecs_id_str(it->world, filter); 127 | ecs_err("failed to create query for filter '%s'", filter_str); 128 | ecs_os_free(filter_str); 129 | } 130 | } 131 | } 132 | 133 | static 134 | void EcsUpdateSpatialQuery(ecs_iter_t *it) { 135 | EcsSpatialQuery *q = ecs_field(it, EcsSpatialQuery, 0); 136 | 137 | for (int i = 0; i < it->count; i ++) { 138 | if (!q[i].query) { 139 | char *filter_str = ecs_id_str(it->world, ecs_field_id(it, 1)); 140 | ecs_err("missing spatial query for '%s'", filter_str); 141 | ecs_os_free(filter_str); 142 | continue; 143 | } 144 | 145 | ecs_squery_update(q[i].query); 146 | } 147 | } 148 | 149 | void FlecsSystemsPhysicsImport( 150 | ecs_world_t *world) 151 | { 152 | ECS_MODULE(world, FlecsSystemsPhysics); 153 | ECS_IMPORT(world, FlecsComponentsTransform); 154 | ECS_IMPORT(world, FlecsComponentsPhysics); 155 | ECS_IMPORT(world, FlecsComponentsGeometry); 156 | 157 | ecs_set_name_prefix(world, "Ecs"); 158 | 159 | ECS_COMPONENT_DEFINE(world, EcsSpatialQuery); 160 | ECS_COMPONENT_DEFINE(world, EcsSpatialQueryResult); 161 | 162 | ecs_struct(world, { 163 | .entity = ecs_id(EcsSpatialQuery), 164 | .members = { 165 | {"center", ecs_id(vec3)}, 166 | {"size", ecs_id(ecs_f32_t)} 167 | } 168 | }); 169 | 170 | ecs_set_hooks(world, EcsSpatialQuery, { 171 | .ctor = ecs_ctor(EcsSpatialQuery), 172 | .dtor = ecs_dtor(EcsSpatialQuery), 173 | .move = ecs_move(EcsSpatialQuery) 174 | }); 175 | 176 | ecs_add_pair(world, ecs_id(EcsSpatialQuery), EcsOnInstantiate, EcsInherit); 177 | 178 | ecs_set_hooks(world, EcsSpatialQueryResult, { 179 | .ctor = ecs_ctor(EcsSpatialQueryResult), 180 | .dtor = ecs_dtor(EcsSpatialQueryResult), 181 | .move = ecs_move(EcsSpatialQueryResult) 182 | }); 183 | 184 | ecs_add_pair(world, ecs_id(EcsVelocity2), 185 | EcsWith, ecs_id(EcsPosition2)); 186 | ecs_add_pair(world, ecs_id(EcsVelocity3), 187 | EcsWith, ecs_id(EcsPosition3)); 188 | ecs_add_pair(world, ecs_id(EcsRotation3), 189 | EcsWith, ecs_id(EcsPosition3)); 190 | ecs_add_pair(world, ecs_id(EcsAngularVelocity), 191 | EcsWith, ecs_id(EcsRotation3)); 192 | 193 | ECS_SYSTEM(world, EcsMove2, EcsOnUpdate, 194 | [inout] flecs.components.transform.Position2, 195 | [in] flecs.components.physics.Velocity2); 196 | 197 | ECS_SYSTEM(world, EcsMove3, EcsOnUpdate, 198 | [inout] flecs.components.transform.Position3, 199 | [in] flecs.components.physics.Velocity3); 200 | 201 | ECS_SYSTEM(world, EcsRotate2, EcsOnUpdate, 202 | [inout] flecs.components.transform.Rotation2, 203 | [in] flecs.components.physics.AngularSpeed); 204 | 205 | ECS_SYSTEM(world, EcsRotate3, EcsOnUpdate, 206 | [inout] flecs.components.transform.Rotation3, 207 | [in] flecs.components.physics.AngularVelocity); 208 | 209 | ECS_SYSTEM(world, EcsAddBoxCollider, EcsPostLoad, 210 | flecs.components.physics.Collider, 211 | flecs.components.geometry.Box, 212 | !(flecs.components.physics.Collider, flecs.components.geometry.Box)); 213 | 214 | ECS_OBSERVER(world, EcsOnSetSpatialQuery, EcsOnSet, 215 | SpatialQuery(self, *), ?Prefab); 216 | 217 | ECS_SYSTEM(world, EcsUpdateSpatialQuery, EcsPreUpdate, 218 | SpatialQuery(self, *), ?Prefab); 219 | } 220 | 221 | 222 | #define MAX_PER_OCTANT (8) 223 | 224 | typedef struct cube_t { 225 | struct cube_t *parent; 226 | struct cube_t *nodes[8]; 227 | ecs_vec_t entities; 228 | int32_t id; 229 | bool is_leaf; 230 | } cube_t; 231 | 232 | struct ecs_octree_t { 233 | ecs_sparse_t cubes; 234 | ecs_vec_t free_cubes; 235 | cube_t root; 236 | vec3 center; 237 | float size; 238 | int32_t count; 239 | }; 240 | 241 | static 242 | void cube_split( 243 | ecs_octree_t *ot, 244 | cube_t *cube, 245 | vec3 center, 246 | float size); 247 | 248 | static 249 | cube_t *new_cube( 250 | ecs_octree_t *ot, 251 | cube_t *parent) 252 | { 253 | cube_t *result = NULL; 254 | if (!ecs_vec_count(&ot->free_cubes)) { 255 | result = ecs_sparse_add_t(&ot->cubes, cube_t); 256 | result->id = (int32_t)ecs_sparse_last_id(&ot->cubes); 257 | } else { 258 | result = ecs_vec_last_t(&ot->free_cubes, cube_t*)[0]; 259 | ecs_vec_remove_last(&ot->free_cubes); 260 | } 261 | 262 | result->parent = parent; 263 | result->is_leaf = true; 264 | 265 | return result; 266 | } 267 | 268 | static 269 | bool is_inside_dim( 270 | float center, 271 | float size, 272 | float e_pos, 273 | float e_size) 274 | { 275 | bool 276 | result = e_pos - e_size >= center - size; 277 | result &= e_pos + e_size <= center + size; 278 | return result; 279 | } 280 | 281 | static 282 | bool is_inside( 283 | vec3 center, 284 | float size, 285 | vec3 e_pos, 286 | vec3 e_size) 287 | { 288 | bool 289 | result = is_inside_dim(center[0], size, e_pos[0], e_size[0]); 290 | result &= is_inside_dim(center[1], size, e_pos[1], e_size[1]); 291 | result &= is_inside_dim(center[2], size, e_pos[2], e_size[2]); 292 | return result; 293 | } 294 | 295 | /* Returns 0 if no overlap, 2 if contains */ 296 | static 297 | int overlap_dim( 298 | float center, 299 | float size, 300 | float pos, 301 | float range) 302 | { 303 | float left = center - size; 304 | float right = center + size; 305 | float q_left = pos - range; 306 | float q_right = pos + range; 307 | 308 | bool left_outside = q_left < left; 309 | bool left_nomatch = q_left > right; 310 | 311 | bool right_outside = q_right > right; 312 | bool right_nomatch = q_right < left; 313 | 314 | return (!(left_nomatch | right_nomatch)) * (1 + (left_outside && right_outside)); 315 | } 316 | 317 | /* Returns 0 if no overlap, 8 if contains, < 8 if overlaps */ 318 | static 319 | int entity_overlaps( 320 | vec3 center, 321 | float size, 322 | vec3 e_pos, 323 | vec3 e_size) 324 | { 325 | int result = overlap_dim(center[0], size, e_pos[0], e_size[0]); 326 | result *= overlap_dim(center[1], size, e_pos[1], e_size[1]); 327 | result *= overlap_dim(center[2], size, e_pos[2], e_size[2]); 328 | return result; 329 | } 330 | 331 | static 332 | int cube_overlaps( 333 | vec3 center, 334 | float size, 335 | vec3 pos, 336 | float range) 337 | { 338 | int result = overlap_dim(center[0], size, pos[0], range); 339 | result *= overlap_dim(center[1], size, pos[1], range); 340 | result *= overlap_dim(center[2], size, pos[2], range); 341 | return result; 342 | } 343 | 344 | /* Convenience macro for contains */ 345 | #define CONTAINS_CUBE (8) 346 | 347 | static 348 | int8_t get_side( 349 | float center, 350 | float coord) 351 | { 352 | return coord > center; 353 | } 354 | 355 | static 356 | int8_t next_cube_index( 357 | vec3 center, 358 | float size, 359 | vec3 pos) 360 | { 361 | int8_t left_right = get_side(center[0], pos[0]); 362 | int8_t top_bottom = get_side(center[1], pos[1]); 363 | int8_t front_back = get_side(center[2], pos[2]); 364 | center[0] += size * (left_right * 2 - 1); 365 | center[1] += size * (top_bottom * 2 - 1); 366 | center[2] += size * (front_back * 2 - 1); 367 | return left_right + top_bottom * 2 + front_back * 4; 368 | } 369 | 370 | static const vec3 cube_map[] = { 371 | {-1, -1, -1}, 372 | { 1, -1, -1}, 373 | {-1, 1, -1}, 374 | { 1, 1, -1}, 375 | {-1, -1, 1}, 376 | { 1, -1, 1}, 377 | {-1, 1, 1}, 378 | { 1, 1, 1}, 379 | }; 380 | 381 | static 382 | void get_cube_center( 383 | vec3 center, 384 | float size, 385 | int8_t index, 386 | vec3 result) 387 | { 388 | result[0] = center[0] + cube_map[index][0] * size; 389 | result[1] = center[1] + cube_map[index][1] * size; 390 | result[2] = center[2] + cube_map[index][2] * size; 391 | } 392 | 393 | static 394 | void cube_add_entity( 395 | cube_t *cube, 396 | ecs_oct_entity_t *ce) 397 | { 398 | ecs_vec_init_if_t(&cube->entities, ecs_oct_entity_t); 399 | ecs_oct_entity_t *elem = ecs_vec_append_t( 400 | NULL, &cube->entities, ecs_oct_entity_t); 401 | *elem = *ce; 402 | } 403 | 404 | static 405 | cube_t* cube_insert( 406 | ecs_octree_t *ot, 407 | ecs_oct_entity_t *ce, 408 | cube_t *cube, 409 | vec3 cube_center, 410 | float cube_size) 411 | { 412 | /* create private variable that can be modified during iteration */ 413 | vec3 center; 414 | glm_vec3_copy(cube_center, center); 415 | float size = cube_size / 2; 416 | cube_t *cur = cube; 417 | ecs_oct_entity_t e = *ce; 418 | 419 | if (!is_inside(center, size, ce->pos, ce->size)) { 420 | return NULL; 421 | } 422 | 423 | do { 424 | bool is_leaf = cur->is_leaf; 425 | 426 | /* If cube is a leaf and has space, insert */ 427 | if (is_leaf && ecs_vec_count(&cur->entities) < MAX_PER_OCTANT) { 428 | break; 429 | } 430 | 431 | /* Find next cube */ 432 | vec3 child_center; 433 | glm_vec3_copy(center, child_center); 434 | float child_size = size / 2; 435 | int8_t cube_i = next_cube_index(child_center, child_size, e.pos); 436 | 437 | /* If entity does not fit in child cube, insert into current */ 438 | if (!is_inside(child_center, child_size, e.pos, e.size)) { 439 | break; 440 | } 441 | 442 | /* Entity should not be inserted in current node. Check if node is a 443 | * leaf. If it is, split it up */ 444 | if (is_leaf) { 445 | cube_split(ot, cur, center, size * 2); 446 | } 447 | 448 | cube_t *next = cur->nodes[cube_i]; 449 | if (!next) { 450 | next = new_cube(ot, cur); 451 | ecs_assert(next != cur, ECS_INTERNAL_ERROR, NULL); 452 | 453 | cur->nodes[cube_i] = next; 454 | cur = next; 455 | 456 | /* Node is guaranteed to be empty, so stop looking */ 457 | break; 458 | } else { 459 | ecs_assert(next != cur, ECS_INTERNAL_ERROR, NULL); 460 | cur = next; 461 | } 462 | 463 | size = child_size; 464 | glm_vec3_copy(child_center, center); 465 | } while (1); 466 | 467 | cube_add_entity(cur, &e); 468 | 469 | return cur; 470 | } 471 | 472 | static 473 | void cube_split( 474 | ecs_octree_t *ot, 475 | cube_t *cube, 476 | vec3 center, 477 | float size) 478 | { 479 | int32_t i, count = ecs_vec_count(&cube->entities); 480 | 481 | /* This will force entities to be pushed to child nodes */ 482 | cube->is_leaf = false; 483 | 484 | for (i = 0; i < count; i ++) { 485 | ecs_oct_entity_t *entities = ecs_vec_first_t(&cube->entities, ecs_oct_entity_t); 486 | cube_t *new_cube = cube_insert(ot, &entities[i], cube, center, size); 487 | ecs_assert(new_cube != NULL, ECS_INTERNAL_ERROR, NULL); 488 | 489 | if (new_cube != cube) { 490 | ecs_vec_remove_t(&cube->entities, ecs_oct_entity_t, i); 491 | i --; 492 | count --; 493 | ecs_assert(count == ecs_vec_count(&cube->entities), ECS_INTERNAL_ERROR, NULL); 494 | } else { 495 | ecs_vec_remove_last(&cube->entities); 496 | } 497 | } 498 | 499 | ecs_assert(count == ecs_vec_count(&cube->entities), ECS_INTERNAL_ERROR, NULL); 500 | } 501 | 502 | static 503 | void result_add_entity( 504 | ecs_vec_t *result, 505 | ecs_oct_entity_t *e) 506 | { 507 | ecs_oct_entity_t *elem = ecs_vec_append_t(NULL, result, ecs_oct_entity_t); 508 | *elem = *e; 509 | } 510 | 511 | static 512 | void cube_find_all( 513 | cube_t *cube, 514 | ecs_vec_t *result) 515 | { 516 | ecs_vec_init_if_t(result, ecs_oct_entity_t); 517 | ecs_oct_entity_t *entities = ecs_vec_first_t(&cube->entities, ecs_oct_entity_t); 518 | int32_t i, count = ecs_vec_count(&cube->entities); 519 | for (i = 0; i < count; i ++) { 520 | result_add_entity(result, &entities[i]); 521 | } 522 | 523 | for (i = 0; i < 8; i ++) { 524 | cube_t *child = cube->nodes[i]; 525 | if (!child) { 526 | continue; 527 | } 528 | 529 | cube_find_all(child, result); 530 | } 531 | } 532 | 533 | static 534 | void cube_findn( 535 | cube_t *cube, 536 | vec3 center, 537 | float size, 538 | vec3 pos, 539 | float range, 540 | ecs_vec_t *result) 541 | { 542 | size /= 2; 543 | 544 | ecs_vec_init_if_t(result, ecs_oct_entity_t); 545 | ecs_oct_entity_t *entities = ecs_vec_first_t(&cube->entities, ecs_oct_entity_t); 546 | int32_t i, count = ecs_vec_count(&cube->entities); 547 | 548 | for (i = 0; i < count; i ++) { 549 | ecs_oct_entity_t *e = &entities[i]; 550 | if (entity_overlaps(pos, range, e->pos, e->size)) { 551 | result_add_entity(result, e); 552 | } 553 | } 554 | 555 | for (i = 0; i < 8; i ++) { 556 | cube_t *child = cube->nodes[i]; 557 | if (!child) { 558 | continue; 559 | } 560 | 561 | vec3 child_center; 562 | get_cube_center(center, size, i, child_center); 563 | int overlap = cube_overlaps(child_center, size, pos, range); 564 | 565 | if (overlap == CONTAINS_CUBE) { 566 | cube_find_all(child, result); 567 | } else if (overlap) { 568 | cube_findn(child, child_center, size, pos, range, result); 569 | } 570 | } 571 | } 572 | 573 | ecs_octree_t* ecs_octree_new( 574 | vec3 center, 575 | float size) 576 | { 577 | ecs_octree_t *result = ecs_os_calloc(sizeof(ecs_octree_t)); 578 | glm_vec3_copy(center, result->center); 579 | result->size = size; 580 | ecs_sparse_init_t(&result->cubes, cube_t); 581 | return result; 582 | } 583 | 584 | void ecs_octree_clear( 585 | ecs_octree_t *ot) 586 | { 587 | ecs_assert(ot != NULL, ECS_INVALID_PARAMETER, NULL); 588 | 589 | /* Keep existing cubes intact so that we can reuse them when the octree is 590 | * repopulated. This lets us keep the entity vectors, and should cause the 591 | * octree memory to stabilize eventually. */ 592 | int32_t i, count = ecs_sparse_count(&ot->cubes); 593 | for (i = 0; i < count; i ++) { 594 | cube_t *cube = ecs_sparse_get_dense_t(&ot->cubes, cube_t, i); 595 | ecs_vec_clear(&cube->entities); 596 | ecs_os_memset_n(cube->nodes, 0, cube_t*, 8); 597 | 598 | if (cube->parent) { 599 | ecs_vec_init_if_t(&ot->free_cubes, cube_t*); 600 | cube_t **cptr = ecs_vec_append_t(NULL, &ot->free_cubes, cube_t*); 601 | *cptr = cube; 602 | cube->parent = NULL; 603 | } 604 | } 605 | 606 | /* Clear entities of root */ 607 | ecs_vec_clear(&ot->root.entities); 608 | ecs_os_memset_n(ot->root.nodes, 0, cube_t*, 8); 609 | ot->count = 0; 610 | } 611 | 612 | void ecs_octree_free( 613 | ecs_octree_t *ot) 614 | { 615 | } 616 | 617 | int32_t ecs_octree_insert( 618 | ecs_octree_t *ot, 619 | ecs_entity_t e, 620 | vec3 e_pos, 621 | vec3 e_size) 622 | { 623 | ecs_assert(ot != NULL, ECS_INVALID_PARAMETER, NULL); 624 | 625 | ecs_oct_entity_t ce; 626 | ce.id = e; 627 | glm_vec3_copy(e_pos, ce.pos); 628 | glm_vec3_copy(e_size, ce.size); 629 | cube_t *cube = cube_insert(ot, &ce, &ot->root, ot->center, ot->size); 630 | if (cube) { 631 | ot->count ++; 632 | return cube->id; 633 | } else { 634 | return -1; 635 | } 636 | } 637 | 638 | void ecs_octree_findn( 639 | ecs_octree_t *ot, 640 | vec3 pos, 641 | float range, 642 | ecs_vec_t *result) 643 | { 644 | ecs_assert(ot != NULL, ECS_INVALID_PARAMETER, NULL); 645 | 646 | ecs_vec_clear(result); 647 | cube_findn(&ot->root, ot->center, ot->size / 2, pos, range, result); 648 | } 649 | 650 | static 651 | int cube_dump( 652 | cube_t *cube, 653 | vec3 center, 654 | float size) 655 | { 656 | vec3 c; 657 | glm_vec3_copy(center, c); 658 | 659 | size /= 2; 660 | int i, count = 0; 661 | for (i = 0; i < 8; i ++) { 662 | if (cube->nodes[i]) { 663 | vec3 child_center; 664 | get_cube_center(c, size, i, child_center); 665 | count += cube_dump(cube->nodes[i], child_center, size); 666 | } 667 | } 668 | 669 | return ecs_vec_count(&cube->entities) + count; 670 | } 671 | 672 | int32_t ecs_octree_dump( 673 | ecs_octree_t *ot) 674 | { 675 | ecs_assert(ot != NULL, ECS_INVALID_PARAMETER, NULL); 676 | int32_t ret = cube_dump(&ot->root, ot->center, ot->size / 2); 677 | printf("counted = %d, actual = %d\n", ret, ot->count); 678 | ecs_assert(ret == ot->count, ECS_INTERNAL_ERROR, NULL); 679 | return ret; 680 | } 681 | 682 | 683 | struct ecs_squery_t { 684 | ecs_query_t *q; 685 | ecs_octree_t *ot; 686 | }; 687 | 688 | #define EXPR_PREFIX\ 689 | "[in] flecs.components.transform.Position3,"\ 690 | "[in] (flecs.components.physics.Collider, flecs.components.geometry.Box) || flecs.components.geometry.Box," 691 | 692 | ecs_squery_t* ecs_squery_new( 693 | ecs_world_t *world, 694 | ecs_id_t filter, 695 | vec3 center, 696 | float size) 697 | { 698 | ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); 699 | ecs_assert(size > 0, ECS_INVALID_PARAMETER, NULL); 700 | 701 | ecs_squery_t *result = ecs_os_calloc(sizeof(ecs_squery_t)); 702 | 703 | result->q = ecs_query(world, { 704 | .terms = { 705 | { ecs_id(EcsPosition3), .inout = EcsIn }, 706 | { ecs_pair(EcsCollider, ecs_id(EcsBox)), .inout = EcsIn, .oper = EcsOr }, 707 | { ecs_id(EcsBox) }, 708 | { filter, .inout = EcsIn } 709 | }, 710 | .cache_kind = EcsQueryCacheAuto 711 | }); 712 | 713 | result->ot = ecs_octree_new(center, size); 714 | 715 | ecs_assert(result->q != NULL, ECS_INTERNAL_ERROR, NULL); 716 | ecs_assert(result->ot != NULL, ECS_INTERNAL_ERROR, NULL); 717 | 718 | return result; 719 | } 720 | 721 | void ecs_squery_free( 722 | ecs_squery_t *sq) 723 | { 724 | ecs_query_fini(sq->q); 725 | ecs_octree_free(sq->ot); 726 | ecs_os_free(sq); 727 | } 728 | 729 | void ecs_squery_update( 730 | ecs_squery_t *sq) 731 | { 732 | ecs_assert(sq != NULL, ECS_INVALID_PARAMETER, NULL); 733 | ecs_assert(sq->q != NULL, ECS_INVALID_PARAMETER, NULL); 734 | ecs_assert(sq->ot != NULL, ECS_INVALID_PARAMETER, NULL); 735 | 736 | if (ecs_query_changed(sq->q)) { 737 | ecs_octree_clear(sq->ot); 738 | 739 | const ecs_world_t *world = ecs_get_world(sq->q); 740 | ecs_iter_t it = ecs_query_iter(world, sq->q); 741 | while (ecs_query_next(&it)) { 742 | EcsPosition3 *p = ecs_field(&it, EcsPosition3, 0); 743 | EcsBox *b = ecs_field(&it, EcsBox, 1); 744 | 745 | if (ecs_field_is_self(&it, 1)) { 746 | int i; 747 | for (i = 0; i < it.count; i ++) { 748 | vec3 vp, vs; 749 | vp[0] = p[i].x; 750 | vp[1] = p[i].y; 751 | vp[2] = p[i].z; 752 | 753 | vs[0] = b[i].width; 754 | vs[1] = b[i].height; 755 | vs[2] = b[i].depth; 756 | 757 | ecs_octree_insert(sq->ot, it.entities[i], vp, vs); 758 | } 759 | } else { 760 | int i; 761 | for (i = 0; i < it.count; i ++) { 762 | vec3 vp, vs; 763 | vp[0] = p[i].x; 764 | vp[1] = p[i].y; 765 | vp[2] = p[i].z; 766 | 767 | vs[0] = b->width; 768 | vs[1] = b->height; 769 | vs[2] = b->depth; 770 | 771 | ecs_octree_insert(sq->ot, it.entities[i], vp, vs); 772 | } 773 | } 774 | } 775 | } 776 | } 777 | 778 | void ecs_squery_findn( 779 | const ecs_squery_t *sq, 780 | vec3 position, 781 | float range, 782 | ecs_vec_t *result) 783 | { 784 | ecs_assert(sq != NULL, ECS_INVALID_PARAMETER, NULL); 785 | ecs_assert(sq->q != NULL, ECS_INVALID_PARAMETER, NULL); 786 | ecs_assert(sq->ot != NULL, ECS_INVALID_PARAMETER, NULL); 787 | 788 | ecs_octree_findn(sq->ot, position, range, result); 789 | } 790 | 791 | -------------------------------------------------------------------------------- /deps/flecs_game.c: -------------------------------------------------------------------------------- 1 | #define FLECS_GAME_IMPL 2 | 3 | #include "flecs_game.h" 4 | 5 | #define VARIATION_SLOTS_MAX (20) 6 | 7 | ECS_DECLARE(EcsCameraController); 8 | 9 | void FlecsGameCameraControllerImport(ecs_world_t *world); 10 | void FlecsGameLightControllerImport(ecs_world_t *world); 11 | 12 | ECS_CTOR(EcsParticleEmitter, ptr, { 13 | ptr->particle = 0; 14 | ptr->spawn_interval = 1.0; 15 | ptr->lifespan = 1000.0; 16 | ptr->size_decay = 1.0; 17 | ptr->color_decay = 1.0; 18 | ptr->velocity_decay = 1.0; 19 | ptr->t = 0; 20 | }) 21 | 22 | static 23 | float randf(float max) { 24 | return max * (float)rand() / (float)RAND_MAX; 25 | } 26 | 27 | typedef struct { 28 | float x_count; 29 | float y_count; 30 | float z_count; 31 | float x_spacing; 32 | float y_spacing; 33 | float z_spacing; 34 | float x_half; 35 | float y_half; 36 | float z_half; 37 | float x_var; 38 | float y_var; 39 | float z_var; 40 | float variations_total; 41 | int32_t variations_count; 42 | ecs_entity_t variations[VARIATION_SLOTS_MAX]; 43 | ecs_entity_t prefab; 44 | } flecs_grid_params_t; 45 | 46 | static 47 | ecs_entity_t get_prefab( 48 | ecs_world_t *world, 49 | ecs_entity_t parent, 50 | ecs_entity_t prefab) 51 | { 52 | if (!prefab) { 53 | return 0; 54 | } 55 | 56 | /* If prefab is a script/assembly, create a private instance of the 57 | * assembly for the grid with default values. This allows applications to 58 | * use assemblies directly vs. having to create a dummy prefab */ 59 | ecs_entity_t result = prefab; 60 | if (ecs_has(world, prefab, EcsScript) && ecs_has(world, prefab, EcsComponent)) { 61 | result = ecs_new(world); 62 | ecs_add_pair(world, result, EcsChildOf, parent); 63 | ecs_add_id(world, result, EcsPrefab); 64 | ecs_add_id(world, result, prefab); 65 | } 66 | 67 | return result; 68 | } 69 | 70 | static 71 | ecs_entity_t generate_tile( 72 | ecs_world_t *world, 73 | ecs_entity_t parent, 74 | const EcsGrid *grid, 75 | float xc, 76 | float yc, 77 | float zc, 78 | const flecs_grid_params_t *params) 79 | { 80 | if (params->x_var) { 81 | xc += randf(params->x_var) - params->x_var / 2; 82 | } 83 | if (params->y_var) { 84 | yc += randf(params->y_var) - params->y_var / 2; 85 | } 86 | if (params->z_var) { 87 | zc += randf(params->z_var) - params->z_var / 2; 88 | } 89 | 90 | ecs_entity_t slot = 0; 91 | if (params->prefab) { 92 | slot = params->prefab; 93 | } else { 94 | float p = randf(params->variations_total), cur = 0; 95 | for (int v = 0; v < params->variations_count; v ++) { 96 | cur += grid->variations[v].chance; 97 | if (p <= cur) { 98 | slot = params->variations[v]; 99 | break; 100 | } 101 | } 102 | } 103 | 104 | ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, parent); 105 | ecs_add_pair(world, inst, EcsIsA, slot); 106 | ecs_set(world, inst, EcsPosition3, {xc, yc, zc}); 107 | return inst; 108 | } 109 | 110 | static 111 | void generate_grid( 112 | ecs_world_t *world, 113 | ecs_entity_t parent, 114 | const EcsGrid *grid) 115 | { 116 | flecs_grid_params_t params = {0}; 117 | 118 | params.x_count = glm_max(1, grid->x.count); 119 | params.y_count = glm_max(1, grid->y.count); 120 | params.z_count = glm_max(1, grid->z.count); 121 | 122 | bool border = false; 123 | if (grid->border.x || grid->border.y || grid->border.z) { 124 | params.x_spacing = grid->border.x / params.x_count; 125 | params.y_spacing = grid->border.y / params.y_count; 126 | params.z_spacing = grid->border.z / params.z_count; 127 | border = true; 128 | } else { 129 | params.x_spacing = glm_max(0.001, grid->x.spacing); 130 | params.y_spacing = glm_max(0.001, grid->y.spacing); 131 | params.z_spacing = glm_max(0.001, grid->z.spacing); 132 | } 133 | 134 | params.x_half = ((params.x_count - 1) / 2.0) * params.x_spacing; 135 | params.y_half = ((params.y_count - 1) / 2.0) * params.y_spacing; 136 | params.z_half = ((params.z_count - 1) / 2.0) * params.z_spacing; 137 | 138 | params.x_var = grid->x.variation; 139 | params.y_var = grid->y.variation; 140 | params.z_var = grid->z.variation; 141 | 142 | ecs_entity_t prefab = grid->prefab; 143 | params.variations_total = 0; 144 | params.variations_count = 0; 145 | if (!prefab) { 146 | for (int i = 0; i < VARIATION_SLOTS_MAX; i ++) { 147 | if (!grid->variations[i].prefab) { 148 | break; 149 | } 150 | params.variations[i] = get_prefab(world, parent, 151 | grid->variations[i].prefab); 152 | params.variations_total += grid->variations[i].chance; 153 | params.variations_count ++; 154 | } 155 | } else { 156 | prefab = params.prefab = get_prefab(world, parent, prefab); 157 | } 158 | 159 | if (!prefab && !params.variations_count) { 160 | return; 161 | } 162 | 163 | if (!border) { 164 | for (int32_t x = 0; x < params.x_count; x ++) { 165 | for (int32_t y = 0; y < params.y_count; y ++) { 166 | for (int32_t z = 0; z < params.z_count; z ++) { 167 | float xc = (float)x * params.x_spacing - params.x_half; 168 | float yc = (float)y * params.y_spacing - params.y_half; 169 | float zc = (float)z * params.z_spacing - params.z_half; 170 | generate_tile(world, parent, grid, xc, yc, zc, ¶ms); 171 | } 172 | } 173 | } 174 | } else { 175 | for (int32_t x = 0; x < params.x_count; x ++) { 176 | float xc = (float)x * params.x_spacing - params.x_half; 177 | float zc = grid->border.z / 2 + grid->border_offset.z; 178 | generate_tile(world, parent, grid, xc, 0, -zc, ¶ms); 179 | generate_tile(world, parent, grid, xc, 0, zc, ¶ms); 180 | } 181 | 182 | for (int32_t x = 0; x < params.z_count; x ++) { 183 | float xc = grid->border.x / 2 + grid->border_offset.x; 184 | float zc = (float)x * params.z_spacing - params.z_half; 185 | ecs_entity_t inst; 186 | inst = generate_tile(world, parent, grid, xc, 0, zc, ¶ms); 187 | ecs_set(world, inst, EcsRotation3, {0, GLM_PI / 2, 0}); 188 | inst = generate_tile(world, parent, grid, -xc, 0, zc, ¶ms); 189 | ecs_set(world, inst, EcsRotation3, {0, GLM_PI / 2, 0}); 190 | } 191 | } 192 | } 193 | 194 | static 195 | void SetGrid(ecs_iter_t *it) { 196 | EcsGrid *grid = ecs_field(it, EcsGrid, 0); 197 | 198 | for (int i = 0; i < it->count; i ++) { 199 | ecs_entity_t g = it->entities[i]; 200 | ecs_delete_with(it->world, ecs_pair(EcsChildOf, g)); 201 | generate_grid(it->world, g, &grid[i]); 202 | } 203 | } 204 | 205 | static 206 | void ParticleEmit(ecs_iter_t *it) { 207 | EcsParticleEmitter *e = ecs_field(it, EcsParticleEmitter, 0); 208 | EcsBox *box = ecs_field(it, EcsBox, 1); 209 | 210 | for (int i = 0; i < it->count; i ++) { 211 | e->t += it->delta_time; 212 | if (e->t > e->spawn_interval) { 213 | e->t -= e->spawn_interval; 214 | 215 | ecs_entity_t p = ecs_insert(it->world, 216 | {ecs_childof(it->entities[i])}, 217 | {ecs_isa(e->particle)}, 218 | ecs_value(EcsParticle, { 219 | .t = e->lifespan 220 | })); 221 | 222 | if (box) { 223 | EcsPosition3 pos = {0, 0, 0}; 224 | pos.x = randf(box[i].width) - box[i].width / 2; 225 | pos.y = randf(box[i].height) - box[i].height / 2; 226 | pos.z = randf(box[i].depth) - box[i].depth / 2; 227 | ecs_set_ptr(it->world, p, EcsPosition3, &pos); 228 | } 229 | 230 | ecs_set(it->world, p, EcsRotation3, { 0, randf(4 * 3.1415926), 0 }); 231 | } 232 | } 233 | } 234 | 235 | static 236 | void ParticleProgress(ecs_iter_t *it) { 237 | EcsParticle *p = ecs_field(it, EcsParticle, 0); 238 | EcsParticleEmitter *e = ecs_field(it, EcsParticleEmitter, 1); 239 | EcsBox *box = ecs_field(it, EcsBox, 2); 240 | EcsRgb *color = ecs_field(it, EcsRgb, 3); 241 | EcsVelocity3 *vel = ecs_field(it, EcsVelocity3, 4); 242 | 243 | for (int i = 0; i < it->count; i ++) { 244 | p[i].t -= it->delta_time; 245 | if (p[i].t <= 0) { 246 | ecs_delete(it->world, it->entities[i]); 247 | } 248 | } 249 | 250 | if (box) { 251 | for (int i = 0; i < it->count; i ++) { 252 | box[i].width *= pow(e->size_decay, it->delta_time); 253 | box[i].height *= pow(e->size_decay, it->delta_time); 254 | box[i].depth *= pow(e->size_decay, it->delta_time); 255 | 256 | if ((box[i].width + box[i].height + box[i].depth) < 0.1) { 257 | ecs_delete(it->world, it->entities[i]); 258 | } 259 | } 260 | } 261 | if (color) { 262 | for (int i = 0; i < it->count; i ++) { 263 | color[i].r *= pow(e->color_decay, it->delta_time); 264 | color[i].g *= pow(e->color_decay, it->delta_time); 265 | color[i].b *= pow(e->color_decay, it->delta_time); 266 | } 267 | } 268 | if (vel) { 269 | for (int i = 0; i < it->count; i ++) { 270 | vel[i].x *= pow(e->velocity_decay, it->delta_time); 271 | vel[i].y *= pow(e->velocity_decay, it->delta_time); 272 | vel[i].z *= pow(e->velocity_decay, it->delta_time); 273 | } 274 | } 275 | } 276 | 277 | void FlecsGameImport(ecs_world_t *world) { 278 | ECS_MODULE(world, FlecsGame); 279 | 280 | ECS_IMPORT(world, FlecsComponentsTransform); 281 | ECS_IMPORT(world, FlecsComponentsPhysics); 282 | ECS_IMPORT(world, FlecsComponentsGraphics); 283 | ECS_IMPORT(world, FlecsComponentsGui); 284 | ECS_IMPORT(world, FlecsComponentsInput); 285 | ECS_IMPORT(world, FlecsSystemsPhysics); 286 | 287 | ecs_set_name_prefix(world, "Ecs"); 288 | 289 | ECS_TAG_DEFINE(world, EcsCameraController); 290 | ECS_META_COMPONENT(world, EcsCameraAutoMove); 291 | ECS_META_COMPONENT(world, EcsTimeOfDay); 292 | ECS_META_COMPONENT(world, ecs_grid_slot_t); 293 | ECS_META_COMPONENT(world, ecs_grid_coord_t); 294 | ECS_META_COMPONENT(world, EcsGrid); 295 | ECS_META_COMPONENT(world, EcsParticleEmitter); 296 | ECS_META_COMPONENT(world, EcsParticle); 297 | 298 | ecs_add_id(world, ecs_id(EcsTimeOfDay), EcsSingleton); 299 | 300 | FlecsGameCameraControllerImport(world); 301 | FlecsGameLightControllerImport(world); 302 | 303 | ecs_set_hooks(world, EcsTimeOfDay, { 304 | .ctor = flecs_default_ctor 305 | }); 306 | 307 | ECS_OBSERVER(world, SetGrid, EcsOnSet, Grid); 308 | 309 | ecs_set_hooks(world, EcsParticleEmitter, { 310 | .ctor = ecs_ctor(EcsParticleEmitter) 311 | }); 312 | 313 | ECS_SYSTEM(world, ParticleEmit, EcsOnUpdate, 314 | ParticleEmitter, 315 | ?flecs.components.geometry.Box); 316 | 317 | ECS_SYSTEM(world, ParticleProgress, EcsOnUpdate, 318 | Particle, 319 | ParticleEmitter(up), 320 | ?flecs.components.geometry.Box(self), 321 | ?flecs.components.graphics.Rgb(self), 322 | ?flecs.components.physics.Velocity3(self)); 323 | } 324 | 325 | 326 | #define CAMERA_DECELERATION 100.0 327 | #define CAMERA_ANGULAR_DECELERATION 5.0 328 | 329 | static const float CameraDeceleration = CAMERA_DECELERATION; 330 | static const float CameraAcceleration = 50.0 + CAMERA_DECELERATION; 331 | static const float CameraAngularDeceleration = CAMERA_ANGULAR_DECELERATION; 332 | static const float CameraAngularAcceleration = 2.5 + CAMERA_ANGULAR_DECELERATION; 333 | static const float CameraMaxSpeed = 40.0; 334 | 335 | static 336 | void CameraControllerAddPosition(ecs_iter_t *it) { 337 | for (int i = 0; i < it->count; i ++) { 338 | ecs_set(it->world, it->entities[i], EcsPosition3, {0, -1.5}); 339 | } 340 | } 341 | 342 | static 343 | void CameraControllerAddRotation(ecs_iter_t *it) { 344 | for (int i = 0; i < it->count; i ++) { 345 | ecs_set(it->world, it->entities[i], EcsRotation3, {0, 0}); 346 | } 347 | } 348 | 349 | static 350 | void CameraControllerAddVelocity(ecs_iter_t *it) { 351 | for (int i = 0; i < it->count; i ++) { 352 | ecs_set(it->world, it->entities[i], EcsVelocity3, {0, 0}); 353 | } 354 | } 355 | 356 | static 357 | void CameraControllerAddAngularVelocity(ecs_iter_t *it) { 358 | for (int i = 0; i < it->count; i ++) { 359 | ecs_set(it->world, it->entities[i], EcsAngularVelocity, {0, 0}); 360 | } 361 | } 362 | 363 | static 364 | void CameraControllerSyncPosition(ecs_iter_t *it) { 365 | EcsCamera *camera = ecs_field(it, EcsCamera, 0); 366 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 1); 367 | 368 | for (int i = 0; i < it->count; i ++) { 369 | camera[i].position[0] = p[i].x; 370 | camera[i].position[1] = p[i].y; 371 | camera[i].position[2] = p[i].z; 372 | } 373 | } 374 | 375 | static 376 | void CameraControllerSyncRotation(ecs_iter_t *it) { 377 | EcsCamera *camera = ecs_field(it, EcsCamera, 0); 378 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 1); 379 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 2); 380 | 381 | for (int i = 0; i < it->count; i ++) { 382 | camera[i].lookat[0] = p[i].x + sin(r[i].y) * cos(r[i].x); 383 | camera[i].lookat[1] = p[i].y + sin(r[i].x); 384 | camera[i].lookat[2] = p[i].z + cos(r[i].y) * cos(r[i].x);; 385 | } 386 | } 387 | 388 | static 389 | void CameraControllerSyncLookAt(ecs_iter_t *it) { 390 | EcsCamera *camera = ecs_field(it, EcsCamera, 0); 391 | EcsLookAt *lookat = ecs_field(it, EcsLookAt, 1); 392 | 393 | for (int i = 0; i < it->count; i ++) { 394 | camera[i].lookat[0] = lookat[i].x; 395 | camera[i].lookat[1] = lookat[i].y; 396 | camera[i].lookat[2] = lookat[i].z; 397 | } 398 | } 399 | 400 | static 401 | void CameraControllerAccelerate(ecs_iter_t *it) { 402 | EcsInput *input = ecs_field(it, EcsInput, 0); 403 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 1); 404 | EcsVelocity3 *v = ecs_field(it, EcsVelocity3, 2); 405 | EcsAngularVelocity *av = ecs_field(it, EcsAngularVelocity, 3); 406 | 407 | for (int i = 0; i < it->count; i ++) { 408 | float angle = r[i].y; 409 | float accel = CameraAcceleration * it->delta_time; 410 | float angular_accel = CameraAngularAcceleration * it->delta_time; 411 | 412 | // Camera XZ movement 413 | if (input->keys[ECS_KEY_W].state) { 414 | v[i].x += sin(angle) * accel; 415 | v[i].z += cos(angle) * accel; 416 | } 417 | if (input->keys[ECS_KEY_S].state) { 418 | v[i].x += sin(angle + GLM_PI) * accel; 419 | v[i].z += cos(angle + GLM_PI) * accel; 420 | } 421 | 422 | if (input->keys[ECS_KEY_D].state) { 423 | v[i].x += cos(angle) * accel; 424 | v[i].z -= sin(angle) * accel; 425 | } 426 | if (input->keys[ECS_KEY_A].state) { 427 | v[i].x += cos(angle + GLM_PI) * accel; 428 | v[i].z -= sin(angle + GLM_PI) * accel; 429 | } 430 | 431 | // Camera Y movement 432 | if (input->keys[ECS_KEY_E].state) { 433 | v[i].y += accel; 434 | } 435 | if (input->keys[ECS_KEY_Q].state) { 436 | v[i].y -= accel; 437 | } 438 | 439 | // Camera Y rotation 440 | if (input->keys[ECS_KEY_LEFT].state) { 441 | av[i].y -= angular_accel; 442 | } 443 | if (input->keys[ECS_KEY_RIGHT].state) { 444 | av[i].y += angular_accel; 445 | } 446 | 447 | // Camera X rotation 448 | if (input->keys[ECS_KEY_UP].state) { 449 | av[i].x += angular_accel; 450 | } 451 | if (input->keys[ECS_KEY_DOWN].state) { 452 | av[i].x -= angular_accel; 453 | } 454 | } 455 | } 456 | 457 | static 458 | void camera_controller_decel(float *v_ptr, float a, float dt) { 459 | float v = v_ptr[0]; 460 | 461 | if (v > 0) { 462 | v = glm_clamp(v - a * dt, 0, v); 463 | } 464 | if (v < 0) { 465 | v = glm_clamp(v + a * dt, v, 0); 466 | } 467 | 468 | v_ptr[0] = v; 469 | } 470 | 471 | static 472 | void CameraControllerDecelerate(ecs_iter_t *it) { 473 | EcsVelocity3 *v = ecs_field(it, EcsVelocity3, 0); 474 | EcsAngularVelocity *av = ecs_field(it, EcsAngularVelocity, 1); 475 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 2); 476 | 477 | float dt = it->delta_time; 478 | 479 | vec3 zero = {0}; 480 | 481 | for (int i = 0; i < it->count; i ++) { 482 | vec3 v3 = {v[i].x, v[i].y, v[i].z}, vn3; 483 | glm_vec3_normalize_to(v3, vn3); 484 | 485 | float speed = glm_vec3_distance(zero, v3); 486 | if (speed > CameraMaxSpeed) { 487 | glm_vec3_scale(v3, CameraMaxSpeed / speed, v3); 488 | v[i].x = v3[0]; 489 | v[i].y = v3[1]; 490 | v[i].z = v3[2]; 491 | } 492 | 493 | camera_controller_decel(&v[i].x, CameraDeceleration * fabs(vn3[0]), dt); 494 | camera_controller_decel(&v[i].y, CameraDeceleration * fabs(vn3[1]), dt); 495 | camera_controller_decel(&v[i].z, CameraDeceleration * fabs(vn3[2]), dt); 496 | 497 | camera_controller_decel(&av[i].x, CameraAngularDeceleration, dt); 498 | camera_controller_decel(&av[i].y, CameraAngularDeceleration, dt); 499 | 500 | if (r[i].x > GLM_PI / 2.0) { 501 | r[i].x = GLM_PI / 2.0 - 0.0001; 502 | av[i].x = 0; 503 | } 504 | if (r[i].x < -GLM_PI / 2.0) { 505 | r[i].x = -(GLM_PI / 2.0) + 0.0001; 506 | av[i].x = 0; 507 | } 508 | } 509 | } 510 | 511 | static 512 | void CameraAutoMove(ecs_iter_t *it) { 513 | EcsVelocity3 *v = ecs_field(it, EcsVelocity3, 0); 514 | EcsCameraAutoMove *m = ecs_field(it, EcsCameraAutoMove, 1); 515 | 516 | float dt = it->delta_time; 517 | 518 | for (int i = 0; i < it->count; i ++) { 519 | EcsVelocity3 *vcur = &v[i]; 520 | m->t += dt; 521 | if ((m->t < m->after) && (vcur->x || vcur->y || vcur->z)) { 522 | m->t = 0; 523 | } 524 | if (m->t > m->after) { 525 | vcur->z = 10; 526 | } 527 | } 528 | } 529 | 530 | void FlecsGameCameraControllerImport(ecs_world_t *world) { 531 | ECS_SYSTEM(world, CameraControllerSyncPosition, EcsOnUpdate, 532 | [out] flecs.components.graphics.Camera, 533 | [in] flecs.components.transform.Position3, 534 | [none] CameraController); 535 | 536 | ECS_SYSTEM(world, CameraControllerSyncRotation, EcsOnUpdate, 537 | [out] flecs.components.graphics.Camera, 538 | [in] flecs.components.transform.Position3, 539 | [in] flecs.components.transform.Rotation3, 540 | [none] CameraController); 541 | 542 | ECS_SYSTEM(world, CameraControllerSyncLookAt, EcsOnUpdate, 543 | [out] flecs.components.graphics.Camera, 544 | [in] flecs.components.graphics.LookAt, 545 | [none] CameraController); 546 | 547 | ECS_SYSTEM(world, CameraControllerAccelerate, EcsOnUpdate, 548 | [in] flecs.components.input.Input, 549 | [in] flecs.components.transform.Rotation3, 550 | [inout] flecs.components.physics.Velocity3, 551 | [inout] flecs.components.physics.AngularVelocity, 552 | [none] CameraController); 553 | 554 | ECS_SYSTEM(world, CameraControllerDecelerate, EcsOnUpdate, 555 | [inout] flecs.components.physics.Velocity3, 556 | [inout] flecs.components.physics.AngularVelocity, 557 | [inout] flecs.components.transform.Rotation3, 558 | [none] CameraController); 559 | 560 | ECS_SYSTEM(world, CameraAutoMove, EcsOnUpdate, 561 | [inout] flecs.components.physics.Velocity3, 562 | [inout] CameraAutoMove); 563 | 564 | ecs_add_pair(world, ecs_id(EcsCameraController), EcsWith, ecs_id(EcsPosition3)); 565 | ecs_add_pair(world, ecs_id(EcsCameraController), EcsWith, ecs_id(EcsRotation3)); 566 | ecs_add_pair(world, ecs_id(EcsCameraController), EcsWith, ecs_id(EcsVelocity3)); 567 | ecs_add_pair(world, ecs_id(EcsCameraController), EcsWith, ecs_id(EcsAngularVelocity)); 568 | } 569 | 570 | 571 | static 572 | void LightControllerSyncPosition(ecs_iter_t *it) { 573 | EcsDirectionalLight *light = ecs_field(it, EcsDirectionalLight, 0); 574 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 1); 575 | 576 | for (int i = 0; i < it->count; i ++) { 577 | light[i].position[0] = p[i].x; 578 | light[i].position[1] = p[i].y; 579 | light[i].position[2] = p[i].z; 580 | } 581 | } 582 | 583 | static 584 | void LightControllerSyncRotation(ecs_iter_t *it) { 585 | EcsDirectionalLight *light = ecs_field(it, EcsDirectionalLight, 0); 586 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 1); 587 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 2); 588 | 589 | for (int i = 0; i < it->count; i ++) { 590 | light[i].direction[0] = p[i].x + sin(r[i].y) * cos(r[i].x); 591 | light[i].direction[1] = p[i].y + sin(r[i].x); 592 | light[i].direction[2] = p[i].z + cos(r[i].y) * cos(r[i].x); 593 | } 594 | } 595 | 596 | static 597 | void LightControllerSyncColor(ecs_iter_t *it) { 598 | EcsDirectionalLight *light = ecs_field(it, EcsDirectionalLight, 0); 599 | EcsRgb *color = ecs_field(it, EcsRgb, 1); 600 | 601 | for (int i = 0; i < it->count; i ++) { 602 | light[i].color[0] = color[i].r; 603 | light[i].color[1] = color[i].g; 604 | light[i].color[2] = color[i].b; 605 | } 606 | } 607 | 608 | static 609 | void LightControllerSyncIntensity(ecs_iter_t *it) { 610 | EcsDirectionalLight *light = ecs_field(it, EcsDirectionalLight, 0); 611 | EcsLightIntensity *intensity = ecs_field(it, EcsLightIntensity, 1); 612 | 613 | for (int i = 0; i < it->count; i ++) { 614 | light[i].intensity = intensity[i].value; 615 | } 616 | } 617 | 618 | static 619 | void TimeOfDayUpdate(ecs_iter_t *it) { 620 | EcsTimeOfDay *tod = ecs_field(it, EcsTimeOfDay, 0); 621 | tod->t += it->delta_system_time * tod->speed; 622 | } 623 | 624 | static 625 | float get_time_of_day(float t) { 626 | return (t + 1.0) * GLM_PI; 627 | } 628 | 629 | static 630 | float get_sun_height(float t) { 631 | return -sin(get_time_of_day(t)); 632 | } 633 | 634 | static 635 | void LightControllerTimeOfDay(ecs_iter_t *it) { 636 | EcsTimeOfDay *tod = ecs_field(it, EcsTimeOfDay, 0); 637 | EcsRotation3 *r = ecs_field(it, EcsRotation3, 1); 638 | EcsRgb *color = ecs_field(it, EcsRgb, 2); 639 | EcsLightIntensity *light_intensity = ecs_field(it, EcsLightIntensity, 3); 640 | 641 | static vec3 day = {0.8, 0.8, 0.75}; 642 | static vec3 twilight = {1.0, 0.1, 0.01}; 643 | float twilight_angle = 0.3; 644 | 645 | for (int i = 0; i < it->count; i ++) { 646 | r[i].x = get_time_of_day(tod[i].t); 647 | 648 | float t_sin = get_sun_height(tod[i].t); 649 | float t_sin_low = twilight_angle - t_sin; 650 | vec3 sun_color; 651 | if (t_sin_low > 0) { 652 | t_sin_low *= 1.0 / twilight_angle; 653 | glm_vec3_lerp(day, twilight, t_sin_low, sun_color); 654 | } else { 655 | glm_vec3_copy(day, sun_color); 656 | } 657 | 658 | float intensity = t_sin; 659 | if (intensity < 0) { 660 | intensity = 0; 661 | } 662 | 663 | color[i].r = sun_color[0]; 664 | color[i].g = sun_color[1]; 665 | color[i].b = sun_color[2]; 666 | light_intensity[i].value = intensity; 667 | } 668 | } 669 | 670 | static 671 | void AmbientLightControllerTimeOfDay(ecs_iter_t *it) { 672 | EcsTimeOfDay *tod = ecs_field(it, EcsTimeOfDay, 0); 673 | EcsCanvas *canvas = ecs_field(it, EcsCanvas, 1); 674 | 675 | static vec3 ambient_day = {0.03, 0.06, 0.09}; 676 | static vec3 ambient_night = {0.001, 0.008, 0.016}; 677 | static vec3 ambient_twilight = {0.01, 0.017, 0.02}; 678 | static float twilight_zone = 0.2; 679 | 680 | for (int i = 0; i < it->count; i ++) { 681 | float t_sin = get_sun_height(tod[i].t); 682 | t_sin = (t_sin + 1.0) / 2; 683 | 684 | float t_twilight = glm_max(0.0, twilight_zone - fabs(t_sin - 0.5)); 685 | t_twilight *= (1.0 / twilight_zone); 686 | 687 | vec3 ambient_color; 688 | glm_vec3_lerp(ambient_night, ambient_day, t_sin, ambient_color); 689 | glm_vec3_lerp(ambient_color, ambient_twilight, t_twilight, ambient_color); 690 | canvas[i].ambient_light.r = ambient_color[0]; 691 | canvas[i].ambient_light.g = ambient_color[1]; 692 | canvas[i].ambient_light.b = ambient_color[2]; 693 | } 694 | } 695 | 696 | void FlecsGameLightControllerImport(ecs_world_t *world) { 697 | ECS_SYSTEM(world, LightControllerSyncPosition, EcsOnUpdate, 698 | [out] flecs.components.graphics.DirectionalLight, 699 | [in] flecs.components.transform.Position3); 700 | 701 | ECS_SYSTEM(world, LightControllerSyncRotation, EcsOnUpdate, 702 | [out] flecs.components.graphics.DirectionalLight, 703 | [in] flecs.components.transform.Position3, 704 | [in] flecs.components.transform.Rotation3); 705 | 706 | ECS_SYSTEM(world, LightControllerSyncIntensity, EcsOnUpdate, 707 | [out] flecs.components.graphics.DirectionalLight, 708 | [in] flecs.components.graphics.LightIntensity); 709 | 710 | ECS_SYSTEM(world, LightControllerSyncColor, EcsOnUpdate, 711 | [out] flecs.components.graphics.DirectionalLight, 712 | [in] flecs.components.graphics.Rgb); 713 | 714 | ECS_SYSTEM(world, TimeOfDayUpdate, EcsOnUpdate, 715 | [inout] TimeOfDay); 716 | 717 | ECS_SYSTEM(world, LightControllerTimeOfDay, EcsOnUpdate, 718 | [in] TimeOfDay, 719 | [out] flecs.components.transform.Rotation3, 720 | [out] flecs.components.graphics.Rgb, 721 | [out] flecs.components.graphics.LightIntensity, 722 | [none] flecs.components.graphics.Sun); 723 | 724 | ECS_SYSTEM(world, AmbientLightControllerTimeOfDay, EcsOnUpdate, 725 | [in] TimeOfDay, 726 | [out] flecs.components.gui.Canvas); 727 | 728 | ecs_add_pair(world, EcsSun, EcsWith, ecs_id(EcsRotation3)); 729 | ecs_add_pair(world, EcsSun, EcsWith, ecs_id(EcsDirectionalLight)); 730 | ecs_add_pair(world, EcsSun, EcsWith, ecs_id(EcsRgb)); 731 | ecs_add_pair(world, EcsSun, EcsWith, ecs_id(EcsLightIntensity)); 732 | 733 | ecs_system(world, { 734 | .entity = ecs_id(TimeOfDayUpdate), 735 | .interval = 1.0 736 | }); 737 | } 738 | 739 | -------------------------------------------------------------------------------- /src/module.c: -------------------------------------------------------------------------------- 1 | #define FLECS_CITY_IMPL 2 | 3 | #include 4 | 5 | /* Constants */ 6 | #define PAVEMENT_HEIGHT (0.2f) 7 | #define PARK_HEIGHT (0.3f) 8 | #define PLAZA_HEIGHT (0.3f) 9 | #define PLAZA_POLE_HEIGHT (6.0f) 10 | #define STREETS_HEIGHT (10.0f) 11 | #define TRAFFIC_FREQUENCY (10.0f) 12 | #define SMALL_BUILDING_THRESHOLD (20.0f) 13 | 14 | typedef struct { 15 | float width; 16 | float height; 17 | float left; 18 | float right; 19 | float top; 20 | float bottom; 21 | } CityBound; 22 | 23 | /* Internal component */ 24 | typedef struct { 25 | float elapsed; 26 | EcsVelocity3 initial_velocity; 27 | EcsRotation3 initial_rotation; 28 | CityBound bound; 29 | float frequency; 30 | } CityTrafficEmitter; 31 | 32 | ECS_COMPONENT_DECLARE(CityTrafficEmitter); 33 | ECS_COMPONENT_DECLARE(CityBound); 34 | 35 | /* Resources loaded from module file */ 36 | static ecs_entity_t CityStreet; 37 | static ecs_entity_t CityPavement; 38 | static ecs_entity_t CityAc; 39 | static ecs_entity_t CityTree; 40 | static ecs_entity_t CitySidewalkTree; 41 | static ecs_entity_t CityBin; 42 | static ecs_entity_t CityHydrant; 43 | static ecs_entity_t CityLantern; 44 | static ecs_entity_t CityBench; 45 | static ecs_entity_t CityPark; 46 | static ecs_entity_t CityPlaza; 47 | static ecs_entity_t CityBuilding; 48 | static ecs_entity_t CityModernBuilding; 49 | static ecs_entity_t CityCar; 50 | 51 | static ecs_entity_t CarsRoot; 52 | 53 | /* Internal types */ 54 | typedef struct { 55 | const City *city; 56 | ecs_entity_t parent; 57 | float x, y; 58 | float build_area_width; 59 | float build_area_height; 60 | } CityBlock; 61 | 62 | /* Utilities */ 63 | static 64 | float randf(float scale) { 65 | return scale * ((float)rand() / (float)RAND_MAX); 66 | } 67 | 68 | static 69 | float srandf(float scale) { 70 | return scale * ((float)rand() / (float)RAND_MAX) - scale / 2.0; 71 | } 72 | 73 | static 74 | bool rolldice(float chance) { 75 | return randf(1.0) < chance; 76 | } 77 | 78 | static 79 | ecs_entity_t plant_object(ecs_world_t *world, CityBlock *block, float x, float y, ecs_entity_t prop) { 80 | ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, block->parent); 81 | ecs_add_pair(world, e, EcsIsA, prop); 82 | ecs_set(world, e, EcsPosition3, { 83 | .x = block->x + x, 84 | .y = PAVEMENT_HEIGHT, 85 | .z = block->y + y 86 | }); 87 | return e; 88 | } 89 | 90 | static 91 | ecs_entity_t plant_tree(ecs_world_t *world, CityBlock *block, float x, float y) { 92 | return plant_object(world, block, x, y, CityTree); 93 | } 94 | 95 | static 96 | ecs_entity_t plant_prop(ecs_world_t *world, CityBlock *block, float x, float y) { 97 | const CityProps *props = &block->city->props; 98 | ecs_entity_t result = 0; 99 | 100 | float p_sum = 101 | props->tree_chance + 102 | props->bin_chance + 103 | props->hydrant_chance + 104 | props->bench_chance; 105 | 106 | float p = props->tree_chance; 107 | float dice = randf(1.0) * p_sum; 108 | 109 | if (dice < p) { 110 | result += plant_object(world, block, x, y, CitySidewalkTree); 111 | } 112 | 113 | p += props->bin_chance; 114 | if (!result && dice < p) { 115 | result = plant_object(world, block, x, y, CityBin); 116 | } 117 | 118 | p += props->hydrant_chance; 119 | if (!result && dice < p) { 120 | result = plant_object(world, block, x, y, CityHydrant); 121 | } 122 | 123 | p += props->bench_chance; 124 | if (!result && dice < p) { 125 | result = plant_object(world, block, x, y, CityBench); 126 | } 127 | 128 | if (x < 0) { 129 | ecs_set(world, result, EcsRotation3, {0, GLM_PI / 2.0, 0}); 130 | } else if (x > 0) { 131 | ecs_set(world, result, EcsRotation3, {0, -GLM_PI / 2.0, 0}); 132 | } else if (y > 0) { 133 | ecs_set(world, result, EcsRotation3, {0, GLM_PI, 0}); 134 | } 135 | 136 | return result; 137 | } 138 | 139 | static 140 | ecs_entity_t plant_lantern(ecs_world_t *world, CityBlock *block, float x, float y) { 141 | return plant_object(world, block, x, y, CityLantern); 142 | } 143 | 144 | static 145 | void plant_park(ecs_world_t *world, CityBlock *block, float x, float y) { 146 | ecs_entity_t prop = plant_object(world, block, x, y, CityPark); 147 | ecs_set(world, prop, EcsBox, { 148 | block->build_area_width, PARK_HEIGHT, block->build_area_height 149 | }); 150 | } 151 | 152 | static 153 | void plant_backyard(ecs_world_t *world, CityBlock *block, float x, float y) { 154 | ecs_entity_t prop = plant_object(world, block, x, y, CityPark); 155 | ecs_set(world, prop, EcsBox, { 156 | block->build_area_width - 5.0, 157 | PARK_HEIGHT, 158 | block->build_area_height - 5.0 159 | }); 160 | } 161 | 162 | static 163 | void plant_plaza(ecs_world_t *world, CityBlock *block, float x, float y) { 164 | ecs_entity_t prop = plant_object(world, block, x, y, CityPlaza); 165 | ecs_set(world, prop, EcsBox, { 166 | block->build_area_width, PLAZA_HEIGHT, block->build_area_height 167 | }); 168 | 169 | prop = plant_object(world, block, x, y, CityPlaza); 170 | ecs_set(world, prop, EcsBox, { 171 | block->build_area_width * 0.75, PLAZA_HEIGHT * 2, block->build_area_height * 0.75 172 | }); 173 | 174 | prop = plant_object(world, block, x, y, CityPlaza); 175 | ecs_set(world, prop, EcsBox, { 176 | 0.75, PLAZA_POLE_HEIGHT, 0.75 177 | }); 178 | ecs_set(world, prop, EcsPosition3, { 179 | .x = block->x + x, 180 | .y = 0.1 + PLAZA_POLE_HEIGHT / 2.0, 181 | .z = block->y + y 182 | }); 183 | } 184 | 185 | static 186 | ecs_entity_t plant_building_top( 187 | ecs_world_t *world, 188 | ecs_entity_t city, 189 | float x, 190 | float y, 191 | float building_height, 192 | float top_height, 193 | float top_x, 194 | float top_y, 195 | EcsRgb *color, 196 | bool modern) 197 | { 198 | ecs_entity_t top = ecs_new_w_pair(world, EcsChildOf, city); 199 | ecs_add_pair(world, top, EcsIsA, CityBuilding); 200 | ecs_set(world, top, EcsPosition3, { 201 | .x = x, 202 | .y = building_height + (top_height / 2.0), 203 | .z = y 204 | }); 205 | ecs_set(world, top, EcsBox, { top_x, top_height, top_y }); 206 | ecs_set_ptr(world, top, EcsRgb, color); 207 | if (modern) ecs_add_pair(world, top, EcsIsA, CityModernBuilding); 208 | return top; 209 | } 210 | 211 | static 212 | void plant_building( 213 | ecs_world_t *world, 214 | const City *city, 215 | ecs_entity_t parent, 216 | float x, 217 | float y, 218 | float building_height, 219 | float x_size, 220 | float y_size, 221 | bool modern, 222 | bool skyscraper) 223 | { 224 | /* Generate building */ 225 | ecs_entity_t building = ecs_new_w_pair(world, EcsChildOf, parent); 226 | ecs_add_pair(world, building, EcsIsA, CityBuilding); 227 | ecs_set(world, building, EcsPosition3, { 228 | .x = x, 229 | .y = building_height / 2.0, 230 | .z = y 231 | }); 232 | ecs_set(world, building, EcsBox, { 233 | x_size, 234 | building_height, 235 | y_size 236 | }); 237 | float bc = 0.1f + randf(0.7f); 238 | 239 | EcsRgb color = {bc, bc, bc}; 240 | 241 | if (modern) { 242 | color = (EcsRgb){bc * 0.8, bc * 1.05, bc * 1.1}; 243 | ecs_add_pair(world, building, EcsIsA, CityModernBuilding); 244 | modern = true; 245 | } else { 246 | float color_chance = randf(1.0); 247 | if (color_chance < 0.2) { 248 | /* red brick */ 249 | if (color_chance < 0.01 && building_height < 20) { 250 | color = (EcsRgb){bc * 1.0, bc * 0.32, bc * 0.27}; 251 | } else { 252 | color = (EcsRgb){bc * 1.0, bc * 0.7, bc * 0.6}; 253 | } 254 | } 255 | } 256 | 257 | ecs_set_ptr(world, building, EcsRgb, &color); 258 | 259 | if (randf(1.0) > 0.2 && (building_height > city->buildings.small_height)) { 260 | float top_height = 10.0 * (randf(0.5) + 0.5); 261 | float top_dw = x_size * 0.3; 262 | top_height = glm_min(top_height, building_height * 0.05); 263 | 264 | bool tall_building = (building_height > city->buildings.max_height); 265 | 266 | plant_building_top(world, parent, x, y, building_height, 267 | top_height, x_size - top_dw, y_size - top_dw, 268 | &color, modern); 269 | 270 | if (randf(1.0) > 0.2) { 271 | top_dw = x_size * 0.6; 272 | plant_building_top(world, parent, x, y, building_height, 273 | top_height * 2.0, x_size - top_dw, x_size - top_dw, 274 | &color, modern); 275 | 276 | if (tall_building || (randf(1.0) > 0.5)) { 277 | top_dw = x_size * 0.9; 278 | plant_building_top(world, parent, x, y, building_height, 279 | top_height * 3.0, x_size - top_dw, x_size - top_dw, 280 | &color, modern); 281 | 282 | if (tall_building) { 283 | top_dw = x_size * 0.96; 284 | plant_building_top(world, parent, x, y, building_height, 285 | top_height * 6.0, x_size - top_dw, x_size - top_dw, 286 | &color, modern); 287 | } 288 | } 289 | } 290 | } 291 | } 292 | 293 | /* Generate city block */ 294 | void plant_city_block( 295 | ecs_world_t *world, 296 | ecs_entity_t e, 297 | const City *city, 298 | int32_t x_i, 299 | int32_t y_i, 300 | int32_t x, 301 | int32_t y) 302 | { 303 | float block_width = city->block_width; 304 | float block_height = city->block_height; 305 | float pave_width = city->pavement_width; 306 | float corner_w = block_width / 2.0 - pave_width / 2.0; 307 | float corner_h = block_height / 2.0 - pave_width / 2.0; 308 | float build_area_width = block_width - pave_width * 2; 309 | float build_area_height = block_height - pave_width * 2; 310 | float building_min_width = city->buildings.min_width; 311 | 312 | CityBlock block = { 313 | .city = city, 314 | .parent = e, 315 | .x = x, 316 | .y = y, 317 | .build_area_width = build_area_width, 318 | .build_area_height = build_area_height 319 | }; 320 | 321 | /* Create pavement */ 322 | ecs_entity_t pavement = ecs_new_w_pair(world, EcsChildOf, e); 323 | ecs_add_pair(world, pavement, EcsIsA, CityPavement); 324 | ecs_set(world, pavement, EcsPosition3, { 325 | .x = x, 326 | .y = PAVEMENT_HEIGHT / 2.0, 327 | .z = y 328 | }); 329 | ecs_set(world, pavement, EcsBox, { 330 | block_width, PAVEMENT_HEIGHT, block_height 331 | }); 332 | 333 | if (!rolldice(city->parks.chance)) { 334 | float min_height = city->buildings.min_height; 335 | float max_height = city->buildings.max_height; 336 | 337 | /* Compute distsance to center, which hosts the most large buildings */ 338 | float max_dx = city->blocks_x / 2.0; 339 | float max_dy = city->blocks_y / 2.0; 340 | float dx = (x_i - max_dx) / max_dx; 341 | float dy = (y_i - max_dy) / max_dy; 342 | float d = 1.0 - sqrt(dx * dx + dy * dy); 343 | 344 | d *= d; 345 | 346 | /* Add some variability so the falloff isn't too predictable */ 347 | d = randf(0.1) + d * 0.9; 348 | 349 | float height_v = (max_height - min_height); /* max variation */ 350 | float height = min_height + height_v * randf(1.0) * d; 351 | float x_size = build_area_width; 352 | float y_size = build_area_height; 353 | 354 | if (height > city->buildings.small_height) { 355 | bool modern = rolldice(city->buildings.modern_chance); 356 | 357 | bool skyscraper = false; 358 | if (height > max_height * 0.75) { 359 | skyscraper = rolldice(city->buildings.skyscraper_chance); 360 | if (skyscraper) { 361 | height = height * 1.5; 362 | } 363 | } 364 | 365 | plant_building(world, city, e, x, y, height, x_size, 366 | y_size, modern, skyscraper); 367 | } else { 368 | float x_size, y_size = building_min_width + 369 | (build_area_height - building_min_width) * (0.5 * randf(0.5)); 370 | 371 | int bx, num_x = 2.0; 372 | x_size = build_area_width / (float)num_x; 373 | 374 | y_size = glm_min(x_size, y_size); 375 | 376 | int by, num_y = build_area_height / y_size; 377 | y_size = build_area_height / (float)num_y; 378 | 379 | float x_start = x - build_area_width / 2.0 + x_size / 2.0; 380 | float y_start = y - build_area_height / 2.0 + y_size / 2.0; 381 | 382 | bool backyard = false; 383 | for (bx = 0; bx < num_x; bx ++) { 384 | float y_delta = 0; 385 | 386 | for (by = 0; by < num_y; by ++) { 387 | float xc = x_start + bx * x_size; 388 | float yc = y_start + by * y_size; 389 | float h = glm_max(height + srandf(10.0), min_height); 390 | float x_size_loc = x_size; 391 | 392 | if (num_x == 2 && (by > 0 && by != (num_y - 1))) { 393 | if (rolldice(city->buildings.backyard_chance)) { 394 | float backyard_size = randf(build_area_width / 8.0); 395 | if (backyard_size > 0.2) { 396 | backyard = true; 397 | x_size_loc -= backyard_size; 398 | if (!bx) { 399 | xc -= backyard_size / 2.0; 400 | } else { 401 | xc += backyard_size / 2.0; 402 | } 403 | } 404 | } 405 | 406 | float x_var = randf(city->buildings.x_variation); 407 | if (!bx) { 408 | xc += x_var / 2.0; 409 | } else { 410 | xc -= x_var / 2.0; 411 | } 412 | x_size_loc -= x_var; 413 | } 414 | 415 | float y_var = 0; 416 | if (by != (num_y - 1)) { 417 | y_var = srandf(city->buildings.y_variation); 418 | y_delta += y_var / 2.0; 419 | } else { 420 | y_var = -y_delta; 421 | y_delta += (y_var / 2.0); 422 | } 423 | 424 | bool modern = rolldice(city->buildings.modern_chance); 425 | if (h > city->buildings.small_height) { 426 | h = city->buildings.small_height; 427 | } 428 | 429 | plant_building(world, city, e, xc, yc + y_delta, h, 430 | x_size_loc, y_size + y_var, modern, false); 431 | 432 | y_delta += y_var / 2.0; 433 | } 434 | } 435 | if (backyard) { 436 | plant_backyard(world, &block, 0, 0); 437 | } 438 | } 439 | } else { 440 | if (rolldice(city->parks.plaza_chance)) { 441 | plant_plaza(world, &block, 0, 0); 442 | } else { 443 | /* Generate park */ 444 | plant_park(world, &block, 0, 0); 445 | 446 | if (rolldice(city->parks.tree_chance)) { 447 | if (rolldice(city->parks.tree_chance)) { 448 | for (int t = 0; t < city->parks.tree_count; t ++) { 449 | float tx = srandf(block_width - pave_width * 4); 450 | float ty = srandf(block_height - pave_width * 4); 451 | plant_tree(world, &block, tx, ty + PARK_HEIGHT); 452 | } 453 | } 454 | } 455 | } 456 | } 457 | 458 | if (rolldice(city->props.chance)) { 459 | plant_prop(world, &block, -corner_w, 0); 460 | } 461 | if (rolldice(city->props.chance)) { 462 | plant_prop(world, &block, corner_w, 0); 463 | } 464 | if (rolldice(city->props.chance)) { 465 | plant_prop(world, &block, 0, -corner_h); 466 | } 467 | if (rolldice(city->props.chance)) { 468 | plant_prop(world, &block, 0, corner_h); 469 | } 470 | 471 | /* Lanterns on street corners */ 472 | if (city->lanterns) { 473 | if (!(x_i % 2) && !(y_i % 2)) { 474 | plant_lantern(world, &block, corner_w, corner_h); 475 | plant_lantern(world, &block, corner_w, -corner_h); 476 | plant_lantern(world, &block, -corner_w, -corner_h); 477 | plant_lantern(world, &block, -corner_w, corner_h); 478 | } else if (!((x_i + 1) % 2) && !((y_i + 1) % 2)) { 479 | plant_lantern(world, &block, corner_w, corner_h); 480 | plant_lantern(world, &block, corner_w, -corner_h); 481 | plant_lantern(world, &block, -corner_w, -corner_h); 482 | plant_lantern(world, &block, -corner_w, corner_h); 483 | } 484 | } 485 | } 486 | 487 | /* Generate city */ 488 | void SetCity(ecs_iter_t *it) { 489 | City *cities = ecs_field(it, City, 0); 490 | 491 | ecs_world_t *world = it->world; 492 | 493 | for (int i = 0; i < it->count; i ++) { 494 | ecs_entity_t e = it->entities[i]; 495 | 496 | ecs_delete_with(world, ecs_pair(EcsChildOf, e)); 497 | 498 | City *city = &cities[i]; 499 | int blocks_x = city->blocks_x; 500 | int blocks_y = city->blocks_y; 501 | int min_height = city->buildings.min_height; 502 | int max_height = city->buildings.max_height; 503 | int block_width = city->block_width; 504 | int block_height = city->block_height; 505 | int building_min_width = city->buildings.min_width; 506 | float pavement_width = city->pavement_width; 507 | float road_width = city->road_width; 508 | float traffic_speed = city->traffic.speed; 509 | float small_height = city->buildings.small_height; 510 | 511 | if (!blocks_x) { 512 | city->blocks_x = blocks_x = 10; 513 | } 514 | if (!blocks_y) { 515 | city->blocks_y = blocks_y = 10; 516 | } 517 | if (!min_height) { 518 | city->buildings.min_height = min_height = 5.0; 519 | } 520 | if (!max_height) { 521 | city->buildings.max_height = max_height = 20.0; 522 | } 523 | if (!block_width) { 524 | city->block_width = block_width = 20; 525 | } 526 | if (!block_height) { 527 | city->block_height = block_height = block_width; 528 | } 529 | if (!building_min_width) { 530 | city->buildings.min_width = 4.0; 531 | } 532 | if (!pavement_width) { 533 | city->pavement_width = 2.0; 534 | } 535 | if (!road_width) { 536 | city->road_width = road_width = 10; 537 | } 538 | if (!traffic_speed) { 539 | traffic_speed = 50.0; 540 | } 541 | if (!small_height) { 542 | city->buildings.small_height = 15.0; 543 | } 544 | 545 | float width = (float)block_width + (float)road_width; 546 | float height = (float)block_height + (float)road_width; 547 | float city_width = blocks_x * width; 548 | float city_height = blocks_y * height; 549 | float left = -(city_width / 2.0f); 550 | float right = (city_width / 2.0f); 551 | float top = -(city_height / 2.0f); 552 | float bottom = (city_height / 2.0f); 553 | 554 | CityBound bound = { 555 | .width = city_width, 556 | .height = city_height, 557 | .left = left, 558 | .right = left + city_width, 559 | .top = top, 560 | .bottom = top + city_height 561 | }; 562 | 563 | ecs_entity_t streets = ecs_entity(world, { 564 | .parent = e, 565 | .add = ecs_ids(ecs_isa(CityStreet)) 566 | }); 567 | 568 | ecs_set(world, streets, EcsPosition3, {0, -(STREETS_HEIGHT / 2.0), 0}); 569 | ecs_set(world, streets, EcsBox, { 570 | city_width, STREETS_HEIGHT, city_height}); 571 | 572 | for (int x = 0; x < blocks_x; x ++) { 573 | for (int y = 0; y < blocks_y; y ++) { 574 | plant_city_block(world, e, city, x, y, 575 | left + width * x + width / 2.0f, 576 | top + height * y + height / 2.0f); 577 | } 578 | } 579 | 580 | if (city->traffic.frequency) { 581 | for (int x = 0; x < blocks_x; x ++) { 582 | float block_left = left + width * x; 583 | float lane_width = road_width / 2.0; 584 | float lane_center = block_left + lane_width / 2.0; 585 | 586 | // float lheight = randf(120); // flying cars 587 | float lheight = 0; 588 | 589 | ecs_entity_t emit_1 = ecs_new_w_pair(world, EcsChildOf, e); 590 | ecs_set(world, emit_1, EcsPosition3, { 591 | lane_center, lheight, top 592 | }); 593 | ecs_set(world, emit_1, CityTrafficEmitter, { 594 | .initial_velocity = {0, 0.0, traffic_speed}, 595 | .initial_rotation = {0}, 596 | .bound = bound, 597 | .frequency = city->traffic.frequency, 598 | .elapsed = city->traffic.frequency * randf(1.0) 599 | }); 600 | 601 | lane_center -= lane_width; 602 | 603 | ecs_entity_t emit_2 = ecs_new_w_pair(world, EcsChildOf, e); 604 | ecs_set(world, emit_2, EcsPosition3, { 605 | lane_center, lheight, bottom 606 | }); 607 | ecs_set(world, emit_2, CityTrafficEmitter, { 608 | .initial_velocity = {0, 0.0, -traffic_speed}, 609 | .initial_rotation = {0, GLM_PI}, 610 | .bound = bound, 611 | .frequency = city->traffic.frequency, 612 | .elapsed = city->traffic.frequency * randf(1.0) 613 | }); 614 | } 615 | 616 | for (int y = 0; y < blocks_y; y ++) { 617 | float block_left = top + height * y; 618 | float lane_width = road_width / 2.0; 619 | float lane_center = block_left + lane_width / 2.0; 620 | 621 | // float lheight = randf(120); // flying cars 622 | float lheight = 0; 623 | 624 | ecs_entity_t emit_1 = ecs_new_w_pair(world, EcsChildOf, e); 625 | ecs_set(world, emit_1, EcsPosition3, { 626 | left, lheight, lane_center 627 | }); 628 | ecs_set(world, emit_1, CityTrafficEmitter, { 629 | .initial_velocity = {traffic_speed, 0.0, 0.0}, 630 | .initial_rotation = {0, GLM_PI / 2.0}, 631 | .bound = bound, 632 | .frequency = city->traffic.frequency, 633 | .elapsed = city->traffic.frequency * randf(1.0) 634 | }); 635 | 636 | lane_center -= lane_width; 637 | 638 | ecs_entity_t emit_2 = ecs_new_w_pair(world, EcsChildOf, e); 639 | ecs_set(world, emit_2, EcsPosition3, { 640 | right, lheight, lane_center 641 | }); 642 | ecs_set(world, emit_2, CityTrafficEmitter, { 643 | .initial_velocity = {-traffic_speed, 0.0, 0}, 644 | .initial_rotation = {0, -GLM_PI / 2.0}, 645 | .bound = bound, 646 | .frequency = city->traffic.frequency, 647 | .elapsed = city->traffic.frequency * randf(1.0) 648 | }); 649 | } 650 | } 651 | } 652 | } 653 | 654 | static 655 | void GenerateTraffic(ecs_iter_t *it) { 656 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 0); 657 | CityTrafficEmitter *emit = ecs_field(it, CityTrafficEmitter, 1); 658 | City *city = ecs_field(it, City, 2); 659 | 660 | ecs_world_t *world = it->world; 661 | 662 | for (int i = 0; i < it->count; i ++) { 663 | emit[i].elapsed += it->delta_time; 664 | 665 | if (emit[i].elapsed > (1.0f / emit[i].frequency)) { 666 | if (randf(1.0) > (1.0 - city->traffic.chance)) { 667 | ecs_entity_t e = ecs_new_w_pair( 668 | world, EcsChildOf, CarsRoot); 669 | ecs_set_ptr(world, e, EcsVelocity3, &emit[i].initial_velocity); 670 | ecs_set_ptr(world, e, EcsRotation3, &emit[i].initial_rotation); 671 | ecs_set_ptr(world, e, CityBound, &emit[i].bound); 672 | ecs_set_ptr(world, e, EcsPosition3, &p[i]); 673 | ecs_add_pair(world, e, EcsIsA, CityCar); 674 | } 675 | emit[i].elapsed = 0; 676 | } 677 | } 678 | } 679 | 680 | static 681 | void ExpireTraffic(ecs_iter_t *it) { 682 | EcsPosition3 *p = ecs_field(it, EcsPosition3, 0); 683 | CityBound *b = ecs_field(it, CityBound, 1); 684 | 685 | ecs_world_t *world = it->world; 686 | 687 | for (int i = 0; i < it->count; i ++) { 688 | ecs_entity_t e = it->entities[i]; 689 | ecs_entity_t to_delete = 0; 690 | 691 | if(p[i].x < b[i].left) { 692 | to_delete = e; 693 | } else if(p[i].x > b[i].right) { 694 | to_delete = e; 695 | } else if(p[i].z < b[i].top) { 696 | to_delete = e; 697 | } else if(p[i].z > b[i].bottom) { 698 | to_delete = e; 699 | } 700 | if(to_delete){ 701 | ecs_delete(world, to_delete); 702 | } 703 | } 704 | } 705 | 706 | void FlecsCityImport( 707 | ecs_world_t *world) 708 | { 709 | ECS_MODULE(world, FlecsCity); 710 | 711 | ECS_IMPORT(world, FlecsComponentsTransform); 712 | ECS_IMPORT(world, FlecsComponentsGraphics); 713 | ECS_IMPORT(world, FlecsComponentsGeometry); 714 | 715 | ecs_set_name_prefix(world, "City"); 716 | 717 | ecs_set_name_prefix(world, NULL); 718 | 719 | ECS_COMPONENT_DEFINE(world, CityTrafficEmitter); 720 | ECS_COMPONENT_DEFINE(world, CityBound); 721 | ECS_META_COMPONENT(world, CityBuildings); 722 | ECS_META_COMPONENT(world, CityParks); 723 | ECS_META_COMPONENT(world, CityProps); 724 | ECS_META_COMPONENT(world, CityTraffic); 725 | ECS_META_COMPONENT(world, City); 726 | 727 | ecs_set_hooks(world, City, { 728 | .on_set = SetCity 729 | }); 730 | 731 | ECS_SYSTEM(world, GenerateTraffic, EcsOnUpdate, 732 | [in] flecs.components.transform.Position3, 733 | [inout] CityTrafficEmitter, 734 | [in] City(up)); 735 | 736 | ECS_SYSTEM(world, ExpireTraffic, EcsOnUpdate, 737 | [in] flecs.components.transform.Position3, 738 | [in] CityBound); 739 | 740 | /* Load module assets */ 741 | if (ecs_script_run_file(world, "etc/assets/city.flecs") == 0) { 742 | CityStreet = ecs_lookup(world, "Street"); 743 | CityPavement = ecs_lookup(world, "Pavement"); 744 | CityAc = ecs_lookup(world, "Ac"); 745 | CityTree = ecs_lookup(world, "Tree"); 746 | CitySidewalkTree = ecs_lookup(world, "SidewalkTree"); 747 | CityBin = ecs_lookup(world, "Bin"); 748 | CityHydrant = ecs_lookup(world, "Hydrant"); 749 | CityBench = ecs_lookup(world, "Bench"); 750 | CityLantern = ecs_lookup(world, "Lantern"); 751 | CityPark = ecs_lookup(world, "Park"); 752 | CityPlaza = ecs_lookup(world, "Plaza"); 753 | CityBuilding = ecs_lookup(world, "Building"); 754 | CityModernBuilding = ecs_lookup(world, "ModernBuilding"); 755 | CityCar = ecs_lookup(world, "Car"); 756 | } else { 757 | ecs_err("failed to load city assets"); 758 | } 759 | 760 | CarsRoot = ecs_entity(world, { 761 | .name = "#0.cars" 762 | }); 763 | } 764 | --------------------------------------------------------------------------------