paella/src/Animation/AnimatedMesh.cpp

400 lines
15 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Paella
// Copyright (C) 2015 - Thomas FORGIONE, Emilie JALRAS, Marion LENFANT, Thierry MALON, Amandine PAILLOUX
// Authors :
// Thomas FORGIONE
// Emilie JALRAS
// Marion LENFANT
// Thierry MALON
// Amandine PAILLOUX
//
// This file is part of the project Paella
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
////////////////////////////////////////////////////////////////////////////////
#include <GL/glu.h>
#include <Animation/AnimatedMesh.hpp>
#include <Animation/Click.hpp>
namespace pae
{
AnimatedMesh::AnimatedMesh(Skeleton3D const& skeleton)
{
auto segments = click(skeleton);
this->segments = segments.first;
auto mesh = skeleton.computeMesh();
associateBranches(segments.first, mesh);
findFaces(associatedSegment, mesh, segments.first.size());
mesh.prepare();
vertices = mesh.vertices;
geo::Tree<geo::Vector3<float>> tree{segments.second};
listToTree(this->segments, tree);
genStructure(this->segments, tree, structure);
genStructure(this->segments, tree, roots);
genRotations(roots, rotations);
genStructuredFaces(facesPerSegment, structure, structuredFaces);
genOtherVertices(vertices, facesPerSegment[facesPerSegment.size()-1], associatedSegment, structure, otherVertices);
genColors(structure, colors);
paths.push_back({});
genPaths(structure, paths);
structure.node = -1;
std::cout << tree << std::endl << "-------------------------" << std::endl;
std::cout << structure << std::endl;
std::cout << "structure size = " << structure.size() << std::endl;
}
geo::Tree<int>& genStructure(std::vector<geo::Segment<float,3>> const& segments, geo::Tree<geo::Vector3<float>> const& tree, geo::Tree<int>& ret)
{
constexpr float eps = 0.0001;
for (auto& child : tree.children)
{
for (unsigned int i = 0; i < segments.size(); i++)
{
// Compare segments[i] and (tree, child)
if ( (((segments[i].first - tree.node).norm2() < eps)
&& ((segments[i].second - child.node).norm2() < eps))
|| (((segments[i].first - child.node).norm2() < eps)
&& ((segments[i].second - tree.node).norm2() < eps)))
{
ret.children.push_back(geo::Tree<int>{static_cast<int>(i)});
genStructure(segments, child, ret.children[ret.children.size()-1]);
}
}
}
return ret;
}
geo::Tree<geo::Vector3<float>>& genStructure(std::vector<geo::Segment<float,3>> const& segments, geo::Tree<geo::Vector3<float>> const& tree, geo::Tree<geo::Vector3<float>>& ret)
{
constexpr float eps = 0.0001;
for (auto& child : tree.children)
{
for (unsigned int i = 0; i < segments.size(); i++)
{
// Compare segments[i] and (tree, child)
if ( (((segments[i].first - tree.node).norm2() < eps)
&& ((segments[i].second - child.node).norm2() < eps))
|| (((segments[i].first - child.node).norm2() < eps)
&& ((segments[i].second - tree.node).norm2() < eps)))
{
ret.children.push_back(geo::Tree<geo::Vector3<float>>{child.node});
genStructure(segments, child, ret.children[ret.children.size()-1]);
}
}
}
return ret;
}
geo::Tree<std::vector<geo::Vector3<unsigned int>>>&
genStructuredFaces(
std::vector<std::vector<geo::Vector3<unsigned int>>> const& facesPerSegment,
geo::Tree<int> const& structure,
geo::Tree<std::vector<geo::Vector3<unsigned int>>>& faces
)
{
// for (auto const& face_group : facesPerSegment)
for (auto child : structure.children)
{
auto tree = geo::Tree<std::vector<geo::Vector3<unsigned int>>>{};
tree.node = facesPerSegment[child.node];
faces.children.push_back(tree);
genStructuredFaces(facesPerSegment, child, faces.children[faces.children.size()-1]);
}
return faces;
}
std::unordered_map<unsigned int, geo::Path>&
genOtherVertices(
std::vector<geo::Vector3<float>> const& vertices,
std::vector<geo::Vector3<unsigned int>> const& otherFaces,
std::vector<unsigned int> associatedSegment,
geo::Tree<int> const& structure,
std::unordered_map<unsigned int, geo::Path>& ret,
geo::Path path
)
{
// for (auto const& child : structure.children)
path.push_back(0);
for (unsigned int i = 0; i < structure.children.size(); i++)
{
path[path.size()-1] = i;
for (auto const& f : otherFaces)
{
if (associatedSegment[f.x()] == static_cast<unsigned int>(structure.children[i].node))
{
ret.insert({f.x(), path});
}
if (associatedSegment[f.y()] == static_cast<unsigned int>(structure.children[i].node))
{
ret.insert({f.y(), path});
}
if (associatedSegment[f.z()] == static_cast<unsigned int>(structure.children[i].node))
{
ret.insert({f.z(), path});
}
}
genOtherVertices(vertices, otherFaces, associatedSegment, structure.children[i], ret, path);
}
return ret;
}
geo::Tree<std::array<float,3>>&
genColors(
geo::Tree<int> const& structure,
geo::Tree<std::array<float,3>>& colors)
{
for (auto const& child : structure.children)
{
geo::Tree<std::array<float,3>> tree;
tree.node = {{
static_cast<float>(std::rand())/RAND_MAX,
static_cast<float>(std::rand())/RAND_MAX,
static_cast<float>(std::rand())/RAND_MAX
}};
colors.children.push_back(tree);
genColors(child, colors.children[colors.children.size()-1]);
}
return colors;
}
geo::Tree<geo::Rotation>&
genRotations(
geo::Tree<geo::Vector3<float>> const& extremities,
geo::Tree<geo::Rotation>& ret
)
{
for (auto const& child : extremities.children)
{
geo::Rotation r;
r.center = extremities.node;
geo::Tree<geo::Rotation> tree;
tree.node = r;
ret.children.push_back(tree);
genRotations(child, ret.children[ret.children.size()-1]);
}
return ret;
}
std::vector<geo::Path>&
genPaths(
geo::Tree<int> const& structure,
std::vector<geo::Path>& paths,
geo::Path currentPath)
{
currentPath.push_back(0);
// for (auto const& child : structure.children)
for (unsigned int i = 0; i < structure.children.size(); i++)
{
currentPath[currentPath.size()-1] = i;
paths.push_back(currentPath);
genPaths(structure.children[i], paths, currentPath);
}
return paths;
}
void AnimatedMesh::associateBranches(std::vector<geo::Segment<float,3>> const& segments, geo::Mesh& mesh)
{
// stock each point at the index corresponding to the nearest segment
for(unsigned int i=0;i<mesh.vertices.size();i++)
{
geo::Point<float> p = geo::Point<float>{i,mesh.vertices[i]};
associatedSegment.push_back(findNearestSegment(segments, p));
}
}
//finds the nearest segment to a point
unsigned int AnimatedMesh::findNearestSegment(std::vector<geo::Segment<float,3>> const& segments, geo::Point<float> const& p)
{
unsigned int nearestSegment = 0;
float shortest_distance = std::numeric_limits<float>::max();
for(unsigned int i=0;i<segments.size();i++)
{
// find distance between point and segment
float distance = geo::distanceToSegment(p.second, segments[i].first, segments[i].second);
if (shortest_distance > distance)
{
shortest_distance = distance;
nearestSegment = i;
}
}
return nearestSegment;
}
void AnimatedMesh::findFaces(std::vector<unsigned int> const& associatedSegments, geo::Mesh& mesh, unsigned int nbSegments)
{
for (unsigned int i=0;i<nbSegments+1;i++)
{
facesPerSegment.push_back(std::vector<geo::Vector3<unsigned int>>{});
}
unsigned int segment;
for(unsigned int i=0;i<mesh.faces.size();i++)
{
segment = associatedSegments[mesh.faces[i].x()];
if ((segment == associatedSegments[mesh.faces[i].y()]) && (segment == associatedSegments[mesh.faces[i].z()]))
{
facesPerSegment[segment].push_back(mesh.faces[i]);
}
else
{
facesPerSegment[nbSegments].push_back(mesh.faces[i]);
}
}
}
void drawTree(std::vector<geo::Vector3<float>> const& vertices, geo::Tree<std::vector<geo::Vector3<unsigned int>>> const& faces, geo::Tree<std::array<float,3>> const& colors, geo::Tree<geo::Vector3<float>> const& extremities, geo::Tree<geo::Rotation> const& rotations)
{
// for (auto const& child : faces.children)
for (unsigned int i = 0; i < faces.children.size(); i++)
{
// Apply good rotation
glPushMatrix();
rotations.children[i].node();
glBegin(GL_TRIANGLES);
for (auto const& face : faces.children[i].node)
{
glColor3fv(&colors.children[i].node[0]);
glVertex3fv(&vertices[face.x()].data[0]);
glVertex3fv(&vertices[face.y()].data[0]);
glVertex3fv(&vertices[face.z()].data[0]);
}
glEnd();
drawTree(vertices, faces.children[i], colors.children[i], extremities.children[i], rotations.children[i]);
glPopMatrix();
}
}
void drawOthers(std::vector<geo::Vector3<float>> const& vertices, std::unordered_map<unsigned int, geo::Path> const& other_vertices, std::vector<geo::Vector3<unsigned int>> const& faces, geo::Tree<std::array<float,3>> const& colors, geo::Tree<geo::Rotation> const& rotations)
{
// OpenGL is shit, so this will be a bit tricky
// The thing is you can't use glRotatef, or glPush/PopMatrix inside glBegin() ... glEnd()
// but we need to do this since the transformation we have to apply is different for each
// vertex (since the vertices of the face are not mapped to the same segment)
// What we'll do is to call gluProject after applying the rotations, and glUnProject
// after reseting the modelview matrix. This way, each vertex will have the good transform
// and we will be able to draw the face.
for (auto const& f : faces)
{
// To avoid some copies of GL_MODELVIEW_MATRIX, we will first project everything,
// and then unproject everything.
// Ok, let's do the projection
// Some variables needed to store de matrices
GLdouble modelview[16];
GLdouble projection[16];
GLint viewport[4];
// tmp will be the projected vertices, and v will be the unprojected ones
geo::Vector3<double> tmp1,tmp2,tmp3;
geo::Vector3<double> v1,v2,v3;
// First, let's save the state of the projection and viewport matrices
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
// Now Let's project the vertices, after applying the transformation
// Projection of the first vertex
glPushMatrix();
// Apply the transformation to the vertex
rotations.execPath(other_vertices.at(f.x()));
// Get the new modelview matrix
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
// Compute the result of the projection and store it into tmp1
gluProject(vertices[f.x()].x(), vertices[f.x()].y(), vertices[f.x()].z(),
modelview, projection, viewport,
&tmp1.data[0], &tmp1.data[1], &tmp1.data[2]);
glPopMatrix();
// Same for the second
glPushMatrix();
rotations.execPath(other_vertices.at(f.y()));
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
gluProject(vertices[f.y()].x(), vertices[f.y()].y(), vertices[f.y()].z(),
modelview, projection, viewport,
&tmp2.data[0], &tmp2.data[1], &tmp2.data[2]);
glPopMatrix();
// And for the third
glPushMatrix();
rotations.execPath(other_vertices.at(f.z()));
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
gluProject(vertices[f.z()].x(), vertices[f.z()].y(), vertices[f.z()].z(),
modelview, projection, viewport,
&tmp3.data[0], &tmp3.data[1], &tmp3.data[2]);
glPopMatrix();
// Here, tmp1 tmp2 and tmp3 store the projected vertices. We will now
// unproject them with the modelview matrix WITHOUT taking account
// of the vertex-dependant transformation
// We will get back the first modelview matrix WITHOUT transformations
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
// And unproject everything with this modelview matrix
gluUnProject(tmp1.data[0], tmp1.data[1], tmp1.data[2],
modelview, projection, viewport,
&v1.data[0], &v1.data[1], &v1.data[2]);
// Same for v2
gluUnProject(tmp2.data[0], tmp2.data[1], tmp2.data[2],
modelview, projection, viewport,
&v2.data[0], &v2.data[1], &v2.data[2]);
// And for v3
gluUnProject(tmp3.data[0], tmp3.data[1], tmp3.data[2],
modelview, projection, viewport,
&v3.data[0], &v3.data[1], &v3.data[2]);
// Here we are, by doing this "hack", we used opengl to compute the
// result of the transformations on the vertices
// That is to say, v1, v2 and v3 are now the three vertices of the face
// with the transformation done correctly.
// Now, we just have to render the triangle with the classic opengl way
glBegin(GL_TRIANGLES);
glColor3fv(&colors.applyPath(other_vertices.at(f.x())).node[0]);
glVertex3d(v1.data[0], v1.data[1], v1.data[2]);
glColor3fv(&colors.applyPath(other_vertices.at(f.y())).node[0]);
glVertex3d(v2.data[0], v2.data[1], v2.data[2]);
glColor3fv(&colors.applyPath(other_vertices.at(f.z())).node[0]);
glVertex3d(v3.data[0], v3.data[1], v3.data[2]);
glEnd();
}
}
void AnimatedMesh::draw() const
{
drawTree(vertices, structuredFaces, colors, roots, rotations);
drawOthers(vertices, otherVertices, facesPerSegment[facesPerSegment.size()-1], colors, rotations);
}
} // namespace pae