mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
would be possible to optimize and avoid this copy, but should be verified everywhere that this change of paradigm is taken into account (i.e. run update after copy transform if needed)
498 lines
11 KiB
C++
498 lines
11 KiB
C++
#include <glm/gtc/type_ptr.hpp>
|
|
#include <glm/gtc/matrix_access.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <glm/gtc/random.hpp>
|
|
|
|
#include <glad/glad.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "defines.h"
|
|
#include "Shader.h"
|
|
#include "Primitives.h"
|
|
#include "Visitor.h"
|
|
#include "GarbageVisitor.h"
|
|
#include "Log.h"
|
|
#include "BaseToolkit.h"
|
|
#include "GlmToolkit.h"
|
|
#include "SessionVisitor.h"
|
|
|
|
#include "Scene.h"
|
|
|
|
#define DEBUG_SCENE 0
|
|
static int num_nodes_ = 0;
|
|
|
|
// Node
|
|
Node::Node() : initialized_(false), visible_(true), refcount_(0)
|
|
{
|
|
// create unique id
|
|
id_ = BaseToolkit::uniqueId();
|
|
|
|
transform_ = glm::identity<glm::mat4>();
|
|
scale_ = glm::vec3(1.f);
|
|
rotation_ = glm::vec3(0.f);
|
|
translation_ = glm::vec3(0.f);
|
|
crop_ = glm::vec3(1.f);
|
|
#if DEBUG_SCENE
|
|
num_nodes_++;
|
|
#endif
|
|
}
|
|
|
|
Node::~Node ()
|
|
{
|
|
clearCallbacks();
|
|
#if DEBUG_SCENE
|
|
num_nodes_--;
|
|
#endif
|
|
}
|
|
|
|
void Node::clearCallbacks()
|
|
{
|
|
std::list<UpdateCallback *>::iterator iter;
|
|
for (iter=update_callbacks_.begin(); iter != update_callbacks_.end(); )
|
|
{
|
|
UpdateCallback *callback = *iter;
|
|
iter = update_callbacks_.erase(iter);
|
|
delete callback;
|
|
}
|
|
}
|
|
|
|
void Node::copyTransform(const Node *other)
|
|
{
|
|
if (!other)
|
|
return;
|
|
transform_ = other->transform_;
|
|
scale_ = other->scale_;
|
|
rotation_ = other->rotation_;
|
|
translation_ = other->translation_;
|
|
crop_ = other->crop_;
|
|
}
|
|
|
|
void Node::update( float dt)
|
|
{
|
|
std::list<UpdateCallback *>::iterator iter;
|
|
for (iter=update_callbacks_.begin(); iter != update_callbacks_.end(); )
|
|
{
|
|
UpdateCallback *callback = *iter;
|
|
|
|
if (callback->enabled())
|
|
callback->update(this, dt);
|
|
|
|
if (callback->finished()) {
|
|
iter = update_callbacks_.erase(iter);
|
|
delete callback;
|
|
}
|
|
else {
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
// update transform matrix from attributes
|
|
transform_ = GlmToolkit::transform(translation_, rotation_, scale_);
|
|
}
|
|
|
|
void Node::accept(Visitor& v)
|
|
{
|
|
v.visit(*this);
|
|
}
|
|
|
|
//
|
|
// Primitive
|
|
//
|
|
|
|
Primitive::~Primitive()
|
|
{
|
|
if ( vao_ )
|
|
glDeleteVertexArrays ( 1, &vao_);
|
|
if (shader_)
|
|
delete shader_;
|
|
}
|
|
|
|
void Primitive::init()
|
|
{
|
|
if ( vao_ )
|
|
glDeleteVertexArrays ( 1, &vao_);
|
|
|
|
// Vertex Array
|
|
glGenVertexArrays( 1, &vao_ );
|
|
// Create and initialize buffer objects
|
|
uint arrayBuffer_;
|
|
uint elementBuffer_;
|
|
glGenBuffers( 1, &arrayBuffer_ );
|
|
glGenBuffers( 1, &elementBuffer_);
|
|
glBindVertexArray( vao_ );
|
|
|
|
// compute the memory needs for points
|
|
std::size_t sizeofPoints = sizeof(glm::vec3) * points_.size();
|
|
std::size_t sizeofColors = sizeof(glm::vec4) * colors_.size();
|
|
std::size_t sizeofTexCoords = sizeof(glm::vec2) * texCoords_.size();
|
|
|
|
// setup the array buffers for vertices
|
|
glBindBuffer( GL_ARRAY_BUFFER, arrayBuffer_ );
|
|
glBufferData( GL_ARRAY_BUFFER, sizeofPoints + sizeofColors + sizeofTexCoords, NULL, GL_STATIC_DRAW);
|
|
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeofPoints, &points_[0] );
|
|
glBufferSubData( GL_ARRAY_BUFFER, sizeofPoints, sizeofColors, &colors_[0] );
|
|
if ( sizeofTexCoords )
|
|
glBufferSubData( GL_ARRAY_BUFFER, sizeofPoints + sizeofColors, sizeofTexCoords, &texCoords_[0] );
|
|
|
|
// setup the element array for the triangle indices
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer_);
|
|
std::size_t sizeofIndices = indices_.size() * sizeof(uint);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeofIndices, &(indices_[0]), GL_STATIC_DRAW);
|
|
|
|
// explain how to read attributes 0, 1 and 2 (for point, color and textcoord respectively)
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void *)0 );
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void *)(sizeofPoints) );
|
|
glEnableVertexAttribArray(1);
|
|
if ( sizeofTexCoords ) {
|
|
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), (void *)(sizeofPoints + sizeofColors) );
|
|
glEnableVertexAttribArray(2);
|
|
}
|
|
|
|
// done
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
|
|
// drawing indications
|
|
drawCount_ = indices_.size();
|
|
|
|
// delete temporary buffers
|
|
if ( arrayBuffer_ )
|
|
glDeleteBuffers ( 1, &arrayBuffer_);
|
|
if ( elementBuffer_ )
|
|
glDeleteBuffers ( 1, &elementBuffer_);
|
|
|
|
// compute AxisAlignedBoundingBox
|
|
bbox_.extend(points_);
|
|
|
|
// arrays of vertices are not needed anymore (STATIC DRAW of vertex object)
|
|
points_.clear();
|
|
colors_.clear();
|
|
texCoords_.clear();
|
|
indices_.clear();
|
|
|
|
Node::init();
|
|
}
|
|
|
|
void Primitive::draw(glm::mat4 modelview, glm::mat4 projection)
|
|
{
|
|
if ( !initialized() )
|
|
init();
|
|
|
|
if ( visible_ ) {
|
|
//
|
|
// prepare and use shader
|
|
//
|
|
if (shader_) {
|
|
shader_->projection = projection;
|
|
shader_->modelview = modelview * transform_;
|
|
shader_->use();
|
|
}
|
|
//
|
|
// draw vertex array object
|
|
//
|
|
if (vao_) {
|
|
glBindVertexArray( vao_ );
|
|
glDrawElements( drawMode_, drawCount_, GL_UNSIGNED_INT, 0 );
|
|
glBindVertexArray(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Primitive::accept(Visitor& v)
|
|
{
|
|
Node::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
void Primitive::replaceShader( Shader *newshader )
|
|
{
|
|
if (newshader) {
|
|
glm::mat4 iTransform = newshader->iTransform;
|
|
glm::vec4 color = newshader->color;
|
|
if (shader_) {
|
|
iTransform = shader_->iTransform;
|
|
color = shader_->color;
|
|
delete shader_;
|
|
}
|
|
shader_ = newshader;
|
|
shader_->iTransform = iTransform;
|
|
shader_->color = color;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Group
|
|
//
|
|
|
|
Group::~Group()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void Group::clear()
|
|
{
|
|
for(NodeSet::iterator it = children_.begin(); it != children_.end(); ) {
|
|
// one less ref to this node
|
|
(*it)->refcount_--;
|
|
// if this group was the only remaining parent
|
|
if ( (*it)->refcount_ < 1 ) {
|
|
// delete
|
|
delete (*it);
|
|
}
|
|
// erase this iterator from the list
|
|
it = children_.erase(it);
|
|
}
|
|
}
|
|
|
|
void Group::attach(Node *child)
|
|
{
|
|
if (child != nullptr) {
|
|
children_.insert(child);
|
|
child->refcount_++;
|
|
}
|
|
}
|
|
|
|
|
|
void Group::sort()
|
|
{
|
|
// reorder list of nodes
|
|
NodeSet ordered_children;
|
|
for(auto it = children_.begin(); it != children_.end(); it++)
|
|
ordered_children.insert(*it);
|
|
children_.swap(ordered_children);
|
|
}
|
|
|
|
void Group::detach(Node *child)
|
|
{
|
|
if (child != nullptr) {
|
|
// find the node with this id, and erase it out of the list of children
|
|
// NB: do NOT delete with remove : this takes all nodes with same depth (i.e. equal depth in set)
|
|
NodeSet::iterator it = std::find_if(children_.begin(), children_.end(), hasId(child->id()));
|
|
if ( it != children_.end()) {
|
|
// detatch child from group parent
|
|
children_.erase(it);
|
|
child->refcount_--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Group::update( float dt )
|
|
{
|
|
Node::update(dt);
|
|
|
|
// update every child node
|
|
for (NodeSet::iterator node = children_.begin();
|
|
node != children_.end(); ++node) {
|
|
(*node)->update ( dt );
|
|
}
|
|
}
|
|
|
|
void Group::draw(glm::mat4 modelview, glm::mat4 projection)
|
|
{
|
|
if ( !initialized() )
|
|
init();
|
|
|
|
if ( visible_ ) {
|
|
|
|
// append the instance transform to the ctm
|
|
glm::mat4 ctm = modelview * transform_;
|
|
|
|
// draw every child node
|
|
for (NodeSet::iterator node = children_.begin();
|
|
node != children_.end(); ++node) {
|
|
(*node)->draw ( ctm, projection );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Group::accept(Visitor& v)
|
|
{
|
|
Node::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
NodeSet::iterator Group::begin()
|
|
{
|
|
return children_.begin();
|
|
}
|
|
|
|
NodeSet::iterator Group::end()
|
|
{
|
|
return children_.end();
|
|
}
|
|
|
|
|
|
Node *Group::front()
|
|
{
|
|
if (!children_.empty())
|
|
return *children_.rbegin();
|
|
return nullptr;
|
|
}
|
|
|
|
Node *Group::back()
|
|
{
|
|
if (!children_.empty())
|
|
return *children_.begin();
|
|
return nullptr;
|
|
}
|
|
|
|
//
|
|
// Switch node
|
|
//
|
|
|
|
Switch::~Switch()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void Switch::clear()
|
|
{
|
|
for(std::vector<Node *>::iterator it = children_.begin(); it != children_.end(); ) {
|
|
// one less ref to this node
|
|
(*it)->refcount_--;
|
|
// if this group was the only remaining parent
|
|
if ( (*it)->refcount_ < 1 ) {
|
|
// delete
|
|
delete (*it);
|
|
}
|
|
// erase this iterator from the list
|
|
it = children_.erase(it);
|
|
}
|
|
|
|
// reset active
|
|
active_ = 0;
|
|
}
|
|
|
|
|
|
void Switch::update( float dt )
|
|
{
|
|
Node::update(dt);
|
|
|
|
// update active child node
|
|
if (!children_.empty())
|
|
(children_[active_])->update( dt );
|
|
}
|
|
|
|
void Switch::draw(glm::mat4 modelview, glm::mat4 projection)
|
|
{
|
|
if ( !initialized() )
|
|
init();
|
|
|
|
if ( visible_ ) {
|
|
// draw current child
|
|
if (!children_.empty())
|
|
(children_[active_])->draw( modelview * transform_, projection);
|
|
}
|
|
}
|
|
|
|
void Switch::accept(Visitor& v)
|
|
{
|
|
Node::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
void Switch::setActive (uint index)
|
|
{
|
|
active_ = MINI(index, children_.size() - 1);
|
|
}
|
|
|
|
Node *Switch::child(uint index) const
|
|
{
|
|
if (!children_.empty()) {
|
|
uint i = MINI(index, children_.size() - 1);
|
|
return children_.at(i);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
uint Switch::attach(Node *child)
|
|
{
|
|
children_.push_back(child);
|
|
child->refcount_++;
|
|
|
|
// make new child active
|
|
active_ = children_.size() - 1;
|
|
return active_;
|
|
}
|
|
|
|
void Switch::detatch(Node *child)
|
|
{
|
|
// if removing the active child, it cannot be active anymore
|
|
if ( children_.at(active_) == child )
|
|
active_ = 0;
|
|
|
|
// find the node with this id, and erase it out of the list of children
|
|
std::vector<Node *>::iterator it = std::find_if(children_.begin(), children_.end(), hasId(child->id()));
|
|
if ( it != children_.end()) {
|
|
// detatch child from group parent
|
|
children_.erase(it);
|
|
child->refcount_--;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Scene
|
|
//
|
|
|
|
Scene::Scene()
|
|
{
|
|
root_ = new Group;
|
|
|
|
background_ = new Group;
|
|
background_->translation_.z = 0.f;
|
|
root_->attach(background_);
|
|
|
|
workspace_ = new Group;
|
|
workspace_->translation_.z = 1.f;
|
|
root_->attach(workspace_);
|
|
|
|
foreground_ = new Group;
|
|
foreground_->translation_.z = SCENE_DEPTH -0.1f;
|
|
root_->attach(foreground_);
|
|
}
|
|
|
|
Scene::~Scene()
|
|
{
|
|
clear();
|
|
// bg and fg are deleted as children of root
|
|
delete root_;
|
|
#if DEBUG_SCENE
|
|
Log::Info("Total scene nodes %d", num_nodes_);
|
|
#endif
|
|
}
|
|
|
|
|
|
void Scene::clear()
|
|
{
|
|
clearForeground();
|
|
clearWorkspace();
|
|
clearBackground();
|
|
}
|
|
|
|
void Scene::clearForeground()
|
|
{
|
|
foreground_->clear();
|
|
}
|
|
|
|
void Scene::clearWorkspace()
|
|
{
|
|
workspace_->clear();
|
|
}
|
|
|
|
void Scene::clearBackground()
|
|
{
|
|
background_->clear();
|
|
}
|
|
|
|
void Scene::update(float dt)
|
|
{
|
|
root_->update( dt );
|
|
}
|
|
|
|
void Scene::accept(Visitor& v)
|
|
{
|
|
v.visit(*this);
|
|
}
|