LumixEngine/src/graphics/render_scene.cpp
Mikulas Florek 751cbc19c3 refactor
2015-08-02 15:36:52 +02:00

2157 lines
No EOL
53 KiB
C++

#include "render_scene.h"
#include "core/array.h"
#include "core/blob.h"
#include "core/crc32.h"
#include "core/FS/file_system.h"
#include "core/FS/ifile.h"
#include "core/json_serializer.h"
#include "core/lifo_allocator.h"
#include "core/log.h"
#include "core/math_utils.h"
#include "core/mtjd/generic_job.h"
#include "core/mtjd/job.h"
#include "core/mtjd/manager.h"
#include "core/profiler.h"
#include "core/resource_manager.h"
#include "core/resource_manager_base.h"
#include "core/timer.h"
#include "core/sphere.h"
#include "core/frustum.h"
#include "engine/engine.h"
#include "graphics/culling_system.h"
#include "graphics/geometry.h"
#include "graphics/material.h"
#include "graphics/model.h"
#include "graphics/model_instance.h"
#include "graphics/pipeline.h"
#include "graphics/renderer.h"
#include "graphics/shader.h"
#include "graphics/terrain.h"
#include "graphics/texture.h"
#include "universe/universe.h"
namespace Lumix
{
static const uint32_t RENDERABLE_HASH = crc32("renderable");
static const uint32_t POINT_LIGHT_HASH = crc32("point_light");
static const uint32_t GLOBAL_LIGHT_HASH = crc32("global_light");
static const uint32_t CAMERA_HASH = crc32("camera");
static const uint32_t TERRAIN_HASH = crc32("terrain");
struct Renderable
{
Renderable(IAllocator& allocator)
: m_pose(allocator)
, m_meshes(allocator)
{
}
Array<RenderableMesh> m_meshes;
int32_t m_component_index;
Pose m_pose;
Model* m_model;
Matrix m_matrix;
Entity m_entity;
bool m_is_always_visible;
private:
Renderable(const Renderable&);
};
struct PointLight
{
Vec3 m_color;
Vec3 m_specular_color;
float m_intensity;
float m_range;
Entity m_entity;
int m_uid;
float m_fov;
};
struct GlobalLight
{
int m_uid;
Vec3 m_color;
float m_intensity;
Vec3 m_ambient_color;
float m_ambient_intensity;
Vec3 m_fog_color;
float m_fog_density;
Entity m_entity;
};
struct Camera
{
static const int MAX_SLOT_LENGTH = 30;
Entity m_entity;
float m_fov;
float m_aspect;
float m_near;
float m_far;
float m_width;
float m_height;
bool m_is_active;
bool m_is_free;
char m_slot[MAX_SLOT_LENGTH + 1];
};
class RenderSceneImpl : public RenderScene
{
private:
typedef HashMap<int32_t, int> DynamicRenderableCache;
class ModelLoadedCallback
{
public:
ModelLoadedCallback(RenderSceneImpl& scene, Model* model)
: m_scene(scene)
, m_ref_count(0)
, m_model(model)
{
m_model->onLoaded<ModelLoadedCallback,
&ModelLoadedCallback::callback>(this);
}
~ModelLoadedCallback()
{
m_model->getObserverCb()
.unbind<ModelLoadedCallback, &ModelLoadedCallback::callback>(
this);
}
void callback(Resource::State, Resource::State new_state)
{
if (new_state == Resource::State::READY)
{
m_scene.modelLoaded(m_model);
}
}
Model* m_model;
int m_ref_count;
RenderSceneImpl& m_scene;
};
public:
RenderSceneImpl(Renderer& renderer,
Engine& engine,
Universe& universe,
bool is_forward_rendered,
IAllocator& allocator)
: m_engine(engine)
, m_universe(universe)
, m_renderer(renderer)
, m_allocator(allocator)
, m_model_loaded_callbacks(m_allocator)
, m_dynamic_renderable_cache(m_allocator)
, m_renderables(m_allocator)
, m_cameras(m_allocator)
, m_terrains(m_allocator)
, m_point_lights(m_allocator)
, m_light_influenced_geometry(m_allocator)
, m_global_lights(m_allocator)
, m_debug_lines(m_allocator)
, m_always_visible(m_allocator)
, m_temporary_infos(m_allocator)
, m_sync_point(true, m_allocator)
, m_jobs(m_allocator)
, m_active_global_light_uid(-1)
, m_global_light_last_uid(-1)
, m_point_light_last_uid(-1)
, m_is_forward_rendered(is_forward_rendered)
, m_applied_camera(INVALID_COMPONENT)
, m_renderable_created(m_allocator)
, m_renderable_destroyed(m_allocator)
{
m_universe.entityMoved()
.bind<RenderSceneImpl, &RenderSceneImpl::onEntityMoved>(this);
m_culling_system =
CullingSystem::create(m_engine.getMTJDManager(), m_allocator);
m_time = 0;
}
~RenderSceneImpl()
{
m_universe.entityMoved()
.unbind<RenderSceneImpl, &RenderSceneImpl::onEntityMoved>(this);
for (int i = 0; i < m_model_loaded_callbacks.size(); ++i)
{
m_allocator.deleteObject(m_model_loaded_callbacks[i]);
}
for (int i = 0; i < m_terrains.size(); ++i)
{
m_allocator.deleteObject(m_terrains[i]);
}
for (int i = 0; i < m_renderables.size(); ++i)
{
if (m_renderables[i]->m_model)
{
m_renderables[i]
->m_model->getResourceManager()
.get(ResourceManager::MODEL)
->unload(*m_renderables[i]->m_model);
}
m_allocator.deleteObject(m_renderables[i]);
}
CullingSystem::destroy(*m_culling_system);
}
virtual Universe& getUniverse() override { return m_universe; }
virtual bool ownComponentType(uint32_t type) const override
{
return type == RENDERABLE_HASH || type == POINT_LIGHT_HASH ||
type == GLOBAL_LIGHT_HASH || type == CAMERA_HASH ||
type == TERRAIN_HASH;
}
virtual IPlugin& getPlugin() const override { return m_renderer; }
virtual void getRay(ComponentIndex camera,
float x,
float y,
Vec3& origin,
Vec3& dir) override
{
Vec3 camera_pos = m_universe.getPosition(m_cameras[camera].m_entity);
float width = m_cameras[camera].m_width;
float height = m_cameras[camera].m_height;
float nx = 2 * (x / width) - 1;
float ny = 2 * ((height - y) / height) - 1;
float fov = m_cameras[camera].m_fov;
float near_plane = m_cameras[camera].m_near;
float far_plane = m_cameras[camera].m_far;
Matrix projection_matrix;
projection_matrix.setPerspective(
Math::degreesToRadians(fov), width, height, near_plane, far_plane);
Matrix view_matrix = m_universe.getMatrix(m_cameras[camera].m_entity);
view_matrix.inverse();
Matrix inverted = (projection_matrix * view_matrix);
inverted.inverse();
Vec4 p0 = inverted * Vec4(nx, ny, -1, 1);
Vec4 p1 = inverted * Vec4(nx, ny, 1, 1);
p0.x /= p0.w;
p0.y /= p0.w;
p0.z /= p0.w;
p1.x /= p1.w;
p1.y /= p1.w;
p1.z /= p1.w;
origin = camera_pos;
dir.x = p1.x - p0.x;
dir.y = p1.y - p0.y;
dir.z = p1.z - p0.z;
dir.normalize();
}
virtual ComponentIndex getAppliedCamera() const override
{
return m_applied_camera;
}
virtual void applyCamera(ComponentIndex cmp) override
{
m_applied_camera = cmp;
Matrix mtx = getUniverse().getMatrix(m_cameras[cmp].m_entity);
m_camera_frustum.computePerspective(mtx.getTranslation(),
mtx.getZVector(),
mtx.getYVector(),
m_cameras[cmp].m_fov,
m_cameras[cmp].m_width /
m_cameras[cmp].m_height,
m_cameras[cmp].m_near,
m_cameras[cmp].m_far);
}
void update(float dt) override
{
PROFILE_FUNCTION();
m_time += dt;
for (int i = m_debug_lines.size() - 1; i >= 0; --i)
{
float life = m_debug_lines[i].m_life;
if (life < 0)
{
m_debug_lines.eraseFast(i);
}
else
{
life -= dt;
m_debug_lines[i].m_life = life;
}
}
}
void serializeCameras(OutputBlob& serializer)
{
serializer.write((int32_t)m_cameras.size());
for (int i = 0, c = m_cameras.size(); i < c; ++i)
{
Camera& camera = m_cameras[i];
serializer.write(camera.m_entity);
serializer.write(camera.m_far);
serializer.write(camera.m_fov);
serializer.write(camera.m_is_active);
serializer.write(camera.m_is_free);
serializer.write(camera.m_near);
serializer.writeString(camera.m_slot);
}
}
void serializeLights(OutputBlob& serializer)
{
serializer.write((int32_t)m_point_lights.size());
for (int i = 0, c = m_point_lights.size(); i < c; ++i)
{
PointLight& point_light = m_point_lights[i];
serializer.write(point_light.m_uid);
serializer.write(point_light.m_color);
serializer.write(point_light.m_intensity);
serializer.write(point_light.m_entity);
serializer.write(point_light.m_range);
serializer.write(point_light.m_fov);
serializer.write(point_light.m_specular_color);
}
serializer.write(m_point_light_last_uid);
serializer.write((int32_t)m_global_lights.size());
for (int i = 0, c = m_global_lights.size(); i < c; ++i)
{
GlobalLight& global_light = m_global_lights[i];
serializer.write(global_light.m_uid);
serializer.write(global_light.m_color);
serializer.write(global_light.m_intensity);
serializer.write(global_light.m_entity);
serializer.write(global_light.m_ambient_color);
serializer.write(global_light.m_ambient_intensity);
serializer.write(global_light.m_fog_color);
serializer.write(global_light.m_fog_density);
}
serializer.write((int32_t)m_global_light_last_uid);
serializer.write((int32_t)m_active_global_light_uid);
}
void serializeRenderables(OutputBlob& serializer)
{
serializer.write((int32_t)m_renderables.size());
for (int i = 0; i < m_renderables.size(); ++i)
{
serializer.write(m_renderables[i]->m_is_always_visible);
serializer.write(m_renderables[i]->m_component_index);
serializer.write(m_renderables[i]->m_entity);
serializer.write(m_culling_system->getLayerMask(i));
serializer.write(m_renderables[i]->m_model->getPath().getHash());
}
}
void serializeTerrains(OutputBlob& serializer)
{
serializer.write((int32_t)m_terrains.size());
for (int i = 0; i < m_terrains.size(); ++i)
{
if (m_terrains[i])
{
serializer.write(true);
m_terrains[i]->serialize(serializer);
}
else
{
serializer.write(false);
}
}
}
virtual void serialize(OutputBlob& serializer) override
{
serializeCameras(serializer);
serializeRenderables(serializer);
serializeLights(serializer);
serializeTerrains(serializer);
}
void deserializeCameras(InputBlob& serializer)
{
int32_t size;
serializer.read(size);
m_cameras.resize(size);
for (int i = 0; i < size; ++i)
{
Camera& camera = m_cameras[i];
serializer.read(camera.m_entity);
serializer.read(camera.m_far);
serializer.read(camera.m_fov);
serializer.read(camera.m_is_active);
serializer.read(camera.m_is_free);
serializer.read(camera.m_near);
serializer.readString(camera.m_slot, sizeof(camera.m_slot));
if (!camera.m_is_free)
{
m_universe.addComponent(
m_cameras[i].m_entity, CAMERA_HASH, this, i);
}
}
}
void deserializeRenderables(InputBlob& serializer)
{
int32_t size = 0;
serializer.read(size);
for (int i = size; i < m_renderables.size(); ++i)
{
setModel(i, nullptr);
m_allocator.deleteObject(m_renderables[i]);
}
m_culling_system->clear();
m_renderables.clear();
m_renderables.reserve(size);
m_dynamic_renderable_cache = DynamicRenderableCache(m_allocator);
m_always_visible.clear();
for (int i = 0; i < size; ++i)
{
m_renderables.push(m_allocator.newObject<Renderable>(m_allocator));
serializer.read(m_renderables[i]->m_is_always_visible);
serializer.read(m_renderables[i]->m_component_index);
if (m_renderables[i]->m_is_always_visible)
{
m_always_visible.push(m_renderables[i]->m_component_index);
}
serializer.read(m_renderables[i]->m_entity);
int64_t layer_mask;
serializer.read(layer_mask);
m_renderables[i]->m_model = nullptr;
m_renderables[i]->m_matrix =
m_universe.getMatrix(m_renderables[i]->m_entity);
uint32_t path;
serializer.read(path);
m_culling_system->addStatic(Sphere(
m_universe.getPosition(m_renderables[i]->m_entity), 1.0f));
m_culling_system->setLayerMask(i, layer_mask);
setModel(i,
static_cast<Model*>(m_engine.getResourceManager()
.get(ResourceManager::MODEL)
->load(Path(path))));
m_universe.addComponent(
m_renderables[i]->m_entity, RENDERABLE_HASH, this, i);
}
}
void deserializeLights(InputBlob& serializer)
{
int32_t size = 0;
serializer.read(size);
m_point_lights.resize(size);
m_light_influenced_geometry.clear();
for (int i = 0; i < size; ++i)
{
m_light_influenced_geometry.push(Array<int>(m_allocator));
PointLight& light = m_point_lights[i];
serializer.read(light.m_uid);
serializer.read(light.m_color);
serializer.read(light.m_intensity);
serializer.read(light.m_entity);
serializer.read(light.m_range);
serializer.read(light.m_fov);
serializer.read(light.m_specular_color);
m_universe.addComponent(
light.m_entity, POINT_LIGHT_HASH, this, light.m_uid);
}
serializer.read(m_point_light_last_uid);
serializer.read(size);
m_global_lights.resize(size);
for (int i = 0; i < size; ++i)
{
GlobalLight& light = m_global_lights[i];
serializer.read(light.m_uid);
serializer.read(light.m_color);
serializer.read(light.m_intensity);
serializer.read(light.m_entity);
serializer.read(light.m_ambient_color);
serializer.read(light.m_ambient_intensity);
serializer.read(light.m_fog_color);
serializer.read(light.m_fog_density);
m_universe.addComponent(
light.m_entity, GLOBAL_LIGHT_HASH, this, light.m_uid);
}
serializer.read(m_global_light_last_uid);
serializer.read(m_active_global_light_uid);
}
void deserializeTerrains(InputBlob& serializer)
{
int32_t size = 0;
serializer.read(size);
for (int i = size; i < m_terrains.size(); ++i)
{
m_allocator.deleteObject(m_terrains[i]);
m_terrains[i] = nullptr;
}
m_terrains.resize(size);
for (int i = 0; i < size; ++i)
{
bool exists;
serializer.read(exists);
if (exists)
{
m_terrains[i] = m_allocator.newObject<Terrain>(
m_renderer, INVALID_ENTITY, *this, m_allocator);
Terrain* terrain = m_terrains[i];
terrain->deserialize(serializer, m_universe, *this, i);
}
else
{
m_terrains[i] = nullptr;
}
}
}
virtual void deserialize(InputBlob& serializer) override
{
deserializeCameras(serializer);
deserializeRenderables(serializer);
deserializeLights(serializer);
deserializeTerrains(serializer);
}
void destroyRenderable(ComponentIndex component)
{
m_renderable_destroyed.invoke(component);
int renderable_index = getRenderable(component);
Entity entity = m_renderables[renderable_index]->m_entity;
for (int i = 0; i < m_light_influenced_geometry.size(); ++i)
{
Array<int>& influenced_geometry = m_light_influenced_geometry[i];
for (int j = 0; j < influenced_geometry.size(); ++j)
{
if (influenced_geometry[j] == renderable_index)
{
influenced_geometry.erase(j);
--j;
}
else if (influenced_geometry[j] > renderable_index)
{
--influenced_geometry[j];
}
}
}
setModel(renderable_index, nullptr);
m_always_visible.eraseItemFast(component);
m_allocator.deleteObject(m_renderables[renderable_index]);
m_renderables.erase(renderable_index);
m_culling_system->removeStatic(renderable_index);
m_universe.destroyComponent(entity, RENDERABLE_HASH, this, component);
Lumix::HashMap<int32_t, int>::iterator
iter = m_dynamic_renderable_cache.begin(),
end = m_dynamic_renderable_cache.end();
while (iter != end)
{
if (iter.value() > renderable_index)
{
--iter.value();
}
++iter;
}
m_dynamic_renderable_cache.erase(entity);
}
virtual void destroyComponent(ComponentIndex component,
uint32_t type) override
{
if (type == RENDERABLE_HASH)
{
destroyRenderable(component);
}
else if (type == GLOBAL_LIGHT_HASH)
{
Entity entity =
m_global_lights[getGlobalLightIndex(component)].m_entity;
m_universe.destroyComponent(entity, type, this, component);
if (component == m_active_global_light_uid)
{
m_active_global_light_uid = -1;
}
m_global_lights.eraseFast(getGlobalLightIndex(component));
}
else if (type == POINT_LIGHT_HASH)
{
int index = getPointLightIndex(component);
Entity entity =
m_point_lights[getPointLightIndex(component)].m_entity;
m_point_lights.eraseFast(index);
m_light_influenced_geometry.eraseFast(index);
m_universe.destroyComponent(entity, type, this, component);
}
else if (type == CAMERA_HASH)
{
Entity entity = m_cameras[component].m_entity;
m_cameras[component].m_is_free = true;
m_universe.destroyComponent(entity, type, this, component);
}
else if (type == TERRAIN_HASH)
{
Entity entity = m_terrains[component]->getEntity();
m_allocator.deleteObject(m_terrains[component]);
m_terrains[component] = nullptr;
m_universe.destroyComponent(entity, type, this, component);
}
else
{
ASSERT(false);
}
}
virtual ComponentIndex createComponent(uint32_t type,
Entity entity) override
{
if (type == TERRAIN_HASH)
{
Terrain* terrain = m_allocator.newObject<Terrain>(
m_renderer, entity, *this, m_allocator);
m_terrains.push(terrain);
m_universe.addComponent(entity, type, this, m_terrains.size() - 1);
return m_terrains.size() - 1;
}
else if (type == CAMERA_HASH)
{
Camera& camera = m_cameras.pushEmpty();
camera.m_is_free = false;
camera.m_is_active = false;
camera.m_entity = entity;
camera.m_fov = 60;
camera.m_width = 800;
camera.m_height = 600;
camera.m_aspect = 800.0f / 600.0f;
camera.m_near = 0.1f;
camera.m_far = 10000.0f;
camera.m_slot[0] = '\0';
m_universe.addComponent(entity, type, this, m_cameras.size() - 1);
return m_cameras.size() - 1;
}
else if (type == RENDERABLE_HASH)
{
return createRenderable(entity);
}
else if (type == GLOBAL_LIGHT_HASH)
{
return createGlobalLight(entity);
}
else if (type == POINT_LIGHT_HASH)
{
return createPointLight(entity);
}
return INVALID_COMPONENT;
}
virtual ComponentIndex getRenderableComponent(Entity entity) override
{
DynamicRenderableCache::iterator iter =
m_dynamic_renderable_cache.find(entity);
if (!iter.isValid())
{
for (int i = 0, c = m_renderables.size(); i < c; ++i)
{
if (m_renderables[i]->m_entity == entity)
{
m_dynamic_renderable_cache.insert(entity, i);
return i;
}
}
}
else
{
return iter.value();
}
return INVALID_COMPONENT;
}
void onEntityMoved(Entity entity)
{
DynamicRenderableCache::iterator iter =
m_dynamic_renderable_cache.find(entity);
int renderable_index = -1;
if (!iter.isValid())
{
for (int i = 0, c = m_renderables.size(); i < c; ++i)
{
if (m_renderables[i]->m_entity == entity)
{
m_dynamic_renderable_cache.insert(entity, i);
m_renderables[i]->m_matrix = m_universe.getMatrix(entity);
m_culling_system->updateBoundingPosition(
m_universe.getPosition(entity), i);
renderable_index = i;
break;
}
}
}
else
{
m_renderables[iter.value()]->m_matrix =
m_universe.getMatrix(entity);
renderable_index = iter.value();
m_culling_system->updateBoundingPosition(
m_universe.getPosition(entity), iter.value());
}
for (int i = 0, c = m_point_lights.size(); i < c; ++i)
{
Frustum frustum;
PointLight& light = m_point_lights[i];
frustum.computeOrtho(m_universe.getPosition(light.m_entity),
Vec3(1, 0, 0),
Vec3(0, 1, 0),
light.m_range,
light.m_range,
-light.m_range,
light.m_range);
if (renderable_index >= 0 && m_is_forward_rendered)
{
for (int j = 0; j < m_light_influenced_geometry[i].size(); ++j)
{
if (m_light_influenced_geometry[i][j] == renderable_index)
{
m_light_influenced_geometry[i].eraseFast(j);
break;
}
}
if (frustum.isSphereInside(
m_universe.getPosition(
m_renderables[renderable_index]->m_entity),
m_renderables[renderable_index]
->m_model->getBoundingRadius()))
{
m_light_influenced_geometry[i].push(renderable_index);
}
}
if (m_point_lights[i].m_entity == entity)
{
detectLightInfluencedGeometry(i);
break;
}
}
}
virtual Engine& getEngine() const override { return m_engine; }
virtual void setTerrainBrush(ComponentIndex cmp,
const Vec3& position,
float size) override
{
m_terrains[cmp]->setBrush(position, size);
}
virtual float
getTerrainHeightAt(ComponentIndex cmp, float x, float z) override
{
return m_terrains[cmp]->getHeight(x, z);
}
virtual void
getTerrainSize(ComponentIndex cmp, float* width, float* height) override
{
m_terrains[cmp]->getSize(width, height);
}
virtual void setTerrainMaterialPath(ComponentIndex cmp,
const char* path) override
{
Material* material =
static_cast<Material*>(m_engine.getResourceManager()
.get(ResourceManager::MATERIAL)
->load(Path(path)));
m_terrains[cmp]->setMaterial(material);
}
virtual Material* getTerrainMaterial(ComponentIndex cmp) override
{
return m_terrains[cmp]->getMaterial();
}
virtual const char* getTerrainMaterialPath(ComponentIndex cmp) override
{
if (m_terrains[cmp]->getMaterial())
{
return m_terrains[cmp]->getMaterial()->getPath().c_str();
}
else
{
return "";
}
}
virtual void setTerrainXZScale(ComponentIndex cmp, float scale) override
{
m_terrains[cmp]->setXZScale(scale);
}
virtual float getTerrainXZScale(ComponentIndex cmp) override
{
return m_terrains[cmp]->getXZScale();
}
virtual void setTerrainYScale(ComponentIndex cmp, float scale) override
{
m_terrains[cmp]->setYScale(scale);
}
virtual float getTerrainYScale(ComponentIndex cmp)
{
return m_terrains[cmp]->getYScale();
}
virtual Pose& getPose(ComponentIndex cmp) override
{
return m_renderables[getRenderable(cmp)]->m_pose;
}
virtual Entity getRenderableEntity(ComponentIndex cmp) override
{
return m_renderables[getRenderable(cmp)]->m_entity;
}
virtual Model* getRenderableModel(ComponentIndex cmp) override
{
return m_renderables[getRenderable(cmp)]->m_model;
}
virtual void showRenderable(ComponentIndex cmp) override
{
m_culling_system->enableStatic(getRenderable(cmp));
}
virtual void hideRenderable(ComponentIndex cmp) override
{
int renderable_index = getRenderable(cmp);
if (!m_renderables[renderable_index]->m_is_always_visible)
{
m_culling_system->disableStatic(renderable_index);
}
}
virtual void setRenderableIsAlwaysVisible(ComponentIndex cmp,
bool value) override
{
int renderable_index = getRenderable(cmp);
m_renderables[renderable_index]->m_is_always_visible = value;
if (value)
{
m_culling_system->disableStatic(renderable_index);
m_always_visible.push(cmp);
}
else
{
m_culling_system->enableStatic(renderable_index);
m_always_visible.eraseItemFast(cmp);
}
}
virtual bool isRenderableAlwaysVisible(ComponentIndex cmp) override
{
return m_renderables[getRenderable(cmp)]->m_is_always_visible;
}
virtual const char* getRenderablePath(ComponentIndex cmp) override
{
int index = getRenderable(cmp);
if (index >= 0 && m_renderables[index]->m_model)
{
return m_renderables[index]->m_model->getPath().c_str();
}
else
{
return "";
}
}
virtual void setRenderableLayer(ComponentIndex cmp,
const int32_t& layer) override
{
m_culling_system->setLayerMask(getRenderable(cmp),
(int64_t)1 << (int64_t)layer);
}
virtual void setRenderablePath(ComponentIndex cmp,
const char* path) override
{
int renderable_index = getRenderable(cmp);
Renderable& r = *m_renderables[renderable_index];
Model* model = static_cast<Model*>(m_engine.getResourceManager()
.get(ResourceManager::MODEL)
->load(Path(path)));
setModel(renderable_index, model);
r.m_matrix = m_universe.getMatrix(r.m_entity);
}
virtual void getTerrainInfos(Array<const TerrainInfo*>& infos,
int64_t layer_mask,
const Vec3& camera_pos,
LIFOAllocator& frame_allocator) override
{
PROFILE_FUNCTION();
infos.reserve(m_terrains.size());
for (int i = 0; i < m_terrains.size(); ++i)
{
if (m_terrains[i] &&
(m_terrains[i]->getLayerMask() & layer_mask) != 0)
{
m_terrains[i]->getInfos(infos, camera_pos, frame_allocator);
}
}
}
virtual void getGrassInfos(const Frustum& frustum,
Array<GrassInfo>& infos,
int64_t layer_mask) override
{
PROFILE_FUNCTION();
for (int i = 0; i < m_terrains.size(); ++i)
{
if (m_terrains[i] &&
(m_terrains[i]->getLayerMask() & layer_mask) != 0)
{
m_terrains[i]->getGrassInfos(
frustum, infos, getAppliedCamera());
}
}
}
virtual void
setGrassDensity(ComponentIndex cmp, int index, int density) override
{
m_terrains[cmp]->setGrassTypeDensity(index, density);
}
virtual int getGrassDensity(ComponentIndex cmp, int index) override
{
return m_terrains[cmp]->getGrassTypeDensity(index);
}
virtual void
setGrassGround(ComponentIndex cmp, int index, int ground) override
{
m_terrains[cmp]->setGrassTypeGround(index, ground);
}
virtual int getGrassGround(ComponentIndex cmp, int index) override
{
return m_terrains[cmp]->getGrassTypeGround(index);
}
virtual void
setGrassPath(ComponentIndex cmp, int index, const char* path) override
{
m_terrains[cmp]->setGrassTypePath(index, Path(path));
}
virtual const char* getGrassPath(ComponentIndex cmp, int index) override
{
return m_terrains[cmp]->getGrassTypePath(index).c_str();
}
virtual int getGrassCount(ComponentIndex cmp) override
{
return m_terrains[cmp]->getGrassTypeCount();
}
virtual void addGrass(ComponentIndex cmp, int index) override
{
m_terrains[cmp]->addGrassType(index);
}
virtual void removeGrass(ComponentIndex cmp, int index) override
{
m_terrains[cmp]->removeGrassType(index);
}
virtual Frustum& getFrustum() override { return m_camera_frustum; }
virtual ComponentIndex getFirstRenderable() override
{
if (m_renderables.empty())
{
return INVALID_COMPONENT;
}
return m_renderables[0]->m_component_index;
}
int getRenderable(int index)
{
int l = 0;
int h = m_renderables.size() - 1;
while (l <= h)
{
int m = (l + h) >> 1;
if (m_renderables[m]->m_component_index < index)
{
l = m + 1;
}
else if (m_renderables[m]->m_component_index > index)
{
h = m - 1;
}
else
{
return m;
}
}
return -1;
}
virtual ComponentIndex getNextRenderable(ComponentIndex cmp) override
{
int i = getRenderable(cmp);
if (i + 1 < m_renderables.size())
{
return m_renderables[i + 1]->m_component_index;
}
return INVALID_COMPONENT;
}
const CullingSystem::Results* cull(const Frustum& frustum,
int64_t layer_mask)
{
PROFILE_FUNCTION();
if (m_renderables.empty())
return nullptr;
m_culling_system->cullToFrustumAsync(frustum, layer_mask);
return &m_culling_system->getResult();
}
void mergeTemporaryInfos(Array<const RenderableMesh*>& all_infos)
{
PROFILE_FUNCTION();
all_infos.reserve(m_renderables.size() * 2);
for (int i = 0; i < m_temporary_infos.size(); ++i)
{
Array<const RenderableMesh*>& subinfos = m_temporary_infos[i];
if (!subinfos.empty())
{
int size = all_infos.size();
all_infos.resize(size + subinfos.size());
memcpy(&all_infos[0] + size,
&subinfos[0],
sizeof(subinfos[0]) * subinfos.size());
}
}
}
void runJobs(Array<MTJD::Job*>& jobs, MTJD::Group& sync_point)
{
PROFILE_FUNCTION();
for (int i = 0; i < jobs.size(); ++i)
{
m_engine.getMTJDManager().schedule(jobs[i]);
}
if (!jobs.empty())
{
sync_point.sync();
}
}
void fillTemporaryInfos(const CullingSystem::Results& results,
const Frustum& frustum,
int64_t layer_mask)
{
PROFILE_FUNCTION();
m_jobs.clear();
while (m_temporary_infos.size() < results.size())
{
m_temporary_infos.emplace(m_allocator);
}
while (m_temporary_infos.size() > results.size())
{
m_temporary_infos.pop();
}
for (int subresult_index = 0; subresult_index < results.size();
++subresult_index)
{
Array<const RenderableMesh*>& subinfos =
m_temporary_infos[subresult_index];
subinfos.clear();
MTJD::Job* job = MTJD::makeJob(
m_engine.getMTJDManager(),
[&subinfos,
layer_mask,
this,
&results,
subresult_index,
&frustum]()
{
Vec3 frustum_position = frustum.getPosition();
const CullingSystem::Subresults& subresults =
results[subresult_index];
for (int i = 0, c = subresults.size(); i < c; ++i)
{
const Renderable* LUMIX_RESTRICT renderable =
m_renderables[subresults[i]];
const Model* LUMIX_RESTRICT model = renderable->m_model;
float squared_distance =
(renderable->m_matrix.getTranslation() -
frustum_position)
.squaredLength();
if (model && model->isReady())
{
LODMeshIndices lod =
model->getLODMeshIndices(squared_distance);
for (int j = lod.getFrom(), c = lod.getTo(); j <= c;
++j)
{
subinfos.push(&renderable->m_meshes[j]);
}
}
}
},
m_allocator);
job->addDependency(&m_sync_point);
m_jobs.push(job);
}
runJobs(m_jobs, m_sync_point);
}
virtual void getPointLights(const Frustum& frustum,
Array<ComponentIndex>& lights) override
{
for (int i = 0, ci = m_point_lights.size(); i < ci; ++i)
{
PointLight& light = m_point_lights[i];
if (frustum.isSphereInside(m_universe.getPosition(light.m_entity),
light.m_range))
{
lights.push(light.m_uid);
}
}
}
virtual Entity getCameraEntity(ComponentIndex camera) const override
{
return m_cameras[camera].m_entity;
}
virtual void
getPointLightInfluencedGeometry(ComponentIndex light_cmp,
const Frustum& frustum,
Array<const RenderableMesh*>& infos,
int64_t layer_mask)
{
PROFILE_FUNCTION();
int light_index = getPointLightIndex(light_cmp);
for (int j = 0, cj = m_light_influenced_geometry[light_index].size();
j < cj;
++j)
{
int renderable_index = m_light_influenced_geometry[light_index][j];
Renderable* renderable = m_renderables[renderable_index];
bool is_layer =
(layer_mask &
m_culling_system->getLayerMask(renderable_index)) != 0;
const Sphere& sphere =
m_culling_system->getSpheres()[renderable_index];
if (is_layer &&
frustum.isSphereInside(sphere.m_position, sphere.m_radius))
{
for (int k = 0, kc = renderable->m_meshes.size(); k < kc; ++k)
{
infos.push(&renderable->m_meshes[k]);
}
}
}
}
virtual void getRenderableInfos(const Frustum& frustum,
Array<const RenderableMesh*>& meshes,
int64_t layer_mask) override
{
PROFILE_FUNCTION();
const CullingSystem::Results* results = cull(frustum, layer_mask);
if (!results)
{
return;
}
fillTemporaryInfos(*results, frustum, layer_mask);
mergeTemporaryInfos(meshes);
for (int i = 0, c = m_always_visible.size(); i < c; ++i)
{
int renderable_index = getRenderable(m_always_visible[i]);
const Renderable* LUMIX_RESTRICT renderable =
m_renderables[renderable_index];
if ((m_culling_system->getLayerMask(renderable_index) &
layer_mask) != 0)
{
for (int j = 0, c = renderable->m_meshes.size(); j < c; ++j)
{
meshes.push(&renderable->m_meshes[j]);
}
}
}
}
virtual void getRenderableMeshes(Array<RenderableMesh>& meshes,
int64_t layer_mask) override
{
PROFILE_FUNCTION();
if (m_renderables.empty())
return;
meshes.reserve(m_renderables.size() * 2);
for (int i = 0, c = m_renderables.size(); i < c; ++i)
{
const Renderable* LUMIX_RESTRICT renderable = m_renderables[i];
if ((m_culling_system->getLayerMask(i) & layer_mask) != 0)
{
for (int j = 0, c = renderable->m_meshes.size(); j < c; ++j)
{
meshes.push(renderable->m_meshes[j]);
}
}
}
}
virtual void setCameraSlot(ComponentIndex camera,
const char* slot) override
{
copyString(
m_cameras[camera].m_slot, Camera::MAX_SLOT_LENGTH, slot);
}
virtual const char* getCameraSlot(ComponentIndex camera) override
{
return m_cameras[camera].m_slot;
}
virtual float getCameraFOV(ComponentIndex camera) override
{
return m_cameras[camera].m_fov;
}
virtual void setCameraFOV(ComponentIndex camera, float fov) override
{
m_cameras[camera].m_fov = fov;
}
virtual void setCameraNearPlane(ComponentIndex camera,
float near_plane) override
{
m_cameras[camera].m_near = near_plane;
}
virtual float getCameraNearPlane(ComponentIndex camera) override
{
return m_cameras[camera].m_near;
}
virtual void setCameraFarPlane(ComponentIndex camera,
float far_plane) override
{
m_cameras[camera].m_far = far_plane;
}
virtual float getCameraFarPlane(ComponentIndex camera) override
{
return m_cameras[camera].m_far;
}
virtual float getCameraWidth(ComponentIndex camera) override
{
return m_cameras[camera].m_width;
}
virtual float getCameraHeight(ComponentIndex camera) override
{
return m_cameras[camera].m_height;
}
virtual void setCameraSize(ComponentIndex camera, int w, int h) override
{
m_cameras[camera].m_width = (float)w;
m_cameras[camera].m_height = (float)h;
m_cameras[camera].m_aspect = w / (float)h;
}
virtual const Array<DebugLine>& getDebugLines() const override
{
return m_debug_lines;
}
virtual void addDebugSphere(const Vec3& center,
float radius,
const Vec3& color,
float life) override
{
static const int COLS = 36;
static const int ROWS = COLS >> 1;
static const float STEP = (Math::PI / 180.0f) * 360.0f / COLS;
int p2 = COLS >> 1;
int r2 = ROWS >> 1;
float prev_ci = 1;
float prev_si = 0;
for (int y = -r2; y < r2; ++y)
{
float cy = cos(y * STEP);
float cy1 = cos((y + 1) * STEP);
float sy = sin(y * STEP);
float sy1 = sin((y + 1) * STEP);
for (int i = -p2; i < p2; ++i)
{
float ci = cos(i * STEP);
float si = sin(i * STEP);
addDebugLine(Vec3(center.x + radius * ci * cy,
center.y + radius * sy,
center.z + radius * si * cy),
Vec3(center.x + radius * ci * cy1,
center.y + radius * sy1,
center.z + radius * si * cy1),
color,
life);
addDebugLine(Vec3(center.x + radius * ci * cy,
center.y + radius * sy,
center.z + radius * si * cy),
Vec3(center.x + radius * prev_ci * cy,
center.y + radius * sy,
center.z + radius * prev_si * cy),
color,
life);
addDebugLine(Vec3(center.x + radius * prev_ci * cy1,
center.y + radius * sy1,
center.z + radius * prev_si * cy1),
Vec3(center.x + radius * ci * cy1,
center.y + radius * sy1,
center.z + radius * si * cy1),
color,
life);
prev_ci = ci;
prev_si = si;
}
}
}
virtual void addDebugCylinder(const Vec3& position,
const Vec3& up,
float radius,
const Vec3& color,
float life) override
{
Vec3 z_vec(-up.y, up.x, 0);
Vec3 x_vec = crossProduct(up, z_vec);
float prevx = radius;
float prevz = 0;
z_vec.normalize();
x_vec.normalize();
Vec3 top = position + up;
for (int i = 1; i <= 32; ++i)
{
float a = i / 32.0f * 2 * Math::PI;
float x = cosf(a) * radius;
float z = sinf(a) * radius;
addDebugLine(position + x_vec * x + z_vec * z,
position + x_vec * prevx + z_vec * prevz,
color,
life);
addDebugLine(top + x_vec * x + z_vec * z,
top + x_vec * prevx + z_vec * prevz,
color,
life);
addDebugLine(position + x_vec * x + z_vec * z,
top + x_vec * x + z_vec * z,
color,
life);
prevx = x;
prevz = z;
}
}
virtual void addDebugCube(const Vec3& min,
const Vec3& max,
const Vec3& color,
float life) override
{
Vec3 a = min;
Vec3 b = min;
b.x = max.x;
addDebugLine(a, b, color, life);
a.set(b.x, b.y, max.z);
addDebugLine(a, b, color, life);
b.set(min.x, a.y, a.z);
addDebugLine(a, b, color, life);
a.set(b.x, b.y, min.z);
addDebugLine(a, b, color, life);
a = min;
a.y = max.y;
b = a;
b.x = max.x;
addDebugLine(a, b, color, life);
a.set(b.x, b.y, max.z);
addDebugLine(a, b, color, life);
b.set(min.x, a.y, a.z);
addDebugLine(a, b, color, life);
a.set(b.x, b.y, min.z);
addDebugLine(a, b, color, life);
a = min;
b = a;
b.y = max.y;
addDebugLine(a, b, color, life);
a.x = max.x;
b.x = max.x;
addDebugLine(a, b, color, life);
a.z = max.z;
b.z = max.z;
addDebugLine(a, b, color, life);
a.x = min.x;
b.x = min.x;
addDebugLine(a, b, color, life);
}
virtual void addDebugFrustum(const Frustum& frustum,
const Vec3& color,
float life) override
{
addDebugFrustum(frustum.getPosition(),
frustum.getDirection(),
frustum.getUp(),
frustum.getFOV(),
frustum.getRatio(),
frustum.getNearDistance(),
frustum.getFarDistance(),
color,
life);
}
virtual void addDebugFrustum(const Vec3& position,
const Vec3& direction,
const Vec3& up,
float fov,
float ratio,
float near_distance,
float far_distance,
const Vec3& color,
float life) override
{
Vec3 points[8];
Vec3 near_center = position + direction * near_distance;
Vec3 far_center = position + direction * far_distance;
Vec3 right = crossProduct(direction, up);
float scale = (float)tan(Math::PI / 180.0f * fov * 0.5f);
Vec3 up_near = up * 0.5f * near_distance * scale;
Vec3 right_near = right * (0.5f * near_distance * scale * ratio);
points[0] = near_center + up_near + right_near;
points[1] = near_center + up_near - right_near;
points[2] = near_center - up_near - right_near;
points[3] = near_center - up_near + right_near;
Vec3 up_far = up * 0.5f * far_distance * scale;
Vec3 right_far = right * (0.5f * far_distance * scale * ratio);
points[4] = far_center + up_far + right_far;
points[5] = far_center + up_far - right_far;
points[6] = far_center - up_far - right_far;
points[7] = far_center - up_far + right_far;
addDebugLine(points[0], points[1], color, life);
addDebugLine(points[1], points[2], color, life);
addDebugLine(points[2], points[3], color, life);
addDebugLine(points[3], points[0], color, life);
addDebugLine(points[4], points[5], color, life);
addDebugLine(points[5], points[6], color, life);
addDebugLine(points[6], points[7], color, life);
addDebugLine(points[7], points[4], color, life);
addDebugLine(points[0], points[4], color, life);
addDebugLine(points[1], points[5], color, life);
addDebugLine(points[2], points[6], color, life);
addDebugLine(points[3], points[7], color, life);
}
virtual void addDebugCircle(const Vec3& center,
const Vec3& up,
float radius,
const Vec3& color,
float life) override
{
Vec3 z_vec(-up.y, up.x, 0);
Vec3 x_vec = crossProduct(up, z_vec);
float prevx = radius;
float prevz = 0;
z_vec.normalize();
x_vec.normalize();
for (int i = 1; i <= 64; ++i)
{
float a = i / 64.0f * 2 * Math::PI;
float x = cosf(a) * radius;
float z = sinf(a) * radius;
addDebugLine(center + x_vec * x + z_vec * z,
center + x_vec * prevx + z_vec * prevz,
color,
life);
prevx = x;
prevz = z;
}
}
virtual void addDebugCross(const Vec3& center,
float size,
const Vec3& color,
float life) override
{
addDebugLine(
center, Vec3(center.x - size, center.y, center.z), color, life);
addDebugLine(
center, Vec3(center.x + size, center.y, center.z), color, life);
addDebugLine(
center, Vec3(center.x, center.y - size, center.z), color, life);
addDebugLine(
center, Vec3(center.x, center.y + size, center.z), color, life);
addDebugLine(
center, Vec3(center.x, center.y, center.z - size), color, life);
addDebugLine(
center, Vec3(center.x, center.y, center.z + size), color, life);
}
virtual void addDebugLine(const Vec3& from,
const Vec3& to,
const Vec3& color,
float life) override
{
DebugLine& line = m_debug_lines.pushEmpty();
line.m_from = from;
line.m_to = to;
line.m_color = ((uint8_t)(color.x * 255) << 24) |
((uint8_t)(color.y * 255) << 16) |
((uint8_t)(color.z * 255) << 8) | 255;
line.m_life = life;
}
virtual void addDebugLine(const Vec3& from,
const Vec3& to,
uint32_t color,
float life) override
{
DebugLine& line = m_debug_lines.pushEmpty();
line.m_from = from;
line.m_to = to;
line.m_color = color;
line.m_life = life;
}
virtual RayCastModelHit castRayTerrain(ComponentIndex terrain,
const Vec3& origin,
const Vec3& dir) override
{
RayCastModelHit hit;
hit.m_is_hit = false;
if (m_terrains[terrain])
{
hit = m_terrains[terrain]->castRay(origin, dir);
hit.m_component = terrain;
hit.m_component_type = TERRAIN_HASH;
hit.m_entity = m_terrains[terrain]->getEntity();
}
return hit;
}
virtual RayCastModelHit castRay(const Vec3& origin,
const Vec3& dir,
ComponentIndex ignored_renderable) override
{
RayCastModelHit hit;
hit.m_is_hit = false;
Universe& universe = getUniverse();
int ignore_index = getRenderable(ignored_renderable);
for (int i = 0; i < m_renderables.size(); ++i)
{
if (ignore_index != i && m_renderables[i]->m_model)
{
const Vec3& pos = m_renderables[i]->m_matrix.getTranslation();
float radius = m_renderables[i]->m_model->getBoundingRadius();
float scale = universe.getScale(m_renderables[i]->m_entity);
Vec3 intersection;
if (dotProduct(pos - origin, pos - origin) < radius * radius ||
Math::getRaySphereIntersection(
origin, dir, pos, radius * scale, intersection))
{
RayCastModelHit new_hit =
m_renderables[i]->m_model->castRay(
origin, dir, m_renderables[i]->m_matrix, scale);
if (new_hit.m_is_hit &&
(!hit.m_is_hit || new_hit.m_t < hit.m_t))
{
new_hit.m_component = i;
new_hit.m_entity = m_renderables[i]->m_entity;
new_hit.m_component_type = RENDERABLE_HASH;
hit = new_hit;
hit.m_is_hit = true;
}
}
}
}
for (int i = 0; i < m_terrains.size(); ++i)
{
if (m_terrains[i])
{
RayCastModelHit terrain_hit =
m_terrains[i]->castRay(origin, dir);
if (terrain_hit.m_is_hit &&
(!hit.m_is_hit || terrain_hit.m_t < hit.m_t))
{
terrain_hit.m_component = i;
terrain_hit.m_component_type = TERRAIN_HASH;
terrain_hit.m_entity = m_terrains[i]->getEntity();
hit = terrain_hit;
}
}
}
return hit;
}
int getPointLightIndex(ComponentIndex cmp) const
{
for (int i = 0; i < m_point_lights.size(); ++i)
{
if (m_point_lights[i].m_uid == cmp)
{
return i;
}
}
return -1;
}
int getGlobalLightIndex(int uid) const
{
for (int i = 0; i < m_global_lights.size(); ++i)
{
if (m_global_lights[i].m_uid == uid)
{
return i;
}
}
return -1;
}
virtual void setFogDensity(ComponentIndex cmp, float density) override
{
m_global_lights[getGlobalLightIndex(cmp)].m_fog_density = density;
}
virtual void setFogColor(ComponentIndex cmp, const Vec3& color) override
{
m_global_lights[getGlobalLightIndex(cmp)].m_fog_color = color;
}
virtual float getFogDensity(ComponentIndex cmp) override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_fog_density;
}
virtual Vec3 getFogColor(ComponentIndex cmp) override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_fog_color;
}
virtual float getLightRange(ComponentIndex cmp) override
{
return m_point_lights[getPointLightIndex(cmp)].m_range;
}
virtual void setLightRange(ComponentIndex cmp, float range) override
{
int index = getPointLightIndex(cmp);
m_point_lights[index].m_range = range;
detectLightInfluencedGeometry(index);
}
virtual void setPointLightIntensity(ComponentIndex cmp,
float intensity) override
{
m_point_lights[getPointLightIndex(cmp)].m_intensity = intensity;
}
virtual void setGlobalLightIntensity(ComponentIndex cmp,
float intensity) override
{
m_global_lights[getGlobalLightIndex(cmp)].m_intensity = intensity;
}
virtual void setPointLightColor(ComponentIndex cmp,
const Vec3& color) override
{
m_point_lights[getPointLightIndex(cmp)].m_color = color;
}
virtual void setGlobalLightColor(ComponentIndex cmp,
const Vec3& color) override
{
m_global_lights[getGlobalLightIndex(cmp)].m_color = color;
}
virtual void setLightAmbientIntensity(ComponentIndex cmp,
float intensity) override
{
m_global_lights[getGlobalLightIndex(cmp)].m_ambient_intensity =
intensity;
}
virtual void setLightAmbientColor(ComponentIndex cmp,
const Vec3& color) override
{
m_global_lights[getGlobalLightIndex(cmp)].m_ambient_color = color;
}
virtual float getPointLightIntensity(ComponentIndex cmp) override
{
return m_point_lights[getPointLightIndex(cmp)].m_intensity;
}
virtual float getGlobalLightIntensity(ComponentIndex cmp) override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_intensity;
}
virtual Vec3 getPointLightColor(ComponentIndex cmp) override
{
return m_point_lights[getPointLightIndex(cmp)].m_color;
}
virtual void setPointLightSpecularColor(ComponentIndex cmp,
const Vec3& color) override
{
m_point_lights[getPointLightIndex(cmp)].m_specular_color = color;
}
virtual Vec3 getPointLightSpecularColor(ComponentIndex cmp) override
{
return m_point_lights[getPointLightIndex(cmp)].m_specular_color;
}
virtual Vec3 getGlobalLightColor(ComponentIndex cmp) override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_color;
}
virtual float getLightAmbientIntensity(ComponentIndex cmp) override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_ambient_intensity;
}
virtual Vec3 getLightAmbientColor(ComponentIndex cmp) override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_ambient_color;
}
virtual void setActiveGlobalLight(ComponentIndex cmp) override
{
ASSERT(cmp == GLOBAL_LIGHT_HASH);
m_active_global_light_uid = cmp;
}
virtual ComponentIndex getActiveGlobalLight() override
{
return m_active_global_light_uid;
};
virtual Entity getPointLightEntity(ComponentIndex cmp) const override
{
return m_point_lights[getPointLightIndex(cmp)].m_entity;
}
virtual Entity getGlobalLightEntity(ComponentIndex cmp) const override
{
return m_global_lights[getGlobalLightIndex(cmp)].m_entity;
}
virtual ComponentIndex getCameraInSlot(const char* slot) override
{
for (int i = 0, c = m_cameras.size(); i < c; ++i)
{
if (!m_cameras[i].m_is_free &&
strcmp(m_cameras[i].m_slot, slot) == 0)
{
return i;
}
}
return INVALID_COMPONENT;
}
virtual float getTime() const override { return m_time; }
private:
void modelLoaded(Model* model, int renderable_index)
{
float bounding_radius =
m_renderables[renderable_index]->m_model->getBoundingRadius();
m_culling_system->updateBoundingRadius(bounding_radius,
renderable_index);
m_renderables[renderable_index]->m_meshes.clear();
m_renderables[renderable_index]->m_pose.resize(model->getBoneCount());
model->getPose(m_renderables[renderable_index]->m_pose);
for (int j = 0; j < model->getMeshCount(); ++j)
{
RenderableMesh& info =
m_renderables[renderable_index]->m_meshes.pushEmpty();
info.m_mesh = &model->getMesh(j);
info.m_pose = &m_renderables[renderable_index]->m_pose;
info.m_matrix = &m_renderables[renderable_index]->m_matrix;
info.m_model = model;
}
for (int i = 0; i < m_point_lights.size(); ++i)
{
PointLight& light = m_point_lights[i];
Renderable* renderable = m_renderables[renderable_index];
Vec3 t = renderable->m_matrix.getTranslation();
float r = renderable->m_model->getBoundingRadius();
if ((t - m_universe.getPosition(light.m_entity)).squaredLength() <
(r + light.m_range) * (r + light.m_range))
{
m_light_influenced_geometry[i].push(renderable_index);
}
}
}
void modelLoaded(Model* model)
{
for (int i = 0; i < m_renderables.size(); ++i)
{
if (m_renderables[i]->m_model == model)
{
modelLoaded(model, i);
}
}
}
ModelLoadedCallback* getModelLoadedCallback(Model* model)
{
for (int i = 0; i < m_model_loaded_callbacks.size(); ++i)
{
if (m_model_loaded_callbacks[i]->m_model == model)
{
return m_model_loaded_callbacks[i];
}
}
ModelLoadedCallback* new_callback =
m_allocator.newObject<ModelLoadedCallback>(*this, model);
m_model_loaded_callbacks.push(new_callback);
return new_callback;
}
void setModel(int renderable_index, Model* model)
{
Model* old_model = m_renderables[renderable_index]->m_model;
if (model == old_model)
{
return;
}
if (old_model)
{
ModelLoadedCallback* callback = getModelLoadedCallback(old_model);
--callback->m_ref_count;
old_model->getResourceManager()
.get(ResourceManager::MODEL)
->unload(*old_model);
}
m_renderables[renderable_index]->m_model = model;
m_renderables[renderable_index]->m_meshes.clear();
if (model)
{
ModelLoadedCallback* callback = getModelLoadedCallback(model);
++callback->m_ref_count;
if (model->isReady())
{
modelLoaded(model, renderable_index);
}
}
}
virtual IAllocator& getAllocator() override { return m_allocator; }
void detectLightInfluencedGeometry(int light_index)
{
if (m_is_forward_rendered)
{
Frustum frustum;
PointLight& light = m_point_lights[light_index];
frustum.computeOrtho(m_universe.getPosition(light.m_entity),
Vec3(1, 0, 0),
Vec3(0, 1, 0),
light.m_range,
light.m_range,
-light.m_range,
light.m_range);
m_culling_system->cullToFrustum(frustum, 0xffffFFFF);
const CullingSystem::Results& results =
m_culling_system->getResult();
Array<int>& influenced_geometry =
m_light_influenced_geometry[light_index];
influenced_geometry.clear();
for (int i = 0; i < results.size(); ++i)
{
const CullingSystem::Subresults& subresult = results[i];
influenced_geometry.reserve(influenced_geometry.size() +
subresult.size());
for (int j = 0, c = subresult.size(); j < c; ++j)
{
influenced_geometry.push(subresult[j]);
}
}
}
}
virtual DelegateList<void(ComponentIndex)>& renderableCreated() override
{
return m_renderable_created;
}
virtual DelegateList<void(ComponentIndex)>& renderableDestroyed() override
{
return m_renderable_destroyed;
}
virtual float getLightFOV(ComponentIndex cmp) override
{
return m_point_lights[getPointLightIndex(cmp)].m_fov;
}
virtual void setLightFOV(ComponentIndex cmp, float fov) override
{
m_point_lights[getPointLightIndex(cmp)].m_fov = fov;
}
ComponentIndex createGlobalLight(Entity entity)
{
GlobalLight& light = m_global_lights.pushEmpty();
light.m_entity = entity;
light.m_color.set(1, 1, 1);
light.m_intensity = 0;
light.m_ambient_color.set(1, 1, 1);
light.m_ambient_intensity = 1;
light.m_fog_color.set(1, 1, 1);
light.m_fog_density = 0;
light.m_uid = ++m_global_light_last_uid;
if (m_global_lights.size() == 1)
{
m_active_global_light_uid = light.m_uid;
}
m_universe.addComponent(entity, GLOBAL_LIGHT_HASH, this, light.m_uid);
return light.m_uid;
}
ComponentIndex createPointLight(Entity entity)
{
PointLight& light = m_point_lights.pushEmpty();
m_light_influenced_geometry.push(Array<int>(m_allocator));
light.m_entity = entity;
light.m_color.set(1, 1, 1);
light.m_intensity = 1;
light.m_range = 10;
light.m_uid = ++m_point_light_last_uid;
light.m_fov = 999;
light.m_specular_color.set(1, 1, 1);
m_universe.addComponent(entity, POINT_LIGHT_HASH, this, light.m_uid);
detectLightInfluencedGeometry(m_point_lights.size() - 1);
return light.m_uid;
}
ComponentIndex createRenderable(Entity entity)
{
int new_index = m_renderables.empty()
? 0
: m_renderables.back()->m_component_index + 1;
Renderable& r = *m_allocator.newObject<Renderable>(m_allocator);
m_renderables.push(&r);
r.m_entity = entity;
r.m_model = nullptr;
r.m_component_index = new_index;
r.m_is_always_visible = false;
r.m_matrix = m_universe.getMatrix(entity);
m_universe.addComponent(
entity, RENDERABLE_HASH, this, r.m_component_index);
m_culling_system->addStatic(
Sphere(m_universe.getPosition(entity), 1.0f));
m_renderable_created.invoke(r.m_component_index);
return r.m_component_index;
}
private:
IAllocator& m_allocator;
Array<ModelLoadedCallback*> m_model_loaded_callbacks;
Array<Renderable*> m_renderables;
Array<int> m_always_visible;
int m_point_light_last_uid;
Array<PointLight> m_point_lights;
Array<Array<int>> m_light_influenced_geometry;
int m_active_global_light_uid;
int m_global_light_last_uid;
Array<GlobalLight> m_global_lights;
Array<Camera> m_cameras;
ComponentIndex m_applied_camera;
Frustum m_camera_frustum;
Array<Terrain*> m_terrains;
Universe& m_universe;
Renderer& m_renderer;
Engine& m_engine;
Array<DebugLine> m_debug_lines;
CullingSystem* m_culling_system;
DynamicRenderableCache m_dynamic_renderable_cache;
Array<Array<const RenderableMesh*>> m_temporary_infos;
MTJD::Group m_sync_point;
Array<MTJD::Job*> m_jobs;
float m_time;
bool m_is_forward_rendered;
DelegateList<void(ComponentIndex)> m_renderable_created;
DelegateList<void(ComponentIndex)> m_renderable_destroyed;
};
RenderScene* RenderScene::createInstance(Renderer& renderer,
Engine& engine,
Universe& universe,
bool is_forward_rendered,
IAllocator& allocator)
{
return allocator.newObject<RenderSceneImpl>(
renderer, engine, universe, is_forward_rendered, allocator);
}
void RenderScene::destroyInstance(RenderScene* scene)
{
scene->getAllocator().deleteObject(static_cast<RenderSceneImpl*>(scene));
}
}