open_toontown_panda3d/pandaapp/src/stitchbase/morphGrid.cxx

473 lines
12 KiB
C++

// Filename: morphGrid.cxx
// Created by: drose (08Nov99)
//
////////////////////////////////////////////////////////////////////
#include "morphGrid.h"
#include "triangle.h"
#include <mathNumbers.h>
#include <math.h>
#include <assert.h>
MorphGrid::Vertex::
Vertex(const LPoint2d &p) {
for (int i = 0; i < (int)TT_num; i++) {
_p[i] = p;
}
_alpha = 1.0;
_over_another = false;
// -1 on the distance counter is a flag that the value hasn't yet
// been computed.
_dist_from_interior = -1;
}
MorphGrid::Triangle::
Triangle(Vertex *v0, Vertex *v1, Vertex *v2) {
_v[0] = v0;
_v[1] = v1;
_v[2] = v2;
}
bool MorphGrid::Triangle::
contains_point(const LPoint2d &p, TableType from) const {
if ((p[0] < _min_p[from][0] || p[0] > _max_p[from][0]) ||
(p[1] < _min_p[from][1] || p[1] > _max_p[from][1])) {
// Doesn't pass the minmax test.
return false;
}
return triangle_contains_point(p, _v[0]->_p[from], _v[1]->_p[from],
_v[2]->_p[from]);
}
LPoint2d MorphGrid::Triangle::
morph_point(const LPoint2d &p, TableType from, TableType to) const {
return (p * _inv[from]) * _mat[to];
}
double MorphGrid::Triangle::
get_alpha(const LPoint2d &p, TableType from) const {
LPoint2d q = p * _inv[from];
// Now q is a point in a right triangle, where (0,1) is v0, (0,0) is
// v1, and (1,0) is v2. Interpolate the appropriate alpha value
// based on this coordinate system.
double alpha01 = (_v[0]->_alpha + q[1] * (_v[1]->_alpha - _v[0]->_alpha));
return (alpha01 + q[0] * (_v[2]->_alpha - alpha01));
}
void MorphGrid::Triangle::
recompute() {
for (int i = 0; i < (int)TT_num; i++) {
for (int a = 0; a < 2; a++) {
_min_p[i][a] = min(min(_v[0]->_p[i][a], _v[1]->_p[i][a]),
_v[2]->_p[i][a]);
_max_p[i][a] = max(max(_v[0]->_p[i][a], _v[1]->_p[i][a]),
_v[2]->_p[i][a]);
}
LPoint2d origin = _v[1]->_p[i];
LVector2d yaxis = _v[0]->_p[i] - origin;
LVector2d xaxis = _v[2]->_p[i] - origin;
_mat[i] = LMatrix3d(xaxis[0], xaxis[1], 0.0,
yaxis[0], yaxis[1], 0.0,
origin[0], origin[1], 1.0);
_inv[i] = invert(_mat[i]);
}
}
MorphGrid::TriangleTree::
TriangleTree(Triangle *a, Triangle *b) {
_has_tris = true;
_u._tri[0] = a;
_u._tri[1] = b;
}
MorphGrid::TriangleTree::
TriangleTree(TriangleTree *a, TriangleTree *b) {
_has_tris = false;
_u._tree[0] = a;
_u._tree[1] = b;
}
MorphGrid::TriangleTree::
~TriangleTree() {
if (!_has_tris) {
delete _u._tree[0];
delete _u._tree[1];
}
}
void MorphGrid::TriangleTree::
recompute() {
if (_has_tris) {
_u._tri[0]->recompute();
_u._tri[1]->recompute();
for (int i = 0; i < (int)TT_num; i++) {
for (int a = 0; a < 2; a++) {
_min_p[i][a] =
min(_u._tri[0]->_min_p[i][a], _u._tri[1]->_min_p[i][a]);
_max_p[i][a] =
max(_u._tri[0]->_max_p[i][a], _u._tri[1]->_max_p[i][a]);
}
}
} else {
_u._tree[0]->recompute();
_u._tree[1]->recompute();
for (int i = 0; i < (int)TT_num; i++) {
for (int a = 0; a < 2; a++) {
_min_p[i][a] =
min(_u._tree[0]->_min_p[i][a], _u._tree[1]->_min_p[i][a]);
_max_p[i][a] =
max(_u._tree[0]->_max_p[i][a], _u._tree[1]->_max_p[i][a]);
}
}
}
}
MorphGrid::Triangle *MorphGrid::TriangleTree::
find_triangle(const LPoint2d &p, TableType from) const {
if ((p[0] < _min_p[from][0] || p[0] > _max_p[from][0]) ||
(p[1] < _min_p[from][1] || p[1] > _max_p[from][1])) {
// Doesn't pass the minmax test.
return NULL;
}
if (_has_tris) {
if (_u._tri[0]->contains_point(p, from)) {
return _u._tri[0];
}
if (_u._tri[1]->contains_point(p, from)) {
return _u._tri[1];
}
return NULL;
} else {
Triangle *t = _u._tree[0]->find_triangle(p, from);
if (t == NULL) {
t = _u._tree[1]->find_triangle(p, from);
}
return t;
}
}
MorphGrid::
MorphGrid() {
_x_verts = 0;
_y_verts = 0;
_last_triangle = NULL;
_tree = NULL;
}
MorphGrid::
~MorphGrid() {
if (_tree != NULL) {
delete _tree;
}
}
bool MorphGrid::
is_empty() const {
return _x_verts <= 0 || _y_verts <= 0;
}
void MorphGrid::
clear() {
init(0, 0);
}
void MorphGrid::
init(int x_verts, int y_verts) {
_x_verts = x_verts;
_y_verts = y_verts;
if (_tree != NULL) {
delete _tree;
_tree = NULL;
}
_triangles.clear();
_last_triangle = NULL;
_table.clear();
if (is_empty()) {
return;
}
// Create a 2-d table of vertices.
_table.reserve(_y_verts);
int x, y;
for (y = 0; y < _y_verts; y++) {
_table.push_back(Row());
_table[y].clear();
_table[y].reserve(_x_verts);
for (x = 0; x < _x_verts; x++) {
LPoint2d p((double)x / (double)(_x_verts - 1),
1.0 - (double)y / (double)(_y_verts - 1));
_table[y].push_back(Vertex(p));
}
}
// Now create a bunch of triangles for these vertices.
int num_tris = (_y_verts - 1) * (_x_verts - 1) * 2;
_triangles.reserve(num_tris);
for (y = 0; y + 1 < _y_verts; y++) {
for (x = 0; x + 1 < _x_verts; x++) {
_triangles.push_back(Triangle(&_table[y][x],
&_table[y + 1][x],
&_table[y + 1][x + 1]));
_triangles.push_back(Triangle(&_table[y][x],
&_table[y + 1][x + 1],
&_table[y][x + 1]));
}
}
assert((int)_triangles.size() == num_tris);
// Now create a 2-d table of TriangleTree nodes, each of which
// points to a pair of triangles. We'll use this to build up the
// TriangleTree structure.
typedef vector<TriangleTree *> TRow;
typedef vector<TRow> TTable;
TTable tree;
int x_tree = _x_verts - 1;
int y_tree = _y_verts - 1;
tree.reserve(y_tree);
int i = 0;
for (y = 0; y < y_tree; y++) {
tree.push_back(TRow());
tree[y].clear();
tree[y].reserve(x_tree);
for (x = 0; x < x_tree; x++) {
tree[y].push_back(new TriangleTree(&_triangles[i],
&_triangles[i + 1]));
i += 2;
}
}
assert(i == num_tris);
// Now repeatedly pair up adjacent TriangleTree nodes, each time
// making a new level with half the number of nodes, until we end up
// with a single node.
while (x_tree > 1 || y_tree > 1) {
// Collapse horizontal pairs.
int tx = 0;
for (int y = 0; y < y_tree; y++) {
tx = 0;
int fx = 0;
while (fx + 1 < x_tree) {
tree[y][tx++] = new TriangleTree(tree[y][fx], tree[y][fx + 1]);
fx += 2;
}
if (fx < x_tree) {
// One more odd element remaining, just copy it up.
tree[y][tx++] = tree[y][fx];
fx++;
}
assert(fx == x_tree);
}
x_tree = tx;
// Collapse vertical pairs.
int ty = 0;
for (int x = 0; x < x_tree; x++) {
ty = 0;
int fy = 0;
while (fy + 1 < y_tree) {
tree[ty++][x] = new TriangleTree(tree[fy][x], tree[fy + 1][x]);
fy += 2;
}
if (fy < y_tree) {
// One more odd element remaining, just copy it up.
tree[ty++][x] = tree[fy][x];
fy++;
}
assert(fy == y_tree);
}
y_tree = ty;
}
assert(x_tree == 1 && y_tree == 1);
_tree = tree[0][0];
}
void MorphGrid::
recompute() {
_tree->recompute();
}
void MorphGrid::
fill_alpha() {
// The stitcher has already made a distinction between interior
// points (that is, points which are over no other image, and must
// be 100% opaque) and exterior points (points which lay over
// another image, and should be feathered). We now need to
// determine the distance each exterior point is from this
// interior/exterior dividing line.
// To do this, we first find an interior point.
bool found_interior = false;
int x, y;
for (y = 0; y < _y_verts && !found_interior; y++) {
for (x = 0; x < _x_verts && !found_interior; x++) {
if (!_table[y][x]._over_another) {
// Here's one!
found_interior = true;
count_dist_from_interior(x, y, 0);
}
}
}
if (!found_interior) {
// There are no interior points in this image--it entirely covers
// other images. (Doesn't seem to be much point to it, does
// there?) We'll just feather the edges a little.
for (y = 0; y < _y_verts; y++) {
_table[y][0]._alpha = 0.0;
_table[y][_x_verts - 1]._alpha = 0.0;
}
for (x = 0; x < _x_verts; x++) {
_table[0][x]._alpha = 0.0;
_table[_y_verts - 1][x]._alpha = 0.0;
}
return;
}
// Now go back through and assign the alpha based on the relative
// distance of each point from the edge and from the interior.
for (y = 0; y < _y_verts; y++) {
for (x = 0; x < _x_verts; x++) {
if (!_table[y][x]._over_another) {
_table[y][x]._alpha = 1.0;
} else {
int dist_from_edge =
min(min(x, y),
min(_x_verts - 1 - x, _y_verts - 1 - y));
assert(_table[y][x]._dist_from_interior >= 0);
// We subtract one from dist_from_interior to give us a bit of
// comfort zone around the interior edge--we're not precisely
// sure where the actual edge is.
int dist_from_interior =
max(_table[y][x]._dist_from_interior - 1, 0);
// Now if dist_from_edge is 0, it must be transparent; if
// dist_from_interior is 0, it must be opaque. Any other
// combination should be some value in between.
if (dist_from_interior == 0) {
_table[y][x]._alpha = 1.0;
} else if (dist_from_edge == 0) {
_table[y][x]._alpha = 0.0;
} else {
double ratio = (double)dist_from_interior /
(double)(dist_from_interior + dist_from_edge);
_table[y][x]._alpha = (cos(ratio * MathNumbers::pi) + 1.0) / 2.0;
}
}
}
}
}
LPoint2d MorphGrid::
morph_point(const LPoint2d &p, TableType from, TableType to) {
if (is_empty()) {
return p;
}
if (_last_triangle != NULL) {
// First, check to see if the point is within the same triangle as
// the last point was. This will save a bit of time if it is.
if (_last_triangle->contains_point(p, from)) {
return _last_triangle->morph_point(p, from, to);
}
}
// Nope, we just blew cache. We'll have to look for the containing
// triangle the hard way.
assert(_tree != NULL);
_last_triangle = _tree->find_triangle(p, from);
if (_last_triangle == NULL) {
return p;
} else {
return _last_triangle->morph_point(p, from, to);
}
}
double MorphGrid::
get_alpha(const LPoint2d &p, TableType from) {
if (is_empty()) {
return 1.0;
}
if (_last_triangle != NULL) {
// First, check to see if the point is within the same triangle as
// the last point was. This will save a bit of time if it is.
if (_last_triangle->contains_point(p, from)) {
return _last_triangle->get_alpha(p, from);
}
}
// Nope, we just blew cache. We'll have to look for the containing
// triangle the hard way.
assert(_tree != NULL);
_last_triangle = _tree->find_triangle(p, from);
if (_last_triangle == NULL) {
return 1.0;
} else {
return _last_triangle->get_alpha(p, from);
}
}
LPoint2d MorphGrid::
morph_in(const LPoint2d &p) const {
return ((MorphGrid *)this)->morph_point(p, TT_out, TT_in);
}
LPoint2d MorphGrid::
morph_out(const LPoint2d &p) const {
return ((MorphGrid *)this)->morph_point(p, TT_in, TT_out);
}
double MorphGrid::
get_alpha(const LPoint2d &p) const {
return ((MorphGrid *)this)->get_alpha(p, TT_in);
}
void MorphGrid::
count_dist_from_interior(int x, int y, int dist) {
if (x >= 0 && x < _x_verts &&
y >= 0 && y < _y_verts) {
Vertex &v = _table[y][x];
if (!v._over_another) {
// Here we are in the interior.
dist = 0;
}
if (v._dist_from_interior < 0 || dist < v._dist_from_interior) {
// Update this point, and recurse to our neighbors.
v._dist_from_interior = dist;
count_dist_from_interior(x + 1, y, dist + 1);
count_dist_from_interior(x - 1, y, dist + 1);
count_dist_from_interior(x, y + 1, dist + 1);
count_dist_from_interior(x, y - 1, dist + 1);
}
}
}