LumixEngine/src/renderer/pipeline.cpp

1869 lines
56 KiB
C++
Raw Normal View History

2018-06-30 15:18:27 +02:00
#include "draw2d.h"
#include "ffr/ffr.h"
2018-07-17 01:13:58 +02:00
#include "engine/blob.h"
2016-05-10 08:24:31 +02:00
#include "engine/crc32.h"
2018-06-30 15:18:27 +02:00
#include "engine/engine.h"
2016-05-10 08:24:31 +02:00
#include "engine/fs/disk_file_device.h"
#include "engine/fs/file_system.h"
2018-06-30 15:18:27 +02:00
#include "engine/fs/ifile_device.h"
2016-05-10 08:24:31 +02:00
#include "engine/geometry.h"
2018-07-22 15:22:36 +02:00
#include "engine/job_system.h"
2016-05-10 08:24:31 +02:00
#include "engine/log.h"
#include "engine/lua_wrapper.h"
2018-07-15 17:35:41 +02:00
#include "engine/mt/atomic.h"
2018-06-30 15:18:27 +02:00
#include "engine/path.h"
2016-05-10 08:24:31 +02:00
#include "engine/profiler.h"
#include "engine/universe/universe.h"
2018-07-08 00:44:45 +02:00
#include "engine/viewport.h"
2018-06-30 15:18:27 +02:00
#include "font_manager.h"
#include "material.h"
#include "model.h"
#include "pipeline.h"
2018-07-17 01:13:58 +02:00
#include "pose.h"
2018-06-30 15:18:27 +02:00
#include "renderer.h"
#include "render_scene.h"
#include "shader.h"
#include "shader_manager.h"
2018-07-22 15:22:36 +02:00
#include "terrain.h"
2018-06-30 15:18:27 +02:00
#include "texture.h"
2018-07-05 15:54:14 +02:00
#include "texture_manager.h"
2018-07-22 15:22:36 +02:00
#include <algorithm>
2018-07-06 21:31:44 +02:00
#include <cmath>
2014-05-20 23:35:09 +02:00
2018-07-22 15:22:36 +02:00
2015-07-04 15:22:28 +02:00
namespace Lumix
{
2016-02-14 16:49:37 +01:00
2018-07-06 21:31:44 +02:00
static const float SHADOW_CAM_NEAR = 50.0f;
static const float SHADOW_CAM_FAR = 5000.0f;
2018-06-30 15:18:27 +02:00
struct PipelineImpl LUMIX_FINAL : Pipeline
2015-07-23 23:17:51 +02:00
{
2018-07-08 00:44:45 +02:00
PipelineImpl(Renderer& renderer, const Path& path, const char* define, IAllocator& allocator)
2018-07-01 23:37:56 +02:00
: m_allocator(allocator)
, m_renderer(renderer)
2016-01-12 13:52:14 +01:00
, m_path(path)
2015-07-23 23:17:51 +02:00
, m_lua_state(nullptr)
2016-01-12 13:52:14 +01:00
, m_is_ready(false)
2018-06-30 15:18:27 +02:00
, m_custom_commands_handlers(allocator)
2018-07-06 21:31:44 +02:00
, m_define(define)
2016-06-16 19:24:15 +02:00
, m_scene(nullptr)
2018-02-03 19:41:47 +01:00
, m_is_first_render(true)
2018-06-30 15:18:27 +02:00
, m_draw2d(allocator)
2018-07-11 23:35:34 +02:00
, m_output(-1)
2018-06-30 15:18:27 +02:00
, m_renderbuffers(allocator)
2018-07-05 15:54:14 +02:00
, m_shaders(allocator)
2014-05-20 23:35:09 +02:00
{
2018-07-08 18:16:16 +02:00
m_viewport.w = m_viewport.h = 800;
2018-06-30 15:18:27 +02:00
ShaderManager& shader_manager = renderer.getShaderManager();
m_draw2d_shader = (Shader*)shader_manager.load(Path("pipelines/draw2d.shd"));
2018-07-01 23:37:56 +02:00
m_debug_shape_shader = (Shader*)shader_manager.load(Path("pipelines/debug_shape.shd"));
2018-07-05 15:54:14 +02:00
TextureManager& texture_manager = renderer.getTextureManager();
m_default_cubemap = (Texture*)texture_manager.load(Path("models/common/default_probe.dds"));
2018-07-01 23:37:56 +02:00
2018-06-30 15:18:27 +02:00
FontAtlas& font_atlas = m_renderer.getFontManager().getFontAtlas();
m_draw2d.FontTexUvWhitePixel = font_atlas.TexUvWhitePixel;
m_draw2d.Clear();
m_draw2d.PushClipRectFullScreen();
m_draw2d.PushTextureID(font_atlas.TexID);
2018-07-15 17:35:41 +02:00
2018-07-22 15:22:36 +02:00
m_terrain_params_uniform = ffr::allocUniform("u_terrain_params", ffr::UniformType::VEC4, 1);
m_rel_camera_pos_uniform = ffr::allocUniform("u_rel_camera_pos", ffr::UniformType::VEC4, 1);
m_terrain_scale_uniform = ffr::allocUniform("u_terrain_scale", ffr::UniformType::VEC4, 1);
m_terrain_matrix_uniform = ffr::allocUniform("u_terrain_matrix", ffr::UniformType::MAT4, 1);
2018-07-15 17:35:41 +02:00
m_model_uniform = ffr::allocUniform("u_model", ffr::UniformType::MAT4, 1);
2018-07-17 01:13:58 +02:00
m_bones_uniform = ffr::allocUniform("u_bones", ffr::UniformType::MAT4, 196);
2018-07-15 17:35:41 +02:00
m_canvas_size_uniform = ffr::allocUniform("u_canvas_size", ffr::UniformType::VEC2, 1);
m_texture_uniform = ffr::allocUniform("u_texture", ffr::UniformType::INT, 1);
m_irradiance_map_uniform = ffr::allocUniform("u_irradiancemap", ffr::UniformType::INT, 1);
m_radiance_map_uniform = ffr::allocUniform("u_radiancemap", ffr::UniformType::INT, 1);
m_material_params_uniform = ffr::allocUniform("u_material_params", ffr::UniformType::VEC4, 1);
2016-01-28 15:19:56 +01:00
}
2018-06-30 15:18:27 +02:00
~PipelineImpl()
2016-01-28 15:19:56 +01:00
{
2018-06-30 15:18:27 +02:00
m_draw2d_shader->getResourceManager().unload(*m_draw2d_shader);
2018-07-01 23:37:56 +02:00
m_debug_shape_shader->getResourceManager().unload(*m_debug_shape_shader);
2018-07-05 15:54:14 +02:00
m_default_cubemap->getResourceManager().unload(*m_default_cubemap);
for(ShaderRef& shader : m_shaders) {
shader.res->getResourceManager().unload(*shader.res);
}
2015-07-23 23:17:51 +02:00
}
2014-05-20 23:35:09 +02:00
2018-06-30 15:18:27 +02:00
void callInitScene()
2015-07-23 23:17:51 +02:00
{
2018-06-30 15:18:27 +02:00
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
lua_getfield(m_lua_state, -1, "initScene");
if (lua_type(m_lua_state, -1) == LUA_TFUNCTION)
2014-05-20 23:35:09 +02:00
{
2018-06-30 15:18:27 +02:00
lua_pushlightuserdata(m_lua_state, this);
if (lua_pcall(m_lua_state, 1, 0, 0) != 0)
2014-05-20 23:35:09 +02:00
{
2018-06-30 15:18:27 +02:00
g_log_error.log("lua") << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
2014-05-20 23:35:09 +02:00
}
2018-06-30 15:18:27 +02:00
}
else
{
lua_pop(m_lua_state, 1);
2014-05-20 23:35:09 +02:00
}
2015-07-23 23:17:51 +02:00
}
2014-05-20 23:35:09 +02:00
2015-05-16 13:17:20 +02:00
2018-06-30 15:18:27 +02:00
void load() override
2016-01-12 13:52:14 +01:00
{
2018-06-30 15:18:27 +02:00
auto& fs = m_renderer.getEngine().getFileSystem();
2016-01-12 13:52:14 +01:00
Delegate<void(FS::IFile&, bool)> cb;
cb.bind<PipelineImpl, &PipelineImpl::onFileLoaded>(this);
2018-06-30 15:18:27 +02:00
fs.openAsync(fs.getDefaultDevice(), m_path, FS::Mode::OPEN_AND_READ, cb);
2016-01-12 13:52:14 +01:00
}
2018-06-30 15:18:27 +02:00
2016-02-22 22:56:46 +01:00
void cleanup()
2015-07-23 23:17:51 +02:00
{
if (m_lua_state)
{
luaL_unref(m_renderer.getEngine().getState(), LUA_REGISTRYINDEX, m_lua_thread_ref);
2016-02-24 01:10:34 +01:00
luaL_unref(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
2015-07-23 23:17:51 +02:00
m_lua_state = nullptr;
}
2016-02-22 22:56:46 +01:00
}
2017-10-03 22:08:41 +02:00
void setDefine()
{
2018-07-06 21:31:44 +02:00
if (m_define == "") return;
StaticString<256> tmp(m_define, " = true");
2017-10-03 22:08:41 +02:00
2018-06-30 15:18:27 +02:00
bool errors = luaL_loadbuffer(m_lua_state, tmp, stringLength(tmp.data), m_path.c_str()) != 0;
2017-10-03 22:08:41 +02:00
if (errors)
{
g_log_error.log("Renderer") << m_path.c_str() << ": " << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
return;
}
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
2018-06-30 15:18:27 +02:00
lua_setfenv(m_lua_state, -2);
errors = lua_pcall(m_lua_state, 0, 0, 0) != 0;
2017-10-03 22:08:41 +02:00
if (errors)
{
g_log_error.log("Renderer") << m_path.c_str() << ": " << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
}
}
2018-06-30 15:18:27 +02:00
void executeCustomCommand(const char* name)
{
u32 name_hash = crc32(name);
for(CustomCommandHandler& handler : m_custom_commands_handlers)
{
if(handler.hash == name_hash)
{
handler.callback.invoke();
break;
}
}
}
void exposeCustomCommandToLua(const CustomCommandHandler& handler)
{
if (!m_lua_state) return;
char tmp[1024];
copyString(tmp, "function ");
catString(tmp, handler.name);
2018-07-05 15:54:14 +02:00
catString(tmp, "() executeCustomCommand(\"");
2018-06-30 15:18:27 +02:00
catString(tmp, handler.name);
catString(tmp, "\") end");
2018-07-05 15:54:14 +02:00
bool errors = luaL_loadbuffer(m_lua_state, tmp, stringLength(tmp), "exposeCustomCommandToLua") != 0;
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
lua_setfenv(m_lua_state, -2);
2018-06-30 15:18:27 +02:00
errors = errors || lua_pcall(m_lua_state, 0, 0, 0) != 0;
if (errors)
{
g_log_error.log("Renderer") << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
}
}
2016-02-22 22:56:46 +01:00
void onFileLoaded(FS::IFile& file, bool success)
{
2016-04-01 00:18:18 +02:00
if (!success)
{
g_log_error.log("Renderer") << "Failed to load " << m_path;
return;
}
2016-02-22 22:56:46 +01:00
cleanup();
2016-02-24 01:10:34 +01:00
m_lua_state = lua_newthread(m_renderer.getEngine().getState());
m_lua_thread_ref = luaL_ref(m_renderer.getEngine().getState(), LUA_REGISTRYINDEX);
2016-02-24 01:10:34 +01:00
lua_newtable(m_lua_state);
lua_pushvalue(m_lua_state, -1);
m_lua_env = luaL_ref(m_lua_state, LUA_REGISTRYINDEX);
lua_pushvalue(m_lua_state, -1);
lua_setmetatable(m_lua_state, -2);
2018-06-30 15:18:27 +02:00
lua_pushvalue(m_lua_state, LUA_GLOBALSINDEX);
2016-02-24 01:10:34 +01:00
lua_setfield(m_lua_state, -2, "__index");
2016-03-31 16:58:42 +02:00
if (m_renderer.getEngine().getDiskFileDevice())
{
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
lua_pushstring(m_lua_state, m_renderer.getEngine().getDiskFileDevice()->getBasePath());
lua_setfield(m_lua_state, -2, "LUA_PATH");
}
2016-02-24 01:10:34 +01:00
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
2016-02-15 15:50:24 +01:00
lua_pushlightuserdata(m_lua_state, this);
2016-02-24 01:10:34 +01:00
lua_setfield(m_lua_state, -2, "this");
2018-07-05 15:54:14 +02:00
registerLuaAPI(m_lua_state);
2018-06-30 15:18:27 +02:00
for (auto& handler : m_custom_commands_handlers)
2015-07-23 23:17:51 +02:00
{
2018-06-30 15:18:27 +02:00
exposeCustomCommandToLua(handler);
2015-07-23 23:17:51 +02:00
}
2015-02-14 00:13:34 +01:00
2018-06-30 15:18:27 +02:00
setDefine();
2014-05-20 23:35:09 +02:00
2018-06-30 15:18:27 +02:00
bool errors =
luaL_loadbuffer(m_lua_state, (const char*)file.getBuffer(), file.size(), m_path.c_str()) != 0;
if (errors)
2015-05-16 13:17:20 +02:00
{
2018-06-30 15:18:27 +02:00
g_log_error.log("Renderer") << m_path.c_str() << ": " << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
return;
2018-03-13 14:56:33 +01:00
}
2018-06-30 15:18:27 +02:00
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
lua_setfenv(m_lua_state, -2);
errors = lua_pcall(m_lua_state, 0, 0, 0) != 0;
if (errors)
2018-03-13 14:56:33 +01:00
{
2018-06-30 15:18:27 +02:00
g_log_error.log("Renderer") << m_path.c_str() << ": " << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
return;
}
2018-07-08 18:16:16 +02:00
m_viewport.w = m_viewport.h = 800;
2018-06-30 15:18:27 +02:00
if (m_scene) callInitScene();
2018-07-08 18:16:16 +02:00
2018-06-30 15:18:27 +02:00
m_is_ready = true;
2018-04-24 00:55:10 +02:00
}
2018-06-30 15:18:27 +02:00
void clearBuffers()
2015-07-23 23:17:51 +02:00
{
2018-06-30 15:18:27 +02:00
for (Renderbuffer& rb : m_renderbuffers) {
++rb.frame_counter;
}
2015-07-04 15:22:28 +02:00
2018-06-30 15:18:27 +02:00
for(int i = m_renderbuffers.size() - 1; i >= 0; --i) {
if(m_renderbuffers[i].frame_counter > 1) {
2018-07-14 10:03:38 +02:00
m_renderer.destroy(m_renderbuffers[i].handle);
2018-06-30 15:18:27 +02:00
m_renderbuffers.eraseFast(i);
}
2018-07-14 10:03:38 +02:00
}
2016-10-22 18:08:53 +02:00
}
2018-07-08 00:44:45 +02:00
virtual void setViewport(const Viewport& viewport) override { m_viewport = viewport; }
2018-06-30 15:18:27 +02:00
bool render() override
{
2015-07-23 23:17:51 +02:00
PROFILE_FUNCTION();
2015-07-04 15:22:28 +02:00
2018-07-08 00:44:45 +02:00
if (!isReady() || !m_scene || m_viewport.w < 0 || m_viewport.h < 0) {
2018-07-06 21:31:44 +02:00
m_is_first_render = true;
return false;
}
2018-06-30 15:18:27 +02:00
if (m_is_first_render) {
2018-02-03 19:41:47 +01:00
// m_draw2d might accumulate too much data to render while pipeline was not ready
// so we clear it on the first frame
m_is_first_render = false;
m_draw2d.Clear();
}
2015-05-16 13:17:20 +02:00
2016-01-28 15:19:56 +01:00
m_stats = {};
2018-07-11 23:35:34 +02:00
for(Renderbuffer& rb : m_renderbuffers) {
if(!rb.use_realtive_size) continue;
const uint w = uint(rb.relative_size.x * m_viewport.w + 0.5f);
const uint h = uint(rb.relative_size.y * m_viewport.h + 0.5f);
if(rb.width != w || rb.height != h) {
rb.width = w;
rb.height = h;
m_renderer.destroy(rb.handle);
rb.handle = m_renderer.createTexture(w, h, rb.format, 0, {0, 0});
}
}
Renderer::GlobalState state;
state.camera_pos = Vec4(m_viewport.pos, 1);
2018-07-22 15:22:36 +02:00
const Matrix view = m_viewport.getViewRotation();
2018-07-11 23:35:34 +02:00
const Matrix projection = m_viewport.getProjection(ffr::isHomogenousDepth());
state.camera_projection = projection;
state.camera_view = view;
state.camera_view_projection = projection * view;
state.camera_inv_view_projection = state.camera_view_projection;
state.camera_inv_view_projection.inverse();
const Entity global_light = m_scene->getActiveGlobalLight();
if(global_light.isValid()) {
state.light_direction = Vec4(m_scene->getUniverse().getRotation(global_light).rotate(Vec3(0, 0, -1)), 456);
state.light_color = m_scene->getGlobalLightColor(global_light);
state.light_intensity = m_scene->getGlobalLightIntensity(global_light);
state.light_indirect_intensity = m_scene->getGlobalLightIndirectIntensity(global_light);
}
m_renderer.setGlobalState(state);
2018-07-14 10:03:38 +02:00
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
lua_getfield(m_lua_state, -1, "main");
if (lua_type(m_lua_state, -1) == LUA_TFUNCTION) {
lua_pushlightuserdata(m_lua_state, this);
if (lua_pcall(m_lua_state, 1, 0, 0) != 0) {
g_log_warning.log("Renderer") << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
}
}
else {
lua_pop(m_lua_state, 1);
2018-07-11 23:35:34 +02:00
}
2018-06-30 15:18:27 +02:00
clearBuffers();
2018-07-08 18:16:16 +02:00
return true;
2015-07-23 23:17:51 +02:00
}
2015-05-15 22:38:34 +02:00
2015-07-04 15:22:28 +02:00
2018-07-01 23:37:56 +02:00
void renderDebugLines()
{
2018-07-14 17:52:06 +02:00
struct Cmd : Renderer::RenderCommandBase
{
Cmd(IAllocator& allocator) : lines(allocator) {}
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
void setup() override
{
const Array<DebugLine>& src_lines = pipeline->m_scene->getDebugLines();
lines.resize(src_lines.size());
copyMemory(&lines[0], &src_lines[0], lines.size() * sizeof(lines[0]));
}
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
void execute() override {
const Shader::Program& shader = pipeline->m_debug_shape_shader->getProgram(0);
struct BaseVertex {
Vec3 pos;
u32 color;
};
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
Array<BaseVertex> vertices(pipeline->m_allocator);
vertices.resize(lines.size() * 2);
for (int j = 0, n = lines.size(); j < n; ++j) {
const DebugLine& line = lines[j];
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
vertices[j * 2].color = line.color;
vertices[j * 2].pos = line.from;
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
vertices[j * 2 + 1].color = line.color;
vertices[j * 2 + 1].pos = line.to;
}
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
ffr::VertexDecl vertex_decl;
vertex_decl.addAttribute(3, ffr::AttributeType::FLOAT, false, false);
vertex_decl.addAttribute(4, ffr::AttributeType::U8, true, false);
2018-07-15 17:35:41 +02:00
ffr::setUniformMatrix4f(pipeline->m_model_uniform, &Matrix::IDENTITY.m11);
2018-07-01 23:37:56 +02:00
2018-07-14 17:52:06 +02:00
// TODO do not create every frame
2018-07-15 17:35:41 +02:00
ffr::setState(0);
ffr::useProgram(shader.handle);
2018-07-14 17:52:06 +02:00
const ffr::BufferHandle vb = ffr::allocBufferHandle();
ffr::createBuffer(vb, vertices.size() * sizeof(vertices[0]), &vertices[0]);
2018-07-15 17:35:41 +02:00
ffr::setVertexBuffer(&vertex_decl, vb, 0, nullptr);
ffr::setIndexBuffer(ffr::INVALID_BUFFER);
ffr::drawArrays(0, lines.size() * 2, ffr::PrimitiveType::LINES);
2018-07-14 17:52:06 +02:00
ffr::destroy(vb);
}
Array<DebugLine> lines;
PipelineImpl* pipeline;
};
const Array<DebugLine>& lines = m_scene->getDebugLines();
if (lines.empty() || !m_debug_shape_shader->isReady()) return;
IAllocator& allocator = m_renderer.getAllocator();
Cmd* cmd = LUMIX_NEW(allocator, Cmd)(allocator);
cmd->pipeline = this;
m_renderer.push(cmd);
2018-07-01 23:37:56 +02:00
}
void renderDebugShapes()
{
renderDebugLines();
/*renderDebugTriangles();
renderDebugPoints();*/
}
2018-06-30 15:18:27 +02:00
void render2D()
{
2018-07-14 21:55:32 +02:00
auto resetDraw2D = [this](){
2018-06-30 15:18:27 +02:00
m_draw2d.Clear();
m_draw2d.PushClipRectFullScreen();
FontAtlas& atlas = m_renderer.getFontManager().getFontAtlas();
m_draw2d.FontTexUvWhitePixel = atlas.TexUvWhitePixel;
m_draw2d.PushTextureID(atlas.TexID);
};
2016-01-12 13:52:14 +01:00
2018-06-30 15:18:27 +02:00
if (!m_draw2d_shader->isReady()) {
resetDraw2D();
return;
}
2018-07-14 21:55:32 +02:00
if (m_draw2d.IdxBuffer.size() == 0) {
2018-06-30 15:18:27 +02:00
resetDraw2D();
return;
}
2018-07-14 21:55:32 +02:00
struct Cmd : Renderer::RenderCommandBase
{
Renderer::MemRef idx_buffer_mem;
Renderer::MemRef vtx_buffer_mem;
int num_indices;
int num_vertices;
Array<Draw2D::DrawCmd> cmd_buffer;
Cmd(IAllocator& allocator) : cmd_buffer(allocator) {}
void setup()
{
size.set((float)pipeline->m_viewport.w, (float)pipeline->m_viewport.h);
Draw2D& draw2d = pipeline->m_draw2d;
2018-07-14 21:55:32 +02:00
num_indices = draw2d.IdxBuffer.size();
num_vertices = draw2d.VtxBuffer.size();
2016-01-15 00:05:53 +01:00
2018-07-14 21:55:32 +02:00
idx_buffer_mem = pipeline->m_renderer.copy(&draw2d.IdxBuffer[0], num_indices * sizeof(ImDrawIdx));
vtx_buffer_mem = pipeline->m_renderer.copy(&draw2d.VtxBuffer[0], num_vertices * sizeof(ImDrawVert));
cmd_buffer.resize(draw2d.CmdBuffer.size());
copyMemory(&cmd_buffer[0], draw2d.CmdBuffer.begin(), sizeof(cmd_buffer[0]) * cmd_buffer.size());
2016-01-15 00:05:53 +01:00
2018-07-14 21:55:32 +02:00
draw2d.Clear();
draw2d.PushClipRectFullScreen();
FontAtlas& atlas = pipeline->m_renderer.getFontManager().getFontAtlas();
draw2d.FontTexUvWhitePixel = atlas.TexUvWhitePixel;
draw2d.PushTextureID(atlas.TexID);
}
void execute()
{
ffr::VertexDecl vertex_decl;
vertex_decl.addAttribute(2, ffr::AttributeType::FLOAT, false, false);
vertex_decl.addAttribute(2, ffr::AttributeType::FLOAT, false, false);
vertex_decl.addAttribute(4, ffr::AttributeType::U8, true, false);
2018-07-15 17:35:41 +02:00
2018-07-14 21:55:32 +02:00
2018-07-15 17:35:41 +02:00
ffr::BufferHandle vb = ffr::allocBufferHandle();
ffr::BufferHandle ib = ffr::allocBufferHandle();
ffr::createBuffer(vb, vtx_buffer_mem.size, vtx_buffer_mem.data);
ffr::createBuffer(ib, idx_buffer_mem.size, idx_buffer_mem.data);
2018-07-14 21:55:32 +02:00
pipeline->m_renderer.free(idx_buffer_mem);
pipeline->m_renderer.free(vtx_buffer_mem);
ffr::pushDebugGroup("draw2d");
ffr::ProgramHandle prg = pipeline->m_draw2d_shader->getProgram(0).handle;
2018-07-15 17:35:41 +02:00
ffr::setUniform2f(pipeline->m_canvas_size_uniform, &size.x);
ffr::setVertexBuffer(&vertex_decl, vb, 0, nullptr);
ffr::setIndexBuffer(ib);
2018-07-14 21:55:32 +02:00
u32 elem_offset = 0;
const Draw2D::DrawCmd* pcmd_begin = cmd_buffer.begin();
const Draw2D::DrawCmd* pcmd_end = cmd_buffer.end();
2018-07-15 17:35:41 +02:00
ffr::setState(0);
ffr::setUniform1i(pipeline->m_texture_uniform, 0);
ffr::useProgram(prg);
2018-07-14 21:55:32 +02:00
ASSERT(pcmd_begin <= pcmd_end - 1); // TODO compute correct offsets
for (const Draw2D::DrawCmd* pcmd = pcmd_begin; pcmd != pcmd_end; pcmd++) {
if (0 == pcmd->ElemCount) continue;
2018-06-30 15:18:27 +02:00
2018-07-14 21:55:32 +02:00
ffr::scissor(uint(Math::maximum(pcmd->ClipRect.x, 0.0f)),
uint(Math::maximum(pcmd->ClipRect.y, 0.0f)),
uint(Math::minimum(pcmd->ClipRect.z, 65535.0f) - Math::maximum(pcmd->ClipRect.x, 0.0f)),
uint(Math::minimum(pcmd->ClipRect.w, 65535.0f) - Math::maximum(pcmd->ClipRect.y, 0.0f)));
2018-06-30 15:18:27 +02:00
2018-07-14 21:55:32 +02:00
const Texture* atlas_texture = pipeline->m_renderer.getFontManager().getAtlasTexture();
ffr::TextureHandle texture_id = atlas_texture->handle;
if (pcmd->TextureId) texture_id = *(ffr::TextureHandle*)pcmd->TextureId;
if(!texture_id.isValid()) texture_id = atlas_texture->handle;
2018-06-30 15:18:27 +02:00
2018-07-15 17:35:41 +02:00
ffr::bindTexture(0, texture_id);
2018-07-14 21:55:32 +02:00
ffr::blending(1);
2018-07-15 17:35:41 +02:00
ffr::drawTriangles(num_indices);
2018-07-14 21:55:32 +02:00
elem_offset += pcmd->ElemCount;
}
ffr::popDebugGroup();
2018-07-15 17:35:41 +02:00
ffr::destroy(vb);
ffr::destroy(ib);
2018-07-14 21:55:32 +02:00
}
2018-07-14 21:55:32 +02:00
Vec2 size;
PipelineImpl* pipeline;
};
IAllocator& allocator = m_renderer.getAllocator();
Cmd* cmd = LUMIX_NEW(allocator, Cmd)(allocator);
cmd->pipeline = this;
m_renderer.push(cmd);
2016-01-15 00:05:53 +01:00
}
2018-06-30 15:18:27 +02:00
void setScene(RenderScene* scene) override
2016-01-15 00:05:53 +01:00
{
2018-06-30 15:18:27 +02:00
m_scene = scene;
if (m_lua_state && m_scene) callInitScene();
2016-01-15 00:05:53 +01:00
}
2018-06-30 15:18:27 +02:00
RenderScene* getScene() const override { return m_scene; }
2016-01-15 00:05:53 +01:00
2018-06-30 15:18:27 +02:00
CustomCommandHandler& addCustomCommandHandler(const char* name) override
2016-01-15 00:05:53 +01:00
{
2018-06-30 15:18:27 +02:00
auto& handler = m_custom_commands_handlers.emplace();
copyString(handler.name, name);
handler.hash = crc32(name);
exposeCustomCommandToLua(handler);
return handler;
2016-01-15 00:05:53 +01:00
}
2018-06-30 15:18:27 +02:00
static ffr::TextureFormat getFormat(const char* name)
{
static const struct
{
const char* name;
ffr::TextureFormat value;
} FORMATS[] = {
{"depth32", ffr::TextureFormat::D32},
{"depth24", ffr::TextureFormat::D24},
{"depth24stencil8", ffr::TextureFormat::D24S8},
{"rgba8", ffr::TextureFormat::RGBA8},
2018-07-05 15:54:14 +02:00
{"srgba", ffr::TextureFormat::SRGBA},
{"srgb", ffr::TextureFormat::SRGB},
2018-06-30 15:18:27 +02:00
{"rgba16f", ffr::TextureFormat::RGBA16F},
{"r16f", ffr::TextureFormat::R16F},
{"r16", ffr::TextureFormat::R16},
{"r32f", ffr::TextureFormat::R32F},
};
2016-01-15 00:05:53 +01:00
2018-06-30 15:18:27 +02:00
for (auto& i : FORMATS)
{
if (equalStrings(i.name, name)) return i.value;
}
g_log_error.log("Renderer") << "Uknown texture format " << name;
return ffr::TextureFormat::RGBA8;
2016-02-14 16:49:37 +01:00
}
2018-06-30 15:18:27 +02:00
int createRenderbuffer(float w, float h, bool relative, const char* format_str)
2016-02-15 15:50:24 +01:00
{
2018-07-11 23:35:34 +02:00
const uint rb_w = uint(relative ? w * m_viewport.w + 0.5f : w);
const uint rb_h = uint(relative ? h * m_viewport.h + 0.5f : h);
2018-06-30 15:18:27 +02:00
const ffr::TextureFormat format = getFormat(format_str);
2016-02-15 15:50:24 +01:00
2018-06-30 15:18:27 +02:00
for (int i = 0, n = m_renderbuffers.size(); i < n; ++i)
{
Renderbuffer& rb = m_renderbuffers[i];
if (rb.frame_counter == 0) continue;
if (rb.width != rb_w) continue;
if (rb.height != rb_h) continue;
if (rb.format != format) continue;
2016-02-15 15:50:24 +01:00
2018-06-30 15:18:27 +02:00
rb.frame_counter = 0;
return i;
}
2018-06-30 15:18:27 +02:00
Renderbuffer& rb = m_renderbuffers.emplace();
2018-07-11 23:35:34 +02:00
rb.use_realtive_size = relative;
rb.relative_size.set(w, h);
2018-06-30 15:18:27 +02:00
rb.frame_counter = 0;
rb.width = rb_w;
rb.height = rb_h;
rb.format = format;
2018-07-08 18:16:16 +02:00
rb.handle = m_renderer.createTexture(rb_w, rb_h, format, 0, {0, 0});
2015-07-23 23:17:51 +02:00
2018-06-30 15:18:27 +02:00
return m_renderbuffers.size() - 1;
}
2017-10-04 13:23:34 +02:00
2015-10-24 15:27:48 +02:00
2018-07-22 15:22:36 +02:00
static int renderTerrains(lua_State* L)
{
PROFILE_FUNCTION();
const int pipeline_idx = lua_upvalueindex(1);
if (lua_type(L, pipeline_idx) != LUA_TLIGHTUSERDATA) {
LuaWrapper::argError<PipelineImpl*>(L, pipeline_idx);
}
PipelineImpl* pipeline = LuaWrapper::toType<PipelineImpl*>(L, pipeline_idx);
const char* define = LuaWrapper::checkArg<const char*>(L, 1);
CameraParams cp;
lua_getfield(L, 2, "frustum");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
luaL_error(L, "Frustum is not a table");
}
float* points = cp.frustum.xs;
for (int i = 0; i < 32 + 24; ++i) {
lua_rawgeti(L, -1, i + 1);
if(!LuaWrapper::isType<float>(L, -1)) {
lua_pop(L, 2);
luaL_error(L, "Frustum must contain exactly 24 floats");
}
points[i] = LuaWrapper::toType<float>(L, -1);
lua_pop(L, 1);
}
cp.frustum.setPlanesFromPoints();
if(!LuaWrapper::checkField(L, 2, "lod_multiplier", &cp.lod_multiplier)) {
luaL_error(L, "Missing lod_multiplier in camera params");
}
if(!LuaWrapper::checkField(L, 2, "position", &cp.pos)) {
luaL_error(L, "Missing position in camera params");
}
IAllocator& allocator = pipeline->m_renderer.getAllocator();
RenderTerrainsCommand* cmd = LUMIX_NEW(allocator, RenderTerrainsCommand)(allocator);
if (lua_gettop(L) > 3 && lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
if(lua_type(L, -1) != LUA_TNUMBER) {
g_log_error.log("Renderer") << "Incorrect global textures arguments of renderTerrains";
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
lua_pop(L, 2);
return 0;
}
if(lua_type(L, -2) != LUA_TSTRING) {
g_log_error.log("Renderer") << "Incorrect global textures arguments of renderTerrains";
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
lua_pop(L, 2);
return 0;
}
if (cmd->m_global_textures_count > lengthOf(cmd->m_global_textures)) {
g_log_error.log("Renderer") << "Too many textures in renderTerrains call";
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
lua_pop(L, 2);
return 0;
}
const char* uniform = lua_tostring(L, -2);
const int rb_idx = (int)lua_tointeger(L, -1);
auto& t = cmd->m_global_textures[cmd->m_global_textures_count];
t.texture = pipeline->m_renderbuffers[rb_idx].handle;
t.uniform = ffr::allocUniform(uniform, ffr::UniformType::INT, 1);
++cmd->m_global_textures_count;
lua_pop(L, 1);
}
}
cmd->m_pipeline = pipeline;
cmd->m_camera_params = cp;
cmd->m_shader_define = define;
pipeline->m_renderer.push(cmd);
return 0;
}
2018-07-14 10:03:38 +02:00
static int renderMeshes(lua_State* L)
{
PROFILE_FUNCTION();
const int pipeline_idx = lua_upvalueindex(1);
if (lua_type(L, pipeline_idx) != LUA_TLIGHTUSERDATA) {
2018-07-22 15:22:36 +02:00
LuaWrapper::argError<PipelineImpl*>(L, pipeline_idx);
2018-07-14 10:03:38 +02:00
}
PipelineImpl* pipeline = LuaWrapper::toType<PipelineImpl*>(L, pipeline_idx);
const u64 layer_mask = LuaWrapper::checkArg<u64>(L, 1);
const char* define = LuaWrapper::checkArg<const char*>(L, 2);
LuaWrapper::checkTableArg(L, 3);
CameraParams cp;
lua_getfield(L, 3, "frustum");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
luaL_error(L, "Frustum is not a table");
}
float* points = cp.frustum.xs;
for (int i = 0; i < 32 + 24; ++i) {
lua_rawgeti(L, -1, i + 1);
if(!LuaWrapper::isType<float>(L, -1)) {
lua_pop(L, 2);
luaL_error(L, "Frustum must contain exactly 24 floats");
}
points[i] = LuaWrapper::toType<float>(L, -1);
lua_pop(L, 1);
}
cp.frustum.setPlanesFromPoints();
if(!LuaWrapper::checkField(L, 3, "lod_multiplier", &cp.lod_multiplier)) {
luaL_error(L, "Missing lod_multiplier in camera params");
}
if(!LuaWrapper::checkField(L, 3, "position", &cp.pos)) {
luaL_error(L, "Missing position in camera params");
}
RenderMeshesCommand* cmd = LUMIX_NEW(pipeline->m_renderer.getAllocator(), RenderMeshesCommand);
if (lua_gettop(L) > 4 && lua_istable(L, 4)) {
lua_pushnil(L);
while (lua_next(L, 4) != 0) {
if(lua_type(L, -1) != LUA_TNUMBER) {
g_log_error.log("Renderer") << "Incorrect global textures arguments of renderMeshes";
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
lua_pop(L, 2);
return 0;
}
if(lua_type(L, -2) != LUA_TSTRING) {
g_log_error.log("Renderer") << "Incorrect global textures arguments of renderMeshes";
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
lua_pop(L, 2);
return 0;
}
if (cmd->global_textures_count > lengthOf(cmd->global_textures)) {
g_log_error.log("Renderer") << "Too many textures in renderMeshes call";
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
lua_pop(L, 2);
return 0;
}
const char* uniform = lua_tostring(L, -2);
const int rb_idx = (int)lua_tointeger(L, -1);
auto& t = cmd->global_textures[cmd->global_textures_count];
2018-07-15 17:35:41 +02:00
t.texture = pipeline->m_renderbuffers[rb_idx].handle;
t.uniform = ffr::allocUniform(uniform, ffr::UniformType::INT, 1);
2018-07-14 10:03:38 +02:00
++cmd->global_textures_count;
lua_pop(L, 1);
}
}
2018-07-14 18:04:26 +02:00
cmd->camera_params = cp;
2018-07-14 10:03:38 +02:00
cmd->pipeline = pipeline;
cmd->layer_mask = layer_mask;
cmd->shader_define = define;
pipeline->m_renderer.push(cmd);
return 0;
}
2018-07-06 21:31:44 +02:00
2018-07-05 15:54:14 +02:00
static int drawArray(lua_State* L)
{
2018-07-14 10:03:38 +02:00
PROFILE_FUNCTION();
struct Cmd : Renderer::RenderCommandBase {
void setup() override {}
void execute() override
{
ffr::ProgramHandle prg = m_shader->getProgram(0).handle;
for(int i = 0; i < m_textures_count; ++i) {
2018-07-15 17:35:41 +02:00
ffr::bindTexture(i, m_textures[i].handle);
ffr::setUniform1i(m_textures[i].uniform, i);
2018-07-14 10:03:38 +02:00
}
for(int i = 0; i < m_uniforms_count; ++i) {
2018-07-15 17:35:41 +02:00
ffr::setUniform4f(m_uniforms[i].handle, &m_uniforms[i].value.x);
2018-07-14 10:03:38 +02:00
}
2018-07-15 17:35:41 +02:00
ffr::setVertexBuffer(nullptr, ffr::INVALID_BUFFER, 0, nullptr);
ffr::useProgram(prg);
ffr::setState(0);
ffr::setIndexBuffer(ffr::INVALID_BUFFER);
ffr::drawArrays(m_indices_offset, m_indices_count, ffr::PrimitiveType::TRIANGLE_STRIP);
2018-07-14 10:03:38 +02:00
}
struct {
ffr::TextureHandle handle;
2018-07-15 17:35:41 +02:00
ffr::UniformHandle uniform;
2018-07-14 10:03:38 +02:00
} m_textures[16];
int m_textures_count = 0;
struct {
Vec4 value;
2018-07-15 17:35:41 +02:00
ffr::UniformHandle handle;
2018-07-14 10:03:38 +02:00
} m_uniforms[16];
int m_uniforms_count = 0;
Shader* m_shader;
int m_indices_count;
int m_indices_offset;
};
const int pipeline_idx = lua_upvalueindex(1);
2018-07-05 15:54:14 +02:00
if (lua_type(L, pipeline_idx) != LUA_TLIGHTUSERDATA) {
2018-07-22 15:22:36 +02:00
LuaWrapper::argError<PipelineImpl*>(L, pipeline_idx);
2018-07-05 15:54:14 +02:00
}
PipelineImpl* pipeline = LuaWrapper::toType<PipelineImpl*>(L, pipeline_idx);
const int indices_offset = LuaWrapper::checkArg<int>(L, 1);
const int indices_count = LuaWrapper::checkArg<int>(L, 2);
int shader_id = LuaWrapper::checkArg<int>(L, 3);
LuaWrapper::checkTableArg(L, 4);
Shader* shader = nullptr;
for (const ShaderRef& s : pipeline->m_shaders) {
if(s.id == shader_id) {
shader = s.res;
break;
}
}
if (!shader) {
g_log_error.log("Renderer") << "Unknown shader id " << shader_id << " in drawArrays.";
return 0;
}
if (shader->isFailure()) {
g_log_error.log("Renderer") << "Shader " << shader->getPath() << " failed to load. `drawArrays` has no effect.";
return 0;
}
if (!shader->isReady()) return 0;
2018-07-14 10:03:38 +02:00
Cmd* cmd = LUMIX_NEW(pipeline->m_renderer.getAllocator(), Cmd);
2018-07-05 15:54:14 +02:00
lua_pushnil(L);
while (lua_next(L, 4) != 0) {
if(lua_type(L, -1) != LUA_TNUMBER) {
g_log_error.log("Renderer") << "Incorrect texture arguments of drawArrays";
2018-07-14 10:03:38 +02:00
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
2018-07-05 15:54:14 +02:00
lua_pop(L, 2);
return 0;
}
if(lua_type(L, -2) != LUA_TSTRING) {
g_log_error.log("Renderer") << "Incorrect texture arguments of drawArrays";
2018-07-14 10:03:38 +02:00
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
2018-07-05 15:54:14 +02:00
lua_pop(L, 2);
return 0;
}
2018-07-14 10:03:38 +02:00
if (cmd->m_textures_count > lengthOf(cmd->m_textures)) {
2018-07-05 15:54:14 +02:00
g_log_error.log("Renderer") << "Too many texture in drawArray call";
2018-07-14 10:03:38 +02:00
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
2018-07-05 15:54:14 +02:00
lua_pop(L, 2);
return 0;
}
2018-07-15 17:35:41 +02:00
const char* uniform_name = lua_tostring(L, -2);
cmd->m_textures[cmd->m_textures_count].uniform = ffr::allocUniform(uniform_name, ffr::UniformType::INT, 1);
2018-07-05 15:54:14 +02:00
const int rb_idx = (int)lua_tointeger(L, -1);
2018-07-14 10:03:38 +02:00
cmd->m_textures[cmd->m_textures_count].handle = pipeline->m_renderbuffers[rb_idx].handle;
++cmd->m_textures_count;
2018-07-05 15:54:14 +02:00
lua_pop(L, 1);
}
2018-07-14 10:03:38 +02:00
2018-07-05 15:54:14 +02:00
if (lua_istable(L, 5)) {
lua_pushnil(L);
while (lua_next(L, 5) != 0) {
if(lua_type(L, -1) != LUA_TTABLE) {
g_log_error.log("Renderer") << "Incorrect uniform arguments of drawArrays";
2018-07-14 10:03:38 +02:00
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
2018-07-05 15:54:14 +02:00
lua_pop(L, 2);
return 0;
}
if(lua_type(L, -2) != LUA_TSTRING) {
g_log_error.log("Renderer") << "Incorrect uniform arguments of drawArrays";
2018-07-14 10:03:38 +02:00
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
2018-07-05 15:54:14 +02:00
lua_pop(L, 2);
return 0;
}
2018-07-15 17:35:41 +02:00
const char* uniform_name = lua_tostring(L, -2);
cmd->m_uniforms[cmd->m_uniforms_count].handle = ffr::allocUniform(uniform_name, ffr::UniformType::VEC4, 1);
2018-07-14 10:03:38 +02:00
float* value = &cmd->m_uniforms[cmd->m_uniforms_count].value.x;
2018-07-05 15:54:14 +02:00
for(int i = 0; i < 4; ++i) {
lua_rawgeti(L, -1, 1 + i);
if (lua_type(L, -1) != LUA_TNUMBER) {
g_log_error.log("Renderer") << "Incorrect uniform arguments of drawArrays. Uniforms can only be Vec4.";
2018-07-14 10:03:38 +02:00
LUMIX_DELETE(pipeline->m_renderer.getAllocator(), cmd);
2018-07-05 15:54:14 +02:00
lua_pop(L, 3);
return 0;
}
value[i] = (float)lua_tonumber(L, -1);
lua_pop(L, 1);
}
2018-07-14 10:03:38 +02:00
++cmd->m_uniforms_count;
2018-07-05 15:54:14 +02:00
lua_pop(L, 1);
}
}
2018-07-08 00:44:45 +02:00
const Vec3 camera_pos = pipeline->m_viewport.pos;
2018-07-05 15:54:14 +02:00
const Entity probe = pipeline->m_scene->getNearestEnvironmentProbe(camera_pos);
if (probe.isValid()) {
Texture* irradiance = pipeline->m_scene->getEnvironmentProbeIrradiance(probe);
Texture* radiance = pipeline->m_scene->getEnvironmentProbeRadiance(probe);
2018-07-14 10:03:38 +02:00
cmd->m_textures[cmd->m_textures_count + 0].handle = irradiance->handle;
cmd->m_textures[cmd->m_textures_count + 1].handle = radiance->handle;
2018-07-05 15:54:14 +02:00
}
else {
2018-07-14 10:03:38 +02:00
cmd->m_textures[cmd->m_textures_count + 0].handle = pipeline->m_default_cubemap->handle;
cmd->m_textures[cmd->m_textures_count + 1].handle = pipeline->m_default_cubemap->handle;
2018-07-05 15:54:14 +02:00
}
2018-07-15 17:35:41 +02:00
cmd->m_textures[cmd->m_textures_count + 0].uniform = pipeline->m_irradiance_map_uniform;
cmd->m_textures[cmd->m_textures_count + 1].uniform = pipeline->m_radiance_map_uniform;
2018-07-14 10:03:38 +02:00
cmd->m_textures_count += 2;
2018-07-05 15:54:14 +02:00
2018-07-14 10:03:38 +02:00
cmd->m_shader = shader;
cmd->m_indices_count = indices_count;
cmd->m_indices_offset = indices_offset;
pipeline->m_renderer.push(cmd);
2018-07-05 15:54:14 +02:00
return 0;
}
2018-07-06 21:31:44 +02:00
struct CameraParams
{
Frustum frustum;
Vec3 pos;
float lod_multiplier;
};
static void pushCameraParams(lua_State* L, const CameraParams& params)
{
lua_createtable(L, 0, 4);
lua_createtable(L, 32+24, 0);
const float* frustum = params.frustum.xs;
for(int i = 0; i < 32+24; ++i) {
LuaWrapper::push(L, frustum[i]);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "frustum");
LuaWrapper::setField(L, -2, "position", params.pos);
LuaWrapper::setField(L, -2, "lod_multiplier", params.lod_multiplier);
}
static int getCameraParams(lua_State* L)
{
const int pipeline_idx = lua_upvalueindex(1);
if (lua_type(L, pipeline_idx) != LUA_TLIGHTUSERDATA) {
2018-07-22 15:22:36 +02:00
LuaWrapper::argError<PipelineImpl*>(L, pipeline_idx);
2018-07-06 21:31:44 +02:00
}
PipelineImpl* pipeline = LuaWrapper::toType<PipelineImpl*>(L, pipeline_idx);
RenderScene* scene = pipeline->m_scene;
CameraParams cp;
2018-07-08 00:44:45 +02:00
cp.pos = pipeline->m_viewport.pos;
cp.frustum = pipeline->m_viewport.getFrustum();
cp.lod_multiplier = scene->getCameraLODMultiplier(pipeline->m_viewport.fov, pipeline->m_viewport.is_ortho);
2018-07-06 21:31:44 +02:00
pushCameraParams(L, cp);
return 1;
}
static void findExtraShadowcasterPlanes(const Vec3& light_forward, const Frustum& camera_frustum, const Vec3& camera_position, Frustum* shadow_camera_frustum)
{
static const Frustum::Planes planes[] = {
Frustum::Planes::LEFT, Frustum::Planes::TOP, Frustum::Planes::RIGHT, Frustum::Planes::BOTTOM };
bool prev_side = dotProduct(light_forward, camera_frustum.getNormal(planes[lengthOf(planes) - 1])) < 0;
int out_plane = (int)Frustum::Planes::EXTRA0;
Vec3 camera_frustum_center = camera_frustum.computeBoundingSphere().position;
for (int i = 0; i < lengthOf(planes); ++i)
{
bool side = dotProduct(light_forward, camera_frustum.getNormal(planes[i])) < 0;
if (prev_side != side)
{
Vec3 n0 = camera_frustum.getNormal(planes[i]);
Vec3 n1 = camera_frustum.getNormal(planes[(i + lengthOf(planes) - 1) % lengthOf(planes)]);
Vec3 line_dir = crossProduct(n1, n0);
Vec3 n = crossProduct(light_forward, line_dir);
float d = -dotProduct(camera_position, n);
if (dotProduct(camera_frustum_center, n) + d < 0)
{
n = -n;
d = -dotProduct(camera_position, n);
}
shadow_camera_frustum->setPlane((Frustum::Planes)out_plane, n, d);
++out_plane;
if (out_plane >(int)Frustum::Planes::EXTRA1) break;
}
prev_side = side;
}
}
static Vec3 shadowmapTexelAlign(const Vec3& shadow_cam_pos,
float shadowmap_width,
float frustum_radius,
const Matrix& light_mtx)
{
Matrix inv = light_mtx;
inv.fastInverse();
Vec3 out = inv.transformPoint(shadow_cam_pos);
float align = 2 * frustum_radius / (shadowmap_width * 0.5f - 2);
out.x -= fmodf(out.x, align);
out.y -= fmodf(out.y, align);
out = light_mtx.transformPoint(out);
return out;
}
static int getShadowCameraParams(lua_State* L)
{
2018-07-14 10:03:38 +02:00
const int pipeline_idx = lua_upvalueindex(1);
2018-07-06 21:31:44 +02:00
if (lua_type(L, pipeline_idx) != LUA_TLIGHTUSERDATA) {
2018-07-22 15:22:36 +02:00
LuaWrapper::argError<PipelineImpl*>(L, pipeline_idx);
2018-07-06 21:31:44 +02:00
}
PipelineImpl* pipeline = LuaWrapper::toType<PipelineImpl*>(L, pipeline_idx);
const int slice = LuaWrapper::checkArg<int>(L, 1);
const int shadowmap_width = LuaWrapper::checkArg<int>(L, 2);
RenderScene* scene = pipeline->m_scene;
const Universe& universe = scene->getUniverse();
const Entity light = scene->getActiveGlobalLight();
2018-07-08 00:44:45 +02:00
const Vec4 cascades = light.isValid() ? scene->getShadowmapCascades(light) : Vec4(3, 10, 60, 150);
const Matrix light_mtx = light.isValid() ? universe.getMatrix(light) : Matrix::IDENTITY;
2018-07-06 21:31:44 +02:00
2018-07-08 00:44:45 +02:00
const float camera_height = (float)pipeline->m_viewport.h;
const float camera_fov = pipeline->m_viewport.fov;
const float camera_ratio = pipeline->m_viewport.w / camera_height;
2018-07-06 21:31:44 +02:00
const float split_distances[] = {0.1f, cascades.x, cascades.y, cascades.z, cascades.w};
Frustum camera_frustum;
2018-07-08 00:44:45 +02:00
camera_frustum.computePerspective(pipeline->m_viewport.pos,
pipeline->m_viewport.rot * Vec3(0, 0, -1),
pipeline->m_viewport.rot * Vec3(0, 1, 0),
2018-07-06 21:31:44 +02:00
camera_fov,
camera_ratio,
split_distances[slice],
split_distances[slice + 1]);
const Sphere frustum_bounding_sphere = camera_frustum.computeBoundingSphere();
const float bb_size = frustum_bounding_sphere.radius;
2018-07-08 00:44:45 +02:00
const Vec3 light_forward = light_mtx.getZVector();
2018-07-06 21:31:44 +02:00
Vec3 shadow_cam_pos = frustum_bounding_sphere.position;
shadow_cam_pos = shadowmapTexelAlign(shadow_cam_pos, 0.5f * shadowmap_width - 2, bb_size, light_mtx);
2018-07-14 10:03:38 +02:00
Renderer::GlobalState global_state = pipeline->m_renderer.getGlobalState();
2018-07-06 21:31:44 +02:00
Matrix projection_matrix;
projection_matrix.setOrtho(-bb_size, bb_size, -bb_size, bb_size, SHADOW_CAM_NEAR, SHADOW_CAM_FAR, ffr::isHomogenousDepth(), true);
shadow_cam_pos -= light_forward * SHADOW_CAM_FAR * 0.5f;
Matrix view_matrix;
view_matrix.lookAt(shadow_cam_pos, shadow_cam_pos + light_forward, light_mtx.getYVector());
const float ymul = ffr::isOriginBottomLeft() ? 0.5f : -0.5f;
const Matrix bias_matrix(
0.5, 0.0, 0.0, 0.0,
0.0, ymul, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.5, 0.5, 0.0, 1.0);
global_state.shadowmap_matrices[slice] = bias_matrix * projection_matrix * view_matrix;
global_state.shadow_view_projection = projection_matrix * view_matrix;
2018-07-14 10:03:38 +02:00
pipeline->m_renderer.setGlobalState(global_state);
2018-07-05 15:54:14 +02:00
2018-07-06 21:31:44 +02:00
CameraParams cp;
cp.lod_multiplier = 1;
2018-07-08 00:44:45 +02:00
cp.pos = pipeline->m_viewport.pos;
2018-07-06 21:31:44 +02:00
cp.frustum.computeOrtho(shadow_cam_pos
, -light_forward
, light_mtx.getYVector()
, bb_size
, bb_size
, SHADOW_CAM_NEAR
, SHADOW_CAM_FAR);
2018-07-05 15:54:14 +02:00
2018-07-08 00:44:45 +02:00
findExtraShadowcasterPlanes(light_forward, camera_frustum, pipeline->m_viewport.pos, &cp.frustum);
2018-07-06 21:31:44 +02:00
pushCameraParams(L, cp);
return 1;
}
2018-07-15 17:35:41 +02:00
2018-07-08 18:16:16 +02:00
2018-07-06 21:31:44 +02:00
static int setRenderTargets(lua_State* L)
2018-06-30 15:18:27 +02:00
{
2018-07-05 15:54:14 +02:00
const int pipeline_idx = lua_upvalueindex(1);
if (lua_type(L, pipeline_idx) != LUA_TLIGHTUSERDATA) {
2018-07-22 15:22:36 +02:00
LuaWrapper::argError<PipelineImpl*>(L, pipeline_idx );
2018-07-05 15:54:14 +02:00
}
PipelineImpl* pipeline = LuaWrapper::toType<PipelineImpl*>(L, pipeline_idx);
2015-07-23 23:17:51 +02:00
2018-07-08 18:16:16 +02:00
const int rb_count = lua_gettop(L) - 1;
int rbs[16];
2018-06-30 15:18:27 +02:00
if(rb_count > lengthOf(rbs)) {
2018-07-05 15:54:14 +02:00
g_log_error.log("Renderer") << "Too many render buffers in " << pipeline->getPath();
return 0;
}
if(rb_count <= 0) {
g_log_error.log("Renderer") << "createFramebuffer without arguments in " << pipeline->getPath();
2018-06-30 15:18:27 +02:00
return 0;
}
2015-07-23 23:17:51 +02:00
2018-07-15 17:35:41 +02:00
struct Cmd : Renderer::RenderCommandBase
{
void setup() override { }
void execute() override
{
PROFILE_FUNCTION();
const ffr::FramebufferHandle fb = pipeline->m_renderer.getFramebuffer();
ffr::update(fb, count, rbs);
ffr::setFramebuffer(fb, true);
ffr::viewport(0, 0, w, h);
if(clear_flags) {
const float c[] = { 0, 0, 0, 1 };
ffr::clear(clear_flags, c, 0);
}
}
PipelineImpl* pipeline;
ffr::TextureHandle rbs[16];
uint count;
uint w;
uint h;
u32 clear_flags;
};
Cmd* cmd = LUMIX_NEW(pipeline->m_renderer.getAllocator(), Cmd);
2018-07-14 10:03:38 +02:00
cmd->clear_flags = LuaWrapper::checkArg<u32>(L, 1);
2018-06-30 15:18:27 +02:00
for(int i = 0; i < rb_count; ++i) {
2018-07-14 10:03:38 +02:00
const int rb_idx = LuaWrapper::checkArg<int>(L, i + 2);
cmd->rbs[i] = pipeline->m_renderbuffers[rb_idx].handle;
2018-06-30 15:18:27 +02:00
}
2018-07-14 10:03:38 +02:00
cmd->pipeline = pipeline;
cmd->count = rb_count;
cmd->w = pipeline->m_viewport.w;
cmd->h = pipeline->m_viewport.h;
pipeline->m_renderer.push(cmd);
2018-07-06 21:31:44 +02:00
2018-06-30 15:18:27 +02:00
return 1;
}
2015-07-04 15:22:28 +02:00
2018-06-30 15:18:27 +02:00
void renderModel(Model& model, const Matrix& mtx) override
{
2018-07-14 16:24:17 +02:00
for(int i = 0; i < model.getMeshCount(); ++i) {
2016-01-24 17:20:03 +01:00
2018-06-30 15:18:27 +02:00
const Mesh& mesh = model.getMesh(i);
const Universe& universe = m_scene->getUniverse();
const Material* material = mesh.material;
2018-07-01 18:13:44 +02:00
const Shader::Program& prog = material->getShader()->getProgram(0); // TODO define
2018-06-30 15:18:27 +02:00
const int textures_count = material->getTextureCount();
2016-01-24 17:20:03 +01:00
2018-07-01 18:13:44 +02:00
if(!prog.handle.isValid()) continue;
2017-11-07 16:49:18 +01:00
2018-06-30 15:18:27 +02:00
for(int i = 0; i < textures_count; ++i) {
2018-07-15 17:35:41 +02:00
ffr::bindTexture(i, material->getTexture(i)->handle);
ffr::setUniform1i(material->getTextureUniform(i), i);
2018-06-30 15:18:27 +02:00
}
2017-11-07 16:49:18 +01:00
2018-07-01 18:13:44 +02:00
int attribute_map[16];
for(uint i = 0; i < mesh.vertex_decl.attributes_count; ++i) {
attribute_map[i] = prog.attribute_by_semantics[(int)mesh.attributes_semantic[i]];
}
2018-07-15 17:35:41 +02:00
ffr::setUniformMatrix4f(m_model_uniform, &mtx.m11);
ffr::useProgram(prog.handle);
ffr::setVertexBuffer(&mesh.vertex_decl, mesh.vertex_buffer_handle, 0, prog.use_semantics ? attribute_map : nullptr);
ffr::setIndexBuffer(mesh.index_buffer_handle);
ffr::setState(u64(ffr::StateFlags::DEPTH_TEST) | material->getRenderStates());
ffr::drawTriangles(mesh.indices_count);
2018-07-14 16:24:17 +02:00
}
}
2017-09-27 15:38:37 +02:00
2018-07-22 15:22:36 +02:00
struct RenderTerrainsCommand : Renderer::RenderCommandBase
2018-07-06 21:31:44 +02:00
{
2018-07-22 15:22:36 +02:00
RenderTerrainsCommand(IAllocator& allocator)
: m_allocator(allocator)
, m_instance_data(allocator)
, m_batches(allocator)
{
}
void setup() override
{
PROFILE_FUNCTION();
Array<TerrainInfo> infos(m_allocator);
m_pipeline->m_scene->getTerrainInfos(m_camera_params.frustum, m_camera_params.pos, infos);
if (infos.empty()) return;
m_define_mask = m_shader_define.empty()
? 0
: 1 << m_pipeline->m_renderer.getShaderDefineIdx(m_shader_define);
std::sort(infos.begin(), infos.end(), [](const TerrainInfo& a, const TerrainInfo& b) {
if (a.m_terrain == b.m_terrain) return a.m_index < b.m_index;
return a.m_terrain < b.m_terrain;
});
m_instance_data.resize(infos.size());
Terrain* prev_terrain = nullptr;
int prev_idx = -1;
int prev_submesh = -1;
for (int i = 0, c = infos.size(); i < c; ++i) {
const TerrainInfo& info = infos[i];
if (info.m_terrain != prev_terrain || prev_submesh != info.m_index) {
if (prev_terrain) {
Batch& b = m_batches.emplace();
b.terrain = prev_terrain;
b.shader = infos[prev_idx].m_shader;
b.matrix = infos[prev_idx].m_world_matrix;
b.matrix.setTranslation(b.matrix.getTranslation() - m_camera_params.pos);
b.submesh = infos[prev_idx].m_index;
b.from = prev_idx;
b.to = i - 1;
}
prev_idx = i;
prev_terrain = info.m_terrain;
prev_submesh = info.m_index;
}
m_instance_data[i].size = info.m_size;
m_instance_data[i].quad_min = info.m_min;
m_instance_data[i].morph_consts = info.m_morph_const;
}
Batch& b = m_batches.emplace();
b.terrain = prev_terrain;
b.shader = infos[prev_idx].m_shader;
b.matrix = infos[prev_idx].m_world_matrix;
b.matrix.setTranslation(b.matrix.getTranslation() - m_camera_params.pos);
b.submesh = infos[prev_idx].m_index;
b.from = prev_idx;
b.to = infos.size() - 1;
}
void execute() override
{
if(m_instance_data.empty()) return;
ffr::pushDebugGroup("terrains");
Renderer::TransientSlice instance_buffer = m_pipeline->m_renderer.allocTransient(m_instance_data.byte_size());
ffr::update(instance_buffer.buffer, m_instance_data.begin(), 0, m_instance_data.byte_size());
ffr::VertexDecl decl;
decl.addAttribute(3, ffr::AttributeType::FLOAT, false, false);
decl.addAttribute(2, ffr::AttributeType::FLOAT, false, false);
ffr::VertexDecl instance_decl;
instance_decl.addAttribute(3, ffr::AttributeType::FLOAT, false, false);
instance_decl.addAttribute(1, ffr::AttributeType::FLOAT, false, false);
instance_decl.addAttribute(3, ffr::AttributeType::FLOAT, false, false);
const Vec3 camera_pos = m_camera_params.pos;
for (const Batch& batch : m_batches) {
Texture* detail_texture = batch.terrain->getDetailTexture();
if (!detail_texture) continue;
Texture* splat_texture = batch.terrain->getSplatmap();
if (!splat_texture) continue;
const Matrix inv_world_matrix = batch.matrix.fastInverted();
const Vec4 rel_cam_pos(inv_world_matrix.transformPoint(camera_pos) / batch.terrain->getXZScale(), 1);
const Vec4 terrain_scale(batch.terrain->getScale(), 0);
const Vec4 terrain_params(batch.terrain->getRootSize()
, (float)detail_texture->width
, (float)splat_texture->width
, 0);
ffr::setUniform4f(m_pipeline->m_terrain_params_uniform, &terrain_params.x);
ffr::setUniform4f(m_pipeline->m_rel_camera_pos_uniform, &rel_cam_pos.x);
ffr::setUniform4f(m_pipeline->m_terrain_scale_uniform, &terrain_scale.x);
ffr::setUniformMatrix4f(m_pipeline->m_terrain_matrix_uniform, &batch.matrix.m11);
const ffr::ProgramHandle prg = batch.shader->getProgram(m_define_mask).handle;
ffr::useProgram(prg);
/*
for (int i = 0; i < m_global_textures_count; ++i) {
const auto& t = m_global_textures[i];
ffr::bindTexture(i, t.texture);
ffr::setUniform1i(t.uniform, i);
}
*/
const Material* material = batch.terrain->m_material;
const int textures_count = material->getTextureCount();
for (int i = 0; i < textures_count; ++i) {
ffr::bindTexture(i + 0, material->getTexture(i)->handle);
ffr::setUniform1i(material->getTextureUniform(i), i + 0);
}
const Mesh& mesh = *batch.terrain->getMesh();
ffr::setVertexBuffer(&decl, mesh.vertex_buffer_handle, 0, nullptr);
ffr::setInstanceBuffer(instance_decl, instance_buffer.buffer, instance_buffer.offset + batch.from * sizeof(m_instance_data[0]), 2);
ffr::setIndexBuffer(mesh.index_buffer_handle);
ffr::setState(u64(ffr::StateFlags::DEPTH_TEST) | batch.terrain->m_material->getRenderStates());
const int submesh_indices_count = mesh.indices_count / 4;
ffr::drawTrianglesInstanced(batch.submesh * submesh_indices_count * sizeof(u16), submesh_indices_count , 1 + batch.to - batch.from);
}
ffr::popDebugGroup();
}
struct InstanceData
{
Vec3 quad_min;
float size;
Vec3 morph_consts;
};
struct Batch
{
Terrain* terrain;
Shader* shader;
Matrix matrix;
uint submesh;
uint from;
uint to;
2018-07-17 01:13:58 +02:00
};
2018-07-22 15:22:36 +02:00
IAllocator& m_allocator;
PipelineImpl* m_pipeline;
CameraParams m_camera_params;
StaticString<32> m_shader_define;
u32 m_define_mask;
Array<InstanceData> m_instance_data;
Array<Batch> m_batches;
struct {
ffr::TextureHandle texture;
ffr::UniformHandle uniform;
} m_global_textures[16];
int m_global_textures_count = 0;
};
2018-07-17 01:13:58 +02:00
2018-07-22 15:22:36 +02:00
struct RenderMeshesCommand : Renderer::RenderCommandBase
{
2018-07-14 10:03:38 +02:00
void setup() override
2018-07-08 18:16:16 +02:00
{
2018-07-14 10:03:38 +02:00
if(!pipeline->m_scene) return;
2018-07-11 23:35:34 +02:00
2018-07-14 10:03:38 +02:00
Renderer& renderer = pipeline->m_renderer;
define_mask = shader_define.empty()
2018-07-08 18:16:16 +02:00
? 0
2018-07-14 10:03:38 +02:00
: 1 << renderer.getShaderDefineIdx(shader_define);
RenderScene* scene = pipeline->getScene();
2018-07-14 18:04:26 +02:00
const Universe& universe = scene->getUniverse();
const Frustum frustum = camera_params.frustum;;
2018-07-22 15:22:36 +02:00
const Vec3 camera_pos = camera_params.pos;
2018-07-14 18:04:26 +02:00
const float lod_multiplier = camera_params.lod_multiplier;
2018-07-14 10:03:38 +02:00
Array<Array<MeshInstance>> meshes(renderer.getAllocator());
2018-07-22 15:22:36 +02:00
scene->getModelInstanceInfos(frustum, camera_pos, lod_multiplier, layer_mask, meshes);
2018-07-08 18:16:16 +02:00
int count = 0;
for(const auto& submeshes : meshes) count += submeshes.size();
2018-07-22 15:22:36 +02:00
m_meshes_mem = renderer.allocate((sizeof(Matrix) + sizeof(Mesh*) + sizeof(Entity)) * count);
m_meshes.matrices = (Matrix*)m_meshes_mem.data;
m_meshes.meshes = (Mesh**)&m_meshes.matrices[count];
m_meshes.owners = (Entity*)&m_meshes.meshes[count];
m_meshes.count = 0;
JobSystem::JobDecl jobs[64];
JobSystem::LambdaJob job_storage[64];
ASSERT(meshes.size() < lengthOf(jobs));
if (!meshes.empty()) {
volatile int counter = 0;
for(const auto& submeshes : meshes) {
const int idx = int(&submeshes - &meshes[0]);
const int offset = m_meshes.count;
JobSystem::fromLambda([idx, this, &meshes, &universe, camera_pos, offset](){
const auto& submeshes = meshes[idx];
int midx = 0;
Mesh** LUMIX_RESTRICT meshes = &m_meshes.meshes[offset];
Matrix* LUMIX_RESTRICT matrices = &m_meshes.matrices[offset];
Entity* LUMIX_RESTRICT owners = &m_meshes.owners[offset];
for(const auto& mesh : submeshes) {
matrices[midx] = universe.getRelativeMatrix(mesh.owner, camera_pos);
meshes[midx] = mesh.mesh;
owners[midx] = mesh.owner;
++midx;
}
}, &job_storage[idx], &jobs[idx], nullptr);
m_meshes.count += submeshes.size();
2018-07-08 18:16:16 +02:00
}
2018-07-22 15:22:36 +02:00
JobSystem::runJobs(jobs, meshes.size(), &counter);
JobSystem::wait(&counter);
2018-07-08 18:16:16 +02:00
}
2018-07-05 15:54:14 +02:00
2018-07-22 15:22:36 +02:00
MT::atomicAdd(&pipeline->m_stats.draw_call_count, m_meshes.count);
2018-07-15 17:35:41 +02:00
2018-07-14 10:03:38 +02:00
const Entity probe = scene->getNearestEnvironmentProbe(pipeline->m_viewport.pos);
2018-07-08 18:16:16 +02:00
if (probe.isValid()) {
2018-07-14 10:03:38 +02:00
const Texture* irradiance = pipeline->m_scene->getEnvironmentProbeIrradiance(probe);
const Texture* radiance = pipeline->m_scene->getEnvironmentProbeRadiance(probe);
irradiance_map = irradiance->handle;
radiance_map = radiance->handle;
2018-07-08 18:16:16 +02:00
}
else {
2018-07-14 10:03:38 +02:00
irradiance_map = pipeline->m_default_cubemap->handle;
radiance_map = pipeline->m_default_cubemap->handle;
2018-07-08 18:16:16 +02:00
}
}
2018-07-17 01:13:58 +02:00
2018-07-22 15:22:36 +02:00
void renderSkinnedMesh(const Matrix& matrix, const Mesh& mesh, const Pose& pose, const Model& model, int model_uniform_loc, int bones_uniform_loc) const
2018-07-17 01:13:58 +02:00
{
Matrix bone_mtx[196];
const Vec3* poss = pose.positions;
const Quat* rots = pose.rotations;
ASSERT(pose.count <= lengthOf(bone_mtx));
for (int bone_index = 0, bone_count = pose.count; bone_index < bone_count; ++bone_index)
{
auto& bone = model.getBone(bone_index);
RigidTransform tmp = {poss[bone_index], rots[bone_index]};
bone_mtx[bone_index] = (tmp * bone.inv_bind_transform).toMatrix();
}
ffr::applyUniformMatrix4fv(bones_uniform_loc, pose.count, &bone_mtx[0].m11);
2018-07-22 15:22:36 +02:00
ffr::applyUniformMatrix4f(model_uniform_loc, &matrix.m11);
ffr::drawTriangles(mesh.indices_count);
2018-07-17 01:13:58 +02:00
}
2018-07-08 18:16:16 +02:00
2018-07-14 10:03:38 +02:00
void execute() override
2018-07-08 18:16:16 +02:00
{
2018-07-11 23:35:34 +02:00
PROFILE_FUNCTION();
2018-07-14 10:03:38 +02:00
ffr::pushDebugGroup(shader_define.empty() ? "meshes" : shader_define);
2018-07-15 17:35:41 +02:00
ffr::setUniform1i(pipeline->m_irradiance_map_uniform, 0);
ffr::setUniform1i(pipeline->m_radiance_map_uniform, 1);
2018-07-08 18:16:16 +02:00
2018-07-15 17:35:41 +02:00
ffr::bindTexture(0, irradiance_map);
ffr::bindTexture(1, radiance_map);
2018-07-01 18:13:44 +02:00
2018-07-15 17:35:41 +02:00
for (int i = 0; i < global_textures_count; ++i) {
const auto& t = global_textures[i];
ffr::bindTexture(2 + i, t.texture);
ffr::setUniform1i(t.uniform, 2 + i);
}
2018-07-17 01:13:58 +02:00
ffr::VertexDecl instance_decl;
instance_decl.addAttribute(4, ffr::AttributeType::FLOAT, false, false);
instance_decl.addAttribute(4, ffr::AttributeType::FLOAT, false, false);
instance_decl.addAttribute(4, ffr::AttributeType::FLOAT, false, false);
instance_decl.addAttribute(4, ffr::AttributeType::FLOAT, false, false);
Renderer& renderer = pipeline->m_renderer;
const u32 instanced_define_mask = 1 << renderer.getShaderDefineIdx("INSTANCED");
const u32 skinned_define_mask = 1 << renderer.getShaderDefineIdx("SKINNED");
const ModelInstance* model_instances = pipeline->m_scene->getModelInstances();
2018-07-22 15:22:36 +02:00
Mesh** LUMIX_RESTRICT meshes = m_meshes.meshes;
const Matrix* LUMIX_RESTRICT matrices = m_meshes.matrices;
const Entity* LUMIX_RESTRICT owners = m_meshes.owners;
int start_instance = -1;
for (int batch = 0, c = m_meshes.count; batch < c; batch += 8 * 1024) {
const Material* prev_material = nullptr;
const Lumix::Mesh* prev_mesh = nullptr;
const Shader::Program* program = nullptr;
int model_uniform_loc = -1;
int bones_uniform_loc = -1;
for (int i = batch, c = Math::minimum(batch + 8 * 1024, m_meshes.count); i < c; ++i) {
const Mesh* mesh = meshes[i];
if(mesh != prev_mesh) {
const Material* material = mesh->material;
if (!material->isReady()) continue;
if(material != prev_material) {
u32 final_define_mask = material->getDefineMask() | define_mask;
if(mesh->type == Mesh::RIGID_INSTANCED) {
final_define_mask |= instanced_define_mask;
}
2018-07-17 01:13:58 +02:00
2018-07-22 15:22:36 +02:00
const Shader::Program& prog = material->getShader()->getProgram(final_define_mask);
if (!prog.handle.isValid()) continue;
program = &prog;
model_uniform_loc = ffr::getUniformLocation(prog.handle, pipeline->m_model_uniform);
bones_uniform_loc = ffr::getUniformLocation(prog.handle, pipeline->m_bones_uniform);
const int textures_count = material->getTextureCount();
for (int i = 0; i < textures_count; ++i) {
ffr::bindTexture(i + 2 + global_textures_count, material->getTexture(i)->handle);
ffr::setUniform1i(material->getTextureUniform(i), i + 2 + global_textures_count);
}
const Vec4 material_params(material->getRoughness()
, material->getMetallic()
, 0
, 1
);
ffr::setUniform4f(pipeline->m_material_params_uniform, &material_params.x);
prev_material = material;
ffr::setState(u64(ffr::StateFlags::DEPTH_TEST) | mesh->material->getRenderStates());
ffr::useProgram(prog.handle);
2018-07-15 17:35:41 +02:00
}
2018-07-22 15:22:36 +02:00
prev_mesh = mesh;
int attribute_map[16];
for (uint i = 0; i < mesh->vertex_decl.attributes_count; ++i) {
attribute_map[i] = program->attribute_by_semantics[(int)mesh->attributes_semantic[i]];
}
ffr::setVertexBuffer(&mesh->vertex_decl, mesh->vertex_buffer_handle, 0, program->use_semantics ? attribute_map : nullptr);
ffr::setIndexBuffer(mesh->index_buffer_handle);
2018-07-15 17:35:41 +02:00
}
2018-07-01 18:13:44 +02:00
2018-07-22 15:22:36 +02:00
switch(mesh->type) {
case Mesh::RIGID_INSTANCED: {
const int start = i;
const Mesh* const instance_mesh = meshes[start];
++i;
while (meshes[i] == instance_mesh && i < c) {
++i;
}
const int instances_count = i - start;
PROFILE_BLOCK("finish_instances");
const Renderer::TransientSlice instance_buffer = pipeline->m_renderer.allocTransient(instances_count * sizeof(Matrix));
2018-07-17 01:13:58 +02:00
2018-07-22 15:22:36 +02:00
ffr::update(instance_buffer.buffer, matrices + start, instance_buffer.offset, instance_buffer.size);
ffr::setInstanceBuffer(instance_decl, instance_buffer.buffer, instance_buffer.offset, instance_mesh->vertex_decl.attributes_count);
ffr::drawTrianglesInstanced(0, instance_mesh->indices_count, instances_count);
break;
}
case Mesh::RIGID:
ffr::applyUniformMatrix4f(model_uniform_loc, &matrices[i].m11);
ffr::drawTriangles(mesh->indices_count);
break;
case Mesh::SKINNED: {
const ModelInstance& model_instance = model_instances[owners[i].index];
renderSkinnedMesh(matrices[i], *mesh, *model_instance.pose, *model_instance.model, model_uniform_loc, bones_uniform_loc);
break;
}
default:
ASSERT(false);
break;
2018-07-17 01:13:58 +02:00
}
}
2018-06-30 15:18:27 +02:00
}
2018-07-17 01:13:58 +02:00
2018-07-22 15:22:36 +02:00
pipeline->m_renderer.free(m_meshes_mem);
2018-07-08 18:16:16 +02:00
ffr::popDebugGroup();
2018-06-30 15:18:27 +02:00
}
2016-10-22 18:08:53 +02:00
2018-07-08 18:16:16 +02:00
2018-07-14 18:04:26 +02:00
CameraParams camera_params;
2018-07-14 10:03:38 +02:00
PipelineImpl* pipeline;
ffr::TextureHandle irradiance_map;
ffr::TextureHandle radiance_map;
2018-07-22 15:22:36 +02:00
struct {
Entity* owners;
Matrix* matrices;
Mesh** meshes;
int count;
} m_meshes;
Renderer::MemRef m_meshes_mem;
2018-07-14 10:03:38 +02:00
StaticString<32> shader_define;
u32 define_mask;
u64 layer_mask;
struct {
2018-07-15 17:35:41 +02:00
ffr::TextureHandle texture;
ffr::UniformHandle uniform;
2018-07-14 10:03:38 +02:00
} global_textures[16];
int global_textures_count = 0;
2018-07-08 18:16:16 +02:00
};
2018-07-01 18:13:44 +02:00
void blending(const char* mode)
{
if(mode[0]) {
}
else {
ffr::blending(0);
}
}
2018-07-06 21:31:44 +02:00
void clear(u32 flags, float r, float g, float b, float a, float depth)
2016-02-15 15:50:24 +01:00
{
2018-06-30 15:18:27 +02:00
const float c[] = { r, g, b, a };
ffr::clear(flags, c, depth);
2016-02-15 15:50:24 +01:00
}
2017-02-18 00:26:56 +01:00
2018-06-30 15:18:27 +02:00
2018-07-06 21:31:44 +02:00
void viewport(int x, int y, int w, int h)
{
2018-07-14 10:03:38 +02:00
struct Cmd : Renderer::RenderCommandBase {
void setup() override {}
void execute() override { ffr::viewport(x, y, w, h); }
int x, y, w, h;
};
Cmd* cmd = LUMIX_NEW(m_renderer.getAllocator(), Cmd);
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
m_renderer.push(cmd);
2018-07-06 21:31:44 +02:00
}
2018-07-14 10:03:38 +02:00
void beginBlock(const char* name)
2018-07-06 21:31:44 +02:00
{
2018-07-14 10:03:38 +02:00
struct Cmd : Renderer::RenderCommandBase
{
void setup() override {}
void execute() override
{
ffr::pushDebugGroup(name);
renderer->beginProfileBlock(name);
}
StaticString<32> name;
Renderer* renderer;
};
Cmd* cmd = LUMIX_NEW(m_renderer.getAllocator(), Cmd);
cmd->name = name;
cmd->renderer = &m_renderer;
m_renderer.push(cmd);
}
void endBlock()
{
struct Cmd : Renderer::RenderCommandBase
{
void setup() override {}
void execute() override
{
renderer->endProfileBlock();
ffr::popDebugGroup();
}
Renderer* renderer;
};
Cmd* cmd = LUMIX_NEW(m_renderer.getAllocator(), Cmd);
cmd->renderer = &m_renderer;
m_renderer.push(cmd);
2018-07-06 21:31:44 +02:00
}
2018-06-30 15:18:27 +02:00
void setOutput(int rb_index)
{
2018-07-11 23:35:34 +02:00
m_output = rb_index;
}
2016-02-18 01:47:49 +01:00
2017-10-10 13:21:45 +02:00
2018-07-05 15:54:14 +02:00
int preloadShader(const char* path)
{
ShaderManager& sm = m_renderer.getShaderManager();
ShaderRef s;
s.res = (Shader*)sm.load(Path(path));
s.id = 0;
for(ShaderRef& i : m_shaders) {
if(i.id >= s.id) {
s.id = i.id + 1;
}
}
m_shaders.push(s);
return s.id;
}
2018-06-30 15:18:27 +02:00
u64 getLayerMask(const char* layer) { return u64(1) << m_renderer.getLayer(layer); }
2016-02-18 01:47:49 +01:00
2018-06-30 15:18:27 +02:00
void setWindowHandle(void* data) override { ASSERT(false); } // TODO
2014-05-20 23:35:09 +02:00
2018-06-30 15:18:27 +02:00
void callLuaFunction(const char* function) override
{
if (!m_lua_state) return;
2018-06-30 15:18:27 +02:00
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
lua_getfield(m_lua_state, -1, function);
if (lua_type(m_lua_state, -1) != LUA_TFUNCTION)
{
lua_pop(m_lua_state, 2);
return;
}
2018-06-30 15:18:27 +02:00
if (lua_pcall(m_lua_state, 0, 0, 0) != 0)
{
2018-06-30 15:18:27 +02:00
g_log_warning.log("Renderer") << lua_tostring(m_lua_state, -1);
lua_pop(m_lua_state, 1);
}
2018-06-30 15:18:27 +02:00
lua_pop(m_lua_state, 1);
}
2018-06-30 15:18:27 +02:00
2018-07-05 15:54:14 +02:00
void registerLuaAPI(lua_State* L)
{
lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, m_lua_env);
auto registerCFunction = [L, this](const char* name, lua_CFunction function) {
lua_pushlightuserdata(L, this);
lua_pushcclosure(L, function, 1);
lua_setfield(L, -3, name);
};
auto registerConst = [L](const char* name, u32 value)
{
lua_pushinteger(L, value);
lua_setfield(L, -2, name);
};
#define REGISTER_FUNCTION(name) \
do {\
auto f = &LuaWrapper::wrapMethodClosure<PipelineImpl, decltype(&PipelineImpl::name), &PipelineImpl::name>; \
registerCFunction(#name, f); \
} while(false) \
2018-07-14 10:03:38 +02:00
REGISTER_FUNCTION(beginBlock);
2018-07-05 15:54:14 +02:00
REGISTER_FUNCTION(blending);
2018-07-06 21:31:44 +02:00
REGISTER_FUNCTION(clear);
2018-07-05 15:54:14 +02:00
REGISTER_FUNCTION(createRenderbuffer);
2018-07-14 10:03:38 +02:00
REGISTER_FUNCTION(endBlock);
2018-07-05 15:54:14 +02:00
REGISTER_FUNCTION(executeCustomCommand);
REGISTER_FUNCTION(getLayerMask);
REGISTER_FUNCTION(preloadShader);
REGISTER_FUNCTION(render2D);
REGISTER_FUNCTION(renderDebugShapes);
REGISTER_FUNCTION(setOutput);
2018-07-06 21:31:44 +02:00
REGISTER_FUNCTION(viewport);
2018-07-05 15:54:14 +02:00
2018-07-08 00:44:45 +02:00
registerConst("CLEAR_DEPTH", (uint)ffr::ClearFlags::DEPTH);
2018-07-05 15:54:14 +02:00
registerConst("CLEAR_ALL", (uint)ffr::ClearFlags::COLOR | (uint)ffr::ClearFlags::DEPTH);
registerCFunction("drawArray", PipelineImpl::drawArray);
2018-07-06 21:31:44 +02:00
registerCFunction("getCameraParams", PipelineImpl::getCameraParams);
registerCFunction("getShadowCameraParams", PipelineImpl::getShadowCameraParams);
2018-07-14 10:03:38 +02:00
registerCFunction("renderMeshes", PipelineImpl::renderMeshes);
2018-07-22 15:22:36 +02:00
registerCFunction("renderTerrains", PipelineImpl::renderTerrains);
2018-07-06 21:31:44 +02:00
registerCFunction("setRenderTargets", PipelineImpl::setRenderTargets);
2018-07-05 15:54:14 +02:00
lua_pop(L, 1); // pop env
#undef REGISTER_FUNCTION
}
2018-06-30 15:18:27 +02:00
bool isReady() const override { return m_is_ready; }
const Stats& getStats() const override { return m_stats; }
Path& getPath() override { return m_path; }
2018-06-30 15:18:27 +02:00
Draw2D& getDraw2D() override { return m_draw2d; }
2018-07-11 23:35:34 +02:00
ffr::TextureHandle getOutput() override {
2018-07-14 10:03:38 +02:00
if (m_output < 0) return ffr::INVALID_TEXTURE;
2018-07-11 23:35:34 +02:00
return m_renderbuffers[m_output].handle;
}
2018-06-30 15:18:27 +02:00
struct Renderbuffer {
uint width;
uint height;
2018-07-11 23:35:34 +02:00
bool use_realtive_size;
Vec2 relative_size;
2018-06-30 15:18:27 +02:00
ffr::TextureFormat format;
2018-07-11 23:35:34 +02:00
ffr::TextureHandle handle;
2018-06-30 15:18:27 +02:00
int frame_counter;
};
2018-07-05 15:54:14 +02:00
struct ShaderRef {
Lumix::Shader* res;
int id;
};
2018-07-01 23:37:56 +02:00
IAllocator& m_allocator;
2018-06-30 15:18:27 +02:00
Renderer& m_renderer;
Path m_path;
lua_State* m_lua_state;
int m_lua_thread_ref;
int m_lua_env;
bool m_is_ready;
bool m_is_first_render;
2018-07-06 21:31:44 +02:00
StaticString<32> m_define;
2018-06-30 15:18:27 +02:00
RenderScene* m_scene;
Draw2D m_draw2d;
Shader* m_draw2d_shader;
Stats m_stats;
2018-07-08 00:44:45 +02:00
Viewport m_viewport;
2018-07-11 23:35:34 +02:00
int m_output;
2018-07-01 23:37:56 +02:00
Shader* m_debug_shape_shader;
2018-07-05 15:54:14 +02:00
Texture* m_default_cubemap;
2018-06-30 15:18:27 +02:00
Array<CustomCommandHandler> m_custom_commands_handlers;
Array<Renderbuffer> m_renderbuffers;
2018-07-05 15:54:14 +02:00
Array<ShaderRef> m_shaders;
2018-07-15 17:35:41 +02:00
2018-07-22 15:22:36 +02:00
ffr::UniformHandle m_terrain_params_uniform;
ffr::UniformHandle m_rel_camera_pos_uniform;
ffr::UniformHandle m_terrain_scale_uniform;
ffr::UniformHandle m_terrain_matrix_uniform;
2018-07-15 17:35:41 +02:00
ffr::UniformHandle m_model_uniform;
2018-07-17 01:13:58 +02:00
ffr::UniformHandle m_bones_uniform;
2018-07-15 17:35:41 +02:00
ffr::UniformHandle m_canvas_size_uniform;
ffr::UniformHandle m_texture_uniform;
ffr::UniformHandle m_irradiance_map_uniform;
ffr::UniformHandle m_radiance_map_uniform;
ffr::UniformHandle m_material_params_uniform;
2018-06-30 15:18:27 +02:00
};
2017-10-06 17:52:00 +02:00
2018-07-08 00:44:45 +02:00
Pipeline* Pipeline::create(Renderer& renderer, const Path& path, const char* define, IAllocator& allocator)
2015-09-02 11:14:42 +02:00
{
2018-07-08 00:44:45 +02:00
return LUMIX_NEW(allocator, PipelineImpl)(renderer, path, define, allocator);
2015-09-02 11:14:42 +02:00
}
2018-06-30 15:18:27 +02:00
void Pipeline::destroy(Pipeline* pipeline)
2015-07-23 23:17:51 +02:00
{
2018-07-14 17:52:06 +02:00
PipelineImpl* p = (PipelineImpl*)pipeline;
LUMIX_DELETE(p->m_allocator, p);
2015-07-23 23:17:51 +02:00
}
2018-06-30 15:18:27 +02:00
} // namespace Lumix