|
|
@@ -0,0 +1,410 @@
|
|
|
+#include "osg_scene.h"
|
|
|
+#include "osg_utility.h"
|
|
|
+
|
|
|
+#include <spdlog/spdlog.h>
|
|
|
+
|
|
|
+#include <utility>
|
|
|
+#include <osgViewer/Scene>
|
|
|
+
|
|
|
+using namespace osg;
|
|
|
+
|
|
|
+namespace {
|
|
|
+ Matrix to_osg_matrix(glm::mat4 mat) {
|
|
|
+ auto ret = Matrix();
|
|
|
+ for (auto i = 0; i < 4; i++)
|
|
|
+ for (auto j = 0; j < 4; j++) {
|
|
|
+ ret(i, j) = mat[i][j]; // TODO: The matrix in OSG is weird, figure out why
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct TransformSP::impl {
|
|
|
+ TransformSP *q_this;
|
|
|
+ create_config conf;
|
|
|
+ bool is_tracked_once = false;
|
|
|
+ bool ignore_missing = false;
|
|
|
+
|
|
|
+ struct update_callback final : NodeCallback {
|
|
|
+ impl *pimpl;
|
|
|
+
|
|
|
+ explicit update_callback(impl *_pimpl)
|
|
|
+ : pimpl(_pimpl) { (void) 0; }
|
|
|
+
|
|
|
+ void operator()(Node *node, NodeVisitor *nv) override {
|
|
|
+ pimpl->update_transform();
|
|
|
+ traverse(node, nv);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ impl(TransformSP *_q_this, create_config _conf)
|
|
|
+ : q_this(_q_this), conf(std::move(_conf)) {
|
|
|
+ q_this->setNodeMask(0); // disable
|
|
|
+ q_this->setUpdateCallback(new update_callback(this));
|
|
|
+ }
|
|
|
+
|
|
|
+ void update_transform() const {
|
|
|
+ const auto ret = query_transform(conf.key_name).matrix;
|
|
|
+ if (!ret) {
|
|
|
+ if (!ignore_missing || !is_tracked_once) {
|
|
|
+ q_this->setNodeMask(0); // disable
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ q_this->setNodeMask(-1); // enable
|
|
|
+ q_this->setMatrix(to_osg_matrix(*ret));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void show_ui() {
|
|
|
+ // TODO
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+TransformSP::TransformSP(const create_config &conf)
|
|
|
+ : pimpl(std::make_unique<impl>(this, conf)) { (void) 0; }
|
|
|
+
|
|
|
+TransformSP::~TransformSP() = default;
|
|
|
+
|
|
|
+void TransformSP::showUI() {
|
|
|
+ pimpl->show_ui();
|
|
|
+}
|
|
|
+
|
|
|
+#include <assimp/Importer.hpp>
|
|
|
+#include <assimp/scene.h>
|
|
|
+#include <assimp/postprocess.h>
|
|
|
+
|
|
|
+#include <osg/Geometry>
|
|
|
+#include <osg/Program>
|
|
|
+
|
|
|
+#include <vtkPolyDataNormals.h>
|
|
|
+#include <vtkPointData.h>
|
|
|
+
|
|
|
+#include <glm/gtc/type_ptr.hpp>
|
|
|
+
|
|
|
+namespace {
|
|
|
+ struct prog_mesh_type {
|
|
|
+ ref_ptr<Program> prog;
|
|
|
+
|
|
|
+ prog_mesh_type() {
|
|
|
+ prog = new Program();
|
|
|
+ prog->addShader(new Shader(
|
|
|
+ Shader::VERTEX, load_shader("mesh_default.vert")));
|
|
|
+ prog->addShader(new Shader(
|
|
|
+ Shader::FRAGMENT, load_shader("mesh_default.frag")));
|
|
|
+ }
|
|
|
+
|
|
|
+ void apply(StateSet *state) const {
|
|
|
+ state->setAttributeAndModes(prog);
|
|
|
+
|
|
|
+ struct model_mat_callback : UniformCallback {
|
|
|
+ void operator ()(Uniform *uni, NodeVisitor *nv) override {
|
|
|
+ uni->set(computeLocalToWorld(nv->getNodePath()));
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const ref_ptr model_mat = new Uniform(
|
|
|
+ Uniform::FLOAT_MAT4, "model_mat");
|
|
|
+ model_mat->setUpdateCallback(new model_mat_callback());
|
|
|
+ state->addUniform(model_mat);
|
|
|
+
|
|
|
+ const ref_ptr base_weight = new Uniform(
|
|
|
+ Uniform::FLOAT, "base_weight");
|
|
|
+ state->addUniform(base_weight);
|
|
|
+
|
|
|
+ // state->setMode(GL_BLEND, StateAttribute::ON); // TODO: modified
|
|
|
+ // state->setRenderingHint(StateSet::TRANSPARENT_BIN);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ std::optional<prog_mesh_type> prog_mesh;
|
|
|
+}
|
|
|
+
|
|
|
+namespace {
|
|
|
+ auto default_mesh_color = Vec4f(1, 1, 1, 1);
|
|
|
+}
|
|
|
+
|
|
|
+struct MeshSP::impl {
|
|
|
+ MeshSP *q_this;
|
|
|
+ Assimp::Importer importer;
|
|
|
+
|
|
|
+ create_config conf;
|
|
|
+ float base_weight = 0.1f;
|
|
|
+
|
|
|
+ static ref_ptr<Geometry> create_geometry(const aiMesh *mesh) {
|
|
|
+ assert(mesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE);
|
|
|
+ assert(mesh->mNormals != nullptr);
|
|
|
+ const ref_ptr ret = new Geometry();
|
|
|
+
|
|
|
+ const ref_ptr vertices = new Vec3Array();
|
|
|
+ vertices->setName("vertex");
|
|
|
+ for (auto i = 0; i < mesh->mNumVertices; i++) {
|
|
|
+ const auto vertex = mesh->mVertices[i];
|
|
|
+ vertices->push_back({vertex.x, vertex.y, vertex.z});
|
|
|
+ }
|
|
|
+ ret->setVertexArray(vertices);
|
|
|
+
|
|
|
+ const ref_ptr normals = new Vec3Array();
|
|
|
+ normals->setName("normal");
|
|
|
+ for (auto i = 0; i < mesh->mNumVertices; i++) {
|
|
|
+ const auto normal = mesh->mNormals[i];
|
|
|
+ normals->push_back({normal.x, normal.y, normal.z});
|
|
|
+ }
|
|
|
+ ret->setNormalArray(normals, Array::BIND_PER_VERTEX);
|
|
|
+
|
|
|
+ const ref_ptr indices = new DrawElementsUInt(PrimitiveSet::TRIANGLES, 0);
|
|
|
+ for (auto i = 0; i < mesh->mNumFaces; i++) {
|
|
|
+ const auto face = mesh->mFaces[i];
|
|
|
+ indices->push_back(face.mIndices[0]);
|
|
|
+ indices->push_back(face.mIndices[1]);
|
|
|
+ indices->push_back(face.mIndices[2]);
|
|
|
+ }
|
|
|
+ ret->addPrimitiveSet(indices);
|
|
|
+
|
|
|
+ const ref_ptr colors = new Vec4Array();
|
|
|
+ colors->setName("color");
|
|
|
+ if (const auto mesh_color = mesh->mColors[0]; mesh_color == nullptr) {
|
|
|
+ colors->push_back(default_mesh_color);
|
|
|
+ ret->setColorArray(colors, Array::BIND_OVERALL);
|
|
|
+ } else {
|
|
|
+ for (auto i = 0; i < mesh->mNumFaces; i++) {
|
|
|
+ const auto color = mesh_color[i];
|
|
|
+ colors->push_back({color.r, color.g, color.b, color.a});
|
|
|
+ }
|
|
|
+ ret->setColorArray(colors, Array::BIND_PER_VERTEX);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ static ref_ptr<Geometry> create_geometry(vtkPolyData *mesh) {
|
|
|
+ assert(mesh != nullptr);
|
|
|
+ const ref_ptr ret = new Geometry();
|
|
|
+
|
|
|
+ const ref_ptr vertices = new Vec3Array();
|
|
|
+ vertices->setName("vertex");
|
|
|
+ const auto mesh_vertex = mesh->GetPoints();
|
|
|
+ const auto num_vertex = mesh_vertex->GetNumberOfPoints();
|
|
|
+ for (auto i = 0; i < num_vertex; i++) {
|
|
|
+ double vertex[3];
|
|
|
+ mesh_vertex->GetPoint(i, vertex);
|
|
|
+ vertices->push_back(Vec3f(vertex[0], vertex[1], vertex[2]));
|
|
|
+ }
|
|
|
+ ret->setVertexArray(vertices);
|
|
|
+
|
|
|
+ const ref_ptr normals = new Vec3Array();
|
|
|
+ normals->setName("normal");
|
|
|
+ if (mesh->GetPointData()->GetNormals() == nullptr) {
|
|
|
+ vtkNew<vtkPolyDataNormals> filter;
|
|
|
+ filter->SetInputData(mesh);
|
|
|
+ filter->Update();
|
|
|
+ mesh = filter->GetOutput();
|
|
|
+ }
|
|
|
+ const auto mesh_normal = mesh->GetPointData()->GetNormals();
|
|
|
+ for (auto i = 0; i < num_vertex; i++) {
|
|
|
+ double normal[3];
|
|
|
+ mesh_normal->GetTuple(i, normal);
|
|
|
+ normals->push_back(Vec3f(normal[0], normal[1], normal[2]));
|
|
|
+ }
|
|
|
+ ret->setNormalArray(normals, Array::BIND_PER_VERTEX);
|
|
|
+
|
|
|
+ const ref_ptr indices = new DrawElementsUInt(PrimitiveSet::TRIANGLES, 0);
|
|
|
+ const auto faces = mesh->GetPolys();
|
|
|
+ const auto num_face = faces->GetNumberOfCells();
|
|
|
+ vtkNew<vtkIdList> face_index;
|
|
|
+ for (auto i = 0; i < num_face; i++) {
|
|
|
+ faces->GetCellAtId(i, face_index);
|
|
|
+ assert(face_index->GetNumberOfIds() == 3);
|
|
|
+ for (auto j = 0; j < 3; ++j) {
|
|
|
+ indices->push_back(face_index->GetId(j));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ret->addPrimitiveSet(indices);
|
|
|
+
|
|
|
+ const ref_ptr colors = new Vec4Array();
|
|
|
+ colors->setName("color");
|
|
|
+ colors->push_back(default_mesh_color);
|
|
|
+ ret->setColorArray(colors, Array::BIND_OVERALL);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ void load_from_file() {
|
|
|
+ const auto scene = importer.ReadFile(
|
|
|
+ conf.model_path, aiProcessPreset_TargetRealtime_MaxQuality);
|
|
|
+ assert(scene != nullptr);
|
|
|
+ for (auto i = 0; i < scene->mNumMeshes; i++) {
|
|
|
+ q_this->addDrawable(create_geometry(scene->mMeshes[i]));
|
|
|
+ }
|
|
|
+ SPDLOG_INFO("Loaded model {} from file {} with {} components.",
|
|
|
+ conf.name, conf.model_path, scene->mNumMeshes);
|
|
|
+ }
|
|
|
+
|
|
|
+ void load_from_vtk() const {
|
|
|
+ q_this->addDrawable(create_geometry(conf.vtk_model));
|
|
|
+ }
|
|
|
+
|
|
|
+ struct update_callback final : NodeCallback {
|
|
|
+ impl *pimpl;
|
|
|
+
|
|
|
+ explicit update_callback(impl *_pimpl)
|
|
|
+ : pimpl(_pimpl) { (void) 0; }
|
|
|
+
|
|
|
+ void operator()(Node *node, NodeVisitor *nv) override {
|
|
|
+ const auto states = pimpl->q_this->getOrCreateStateSet();
|
|
|
+ assert(states != nullptr);
|
|
|
+ const auto base_weight = states->getUniform("base_weight");
|
|
|
+ base_weight->set(pimpl->base_weight);
|
|
|
+ pimpl->q_this->onUpdate();
|
|
|
+ traverse(node, nv);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ void re_create(const create_config &_conf) {
|
|
|
+ q_this->removeDrawables(0, q_this->getNumDrawables());
|
|
|
+
|
|
|
+ conf = _conf;
|
|
|
+ if (!conf.model_path.empty()) {
|
|
|
+ load_from_file();
|
|
|
+ } else if (conf.vtk_model != nullptr) {
|
|
|
+ load_from_vtk();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl(MeshSP *_q_this, const create_config &_conf)
|
|
|
+ : q_this(_q_this) {
|
|
|
+ if (!prog_mesh) [[unlikely]] {
|
|
|
+ prog_mesh.emplace();
|
|
|
+ }
|
|
|
+ prog_mesh->apply(q_this->getOrCreateStateSet());
|
|
|
+ q_this->setUpdateCallback(new update_callback(this));
|
|
|
+
|
|
|
+ re_create(_conf);
|
|
|
+ }
|
|
|
+
|
|
|
+ void show_ui() {
|
|
|
+ // TODO
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+MeshSP::MeshSP()
|
|
|
+ : pimpl(std::make_unique<impl>(
|
|
|
+ this, create_config())) { (void) 0; }
|
|
|
+
|
|
|
+MeshSP::MeshSP(const create_config &conf)
|
|
|
+ : pimpl(std::make_unique<impl>(this, conf)) {
|
|
|
+}
|
|
|
+
|
|
|
+MeshSP::~MeshSP() = default;
|
|
|
+
|
|
|
+void MeshSP::showUI() {
|
|
|
+ pimpl->show_ui();
|
|
|
+}
|
|
|
+
|
|
|
+struct SceneSP::impl {
|
|
|
+ SceneSP *q_this;
|
|
|
+
|
|
|
+ ref_ptr<Camera> camera;
|
|
|
+ ref_ptr<Uniform> camera_mat;
|
|
|
+ ref_ptr<Uniform> light_direction;
|
|
|
+
|
|
|
+ void setup_uniforms() {
|
|
|
+ //@formatter:off
|
|
|
+ struct camera_mat_callback : UniformCallback {
|
|
|
+ impl *pimpl;
|
|
|
+ explicit camera_mat_callback(impl *_p_impl)
|
|
|
+ : pimpl(_p_impl) { (void) 0; }
|
|
|
+ void operator ()(Uniform *uni, NodeVisitor *) override {
|
|
|
+ const auto cam = pimpl->camera;
|
|
|
+ const auto proj_mat = cam->getProjectionMatrix();
|
|
|
+ const auto view_mat = cam->getViewMatrix();
|
|
|
+ uni->set(view_mat * proj_mat);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ //@formatter:on
|
|
|
+
|
|
|
+ camera = new Camera();
|
|
|
+ camera_mat = new Uniform(
|
|
|
+ Uniform::FLOAT_MAT4, "camera_mat");
|
|
|
+ camera_mat->setUpdateCallback(new camera_mat_callback(this));
|
|
|
+
|
|
|
+ //@formatter:off
|
|
|
+ struct light_direction_callback : UniformCallback {
|
|
|
+ impl *pimpl;
|
|
|
+ explicit light_direction_callback(impl *_p_impl)
|
|
|
+ : pimpl(_p_impl) { (void) 0; }
|
|
|
+ void operator ()(Uniform *uni, NodeVisitor *) override {
|
|
|
+ const auto cam = pimpl->camera;
|
|
|
+ const auto view_mat = cam->getViewMatrix();
|
|
|
+ const auto z_dir = Vec3f(
|
|
|
+ view_mat(2, 0),
|
|
|
+ view_mat(2, 1),
|
|
|
+ view_mat(2, 2));
|
|
|
+ uni->set(-z_dir);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ //@formatter:on
|
|
|
+
|
|
|
+ light_direction = new Uniform(
|
|
|
+ Uniform::FLOAT_VEC3, "light_direction");
|
|
|
+ light_direction->setUpdateCallback(new light_direction_callback(this));
|
|
|
+
|
|
|
+ const auto state = q_this->getOrCreateStateSet();
|
|
|
+ state->addUniform(camera_mat);
|
|
|
+ state->addUniform(light_direction);
|
|
|
+ }
|
|
|
+
|
|
|
+ explicit impl(SceneSP *_q_this)
|
|
|
+ : q_this(_q_this) {
|
|
|
+ setup_uniforms();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+SceneSP::SceneSP()
|
|
|
+ : pimpl(std::make_unique<impl>(this)) {
|
|
|
+}
|
|
|
+
|
|
|
+SceneSP::~SceneSP() = default;
|
|
|
+
|
|
|
+void SceneSP::setCamera(Camera *camera) const {
|
|
|
+ pimpl->camera = camera;
|
|
|
+}
|
|
|
+
|
|
|
+Camera *SceneSP::getCamera() const {
|
|
|
+ return pimpl->camera;
|
|
|
+}
|
|
|
+
|
|
|
+void SceneSP::showUI() {
|
|
|
+ // TODO
|
|
|
+}
|
|
|
+
|
|
|
+namespace {
|
|
|
+ ref_ptr<Node> create_node(const YAML::Node &conf) {
|
|
|
+ const auto name = LOAD_STR("name");
|
|
|
+ if (conf["model"]) {
|
|
|
+ auto model_conf = MeshSP::create_config();
|
|
|
+ model_conf.name = name;
|
|
|
+ model_conf.model_path = LOAD_STR("model");
|
|
|
+ return new MeshSP(model_conf);
|
|
|
+ } else if (conf["transform"]) {
|
|
|
+ auto transform_conf = TransformSP::create_config();
|
|
|
+ transform_conf.name = name;
|
|
|
+ transform_conf.key_name = LOAD_STR("transform");
|
|
|
+ ref_ptr ret = new TransformSP(transform_conf);
|
|
|
+ if (const auto children = conf["children"]; children) {
|
|
|
+ for (auto child: children) {
|
|
|
+ ret->addChild(create_node(child));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ assert(false);
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+ref_ptr<SceneSP> SceneSP::createFromYAML(const YAML::Node &conf) {
|
|
|
+ ref_ptr ret = new SceneSP();
|
|
|
+ for (auto child: conf) {
|
|
|
+ ret->addChild(create_node(child));
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|