Working, except that image upside-down
This commit is contained in:
parent
f2ad8638aa
commit
ba0a2427bf
|
|
@ -1,4 +1,5 @@
|
|||
#include "cardMaker.cxx"
|
||||
#include "heightfieldTesselator.cxx"
|
||||
#include "config_grutil.cxx"
|
||||
#include "lineSegs.cxx"
|
||||
#include "fisheyeMaker.cxx"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
// Filename: heightfieldTesselator.I
|
||||
// Created by: jyelon (17jul06)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::Constructor
|
||||
// Access: Published
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE HeightfieldTesselator::
|
||||
HeightfieldTesselator(const string &name) : Namable(name) {
|
||||
_poly_count = 10000;
|
||||
_visibility_radius = 32768;
|
||||
_focal_x = 0;
|
||||
_focal_y = 0;
|
||||
_horizontal_scale = 1.0;
|
||||
_vertical_scale = 255.0;
|
||||
_max_triangles = 512;
|
||||
_radii_calculated = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::Destructor
|
||||
// Access: Published
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE HeightfieldTesselator::
|
||||
~HeightfieldTesselator() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::heightfield
|
||||
// Access: Published
|
||||
// Description: Returns a reference to the heightfield (a PNMImage)
|
||||
// contained inside the HeightfieldTesselator. You
|
||||
// can use the reference to alter the heightfield.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE PNMImage &HeightfieldTesselator::
|
||||
heightfield() {
|
||||
return _heightfield;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_heightfield
|
||||
// Access: Published
|
||||
// Description: Loads the specified greyscale image file into
|
||||
// the heightfield.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HeightfieldTesselator::
|
||||
set_heightfield(const Filename &filename, PNMFileType *ftype) {
|
||||
_radii_calculated = false;
|
||||
return _heightfield.read(filename, ftype);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_poly_count
|
||||
// Access: Public
|
||||
// Description: Sets the polygon-count target. The tesselator
|
||||
// usually manages to come within about 20% of the
|
||||
// target, plus or minus.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HeightfieldTesselator::
|
||||
set_poly_count(int n) {
|
||||
_radii_calculated = false;
|
||||
_poly_count = n;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_visibility_radius
|
||||
// Access: Published
|
||||
// Description: Sets the visibility radius. Polygons that
|
||||
// are completely outside the radius (relative to
|
||||
// the focal point) are cropped away. The cropping
|
||||
// is imperfect (all approximations are conservative),
|
||||
// so this should be used in conjunction with a far
|
||||
// clipping plane, fog, or some other visibility
|
||||
// limiting mechanism. The units are in pixels.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HeightfieldTesselator::
|
||||
set_visibility_radius(int radius) {
|
||||
_radii_calculated = false;
|
||||
if (radius < 1) radius = 1;
|
||||
if (radius > 32768) radius = 32768;
|
||||
_visibility_radius = radius;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_focal_point
|
||||
// Access: Published
|
||||
// Description: Sets the focal point. The tesselator generates
|
||||
// high-resolution terrain around the focal point, and
|
||||
// progressively lower and lower resolution terrain
|
||||
// as you get farther away. The units are in pixels.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HeightfieldTesselator::
|
||||
set_focal_point(int x, int y) {
|
||||
_focal_x = x;
|
||||
_focal_y = y;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_horizontal_scale
|
||||
// Access: Published
|
||||
// Description: Sets the horizontal scale. The default scale is 1.0,
|
||||
// meaning that each pixel in the heightfield is
|
||||
// 1x1 panda units wide.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HeightfieldTesselator::
|
||||
set_horizontal_scale(double h) {
|
||||
_horizontal_scale = h;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_vertical_scale
|
||||
// Access: Published
|
||||
// Description: Sets the vertical scale. The default scale is 255.0,
|
||||
// meaning that each as the gray value ranges from (0-1),
|
||||
// the elevation ranges from (0-255) feet.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HeightfieldTesselator::
|
||||
set_vertical_scale(double v) {
|
||||
_vertical_scale = v;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::set_max_triangles
|
||||
// Access: Published
|
||||
// Description: Sets the max triangles per geom.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HeightfieldTesselator::
|
||||
set_max_triangles(int n) {
|
||||
_max_triangles = n;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::subdivide
|
||||
// Access: Private
|
||||
// Description: Returns true if the given square should be subdivided.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HeightfieldTesselator::
|
||||
subdivide(int scale, int x, int y) {
|
||||
if (scale == 0) {
|
||||
return false;
|
||||
}
|
||||
int size = 1<<scale;
|
||||
int hsize = size >> 1;
|
||||
int xcenter = x+hsize;
|
||||
int ycenter = y+hsize;
|
||||
int deltax = x - _focal_x;
|
||||
int deltay = y - _focal_y;
|
||||
if (deltax < 0) deltax = -deltax;
|
||||
if (deltay < 0) deltay = -deltay;
|
||||
int dist = (deltax > deltay) ? deltax : deltay;
|
||||
if (dist < _radii[scale-1]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
// Filename: heightfieldTesselator.cxx
|
||||
// Created by: jyelon (17jul06)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "heightfieldTesselator.h"
|
||||
#include "geomNode.h"
|
||||
#include "transformState.h"
|
||||
#include "sceneGraphReducer.h"
|
||||
#include "lvector3.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::fix_heightfield
|
||||
// Access: Published
|
||||
// Description: Makes sure that the heightfield is a grayscale
|
||||
// image of valid dimensions. If necessary, adds a
|
||||
// band of zeros onto the top and right side of
|
||||
// the heightfield, so as to make the size of the
|
||||
// heightfield a multiple of the given size plus one.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
fix_heightfield(int size) {
|
||||
|
||||
// Calculate the padded size of the heightfield.
|
||||
int xsize = _heightfield.get_x_size();
|
||||
int ysize = _heightfield.get_y_size();
|
||||
int xcells = (xsize + size - 2) / size;
|
||||
int ycells = (ysize + size - 2) / size;
|
||||
int xpadded = xcells * size + 1;
|
||||
int ypadded = ycells * size + 1;
|
||||
|
||||
// If the heightfield is already in good shape, done.
|
||||
if ((xpadded == _heightfield.get_x_size()) &&
|
||||
(ypadded == _heightfield.get_y_size()) &&
|
||||
(_heightfield.is_grayscale())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pad the heightfield, and convert to grey.
|
||||
PNMImage unfixed(_heightfield);
|
||||
_heightfield.clear(xpadded, ypadded, 1,
|
||||
unfixed.get_maxval(),
|
||||
unfixed.get_type());
|
||||
for (int y = 0; y < ysize; y++) {
|
||||
for (int x = 0; x < xsize; x++) {
|
||||
_heightfield.set_gray_val(x, y, unfixed.get_gray_val(x, y));
|
||||
}
|
||||
}
|
||||
for (int y = ysize; y < ypadded; y++) {
|
||||
for (int x = xsize; x < xpadded; x++) {
|
||||
_heightfield.set_gray_val(x, y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::get_vertex
|
||||
// Access: Private
|
||||
// Description: Fetches the vertex at (x,y), or if the vertex
|
||||
// does not exist, creates it.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int HeightfieldTesselator::
|
||||
get_vertex(int x, int y) {
|
||||
int xsize = _heightfield.get_x_size();
|
||||
int vtx = _vertex_index[x+y*xsize];
|
||||
if (vtx >= 0) {
|
||||
return vtx;
|
||||
}
|
||||
int nx = x-1;
|
||||
int px = x+1;
|
||||
int ny = y-1;
|
||||
int py = y+1;
|
||||
if (nx < 0) nx++;
|
||||
if (ny < 0) ny++;
|
||||
if (px >= _heightfield.get_x_size()) px--;
|
||||
if (py >= _heightfield.get_y_size()) py--;
|
||||
double drx = _heightfield.get_gray(px,y) - _heightfield.get_gray(nx,y);
|
||||
double dry = _heightfield.get_gray(x,py) - _heightfield.get_gray(x,ny);
|
||||
LVector3f normal(drx * _vertical_scale * 0.5, dry * _vertical_scale * 0.5, _horizontal_scale);
|
||||
normal.normalize();
|
||||
double z = _heightfield.get_gray(x,y);
|
||||
if (z > 2.0) {
|
||||
cerr << "What the heck?";
|
||||
}
|
||||
_vertex_writer->add_data3f(x*_horizontal_scale,y*_horizontal_scale,z*_vertical_scale);
|
||||
_normal_writer->add_data3f(normal);
|
||||
vtx = _next_index++;
|
||||
_dirty_vertices[vtx] = x+y*xsize;
|
||||
_vertex_index[x+y*xsize] = vtx;
|
||||
return vtx;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::generate
|
||||
// Access: Published
|
||||
// Description: Generates a tree of nodes that represents the
|
||||
// heightfield. This can be reparented into the scene.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodePath HeightfieldTesselator::
|
||||
generate() {
|
||||
int scale = 7;
|
||||
int size = 1 << scale;
|
||||
fix_heightfield(size);
|
||||
int xsize = _heightfield.get_x_size();
|
||||
int ysize = _heightfield.get_y_size();
|
||||
int xcells = (xsize + size - 2) / size;
|
||||
int ycells = (ysize + size - 2) / size;
|
||||
|
||||
_vertex_index = new int[xsize * ysize];
|
||||
_dirty_vertices = new int[xsize * ysize];
|
||||
_triangle_totals = new int[xsize * ysize];
|
||||
for (int y=0; y<ysize; y++) {
|
||||
for (int x=0; x<xsize; x++) {
|
||||
_vertex_index[y*xsize+x] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_radii_calculated) {
|
||||
int saved_focal_x = _focal_x;
|
||||
int saved_focal_y = _focal_y;
|
||||
|
||||
_focal_x = _heightfield.get_x_size() >> 1;
|
||||
_focal_y = _heightfield.get_y_size() >> 1;
|
||||
|
||||
calculate_radii(scale);
|
||||
|
||||
_focal_x = saved_focal_x;
|
||||
_focal_y = saved_focal_y;
|
||||
|
||||
_radii_calculated = true;
|
||||
}
|
||||
|
||||
PT(PandaNode) result = new PandaNode(get_name());
|
||||
NodePath root(result);
|
||||
|
||||
int total = 0;
|
||||
for (int y=0; y<ycells; y++) {
|
||||
for (int x=0; x<xcells; x++) {
|
||||
total += count_triangles(scale,x*size,y*size);
|
||||
}
|
||||
}
|
||||
cerr << "Terrain mesh is " << total << " triangles.\n";
|
||||
for (int y=0; y<ycells; y++) {
|
||||
for (int x=0; x<xcells; x++) {
|
||||
generate_square(root,scale,x*size,y*size,true);
|
||||
}
|
||||
}
|
||||
delete _vertex_index;
|
||||
delete _dirty_vertices;
|
||||
delete _triangle_totals;
|
||||
_vertex_index =0;
|
||||
_dirty_vertices =0;
|
||||
_triangle_totals =0;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::calculate_radii
|
||||
// Access: Private
|
||||
// Description: Sets the radii appropriately to achieve the
|
||||
// desired polygon count. This is achieved by binary
|
||||
// search.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
calculate_radii(int scale) {
|
||||
int size = 1 << scale;
|
||||
int xsize = _heightfield.get_x_size();
|
||||
int ysize = _heightfield.get_y_size();
|
||||
int xcells = (xsize + size - 2) / size;
|
||||
int ycells = (ysize + size - 2) / size;
|
||||
|
||||
double lo = 5.0;
|
||||
double hi = _heightfield.get_x_size() + _heightfield.get_y_size();
|
||||
while (1) {
|
||||
double mid = (lo + hi) * 0.5;
|
||||
for (int i=0; i<16; i++) {
|
||||
_radii[i] = (int)(mid * (1<<i));
|
||||
}
|
||||
int total = 0;
|
||||
for (int y=0; y<ycells; y++) {
|
||||
for (int x=0; x<xcells; x++) {
|
||||
total += count_triangles(scale,x*size,y*size);
|
||||
}
|
||||
}
|
||||
if (total > _poly_count) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid;
|
||||
}
|
||||
if (hi - lo < 1.0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
double mid = (lo + hi) * 0.5;
|
||||
for (int i=0; i<16; i++) {
|
||||
_radii[i] = (int)(mid * (1<<i));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::generate_square
|
||||
// Access: Private
|
||||
// Description: Adds a square region to the current geom.
|
||||
// This relies on the following preconditions:
|
||||
//
|
||||
// 1. A square of scale N can be adjacent to
|
||||
// a square of scale N or scale N-1, but not
|
||||
// scale N-2 or smaller.
|
||||
//
|
||||
// 2. A square of scale N can be adjacent to
|
||||
// at most one square of scale N-1.
|
||||
//
|
||||
// Precondition 1 is assured by spacing out the
|
||||
// detail radii sufficiently. Precondition 2 is
|
||||
// assured by using rectangular detail radii.
|
||||
//
|
||||
// I may someday rewrite this code to eliminate
|
||||
// precondition 2, to allow circular detail radii.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
generate_square(NodePath root, int scale, int x, int y, bool forceclose) {
|
||||
// There are nine possible vertices in the square,
|
||||
// which are labeled as follows:
|
||||
//
|
||||
// G--H--I
|
||||
// | |
|
||||
// D E F
|
||||
// | |
|
||||
// A--B--C
|
||||
|
||||
int size = 1<<scale;
|
||||
int hsize = size>>1;
|
||||
|
||||
#define POINTA get_vertex(x ,y)
|
||||
#define POINTB get_vertex(x+hsize,y)
|
||||
#define POINTC get_vertex(x+size ,y)
|
||||
#define POINTD get_vertex(x ,y+hsize)
|
||||
#define POINTE get_vertex(x+hsize,y+hsize)
|
||||
#define POINTF get_vertex(x+size ,y+hsize)
|
||||
#define POINTG get_vertex(x ,y+size)
|
||||
#define POINTH get_vertex(x+hsize,y+size)
|
||||
#define POINTI get_vertex(x+size ,y+size)
|
||||
|
||||
if (_triangles == 0) {
|
||||
open_geom();
|
||||
}
|
||||
if (subdivide(scale, x, y)) {
|
||||
int xc = x+(size>>1);
|
||||
int yc = y+(size>>1);
|
||||
if (_triangle_totals[yc*_heightfield.get_x_size()+xc] > _max_triangles) {
|
||||
if (_next_index) close_geom(root);
|
||||
NodePath subroot = root.attach_new_node(get_name() + " interior");
|
||||
generate_square(subroot, scale-1, x, y , true);
|
||||
generate_square(subroot, scale-1, xc, y , true);
|
||||
generate_square(subroot, scale-1, xc, yc, true);
|
||||
generate_square(subroot, scale-1, x, yc, true);
|
||||
} else {
|
||||
generate_square(root, scale-1, x, y , false);
|
||||
generate_square(root, scale-1, xc, y , false);
|
||||
generate_square(root, scale-1, xc, yc, false);
|
||||
generate_square(root, scale-1, x, yc, false);
|
||||
}
|
||||
} else if (subdivide(scale, x+size, y)) {
|
||||
add_quad(POINTG,POINTA,POINTI,POINTF);
|
||||
add_quad(POINTA,POINTA,POINTF,POINTC);
|
||||
} else if (subdivide(scale, x-size, y)) {
|
||||
add_quad(POINTG,POINTD,POINTI,POINTI);
|
||||
add_quad(POINTD,POINTA,POINTI,POINTC);
|
||||
} else if (subdivide(scale, x, y+size)) {
|
||||
add_quad(POINTG,POINTA,POINTH,POINTA);
|
||||
add_quad(POINTH,POINTA,POINTI,POINTC);
|
||||
} else if (subdivide(scale, x, y-size)) {
|
||||
add_quad(POINTG,POINTA,POINTI,POINTB);
|
||||
add_quad(POINTI,POINTB,POINTI,POINTC);
|
||||
} else {
|
||||
add_quad(POINTG,POINTA,POINTI,POINTC);
|
||||
}
|
||||
if (forceclose || (_next_index > _max_triangles)) {
|
||||
close_geom(root);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::count_triangles
|
||||
// Access: Private
|
||||
// Description: Calculates how many triangles are inside
|
||||
// the given region. The result is stored in
|
||||
// the _poly_totals array, in the center of the
|
||||
// square.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int HeightfieldTesselator::
|
||||
count_triangles(int scale, int x, int y) {
|
||||
int size = 1<<scale;
|
||||
if (subdivide(scale, x, y)) {
|
||||
int xc = x + (size>>1);
|
||||
int yc = y + (size>>1);
|
||||
int n = 0;
|
||||
n += count_triangles(scale-1, x, y );
|
||||
n += count_triangles(scale-1, xc, y );
|
||||
n += count_triangles(scale-1, x, yc);
|
||||
n += count_triangles(scale-1, xc, yc);
|
||||
_triangle_totals[yc*_heightfield.get_x_size() + xc] = n;
|
||||
return n;
|
||||
} else if (subdivide(scale, x+size, y)) {
|
||||
return 3;
|
||||
} else if (subdivide(scale, x-size, y)) {
|
||||
return 3;
|
||||
} else if (subdivide(scale, x, y+size)) {
|
||||
return 3;
|
||||
} else if (subdivide(scale, x, y-size)) {
|
||||
return 3;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::add_quad_to_strip
|
||||
// Access: Private
|
||||
// Description: Adds a quad to the current triangle strip.
|
||||
//
|
||||
// If the quad is adjacent to the previous quad,
|
||||
// then the two quads are stitched together into
|
||||
// a strip. If not, degenerate triangles are
|
||||
// inserted into the strip to separate the two.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
add_quad_to_strip(int v1, int v2, int v3, int v4) {
|
||||
if ((v1 != v2)&&(v2 != v3)&&(v1 != v3)) {
|
||||
_triangles->add_vertices(v1,v2,v3);
|
||||
}
|
||||
if ((v3 != v2)&&(v2 != v4)&&(v4 != v3)) {
|
||||
_triangles->add_vertices(v3,v2,v4);
|
||||
}
|
||||
// if ((v1 == _last_vertex_a)&&(v2 == _last_vertex_b)) {
|
||||
// _tristrip->add_vertices(v3,v4);
|
||||
// _last_vertex_a = v3;
|
||||
// _last_vertex_b = v4;
|
||||
// } else {
|
||||
// if ((_last_vertex_a == -1)&&(_last_vertex_b == -1)) {
|
||||
// _tristrip->add_vertices(v1,v2,v3,v4);
|
||||
// _last_vertex_a = v3;
|
||||
// _last_vertex_b = v4;
|
||||
// } else {
|
||||
// _tristrip->add_vertices(_last_vertex_b, v1, v1, v2);
|
||||
// _tristrip->add_vertices(v1,v2,v3,v4);
|
||||
// _last_vertex_a = v3;
|
||||
// _last_vertex_b = v4;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::add_quad
|
||||
// Access: Private
|
||||
// Description: Adds a quad to the current geom.
|
||||
//
|
||||
// Eventually, I plan to reimplement this. It is
|
||||
// going to add a quad to a table of quads. A
|
||||
// post-processing pass is going to traverse the
|
||||
// table, calling add_quad_to_strip in the optimal
|
||||
// order. For now, though, this routine just calls
|
||||
// add_quad_to_strip directly, which is quite
|
||||
// inefficient.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
add_quad(int v1, int v2, int v3, int v4) {
|
||||
add_quad_to_strip(v1, v2, v3, v4);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::open_geom
|
||||
// Access: Private
|
||||
// Description: Initiates the construction of a geom.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
open_geom() {
|
||||
_vdata = new GeomVertexData
|
||||
("heightfield", GeomVertexFormat::get_v3n3(), Geom::UH_static);
|
||||
_vertex_writer = new GeomVertexWriter(_vdata, InternalName::get_vertex());
|
||||
_normal_writer = new GeomVertexWriter(_vdata, InternalName::get_normal());
|
||||
_triangles = new GeomTriangles(Geom::UH_static);
|
||||
_triangles->set_shade_model(Geom::SM_uniform);
|
||||
|
||||
_next_index = 0;
|
||||
_last_vertex_a = -1;
|
||||
_last_vertex_b = -1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HeightfieldTesselator::close_geom
|
||||
// Access: Private
|
||||
// Description: Completes the construction of a geom.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HeightfieldTesselator::
|
||||
close_geom(NodePath root) {
|
||||
if (_triangles == 0) {
|
||||
return;
|
||||
}
|
||||
_triangles->close_primitive();
|
||||
PT(Geom) geom = new Geom(_vdata);
|
||||
geom->add_primitive(_triangles);
|
||||
PT(GeomNode) gnode = new GeomNode(get_name() + " patch");
|
||||
gnode->add_geom(geom);
|
||||
root.attach_new_node(gnode);
|
||||
delete _vertex_writer;
|
||||
delete _normal_writer;
|
||||
|
||||
for (int i=0; i<_next_index; i++) {
|
||||
_vertex_index[_dirty_vertices[i]] = -1;
|
||||
}
|
||||
|
||||
_next_index = 0;
|
||||
_last_vertex_a = -1;
|
||||
_last_vertex_b = -1;
|
||||
_vertex_writer = 0;
|
||||
_normal_writer = 0;
|
||||
_triangles = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
// Filename: heightfieldTesselator.h
|
||||
// Created by: jyelon (17jul06)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef HEIGHTFIELDTESSELATOR_H
|
||||
#define HEIGHTFIELDTESSELATOR_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
#include "luse.h"
|
||||
#include "pandaNode.h"
|
||||
#include "pointerTo.h"
|
||||
#include "namable.h"
|
||||
#include "pnmImage.h"
|
||||
#include "geom.h"
|
||||
#include "geomTristrips.h"
|
||||
#include "geomTriangles.h"
|
||||
#include "geomVertexWriter.h"
|
||||
#include "geomVertexFormat.h"
|
||||
#include "nodePath.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : HeightfieldTesselator
|
||||
// Description : Converts a height field in the form of a greyscale
|
||||
// image into a scene consisting of a number of GeomNodes.
|
||||
//
|
||||
// The tesselation uses an LOD algorithm. You
|
||||
// supply a "focal point" (X,Y) which tells the
|
||||
// tesselator where the bulk of the detail should
|
||||
// be concentrated. The intent is that as the player
|
||||
// walks around the terrain, you should occasionally
|
||||
// move the focal point to wherever the player is.
|
||||
// You should not move the focal point every frame:
|
||||
// tesselation is not that fast. Also, changing the
|
||||
// focal point may cause popping, so it is best to
|
||||
// minimize the number of changes. There are a number
|
||||
// of parameters that you can use to control tesselation,
|
||||
// such as a target polygon count, and a visibility
|
||||
// radius.
|
||||
//
|
||||
// The heightfield needs to be a multiple of 128 pixels
|
||||
// in each dimension. It does not need to be square,
|
||||
// and it does not need to be a power of two. For
|
||||
// example, a 384 x 640 heightfield is fine.
|
||||
// Be aware that tesselation time is proportional to
|
||||
// heightfield area, so if you plan to use a size larger
|
||||
// than about 512x512, it may be desirable to benchmark.
|
||||
//
|
||||
// Altering parameters, such as the poly count, the
|
||||
// view radius, or the focal point, does not alter any
|
||||
// GeomNodes already generated. Parameter changes only
|
||||
// affect subsequently-generated GeomNodes. It is
|
||||
// possible to cache many different tesselations of the
|
||||
// same terrain.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EXPCL_PANDA HeightfieldTesselator : public Namable {
|
||||
PUBLISHED:
|
||||
INLINE HeightfieldTesselator(const string &name);
|
||||
INLINE ~HeightfieldTesselator();
|
||||
|
||||
INLINE PNMImage &heightfield();
|
||||
INLINE bool set_heightfield(const Filename &filename, PNMFileType *type = NULL);
|
||||
INLINE void set_poly_count(int n);
|
||||
INLINE void set_visibility_radius(int r);
|
||||
INLINE void set_focal_point(int x, int y);
|
||||
INLINE void set_horizontal_scale(double h);
|
||||
INLINE void set_vertical_scale(double v);
|
||||
INLINE void set_max_triangles(int n);
|
||||
|
||||
NodePath generate();
|
||||
|
||||
private:
|
||||
|
||||
// These are initialized during the first 'generate'
|
||||
int _radii[16];
|
||||
bool _radii_calculated;
|
||||
|
||||
// These are only valid during the generate process.
|
||||
int *_triangle_totals;
|
||||
int *_vertex_index;
|
||||
int *_dirty_vertices;
|
||||
|
||||
// These are only valid when a geom is open.
|
||||
int _next_index;
|
||||
int _last_vertex_a;
|
||||
int _last_vertex_b;
|
||||
PT(GeomVertexData) _vdata;
|
||||
GeomVertexWriter *_vertex_writer;
|
||||
GeomVertexWriter *_normal_writer;
|
||||
PT(GeomTriangles) _triangles;
|
||||
|
||||
INLINE bool subdivide(int scale, int x, int y);
|
||||
void calculate_radii(int scale);
|
||||
void generate_square(NodePath root, int scale, int x, int y, bool forceclose);
|
||||
int count_triangles(int scale, int x, int y);
|
||||
int get_vertex(int x, int y);
|
||||
void add_quad_to_strip(int v1, int v2, int v3, int v4);
|
||||
void add_quad(int v1, int v2, int v3, int v4);
|
||||
void fix_heightfield(int size);
|
||||
void open_geom();
|
||||
void close_geom(NodePath root);
|
||||
|
||||
PNMImage _heightfield;
|
||||
int _poly_count;
|
||||
int _visibility_radius;
|
||||
int _focal_x;
|
||||
int _focal_y;
|
||||
double _horizontal_scale;
|
||||
double _vertical_scale;
|
||||
int _max_triangles;
|
||||
};
|
||||
|
||||
#include "heightfieldTesselator.I"
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue