400 lines
15 KiB
C++
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
|