blender/intern/cycles/subd/subd_mesh.cpp

310 lines
6.8 KiB
C++
Raw Normal View History

/*
* Original code in the public domain -- castanyo@yahoo.es
*
* Modifications copyright (c) 2011, Blender Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include "subd_build.h"
#include "subd_edge.h"
#include "subd_face.h"
#include "subd_mesh.h"
#include "subd_patch.h"
#include "subd_split.h"
#include "subd_vert.h"
#include "util_debug.h"
#include "util_foreach.h"
CCL_NAMESPACE_BEGIN
SubdMesh::SubdMesh()
{
}
SubdMesh::~SubdMesh()
{
pair<Key, SubdEdge*> em;
foreach(SubdVert *vertex, verts)
delete vertex;
foreach(em, edge_map)
delete em.second;
foreach(SubdFace *face, faces)
delete face;
verts.clear();
edges.clear();
edge_map.clear();
faces.clear();
}
SubdVert *SubdMesh::add_vert(const float3& co)
{
SubdVert *v = new SubdVert(verts.size());
v->co = co;
verts.push_back(v);
return v;
}
SubdFace *SubdMesh::add_face(int v0, int v1, int v2)
{
int index[3] = {v0, v1, v2};
return add_face(index, 3);
}
SubdFace *SubdMesh::add_face(int v0, int v1, int v2, int v3)
{
int index[4] = {v0, v1, v2, v3};
return add_face(index, 4);
}
SubdFace *SubdMesh::add_face(int *index, int num)
{
/* test non-manifold cases */
if(!can_add_face(index, num)) {
/* we could try to add face in opposite winding instead .. */
fprintf(stderr, "Warning: non manifold mesh, invalid face '%lu'.\n", (unsigned long)faces.size());
return NULL;
}
SubdFace *f = new SubdFace(faces.size());
SubdEdge *first_edge = NULL;
SubdEdge *last = NULL;
SubdEdge *current = NULL;
/* add edges */
for(int i = 0; i < num-1; i++) {
current = add_edge(index[i], index[i+1]);
assert(current != NULL);
current->face = f;
if(last != NULL) {
last->next = current;
current->prev = last;
}
else
first_edge = current;
last = current;
}
current = add_edge(index[num-1], index[0]);
assert(current != NULL);
current->face = f;
last->next = current;
current->prev = last;
current->next = first_edge;
first_edge->prev = current;
f->edge = first_edge;
faces.push_back(f);
return f;
}
bool SubdMesh::can_add_face(int *index, int num)
{
/* manifold check */
for(int i = 0; i < num-1; i++)
if(!can_add_edge(index[i], index[i+1]))
return false;
return can_add_edge(index[num-1], index[0]);
}
bool SubdMesh::can_add_edge(int i, int j)
{
/* check for degenerate edge */
if(i == j)
return false;
/* make sure edge has not been added yet. */
return find_edge(i, j) == NULL;
}
SubdEdge *SubdMesh::add_edge(int i, int j)
{
SubdEdge *edge;
/* find pair */
SubdEdge *pair = find_edge(j, i);
if(pair != NULL) {
/* create edge with same id */
edge = new SubdEdge(pair->id + 1);
/* link edge pairs */
edge->pair = pair;
pair->pair = edge;
/* not sure this is necessary? */
pair->vert->edge = pair;
}
else {
/* create edge */
edge = new SubdEdge(2*edges.size());
/* add only unpaired edges */
edges.push_back(edge);
}
/* assign vertex and put into map */
edge->vert = verts[i];
edge_map[Key(i, j)] = edge;
/* face and next are set by add_face */
return edge;
}
SubdEdge *SubdMesh::find_edge(int i, int j)
{
map<Key, SubdEdge*>::const_iterator it = edge_map.find(Key(i, j));
return (it == edge_map.end())? NULL: it->second;
}
bool SubdMesh::link_boundary()
{
/* link boundary edges once the mesh has been created */
int num = 0;
/* create boundary edges */
int num_edges = edges.size();
for(int e = 0; e < num_edges; e++) {
SubdEdge *edge = edges[e];
if(edge->pair == NULL) {
SubdEdge *pair = new SubdEdge(edge->id + 1);
int i = edge->from()->id;
int j = edge->to()->id;
assert(edge_map.find(Key(j, i)) == edge_map.end());
pair->vert = verts[j];
edge_map[Key(j, i)] = pair;
edge->pair = pair;
pair->pair = edge;
num++;
}
}
/* link boundary edges */
for(int e = 0; e < num_edges; e++) {
SubdEdge *edge = edges[e];
if(edge->pair->face == NULL)
link_boundary_edge(edge->pair);
}
/* detect boundary intersections */
int boundaryIntersections = 0;
int num_verts = verts.size();
for(int v = 0; v < num_verts; v++) {
SubdVert *vertex = verts[v];
int boundarySubdEdges = 0;
for(SubdVert::EdgeIterator it(vertex->edges()); !it.isDone(); it.advance())
if(it.current()->is_boundary())
boundarySubdEdges++;
if(boundarySubdEdges > 2) {
assert((boundarySubdEdges & 1) == 0);
boundaryIntersections++;
}
}
if(boundaryIntersections != 0) {
fprintf(stderr, "Invalid mesh, boundary intersections found!\n");
return false;
}
return true;
}
void SubdMesh::link_boundary_edge(SubdEdge *edge)
{
/* link this boundary edge. */
/* make sure next pointer has not been set. */
assert(edge->face == NULL);
assert(edge->next == NULL);
SubdEdge *next = edge;
while(next->pair->face != NULL) {
/* get pair prev */
SubdEdge *e = next->pair->next;
while(e->next != next->pair)
e = e->next;
next = e;
}
edge->next = next->pair;
next->pair->prev = edge;
/* adjust vertex edge, so that it's the boundary edge. (required for is_boundary()) */
if(edge->vert->edge != edge)
edge->vert->edge = edge;
}
void SubdMesh::tessellate(DiagSplit *split, bool linear, Mesh *mesh, int shader, bool smooth)
{
SubdBuilder *builder = SubdBuilder::create(linear);
int num_faces = faces.size();
for(int f = 0; f < num_faces; f++) {
SubdFace *face = faces[f];
Patch *patch = builder->run(face);
if(patch->is_triangle())
split->split_triangle(mesh, patch, shader, smooth);
else
split->split_quad(mesh, patch, shader, smooth);
delete patch;
}
delete builder;
}
CCL_NAMESPACE_END