asset browser: add test system of rendering model previews
This commit is contained in:
@@ -19,7 +19,7 @@ bool AsyncLoader::is_loading()
|
||||
{
|
||||
for (auto async_obj : _currently_loading)
|
||||
{
|
||||
if (!async_obj->loading_failed() && async_obj->filename.length()) // the filename check is a hack
|
||||
if ((!async_obj->loading_failed() && async_obj->filename.length())) // the filename check is a hack
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -95,7 +95,9 @@ void AsyncLoader::process()
|
||||
catch (...)
|
||||
{
|
||||
std::lock_guard<std::mutex> const lock(_guard);
|
||||
|
||||
object->error_on_loading();
|
||||
_currently_loading.remove(object);
|
||||
|
||||
if (object->is_required_when_saving())
|
||||
{
|
||||
|
||||
@@ -6,7 +6,8 @@ namespace noggit
|
||||
enum NoggitRenderContext
|
||||
{
|
||||
MAP_VIEW,
|
||||
ASSET_BROWSER
|
||||
ASSET_BROWSER,
|
||||
ASSET_BROWSER_PREVIEW
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,8 @@ using namespace noggit::Red::AssetBrowser;
|
||||
static const float XSENS = 15.0f;
|
||||
static const float YSENS = 15.0f;
|
||||
|
||||
ModelViewer::ModelViewer(QWidget *parent)
|
||||
: QOpenGLWidget(parent)
|
||||
, _camera (math::vector_3d(0.0f, 0.0f, 0.0f), math::degrees(0.0f), math::degrees(0.0f))
|
||||
, _settings (new QSettings (this))
|
||||
|
||||
ModelViewer::ModelViewer(QWidget* parent)
|
||||
: PreviewRenderer(0, 0, noggit::NoggitRenderContext::ASSET_BROWSER, parent)
|
||||
{
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking (true);
|
||||
@@ -33,73 +30,6 @@ ModelViewer::ModelViewer(QWidget *parent)
|
||||
connect (&_update_every_event_loop, &QTimer::timeout, [this] { update(); });
|
||||
}
|
||||
|
||||
void ModelViewer::setModel(std::string const &filename)
|
||||
{
|
||||
opengl::context::scoped_setter const _ (::gl, context());
|
||||
makeCurrent();
|
||||
|
||||
// remove old instance
|
||||
if (_model_instance.which() == eEntry_WMO)
|
||||
{
|
||||
delete boost::get<selected_wmo_type>(_model_instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete boost::get<selected_model_type>(_model_instance);
|
||||
}
|
||||
|
||||
// add new model instance
|
||||
QString q_filename = QString(filename.c_str());
|
||||
|
||||
if (q_filename.endsWith(".wmo"))
|
||||
{
|
||||
auto instance = new WMOInstance(filename, noggit::NoggitRenderContext::ASSET_BROWSER);
|
||||
_model_instance = instance;
|
||||
instance->wmo->wait_until_loaded();
|
||||
instance->recalcExtents();
|
||||
|
||||
}
|
||||
else if (q_filename.endsWith(".m2"))
|
||||
{
|
||||
auto instance = new ModelInstance(filename, noggit::NoggitRenderContext::ASSET_BROWSER);
|
||||
_model_instance = instance;
|
||||
instance->model->wait_until_loaded();
|
||||
instance->recalcExtents();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::logic_error("Asset browser only supports viewing M2 and WMO for now.");
|
||||
}
|
||||
|
||||
resetCamera();
|
||||
}
|
||||
|
||||
|
||||
void ModelViewer::resetCamera()
|
||||
{
|
||||
_camera.reset();
|
||||
float radius = 0.f;
|
||||
|
||||
if (_model_instance.which() == eEntry_WMO)
|
||||
{
|
||||
WMOInstance* wmo = boost::get<selected_wmo_type>(_model_instance);
|
||||
_camera.position = (wmo->extents[0] + wmo->extents[1]) / 2.0f;
|
||||
radius = std::max((_camera.position - wmo->extents[0]).length(), (_camera.position - wmo->extents[1]).length());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelInstance* model = boost::get<selected_model_type>(_model_instance);
|
||||
_camera.position = (model->extents()[0] + model->extents()[1]) / 2.0f;
|
||||
radius = std::max((_camera.position - model->extents()[0]).length(), (_camera.position - model->extents()[1]).length());
|
||||
}
|
||||
|
||||
float distance_factor = abs( aspect_ratio() * radius / sin(_camera.fov()._ / 2.f));
|
||||
_camera.move_forward_factor(-1.f, distance_factor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ModelViewer::initializeGL()
|
||||
{
|
||||
opengl::context::scoped_setter const _ (::gl, context());
|
||||
@@ -153,216 +83,11 @@ void ModelViewer::tick(float dt)
|
||||
}
|
||||
}
|
||||
|
||||
void ModelViewer::draw()
|
||||
void ModelViewer::setModel(std::string const& filename)
|
||||
{
|
||||
|
||||
opengl::context::scoped_setter const _ (::gl, context());
|
||||
makeCurrent();
|
||||
|
||||
float culldistance = 10000000;
|
||||
bool draw_doodads_wmo = true;
|
||||
|
||||
math::matrix_4x4 const mvp(model_view().transposed() * projection().transposed());
|
||||
math::frustum const frustum (mvp);
|
||||
|
||||
if (!_m2_program)
|
||||
{
|
||||
setModel("world/wmo/azeroth/buildings/human_farm/farm.wmo");
|
||||
_m2_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("m2_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("m2_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!_m2_instanced_program)
|
||||
{
|
||||
_m2_instanced_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("m2_vs", {"instanced"}) }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("m2_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_m2_ribbons_program)
|
||||
{
|
||||
_m2_ribbons_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("ribbon_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("ribbon_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!_m2_particles_program)
|
||||
{
|
||||
_m2_particles_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("particle_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("particle_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_wmo_program)
|
||||
{
|
||||
_wmo_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("wmo_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("wmo_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_liquid_render)
|
||||
{
|
||||
_liquid_render.emplace();
|
||||
}
|
||||
|
||||
gl.enable(GL_DEPTH_TEST);
|
||||
gl.depthFunc(GL_LEQUAL);
|
||||
gl.enable(GL_BLEND);
|
||||
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// draw WMOs
|
||||
std::unordered_map<std::string, std::vector<ModelInstance*>> _wmo_doodads;
|
||||
|
||||
if (_model_instance.which() == eEntry_WMO)
|
||||
{
|
||||
// set anim time only once per frame
|
||||
{
|
||||
opengl::scoped::use_program water_shader {_liquid_render->shader_program()};
|
||||
water_shader.uniform("animtime", 0 / 2880.f);
|
||||
|
||||
water_shader.uniform("model_view", model_view().transposed());
|
||||
water_shader.uniform("projection", projection().transposed());
|
||||
|
||||
math::vector_4d ocean_color_light(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
math::vector_4d ocean_color_dark(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
math::vector_4d river_color_light(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
math::vector_4d river_color_dark(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
|
||||
water_shader.uniform("ocean_color_light", ocean_color_light);
|
||||
water_shader.uniform("ocean_color_dark", ocean_color_dark);
|
||||
water_shader.uniform("river_color_light", river_color_light);
|
||||
water_shader.uniform("river_color_dark", river_color_dark);
|
||||
water_shader.uniform("use_transform", 1);
|
||||
}
|
||||
|
||||
{
|
||||
opengl::scoped::use_program wmo_program{*_wmo_program.get()};
|
||||
|
||||
wmo_program.uniform("model_view", model_view().transposed());
|
||||
wmo_program.uniform("projection", projection().transposed());
|
||||
wmo_program.uniform("tex1", 0);
|
||||
wmo_program.uniform("tex2", 1);
|
||||
|
||||
wmo_program.uniform("draw_fog", 0);
|
||||
|
||||
wmo_program.uniform("exterior_light_dir", math::vector_3d(0.0f, 1.0f, 0.0f));
|
||||
wmo_program.uniform("exterior_diffuse_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
wmo_program.uniform("exterior_ambient_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
|
||||
auto wmo_instance = boost::get<selected_wmo_type>(_model_instance);
|
||||
|
||||
wmo_instance->draw(
|
||||
wmo_program, model_view().transposed(), projection().transposed(), frustum, culldistance,
|
||||
math::vector_3d(0.0f, 0.0f, 0.0f), false, false // doodads
|
||||
, false, _liquid_render.get(), std::vector<selection_type>(), 0, false, display_mode::in_3D
|
||||
);
|
||||
|
||||
gl.enable(GL_BLEND);
|
||||
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
gl.enable(GL_CULL_FACE);
|
||||
|
||||
if (draw_doodads_wmo)
|
||||
{
|
||||
for (auto &doodad : wmo_instance->get_visible_doodads(frustum, culldistance, _camera.position, false,
|
||||
display_mode::in_3D))
|
||||
{
|
||||
_wmo_doodads[doodad->model->filename].push_back(doodad);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// draw M2
|
||||
std::unordered_map<Model*, std::size_t> model_with_particles;
|
||||
std::unordered_map<Model*, std::size_t> model_boxes_to_draw;
|
||||
|
||||
{
|
||||
opengl::scoped::use_program m2_shader {*_m2_instanced_program.get()};
|
||||
|
||||
m2_shader.uniform("model_view", model_view().transposed());
|
||||
m2_shader.uniform("projection", projection().transposed());
|
||||
m2_shader.uniform("tex1", 0);
|
||||
m2_shader.uniform("tex2", 1);
|
||||
m2_shader.uniform("draw_fog", 0);
|
||||
|
||||
m2_shader.uniform("light_dir", math::vector_3d(0.0f, 1.0f, 0.0f));
|
||||
m2_shader.uniform("diffuse_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
m2_shader.uniform("ambient_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
|
||||
if (_model_instance.which() == eEntry_Model)
|
||||
{
|
||||
auto model_instance = boost::get<selected_model_type>(_model_instance);
|
||||
|
||||
model_instance->model->draw(
|
||||
model_view().transposed()
|
||||
, std::vector<ModelInstance*>{model_instance}
|
||||
, m2_shader
|
||||
, frustum
|
||||
, culldistance
|
||||
, _camera.position
|
||||
, false
|
||||
, 0
|
||||
, false
|
||||
, false
|
||||
, model_with_particles
|
||||
, model_boxes_to_draw
|
||||
, display_mode::in_3D
|
||||
);
|
||||
}
|
||||
|
||||
if (draw_doodads_wmo)
|
||||
{
|
||||
for (auto& it : _wmo_doodads)
|
||||
{
|
||||
it.second[0]->model->draw(
|
||||
model_view().transposed()
|
||||
, it.second
|
||||
, m2_shader
|
||||
, frustum
|
||||
, culldistance
|
||||
, _camera.position
|
||||
, false
|
||||
, 0
|
||||
, false
|
||||
, false
|
||||
, model_with_particles
|
||||
, model_boxes_to_draw
|
||||
, display_mode::in_3D
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gl.bindVertexArray(0);
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
math::matrix_4x4 ModelViewer::model_view() const
|
||||
{
|
||||
return _camera.look_at_matrix();
|
||||
}
|
||||
|
||||
math::matrix_4x4 ModelViewer::projection() const
|
||||
{
|
||||
float far_z = _settings->value("farZ", 2048).toFloat();
|
||||
return math::perspective(_camera.fov(), aspect_ratio(), 1.f, far_z);
|
||||
PreviewRenderer::setModel(filename);
|
||||
}
|
||||
|
||||
float ModelViewer::aspect_ratio() const
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <QElapsedTimer>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
#include <math/matrix_4x4.hpp>
|
||||
#include <math/vector_3d.hpp>
|
||||
#include <noggit/camera.hpp>
|
||||
@@ -20,6 +19,7 @@
|
||||
#include <noggit/Selection.h>
|
||||
#include <noggit/WMO.h>
|
||||
#include <noggit/Model.h>
|
||||
#include <noggit/Red/PreviewRenderer/PreviewRenderer.hpp>
|
||||
|
||||
|
||||
namespace noggit
|
||||
@@ -27,15 +27,13 @@ namespace noggit
|
||||
namespace Red::AssetBrowser
|
||||
{
|
||||
|
||||
class ModelViewer : public QOpenGLWidget
|
||||
class ModelViewer : public PreviewRenderer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ModelViewer(QWidget* parent = nullptr);
|
||||
|
||||
void resetCamera();
|
||||
void setModel(std::string const& filename);
|
||||
void setModel(std::string const& filename) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -47,25 +45,8 @@ namespace noggit
|
||||
QElapsedTimer _startup_time;
|
||||
qreal _last_update = 0.f;
|
||||
|
||||
noggit::camera _camera;
|
||||
QSettings* _settings;
|
||||
|
||||
std::unique_ptr<opengl::program> _m2_program;
|
||||
std::unique_ptr<opengl::program> _m2_instanced_program;
|
||||
std::unique_ptr<opengl::program> _m2_particles_program;
|
||||
std::unique_ptr<opengl::program> _m2_ribbons_program;
|
||||
std::unique_ptr<opengl::program> _m2_box_program;
|
||||
std::unique_ptr<opengl::program> _wmo_program;
|
||||
|
||||
boost::optional<liquid_render> _liquid_render = boost::none;
|
||||
|
||||
selection_type _model_instance;
|
||||
|
||||
void tick(float dt);
|
||||
void draw();
|
||||
math::matrix_4x4 model_view() const;
|
||||
math::matrix_4x4 projection() const;
|
||||
float aspect_ratio() const;
|
||||
float aspect_ratio() const override;
|
||||
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
#include <ui_AssetBrowser.h>
|
||||
#include <noggit/MPQ.h>
|
||||
#include <noggit/Log.h>
|
||||
#include <noggit/ContextObject.hpp>
|
||||
|
||||
#include <QStandardItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QRegularExpression>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
using namespace noggit::Red::AssetBrowser::Ui;
|
||||
|
||||
@@ -18,9 +21,12 @@ AssetBrowserWidget::AssetBrowserWidget(QWidget *parent)
|
||||
ui->setupUi(this);
|
||||
|
||||
_model = new QStandardItemModel(this);
|
||||
|
||||
ui->listfileTree->setIconSize(QSize(128, 128));
|
||||
ui->listfileTree->setModel(_model);
|
||||
|
||||
_preview_renderer = new PreviewRenderer(128, 128, noggit::NoggitRenderContext::ASSET_BROWSER_PREVIEW, this);
|
||||
_preview_renderer->setVisible(false);
|
||||
|
||||
connect(ui->listfileTree->selectionModel(), &QItemSelectionModel::selectionChanged
|
||||
,[=] (const QItemSelection& selected, const QItemSelection& deselected)
|
||||
{
|
||||
@@ -45,7 +51,11 @@ AssetBrowserWidget::AssetBrowserWidget(QWidget *parent)
|
||||
auto path = child.data(Qt::UserRole).toString();
|
||||
if (path.endsWith(".wmo") || path.endsWith(".m2"))
|
||||
{
|
||||
// do icon rendering here
|
||||
_preview_renderer->setModelOffscreen(path.toStdString());
|
||||
|
||||
auto preview_pixmap = _preview_renderer->renderToPixmap();
|
||||
auto item = _model->itemFromIndex(child);
|
||||
item->setIcon(QIcon(*preview_pixmap));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <ui_AssetBrowser.h>
|
||||
#include <noggit/Red/AssetBrowser/Ui/Model/TreeManager.hpp>
|
||||
#include <noggit/Red/PreviewRenderer/PreviewRenderer.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QStandardItemModel>
|
||||
@@ -21,6 +22,8 @@ namespace noggit
|
||||
private:
|
||||
::Ui::AssetBrowser* ui;
|
||||
QStandardItemModel* _model;
|
||||
PreviewRenderer* _preview_renderer;
|
||||
|
||||
|
||||
void updateModelData();
|
||||
void recurseDirectory(Model::TreeManager& tree_mgr, const QString& s_dir, const QString& project_dir);
|
||||
|
||||
388
src/noggit/Red/PreviewRenderer/PreviewRenderer.cpp
Normal file
388
src/noggit/Red/PreviewRenderer/PreviewRenderer.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
#include "PreviewRenderer.hpp"
|
||||
|
||||
#include <opengl/scoped.hpp>
|
||||
#include <math/projection.hpp>
|
||||
#include <noggit/Selection.h>
|
||||
#include <noggit/tool_enums.hpp>
|
||||
#include <noggit/AsyncLoader.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
|
||||
using namespace noggit::Red;
|
||||
|
||||
|
||||
PreviewRenderer::PreviewRenderer(int width, int height, noggit::NoggitRenderContext context, QWidget* parent)
|
||||
: QOpenGLWidget(parent)
|
||||
, _camera (math::vector_3d(0.0f, 0.0f, 0.0f), math::degrees(0.0f), math::degrees(0.0f))
|
||||
, _settings (new QSettings())
|
||||
, _width(width)
|
||||
, _height(height)
|
||||
, _context(context)
|
||||
{
|
||||
_cache = {};
|
||||
|
||||
opengl::context::save_current_context const context_save (::gl);
|
||||
_offscreen_context.create();
|
||||
|
||||
_fmt.setSamples(1);
|
||||
_fmt.setInternalTextureFormat(GL_RGBA8);
|
||||
_fmt.setAttachment(QOpenGLFramebufferObject::Depth);
|
||||
|
||||
_offscreen_surface.create();
|
||||
_offscreen_context.makeCurrent(&_offscreen_surface);
|
||||
|
||||
opengl::context::scoped_setter const context_set (::gl, &_offscreen_context);
|
||||
}
|
||||
|
||||
void PreviewRenderer::setModel(std::string const &filename)
|
||||
{
|
||||
_filename = filename;
|
||||
_model_instances.clear();
|
||||
_wmo_instances.clear();
|
||||
|
||||
// add new model instance
|
||||
QString q_filename = QString(filename.c_str());
|
||||
|
||||
if (q_filename.endsWith(".wmo"))
|
||||
{
|
||||
auto& instance = _wmo_instances.emplace_back(filename, _context);
|
||||
instance.wmo->wait_until_loaded();
|
||||
instance.recalcExtents();
|
||||
|
||||
}
|
||||
else if (q_filename.endsWith(".m2"))
|
||||
{
|
||||
auto& instance = _model_instances.emplace_back(filename, _context);
|
||||
instance.model->wait_until_loaded();
|
||||
instance.recalcExtents();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::logic_error("Preview renderer only supports viewing M2 and WMO for now.");
|
||||
}
|
||||
|
||||
resetCamera();
|
||||
}
|
||||
|
||||
void PreviewRenderer::setModelOffscreen(std::string const& filename)
|
||||
{
|
||||
opengl::context::save_current_context const context_save (::gl);
|
||||
_offscreen_context.makeCurrent(&_offscreen_surface);
|
||||
opengl::context::scoped_setter const context_set (::gl, &_offscreen_context);
|
||||
|
||||
setModel(filename);
|
||||
}
|
||||
|
||||
|
||||
void PreviewRenderer::resetCamera()
|
||||
{
|
||||
_camera.reset();
|
||||
float radius = 0.f;
|
||||
|
||||
std::vector<math::vector_3d> extents = calcSceneExtents();
|
||||
_camera.position = (extents[0] + extents[1]) / 2.0f;
|
||||
radius = std::max((_camera.position - extents[0]).length(), (_camera.position - extents[1]).length());
|
||||
|
||||
float distance_factor = abs( aspect_ratio() * radius / sin(_camera.fov()._ / 2.f));
|
||||
_camera.move_forward_factor(-1.f, distance_factor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PreviewRenderer::draw()
|
||||
{
|
||||
|
||||
float culldistance = 10000000;
|
||||
bool draw_doodads_wmo = true;
|
||||
|
||||
math::matrix_4x4 const mvp(model_view().transposed() * projection().transposed());
|
||||
math::frustum const frustum (mvp);
|
||||
|
||||
if (!_m2_program)
|
||||
{
|
||||
setModel("world/wmo/azeroth/buildings/human_farm/farm.wmo");
|
||||
_m2_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("m2_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("m2_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!_m2_instanced_program)
|
||||
{
|
||||
_m2_instanced_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("m2_vs", {"instanced"}) }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("m2_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_m2_ribbons_program)
|
||||
{
|
||||
_m2_ribbons_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("ribbon_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("ribbon_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!_m2_particles_program)
|
||||
{
|
||||
_m2_particles_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("particle_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("particle_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_wmo_program)
|
||||
{
|
||||
_wmo_program.reset
|
||||
( new opengl::program
|
||||
{ { GL_VERTEX_SHADER, opengl::shader::src_from_qrc("wmo_vs") }
|
||||
, { GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("wmo_fs") }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_liquid_render)
|
||||
{
|
||||
_liquid_render.emplace();
|
||||
}
|
||||
|
||||
gl.enable(GL_DEPTH_TEST);
|
||||
gl.depthFunc(GL_LEQUAL);
|
||||
gl.enable(GL_BLEND);
|
||||
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// draw WMOs
|
||||
std::unordered_map<std::string, std::vector<ModelInstance*>> _wmo_doodads;
|
||||
|
||||
if (!_wmo_instances.empty())
|
||||
{
|
||||
// set anim time only once per frame
|
||||
{
|
||||
opengl::scoped::use_program water_shader {_liquid_render->shader_program()};
|
||||
water_shader.uniform("animtime", 0 / 2880.f);
|
||||
|
||||
water_shader.uniform("model_view", model_view().transposed());
|
||||
water_shader.uniform("projection", projection().transposed());
|
||||
|
||||
math::vector_4d ocean_color_light(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
math::vector_4d ocean_color_dark(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
math::vector_4d river_color_light(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
math::vector_4d river_color_dark(math::vector_3d(1.0f, 1.0f, 1.0f), 1.f);
|
||||
|
||||
water_shader.uniform("ocean_color_light", ocean_color_light);
|
||||
water_shader.uniform("ocean_color_dark", ocean_color_dark);
|
||||
water_shader.uniform("river_color_light", river_color_light);
|
||||
water_shader.uniform("river_color_dark", river_color_dark);
|
||||
water_shader.uniform("use_transform", 1);
|
||||
}
|
||||
|
||||
{
|
||||
opengl::scoped::use_program wmo_program{*_wmo_program.get()};
|
||||
|
||||
wmo_program.uniform("model_view", model_view().transposed());
|
||||
wmo_program.uniform("projection", projection().transposed());
|
||||
wmo_program.uniform("tex1", 0);
|
||||
wmo_program.uniform("tex2", 1);
|
||||
|
||||
wmo_program.uniform("draw_fog", 0);
|
||||
|
||||
wmo_program.uniform("exterior_light_dir", math::vector_3d(0.0f, 1.0f, 0.0f));
|
||||
wmo_program.uniform("exterior_diffuse_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
wmo_program.uniform("exterior_ambient_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
|
||||
for (auto& wmo_instance : _wmo_instances)
|
||||
{
|
||||
wmo_instance.draw(
|
||||
wmo_program, model_view().transposed(), projection().transposed(), frustum, culldistance,
|
||||
math::vector_3d(0.0f, 0.0f, 0.0f), false, false // doodads
|
||||
, false, _liquid_render.get(), std::vector<selection_type>(), 0, false, display_mode::in_3D
|
||||
);
|
||||
|
||||
gl.enable(GL_BLEND);
|
||||
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
gl.enable(GL_CULL_FACE);
|
||||
|
||||
if (draw_doodads_wmo)
|
||||
{
|
||||
for (auto& doodad : wmo_instance.get_visible_doodads(frustum, culldistance, _camera.position, false,
|
||||
display_mode::in_3D))
|
||||
{
|
||||
_wmo_doodads[doodad->model->filename].push_back(doodad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// draw M2
|
||||
std::unordered_map<Model*, std::size_t> model_with_particles;
|
||||
std::unordered_map<Model*, std::size_t> model_boxes_to_draw;
|
||||
|
||||
{
|
||||
opengl::scoped::use_program m2_shader {*_m2_instanced_program.get()};
|
||||
|
||||
m2_shader.uniform("model_view", model_view().transposed());
|
||||
m2_shader.uniform("projection", projection().transposed());
|
||||
m2_shader.uniform("tex1", 0);
|
||||
m2_shader.uniform("tex2", 1);
|
||||
m2_shader.uniform("draw_fog", 0);
|
||||
|
||||
m2_shader.uniform("light_dir", math::vector_3d(0.0f, 1.0f, 0.0f));
|
||||
m2_shader.uniform("diffuse_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
m2_shader.uniform("ambient_color", math::vector_3d(1.0f, 1.0f, 1.0f));
|
||||
|
||||
for (auto& model_instance : _model_instances)
|
||||
{
|
||||
model_instance.model->draw(
|
||||
model_view().transposed()
|
||||
, std::vector<ModelInstance*>{&model_instance}
|
||||
, m2_shader
|
||||
, frustum
|
||||
, culldistance
|
||||
, _camera.position
|
||||
, false
|
||||
, 0
|
||||
, false
|
||||
, false
|
||||
, model_with_particles
|
||||
, model_boxes_to_draw
|
||||
, display_mode::in_3D
|
||||
);
|
||||
}
|
||||
|
||||
if (draw_doodads_wmo)
|
||||
{
|
||||
for (auto& it : _wmo_doodads)
|
||||
{
|
||||
it.second[0]->model->draw(
|
||||
model_view().transposed()
|
||||
, it.second
|
||||
, m2_shader
|
||||
, frustum
|
||||
, culldistance
|
||||
, _camera.position
|
||||
, false
|
||||
, 0
|
||||
, false
|
||||
, false
|
||||
, model_with_particles
|
||||
, model_boxes_to_draw
|
||||
, display_mode::in_3D
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gl.bindVertexArray(0);
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
math::matrix_4x4 PreviewRenderer::model_view() const
|
||||
{
|
||||
return _camera.look_at_matrix();
|
||||
}
|
||||
|
||||
math::matrix_4x4 PreviewRenderer::projection() const
|
||||
{
|
||||
float far_z = _settings->value("farZ", 2048).toFloat();
|
||||
return math::perspective(_camera.fov(), aspect_ratio(), 1.f, far_z);
|
||||
}
|
||||
|
||||
float PreviewRenderer::aspect_ratio() const
|
||||
{
|
||||
return static_cast<float>(_width) / static_cast<float>(_height);
|
||||
}
|
||||
|
||||
std::vector<math::vector_3d> PreviewRenderer::calcSceneExtents()
|
||||
{
|
||||
math::vector_3d min = {std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max()};
|
||||
|
||||
math::vector_3d max = {std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::min()};
|
||||
|
||||
for (auto instance : _model_instances)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
min[i] = std::min(instance.extents()[0][i], min[i]);
|
||||
max[i] = std::max(instance.extents()[1][i], max[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto instance : _wmo_instances)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
min[i] = std::min(instance.extents[0][i], min[i]);
|
||||
max[i] = std::max(instance.extents[1][i], max[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(std::vector<math::vector_3d>{min, max});
|
||||
}
|
||||
|
||||
QPixmap* PreviewRenderer::renderToPixmap()
|
||||
{
|
||||
std::tuple<std::string, int, int> const curEntry{_filename, _width, _height};
|
||||
auto it{_cache.find(curEntry)};
|
||||
|
||||
if(it != _cache.end())
|
||||
return &it->second;
|
||||
|
||||
opengl::context::save_current_context const context_save (::gl);
|
||||
|
||||
_offscreen_context.makeCurrent(&_offscreen_surface);
|
||||
|
||||
opengl::context::scoped_setter const context_set (::gl, &_offscreen_context);
|
||||
|
||||
QOpenGLFramebufferObject pixel_buffer(_width, _height, _fmt);
|
||||
pixel_buffer.bind();
|
||||
|
||||
gl.viewport(0, 0, _width, _height);
|
||||
gl.clearColor(0.75f, 0.5f, 0.5f, 1.f);
|
||||
gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
draw();
|
||||
|
||||
auto& async_loader = AsyncLoader::instance();
|
||||
|
||||
while(async_loader.is_loading())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
draw();
|
||||
}
|
||||
|
||||
QPixmap result{};
|
||||
result = std::move(QPixmap::fromImage(pixel_buffer.toImage()));
|
||||
pixel_buffer.release();
|
||||
|
||||
if (result.isNull())
|
||||
{
|
||||
throw std::runtime_error("failed rendering " + _filename + " to pixmap");
|
||||
}
|
||||
|
||||
return &(_cache[curEntry] = std::move(result));
|
||||
}
|
||||
|
||||
|
||||
79
src/noggit/Red/PreviewRenderer/PreviewRenderer.hpp
Normal file
79
src/noggit/Red/PreviewRenderer/PreviewRenderer.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef NOGGIT_PREVIEWRENDERER_HPP
|
||||
#define NOGGIT_PREVIEWRENDERER_HPP
|
||||
|
||||
#include <math/matrix_4x4.hpp>
|
||||
#include <math/vector_3d.hpp>
|
||||
#include <noggit/camera.hpp>
|
||||
#include <noggit/WMOInstance.h>
|
||||
#include <noggit/ModelInstance.h>
|
||||
#include <noggit/WMO.h>
|
||||
#include <noggit/Model.h>
|
||||
#include <noggit/ContextObject.hpp>
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QSettings>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFramebufferObjectFormat>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace noggit::Red
|
||||
{
|
||||
|
||||
class PreviewRenderer : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PreviewRenderer(int width, int height, noggit::NoggitRenderContext context, QWidget* parent = nullptr);
|
||||
|
||||
void resetCamera();
|
||||
QPixmap* renderToPixmap();
|
||||
|
||||
virtual void setModel(std::string const& filename);
|
||||
void setModelOffscreen(std::string const& filename);
|
||||
virtual void setPrefab(std::string const& filename) {};
|
||||
|
||||
protected:
|
||||
|
||||
noggit::camera _camera;
|
||||
QSettings* _settings;
|
||||
std::string _filename;
|
||||
|
||||
std::unique_ptr<opengl::program> _m2_program;
|
||||
std::unique_ptr<opengl::program> _m2_instanced_program;
|
||||
std::unique_ptr<opengl::program> _m2_particles_program;
|
||||
std::unique_ptr<opengl::program> _m2_ribbons_program;
|
||||
std::unique_ptr<opengl::program> _m2_box_program;
|
||||
std::unique_ptr<opengl::program> _wmo_program;
|
||||
|
||||
boost::optional<liquid_render> _liquid_render = boost::none;
|
||||
|
||||
std::vector<ModelInstance> _model_instances;
|
||||
std::vector<WMOInstance> _wmo_instances;
|
||||
|
||||
std::vector<math::vector_3d> calcSceneExtents();
|
||||
virtual void draw();
|
||||
virtual math::matrix_4x4 model_view() const;
|
||||
virtual math::matrix_4x4 projection() const;
|
||||
virtual float aspect_ratio() const;
|
||||
|
||||
private:
|
||||
int _width;
|
||||
int _height;
|
||||
noggit::NoggitRenderContext _context;
|
||||
|
||||
std::map<std::tuple<std::string, int, int>, QPixmap> _cache;
|
||||
|
||||
QOpenGLContext _offscreen_context;
|
||||
QOpenGLFramebufferObjectFormat _fmt;
|
||||
QOffscreenSurface _offscreen_surface;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //NOGGIT_PREVIEWRENDERER_HPP
|
||||
Reference in New Issue
Block a user