diff --git a/CMakeLists.txt b/CMakeLists.txt index 1405aa0..b80be9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,6 @@ find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG) find_package(OpenAL REQUIRED CONFIG) find_library(physfs REQUIRED NAMES physfs-static PATHS "${CMAKE_PREFIX_PATH}/lib") - # Determine dependencies set(STATIC_LIBS dr_wav diff --git a/src/game/bootloader.cpp b/src/game/bootloader.cpp index 73561c5..2c4b14f 100644 --- a/src/game/bootloader.cpp +++ b/src/game/bootloader.cpp @@ -43,6 +43,7 @@ #include "renderer/passes/clear-pass.hpp" #include "renderer/passes/final-pass.hpp" #include "renderer/passes/material-pass.hpp" +#include "renderer/passes/outline-pass.hpp" #include "renderer/passes/shadow-map-pass.hpp" #include "renderer/passes/sky-pass.hpp" #include "renderer/simple-render-pass.hpp" @@ -439,13 +440,14 @@ void setup_rendering(game_context* ctx) ctx->framebuffer_hdr_color->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); ctx->framebuffer_hdr_color->set_filters(texture_min_filter::linear, texture_mag_filter::linear); ctx->framebuffer_hdr_color->set_max_anisotropy(0.0f); - ctx->framebuffer_hdr_depth = new texture_2d(viewport_dimensions[0], viewport_dimensions[1], pixel_type::float_32, pixel_format::d); + ctx->framebuffer_hdr_depth = new texture_2d(viewport_dimensions[0], viewport_dimensions[1], pixel_type::uint_32, pixel_format::ds); ctx->framebuffer_hdr_depth->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); ctx->framebuffer_hdr_depth->set_filters(texture_min_filter::linear, texture_mag_filter::linear); ctx->framebuffer_hdr_depth->set_max_anisotropy(0.0f); ctx->framebuffer_hdr = new framebuffer(viewport_dimensions[0], viewport_dimensions[1]); ctx->framebuffer_hdr->attach(framebuffer_attachment_type::color, ctx->framebuffer_hdr_color); ctx->framebuffer_hdr->attach(framebuffer_attachment_type::depth, ctx->framebuffer_hdr_depth); + ctx->framebuffer_hdr->attach(framebuffer_attachment_type::stencil, ctx->framebuffer_hdr_depth); // Create shadow map framebuffer int shadow_map_resolution = ctx->config->get("shadow_map_resolution"); @@ -475,13 +477,16 @@ void setup_rendering(game_context* ctx) ctx->overworld_shadow_map_pass = new shadow_map_pass(ctx->rasterizer, ctx->shadow_map_framebuffer, ctx->resource_manager); ctx->overworld_shadow_map_pass->set_split_scheme_weight(0.75f); ctx->overworld_clear_pass = new clear_pass(ctx->rasterizer, ctx->framebuffer_hdr); - ctx->overworld_clear_pass->set_cleared_buffers(true, true, false); + ctx->overworld_clear_pass->set_cleared_buffers(false, true, true); ctx->overworld_sky_pass = new sky_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager); ctx->overworld_sky_pass->set_enabled(false); ctx->overworld_material_pass = new material_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager); ctx->overworld_material_pass->set_fallback_material(ctx->fallback_material); ctx->overworld_material_pass->shadow_map_pass = ctx->overworld_shadow_map_pass; ctx->overworld_material_pass->shadow_map = ctx->shadow_map_depth_texture; + ctx->overworld_outline_pass = new outline_pass(ctx->rasterizer, ctx->framebuffer_hdr, ctx->resource_manager); + ctx->overworld_outline_pass->set_outline_width(0.25f); + ctx->overworld_outline_pass->set_outline_color(float4{1.0f, 1.0f, 1.0f, 1.0f}); ctx->overworld_bloom_pass = new bloom_pass(ctx->rasterizer, ctx->framebuffer_bloom, ctx->resource_manager); ctx->overworld_bloom_pass->set_source_texture(ctx->framebuffer_hdr_color); ctx->overworld_bloom_pass->set_brightness_threshold(1.0f); @@ -496,6 +501,7 @@ void setup_rendering(game_context* ctx) ctx->overworld_compositor->add_pass(ctx->overworld_clear_pass); ctx->overworld_compositor->add_pass(ctx->overworld_sky_pass); ctx->overworld_compositor->add_pass(ctx->overworld_material_pass); + ctx->overworld_compositor->add_pass(ctx->overworld_outline_pass); ctx->overworld_compositor->add_pass(ctx->overworld_bloom_pass); ctx->overworld_compositor->add_pass(ctx->overworld_final_pass); diff --git a/src/game/components/tool-component.hpp b/src/game/components/tool-component.hpp index 5484be5..7404716 100644 --- a/src/game/components/tool-component.hpp +++ b/src/game/components/tool-component.hpp @@ -25,6 +25,8 @@ namespace ecs { struct tool_component { bool active; + float hover_distance; + bool heliotropic; }; } // namespace ecs diff --git a/src/game/entity-commands.cpp b/src/game/entity-commands.cpp index 1448dac..4d19056 100644 --- a/src/game/entity-commands.cpp +++ b/src/game/entity-commands.cpp @@ -21,6 +21,8 @@ #include "game/components/model-component.hpp" #include "game/components/transform-component.hpp" #include "game/components/copy-transform-component.hpp" +#include "game/components/snap-component.hpp" +#include namespace ec { @@ -63,6 +65,17 @@ void set_transform(entt::registry& registry, entt::entity eid, const math::trans } } +void place(entt::registry& registry, entt::entity eid, const float2& translation) +{ + snap_component component; + component.warp = true; + component.relative = false; + component.autoremove = true; + component.ray.origin = {translation[0], 10000.0f, translation[1]}; + component.ray.direction = {0.0f, -1.0f, 0.0f}; + registry.assign_or_replace(eid, component); +} + void assign_render_layers(entt::registry& registry, entt::entity eid, unsigned int layers) { if (registry.has(eid)) diff --git a/src/game/entity-commands.hpp b/src/game/entity-commands.hpp index 102fd95..fd5251f 100644 --- a/src/game/entity-commands.hpp +++ b/src/game/entity-commands.hpp @@ -30,6 +30,7 @@ void translate(entt::registry& registry, entt::entity eid, const float3& transla void move_to(entt::registry& registry, entt::entity eid, const float3& position); void warp_to(entt::registry& registry, entt::entity eid, const float3& position); void set_transform(entt::registry& registry, entt::entity eid, const math::transform& transform, bool warp = false); +void place(entt::registry& registry, entt::entity eid, const float2& translation); void assign_render_layers(entt::registry& registry, entt::entity eid, unsigned int layers); void bind_transform(entt::registry& registry, entt::entity source_eid, entt::entity target_eid); diff --git a/src/game/game-context.hpp b/src/game/game-context.hpp index 71ff31c..5421064 100644 --- a/src/game/game-context.hpp +++ b/src/game/game-context.hpp @@ -81,6 +81,7 @@ class model_instance; class input_event_router; class input_mapper; class cli; +class outline_pass; template class animation; template class material_property; template class tween; @@ -152,6 +153,7 @@ struct game_context material_pass* overworld_material_pass; material_pass* ui_material_pass; material_pass* underworld_material_pass; + outline_pass* overworld_outline_pass; shadow_map_pass* overworld_shadow_map_pass; simple_render_pass* underworld_final_pass; sky_pass* overworld_sky_pass; diff --git a/src/game/states/play-state.cpp b/src/game/states/play-state.cpp index 9e3502c..10391a2 100644 --- a/src/game/states/play-state.cpp +++ b/src/game/states/play-state.cpp @@ -62,6 +62,8 @@ void play_state_enter(game_context* ctx) sky_pass->set_sun_color({2.0f, 2.0f, 2.0f}); sky_pass->set_horizon_color(to_linear(float3{81.0f, 162.0f, 219.0f} / 255.0f)); sky_pass->set_zenith_color(to_linear(float3{7.0f, 134.0f, 206.0f} / 255.0f)); + //sky_pass->set_horizon_color(float3{0.002f, 0.158f, 0.250f}); + //sky_pass->set_zenith_color(float3{0.002f, 0.158f, 0.250f}); resource_manager* resource_manager = ctx->resource_manager; entt::registry& ecs_registry = *ctx->ecs_registry; @@ -71,36 +73,44 @@ void play_state_enter(game_context* ctx) ecs::archetype* maple_tree_archetype = resource_manager->load("maple-tree.ent"); ecs::archetype* nest_archetype = resource_manager->load("harvester-nest.ent"); ecs::archetype* samara_archetype = resource_manager->load("samara.ent"); - ecs::archetype* forceps_archetype = resource_manager->load("lens.ent"); + ecs::archetype* forceps_archetype = resource_manager->load("forceps.ent"); + ecs::archetype* lens_archetype = resource_manager->load("lens.ent"); + ecs::archetype* brush_archetype = resource_manager->load("brush.ent"); ecs::archetype* larva_archetype = resource_manager->load("larva.ent"); ecs::archetype* pebble_archetype = resource_manager->load("pebble.ent"); - ecs::archetype* flashlight_archetype = resource_manager->load("flashlight.ent"); ecs::archetype* flashlight_light_cone_archetype = resource_manager->load("flashlight-light-cone.ent"); - - // Create flashlight + light cone compund entity + + // Create tools + forceps_archetype->assign(ecs_registry, ctx->forceps_entity); + lens_archetype->assign(ecs_registry, ctx->lens_entity); + brush_archetype->assign(ecs_registry, ctx->brush_entity); + + // Create flashlight and light cone, bind light cone to flashlight, and move both to underworld scene flashlight_archetype->assign(ecs_registry, ctx->flashlight_entity); auto flashlight_light_cone = flashlight_light_cone_archetype->create(ecs_registry); ec::bind_transform(ecs_registry, flashlight_light_cone, ctx->flashlight_entity); ec::assign_render_layers(ecs_registry, ctx->flashlight_entity, 2); ec::assign_render_layers(ecs_registry, flashlight_light_cone, 2); - - - ecs::snap_component snap; - snap.warp = true; - snap.relative = false; - snap.autoremove = true; - - auto ant_hill_entity = ant_hill_archetype->create(ecs_registry); - snap.ray.origin = {0, 10000, 0}; - snap.ray.direction = {0, -1, 0}; - ecs_registry.assign(ant_hill_entity, snap); + // Make lens tool's model instance unculled, so its shadow is always visible. + model_instance* lens_model_instance = ctx->render_system->get_model_instance(ctx->lens_entity); + if (lens_model_instance) + { + lens_model_instance->set_culling_mask(&ctx->no_cull); + } + + // Activate brush tools + auto& active_tool_component = ecs_registry.get(ctx->lens_entity); + active_tool_component.active = true; + // Create ant-hill + auto ant_hill_entity = ant_hill_archetype->create(ecs_registry); + ec::place(ecs_registry, ant_hill_entity, {0, 0}); + // Generate pebbles float pebble_radius = 300.0f; int pebble_count = 100; - for (int i = 0; i < pebble_count; ++i) { float x = math::random(-pebble_radius, pebble_radius); @@ -113,17 +123,17 @@ void play_state_enter(game_context* ctx) transform.transform.rotation = math::angle_axis(math::random(0.0f, math::two_pi), {0, 1, 0}); transform.transform.scale = float3{1, 1, 1} * math::random(0.75f, 1.25f); - snap.ray.origin = {x, 10000, z}; - ecs_registry.assign(pebble_entity, snap); + ec::place(ecs_registry, pebble_entity, {x, z}); } + // Create maple tree auto maple_tree_entity = maple_tree_archetype->create(ecs_registry); - snap.ray.origin = {300, 10000, 200}; - snap.ray.direction = {0, -1, 0}; - ecs_registry.assign(maple_tree_entity, snap); + ec::place(ecs_registry, maple_tree_entity, {300, 200}); + // Creat nest auto nest_entity = nest_archetype->create(ecs_registry); + // Create terrain int terrain_radius = 2; for (int x = -terrain_radius; x <= terrain_radius; ++x) { @@ -138,6 +148,7 @@ void play_state_enter(game_context* ctx) } } + // Create samaras for (int i = 0; i < 15; ++i) { auto samara_entity = samara_archetype->create(ecs_registry); @@ -156,13 +167,6 @@ void play_state_enter(game_context* ctx) ecs_registry.assign_or_replace(samara_entity, samara_component); } - - /* - ecs::archetype* grass_archetype = resource_manager->load("grassland-grass.ent"); - auto grass_entity_1 = grass_archetype->create(ecs_registry); - auto grass_entity_2 = grass_archetype->create(ecs_registry); - ecs_registry.get(grass_entity_2).transform.rotation = math::angle_axis(math::radians(120.0f), float3{0, 1, 0}); - */ // Setup camera focal point ecs::transform_component focal_point_transform; @@ -174,7 +178,6 @@ void play_state_enter(game_context* ctx) focal_point_snap.warp = false; focal_point_snap.relative = true; focal_point_snap.autoremove = false; - ecs_registry.assign_or_replace(ctx->focal_point_entity, focal_point_transform); ecs_registry.assign_or_replace(ctx->focal_point_entity, focal_point_follow); ecs_registry.assign_or_replace(ctx->focal_point_entity, focal_point_snap); @@ -185,17 +188,6 @@ void play_state_enter(game_context* ctx) - // Create forceps tool - auto forceps_entity = forceps_archetype->create(ecs_registry); - ecs::tool_component forceps_tool_component; - forceps_tool_component.active = true; - ecs_registry.assign(forceps_entity, forceps_tool_component); - model_instance* forceps_model_instance = ctx->render_system->get_model_instance(forceps_entity); - if (forceps_model_instance) - { - forceps_model_instance->set_culling_mask(&ctx->no_cull); - } - ctx->overworld_scene->update_tweens(); // Allocate a nest diff --git a/src/game/systems/tool-system.cpp b/src/game/systems/tool-system.cpp index 9dba5e0..132df10 100644 --- a/src/game/systems/tool-system.cpp +++ b/src/game/systems/tool-system.cpp @@ -102,16 +102,22 @@ void tool_system::update(double t, double dt) registry.view().each( [&](auto entity, auto& tool, auto& transform) { + /* + if (registry.has(entity)) + { + + }*/ + if (!tool.active) return; if (intersection) { - transform.transform.translation = pick + float3{0, 15, 0}; + transform.transform.translation = pick + float3{0, tool.hover_distance, 0}; } - math::quaternion rotation = math::angle_axis(orbit_cam->get_azimuth() + pick_angle, float3{0, 1, 0}); - transform.transform.rotation = rotation; + //math::quaternion rotation = math::angle_axis(orbit_cam->get_azimuth() + pick_angle, float3{0, 1, 0}); + //transform.transform.rotation = rotation; }); was_pick_enabled = pick_enabled; diff --git a/src/renderer/passes/bloom-pass.cpp b/src/renderer/passes/bloom-pass.cpp index 6005e2b..99170d8 100644 --- a/src/renderer/passes/bloom-pass.cpp +++ b/src/renderer/passes/bloom-pass.cpp @@ -105,9 +105,7 @@ bloom_pass::~bloom_pass() } void bloom_pass::render(render_context* context) const -{ - rasterizer->use_framebuffer(*framebuffer); - +{ glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); diff --git a/src/renderer/passes/clear-pass.cpp b/src/renderer/passes/clear-pass.cpp index ce7411e..c145854 100644 --- a/src/renderer/passes/clear-pass.cpp +++ b/src/renderer/passes/clear-pass.cpp @@ -42,7 +42,7 @@ void clear_pass::render(render_context* context) const if (clear_depth_buffer) glDepthMask(GL_TRUE); if (clear_stencil_buffer) - glStencilMask(GL_TRUE); + glStencilMask(0xFF); rasterizer->use_framebuffer(*framebuffer); diff --git a/src/renderer/passes/material-pass.cpp b/src/renderer/passes/material-pass.cpp index 1926b62..a3196bb 100644 --- a/src/renderer/passes/material-pass.cpp +++ b/src/renderer/passes/material-pass.cpp @@ -109,6 +109,8 @@ void material_pass::render(render_context* context) const glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); + glDisable(GL_STENCIL_TEST); + glStencilMask(0x00); auto viewport = framebuffer->get_dimensions(); rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); @@ -324,6 +326,22 @@ void material_pass::render(render_context* context) const glEnable(GL_DEPTH_TEST); } } + + if ((material_flags & MATERIAL_FLAG_OUTLINE) != (active_material_flags & MATERIAL_FLAG_OUTLINE)) + { + if (material_flags & MATERIAL_FLAG_OUTLINE) + { + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilMask(0xFF); + } + else + { + glDisable(GL_STENCIL_TEST); + glStencilMask(0x00); + } + } active_material_flags = material_flags; diff --git a/src/renderer/passes/outline-pass.cpp b/src/renderer/passes/outline-pass.cpp new file mode 100644 index 0000000..f7bf10b --- /dev/null +++ b/src/renderer/passes/outline-pass.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "renderer/passes/outline-pass.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/render-context.hpp" +#include "renderer/material.hpp" +#include "renderer/material-flags.hpp" +#include "scene/camera.hpp" +#include "math/math.hpp" +#include +#include + +outline_pass::outline_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer), + outline_shader(nullptr) +{ + // Load outline shader + outline_shader = resource_manager->load("outline-unskinned.glsl"); + model_view_projection_input = outline_shader->get_input("model_view_projection"); + outline_width_input = outline_shader->get_input("outline_width"); + outline_color_input = outline_shader->get_input("outline_color"); +} + +outline_pass::~outline_pass() +{} + +void outline_pass::render(render_context* context) const +{ + rasterizer->use_framebuffer(*framebuffer); + + if (outline_color.w < 1.0f) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + { + glDisable(GL_BLEND); + } + + glDisable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_NOTEQUAL, 1, 0xFF); + glStencilMask(0x00); + + // Determine viewport based on framebuffer resolution + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + float4x4 view = context->camera->get_view_tween().interpolate(context->alpha); + float4x4 view_projection = context->camera->get_view_projection_tween().interpolate(context->alpha); + float4x4 model_view_projection; + + // Perform iterative blur subpass + rasterizer->use_program(*outline_shader); + + outline_width_input->upload(outline_width); + outline_color_input->upload(outline_color); + + // Render outlines + for (const render_operation& operation: context->operations) + { + const ::material* material = operation.material; + if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) + continue; + + model_view_projection = view_projection * operation.transform; + model_view_projection_input->upload(model_view_projection); + + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } + + glDisable(GL_STENCIL_TEST); +} + +void outline_pass::set_outline_width(float width) +{ + outline_width = width; +} + +void outline_pass::set_outline_color(const float4& color) +{ + outline_color = color; +} diff --git a/src/renderer/passes/outline-pass.hpp b/src/renderer/passes/outline-pass.hpp new file mode 100644 index 0000000..73db35f --- /dev/null +++ b/src/renderer/passes/outline-pass.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_OUTLINE_PASS_HPP +#define ANTKEEPER_OUTLINE_PASS_HPP + +#include "renderer/render-pass.hpp" +#include "utility/fundamental-types.hpp" + +class shader_program; +class shader_input; +class resource_manager; + +/** + * + */ +class outline_pass: public render_pass +{ +public: + outline_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~outline_pass(); + virtual void render(render_context* context) const final; + + void set_outline_width(float width); + void set_outline_color(const float4& color); + +private: + shader_program* outline_shader; + const shader_input* model_view_projection_input; + const shader_input* outline_width_input; + const shader_input* outline_color_input; + + float outline_width; + float4 outline_color; +}; + +#endif // ANTKEEPER_OUTLINE_PASS_HPP + diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp index 1237dd9..b2e50f2 100644 --- a/src/resources/entity-archetype-loader.cpp +++ b/src/resources/entity-archetype-loader.cpp @@ -27,6 +27,7 @@ #include "game/components/transform-component.hpp" #include "game/components/model-component.hpp" #include "game/components/nest-component.hpp" +#include "game/components/tool-component.hpp" #include "entity/archetype.hpp" #include "game/behavior/ebt.hpp" #include @@ -123,6 +124,22 @@ static bool load_terrain_component(archetype& archetype, const std::vector& parameters) +{ + if (parameters.size() != 4) + { + throw std::runtime_error("load_tool_component(): Invalid parameter count."); + } + + tool_component component; + component.active = static_cast(std::stoi(parameters[1])); + component.hover_distance = std::stof(parameters[2]); + component.heliotropic = static_cast(std::stoi(parameters[3])); + archetype.set(component); + + return true; +} + static bool load_transform_component(archetype& archetype, const std::vector& parameters) { if (parameters.size() != 11) @@ -163,6 +180,7 @@ static bool load_component(archetype& archetype, resource_manager& resource_mana if (parameters[0] == "model") return load_model_component(archetype, resource_manager, parameters); if (parameters[0] == "nest") return load_nest_component(archetype, parameters); if (parameters[0] == "terrain") return load_terrain_component(archetype, parameters); + if (parameters[0] == "tool") return load_tool_component(archetype, parameters); if (parameters[0] == "transform") return load_transform_component(archetype, parameters); std::string message = std::string("load_component(): Unknown component type \"") + parameters[0] + std::string("\"");