2098 lines
39 KiB
C++
2098 lines
39 KiB
C++
/*******************************************************************************
|
|
* hfield.cpp
|
|
*
|
|
* This file implements the height field shape primitive. The shape is
|
|
* implemented as a collection of triangles which are calculated as
|
|
* needed. The basic intersection routine first computes the rays
|
|
* intersection with the box marking the limits of the shape, then
|
|
* follows the line from one intersection point to the other, testing the
|
|
* two triangles which form the pixel for an intersection with the ray at
|
|
* each step.
|
|
*
|
|
* height field added by Doug Muir with lots of advice and support
|
|
* from David Buck and Drew Wells.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/backend/shape/hfield.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Explanation:
|
|
*
|
|
* -
|
|
*
|
|
* Syntax:
|
|
*
|
|
* ---
|
|
*
|
|
* Aug 1994 : Merged functions for CSG height fields into functions for
|
|
* non-CSG height fiels. Moved all height field map related
|
|
* data into one data structure. Fixed memory problems. [DB]
|
|
*
|
|
* Feb 1995 : Major rewrite of the height field intersection tests. [DB]
|
|
*
|
|
*****************************************************************************/
|
|
|
|
// frame.h must always be the first POV file included (pulls in platform config)
|
|
#include "backend/frame.h"
|
|
#include "backend/math/vector.h"
|
|
#include "backend/shape/hfield.h"
|
|
#include "backend/support/imageutil.h"
|
|
#include "backend/math/matrices.h"
|
|
#include "backend/scene/objects.h"
|
|
#include "backend/scene/threaddata.h"
|
|
#include "base/pov_err.h"
|
|
|
|
#include <algorithm>
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace pov
|
|
{
|
|
|
|
/*****************************************************************************
|
|
* Local preprocessor defines
|
|
******************************************************************************/
|
|
|
|
#define sign(x) (((x) >= 0.0) ? 1.0 : -1.0)
|
|
|
|
#define Get_Height(x, z) ((DBL)Data->Map[(z)][(x)])
|
|
|
|
/* Small offest. */
|
|
|
|
const DBL HFIELD_OFFSET = 0.001;
|
|
|
|
const DBL HFIELD_TOLERANCE = 1.0e-6;
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* All_HField_Intersections
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Modified to work with new intersection functions. [DB]
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool HField::All_Intersections(const Ray& ray, IStack& Depth_Stack, TraceThreadData *Thread)
|
|
{
|
|
int Side1, Side2;
|
|
VECTOR Start;
|
|
Ray Temp_Ray;
|
|
DBL depth1, depth2;
|
|
|
|
Thread->Stats()[Ray_HField_Tests]++;
|
|
|
|
MInvTransPoint(Temp_Ray.Origin, ray.Origin, Trans);
|
|
MInvTransDirection(Temp_Ray.Direction, ray.Direction, Trans);
|
|
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Box_Tests]++;
|
|
#endif
|
|
|
|
if (!Box::Intersect(Temp_Ray,NULL,bounding_corner1,bounding_corner2,&depth1,&depth2,&Side1,&Side2))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Box_Tests_Succeeded]++;
|
|
#endif
|
|
|
|
if (depth1 < HFIELD_TOLERANCE)
|
|
{
|
|
depth1 = HFIELD_TOLERANCE;
|
|
|
|
if (depth1 > depth2)
|
|
{
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
VEvaluateRay(Start, Temp_Ray.Origin, depth1, Temp_Ray.Direction);
|
|
|
|
if (block_traversal(Temp_Ray, Start, Depth_Stack, ray, depth1, depth2, Thread))
|
|
{
|
|
Thread->Stats()[Ray_HField_Tests_Succeeded]++;
|
|
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Inside_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool HField::Inside(const VECTOR IPoint, TraceThreadData *Thread) const
|
|
{
|
|
int px, pz;
|
|
DBL x,z,y1,y2,y3,water, dot1, dot2;
|
|
VECTOR Local_Origin, H_Normal, Test;
|
|
|
|
MInvTransPoint(Test, IPoint, Trans);
|
|
|
|
water = bounding_corner1[Y];
|
|
|
|
if ((Test[X] < 0.0) || (Test[X] >= bounding_corner2[X]) ||
|
|
(Test[Z] < 0.0) || (Test[Z] >= bounding_corner2[Z]))
|
|
{
|
|
return(Test_Flag(this, INVERTED_FLAG));
|
|
}
|
|
|
|
if (Test[Y] >= bounding_corner2[Y])
|
|
{
|
|
return(Test_Flag(this, INVERTED_FLAG));
|
|
}
|
|
|
|
if (Test[Y] < water)
|
|
{
|
|
return(!Test_Flag(this, INVERTED_FLAG));
|
|
}
|
|
|
|
px = (int)Test[X];
|
|
pz = (int)Test[Z];
|
|
|
|
x = Test[X] - (DBL)px;
|
|
z = Test[Z] - (DBL)pz;
|
|
|
|
if ((x+z) < 1.0)
|
|
{
|
|
y1 = max(Get_Height(px, pz), water);
|
|
y2 = max(Get_Height(px+1, pz), water);
|
|
y3 = max(Get_Height(px, pz+1), water);
|
|
|
|
Make_Vector(Local_Origin,(DBL)px,y1,(DBL)pz);
|
|
|
|
Make_Vector(H_Normal, y1-y2, 1.0, y1-y3);
|
|
}
|
|
else
|
|
{
|
|
px = (int)ceil(Test[X]);
|
|
pz = (int)ceil(Test[Z]);
|
|
|
|
y1 = max(Get_Height(px, pz), water);
|
|
y2 = max(Get_Height(px-1, pz), water);
|
|
y3 = max(Get_Height(px, pz-1), water);
|
|
|
|
Make_Vector(Local_Origin,(DBL)px,y1,(DBL)pz);
|
|
|
|
Make_Vector(H_Normal, y2-y1, 1.0, y3-y1);
|
|
}
|
|
|
|
VDot(dot1, Test, H_Normal);
|
|
VDot(dot2, Local_Origin, H_Normal);
|
|
|
|
if (dot1 < dot2)
|
|
{
|
|
return(!Test_Flag(this, INVERTED_FLAG));
|
|
}
|
|
|
|
return(Test_Flag(this, INVERTED_FLAG));
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* HField_Normal
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* 2000 : NK crash bugfix
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Normal(VECTOR Result, Intersection *Inter, TraceThreadData *Thread) const
|
|
{
|
|
int px,pz, i;
|
|
DBL x,z,y1,y2,y3,u,v;
|
|
VECTOR Local_Origin;
|
|
VECTOR n[5];
|
|
|
|
if(Inter->haveNormal == true)
|
|
{
|
|
Assign_Vector(Result,Inter->INormal);
|
|
return;
|
|
}
|
|
|
|
MInvTransPoint(Local_Origin, Inter->IPoint, Trans);
|
|
|
|
px = (int)Local_Origin[X];
|
|
pz = (int)Local_Origin[Z];
|
|
|
|
if (px>Data->max_x)
|
|
px=Data->max_x;
|
|
if (pz>Data->max_z)
|
|
pz=Data->max_z;
|
|
|
|
x = Local_Origin[X] - (DBL)px;
|
|
z = Local_Origin[Z] - (DBL)pz;
|
|
|
|
if (Test_Flag(this, SMOOTHED_FLAG))
|
|
{
|
|
n[0][X] = Data->Normals[pz][px][0];
|
|
n[0][Y] = Data->Normals[pz][px][1];
|
|
n[0][Z] = Data->Normals[pz][px][2];
|
|
n[1][X] = Data->Normals[pz][px+1][0];
|
|
n[1][Y] = Data->Normals[pz][px+1][1];
|
|
n[1][Z] = Data->Normals[pz][px+1][2];
|
|
n[2][X] = Data->Normals[pz+1][px][0];
|
|
n[2][Y] = Data->Normals[pz+1][px][1];
|
|
n[2][Z] = Data->Normals[pz+1][px][2];
|
|
n[3][X] = Data->Normals[pz+1][px+1][0];
|
|
n[3][Y] = Data->Normals[pz+1][px+1][1];
|
|
n[3][Z] = Data->Normals[pz+1][px+1][2];
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
MTransNormal(n[i], n[i], Trans);
|
|
VNormalizeEq(n[i]);
|
|
}
|
|
|
|
u = (1.0 - x);
|
|
v = (1.0 - z);
|
|
|
|
Result[X] = v*(u*n[0][X] + x*n[1][X]) + z*(u*n[2][X] + x*n[3][X]);
|
|
Result[Y] = v*(u*n[0][Y] + x*n[1][Y]) + z*(u*n[2][Y] + x*n[3][Y]);
|
|
Result[Z] = v*(u*n[0][Z] + x*n[1][Z]) + z*(u*n[2][Z] + x*n[3][Z]);
|
|
}
|
|
else
|
|
{
|
|
if ((x+z) <= 1.0)
|
|
{
|
|
/* Lower triangle. */
|
|
|
|
y1 = Get_Height(px, pz);
|
|
y2 = Get_Height(px+1, pz);
|
|
y3 = Get_Height(px, pz+1);
|
|
|
|
Make_Vector(Result, y1-y2, 1.0, y1-y3);
|
|
}
|
|
else
|
|
{
|
|
/* Upper triangle. */
|
|
|
|
y1 = Get_Height(px+1, pz+1);
|
|
y2 = Get_Height(px, pz+1);
|
|
y3 = Get_Height(px+1, pz);
|
|
|
|
Make_Vector(Result, y2-y1, 1.0, y3-y1);
|
|
}
|
|
|
|
MTransNormal(Result, Result, Trans);
|
|
}
|
|
|
|
VNormalize(Result, Result);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* normalize
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
DBL HField::normalize(VECTOR A, const VECTOR B)
|
|
{
|
|
DBL tmp;
|
|
|
|
VLength(tmp, B);
|
|
|
|
if (fabs(tmp) > EPSILON)
|
|
{
|
|
VInverseScale(A, B, tmp);
|
|
}
|
|
else
|
|
{
|
|
Make_Vector(A, 0.0, 1.0, 0.0);
|
|
}
|
|
|
|
return(tmp);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* intersect_pixel
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* 2000 : NK crash bugfix
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool HField::intersect_pixel(int x, int z, const Ray &ray, DBL height1, DBL height2, IStack& HField_Stack, const Ray &RRay, DBL mindist, DBL maxdist, TraceThreadData *Thread)
|
|
{
|
|
int Found;
|
|
DBL dot, depth1, depth2;
|
|
DBL s, t, y1, y2, y3, y4;
|
|
DBL min_y2_y3, max_y2_y3;
|
|
DBL max_height, min_height;
|
|
VECTOR P, N, V1;
|
|
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Cell_Tests]++;
|
|
#endif
|
|
|
|
if (z>Data->max_z) z = Data->max_z;
|
|
if (x>Data->max_x) x = Data->max_x;
|
|
|
|
y1 = Get_Height(x, z);
|
|
y2 = Get_Height(x+1, z);
|
|
y3 = Get_Height(x, z+1);
|
|
y4 = Get_Height(x+1, z+1);
|
|
|
|
/* Do we hit this cell at all? */
|
|
|
|
if (y2 < y3)
|
|
{
|
|
min_y2_y3 = y2;
|
|
max_y2_y3 = y3;
|
|
}
|
|
else
|
|
{
|
|
min_y2_y3 = y3;
|
|
max_y2_y3 = y2;
|
|
}
|
|
|
|
min_height = min(min(y1, y4), min_y2_y3);
|
|
max_height = max(max(y1, y4), max_y2_y3);
|
|
|
|
if ((max_height < height1) || (min_height > height2))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Cell_Tests_Succeeded]++;
|
|
#endif
|
|
|
|
Found = false;
|
|
|
|
/* Check if we'll hit first triangle. */
|
|
|
|
min_height = min(y1, min_y2_y3);
|
|
max_height = max(y1, max_y2_y3);
|
|
|
|
if ((max_height >= height1) && (min_height <= height2))
|
|
{
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Triangle_Tests]++;
|
|
#endif
|
|
|
|
/* Set up triangle. */
|
|
|
|
Make_Vector(P, (DBL)x, y1, (DBL)z);
|
|
|
|
/*
|
|
* Calculate the normal vector from:
|
|
*
|
|
* N = V2 x V1, with V1 = <1, y2-y1, 0>, V2 = <0, y3-y1, 1>.
|
|
*/
|
|
|
|
Make_Vector(N, y1-y2, 1.0, y1-y3);
|
|
|
|
/* Now intersect the triangle. */
|
|
|
|
VDot(dot, N, ray.Direction);
|
|
|
|
if ((dot > EPSILON) || (dot < -EPSILON))
|
|
// (Rays virtually parallel to the triangle's plane are asking for mathematical trouble,
|
|
// so they're always presumed to be a no-hit.)
|
|
{
|
|
VSub(V1, P, ray.Origin);
|
|
|
|
VDot(depth1, N, V1);
|
|
|
|
depth1 /= dot;
|
|
|
|
if ((depth1 >= mindist) && (depth1 <= maxdist))
|
|
// (More expensive check to make sure the ray isn't near-parallel to the triangle's plane.)
|
|
{
|
|
s = ray.Origin[X] + depth1 * ray.Direction[X] - (DBL)x;
|
|
t = ray.Origin[Z] + depth1 * ray.Direction[Z] - (DBL)z;
|
|
|
|
if ((s >= -0.0001) && (t >= -0.0001) && ((s+t) <= 1.0001))
|
|
// (Check whether the point of intersection with the plane is within the triangle)
|
|
{
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Triangle_Tests_Succeeded]++;
|
|
#endif
|
|
|
|
VEvaluateRay(P, RRay.Origin, depth1, RRay.Direction);
|
|
|
|
if (Clip.empty() || Point_In_Clip(P, Clip, Thread))
|
|
// (Check whether the point of intersection is within the clipped-by volume)
|
|
{
|
|
if (Test_Flag(this, SMOOTHED_FLAG))
|
|
// Smoothed height field;
|
|
// computation of surface normal is still non-trivial from here,
|
|
// so defer it until we know it's needed.
|
|
HField_Stack->push(Intersection(depth1, P, this));
|
|
else
|
|
{
|
|
// Non-smoothed height field;
|
|
// we've already computed the surface normal by now, so just convert it into
|
|
// world coordinate space, so we don't need to re-compute it in case it's indeed needed later.
|
|
VECTOR tmp;
|
|
Assign_Vector(tmp,N);
|
|
MTransNormal(tmp,tmp,Trans);
|
|
VNormalize(tmp,tmp);
|
|
HField_Stack->push(Intersection(depth1, P, tmp, this));
|
|
}
|
|
|
|
Found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if we'll hit second triangle. */
|
|
|
|
min_height = min(y4, min_y2_y3);
|
|
max_height = max(y4, max_y2_y3);
|
|
|
|
if ((max_height >= height1) && (min_height <= height2))
|
|
{
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Triangle_Tests]++;
|
|
#endif
|
|
|
|
/* Set up triangle. */
|
|
|
|
Make_Vector(P, (DBL)(x+1), y4, (DBL)(z+1));
|
|
|
|
/*
|
|
* Calculate the normal vector from:
|
|
*
|
|
* N = V2 x V1, with V1 = <-1, y3-y4, 0>, V2 = <0, y2-y4, -1>.
|
|
*/
|
|
|
|
Make_Vector(N, y3-y4, 1.0, y2-y4);
|
|
|
|
/* Now intersect the triangle. */
|
|
|
|
VDot(dot, N, ray.Direction);
|
|
|
|
if ((dot > EPSILON) || (dot < -EPSILON))
|
|
// (Rays virtually parallel to the triangle's plane are asking for mathematical trouble,
|
|
// so they're always presumed to be a no-hit.)
|
|
{
|
|
VSub(V1, P, ray.Origin);
|
|
|
|
VDot(depth2, N, V1);
|
|
|
|
depth2 /= dot;
|
|
|
|
if ((depth2 >= mindist) && (depth2 <= maxdist))
|
|
// (More expensive check to make sure the ray isn't near-parallel to the triangle's plane.)
|
|
{
|
|
s = ray.Origin[X] + depth2 * ray.Direction[X] - (DBL)x;
|
|
t = ray.Origin[Z] + depth2 * ray.Direction[Z] - (DBL)z;
|
|
|
|
if ((s <= 1.0001) && (t <= 1.0001) && ((s+t) >= 0.9999))
|
|
// (Check whether the point of intersection with the plane is within the triangle)
|
|
{
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Triangle_Tests_Succeeded]++;
|
|
#endif
|
|
|
|
VEvaluateRay(P, RRay.Origin, depth2, RRay.Direction);
|
|
|
|
if (Clip.empty() || Point_In_Clip(P, Clip, Thread))
|
|
// (Check whether the point of intersection is within the clipped-by volume)
|
|
{
|
|
if (Test_Flag(this, SMOOTHED_FLAG))
|
|
// Smoothed height field;
|
|
// computation of surface normal is still non-trivial from here,
|
|
// so defer it until we know it's needed.
|
|
HField_Stack->push(Intersection(depth2, P, this));
|
|
else
|
|
{
|
|
// Non-smoothed height field;
|
|
// we've already computed the surface normal by now, so just convert it into
|
|
// world coordinate space, so we don't need to re-compute it in case it's indeed needed later.
|
|
VECTOR tmp;
|
|
Assign_Vector(tmp,N);
|
|
MTransNormal(tmp,tmp,Trans);
|
|
VNormalize(tmp,tmp);
|
|
HField_Stack->push(Intersection(depth2, P, tmp, this));
|
|
}
|
|
|
|
Found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(Found);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* add_single_normal
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
int HField::add_single_normal(HF_VAL **data, int xsize, int zsize, int x0, int z0, int x1, int z1, int x2, int z2, VECTOR N)
|
|
{
|
|
VECTOR v0, v1, v2, t0, t1, Nt;
|
|
|
|
if ((x0 < 0) || (z0 < 0) ||
|
|
(x1 < 0) || (z1 < 0) ||
|
|
(x2 < 0) || (z2 < 0) ||
|
|
(x0 > xsize) || (z0 > zsize) ||
|
|
(x1 > xsize) || (z1 > zsize) ||
|
|
(x2 > xsize) || (z2 > zsize))
|
|
{
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
Make_Vector(v0, x0, (DBL)data[z0][x0], z0);
|
|
Make_Vector(v1, x1, (DBL)data[z1][x1], z1);
|
|
Make_Vector(v2, x2, (DBL)data[z2][x2], z2);
|
|
|
|
VSub(t0, v2, v0);
|
|
VSub(t1, v1, v0);
|
|
|
|
VCross(Nt, t0, t1);
|
|
|
|
normalize(Nt, Nt);
|
|
|
|
if (Nt[Y] < 0.0)
|
|
{
|
|
VScaleEq(Nt, -1.0);
|
|
}
|
|
|
|
VAddEq(N, Nt);
|
|
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* smooth_height_field
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Given a height field that only contains an elevation grid, this
|
|
* routine will walk through the data and produce averaged normals
|
|
* for all points on the grid.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::smooth_height_field(int xsize, int zsize)
|
|
{
|
|
int i, j, k;
|
|
VECTOR N;
|
|
HF_VAL **map = Data->Map;
|
|
|
|
/* First off, allocate all the memory needed to store the normal information */
|
|
|
|
Data->Normals_Height = zsize+1;
|
|
|
|
Data->Normals = (HF_Normals **)POV_MALLOC((zsize+1)*sizeof(HF_Normals *), "height field normals");
|
|
|
|
for (i = 0; i <= zsize; i++)
|
|
{
|
|
Data->Normals[i] = (HF_Normals *)POV_MALLOC((xsize+1)*sizeof(HF_Normals), "height field normals");
|
|
}
|
|
|
|
/*
|
|
* For now we will do it the hard way - by generating the normals
|
|
* individually for each elevation point.
|
|
*/
|
|
|
|
for (i = 0; i <= zsize; i++)
|
|
{
|
|
for (j = 0; j <= xsize; j++)
|
|
{
|
|
Make_Vector(N, 0.0, 0.0, 0.0);
|
|
|
|
k = 0;
|
|
|
|
k += add_single_normal(map, xsize, zsize, j, i, j+1, i, j, i+1, N);
|
|
k += add_single_normal(map, xsize, zsize, j, i, j, i+1, j-1, i, N);
|
|
k += add_single_normal(map, xsize, zsize, j, i, j-1, i, j, i-1, N);
|
|
k += add_single_normal(map, xsize, zsize, j, i, j, i-1, j+1, i, N);
|
|
|
|
if (k == 0)
|
|
{
|
|
throw POV_EXCEPTION_STRING("Failed to find any normals at.");
|
|
}
|
|
|
|
normalize(N, N);
|
|
|
|
Data->Normals[i][j][0] = (short)(32767 * N[X]);
|
|
Data->Normals[i][j][1] = (short)(32767 * N[Y]);
|
|
Data->Normals[i][j][2] = (short)(32767 * N[Z]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Compute_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Copy image data into height field map. Create bounding blocks
|
|
* for the block traversal. Calculate normals for smoothed height fields.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Modified to work with new intersection functions. [DB]
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Compute_HField(const ImageData *image)
|
|
{
|
|
int x, z, max_x, max_z;
|
|
HF_VAL min_y, max_y, temp_y;
|
|
|
|
/* Get height field map size. */
|
|
|
|
max_x = image->iwidth;
|
|
max_z = image->iheight;
|
|
|
|
/* Allocate memory for map. */
|
|
|
|
Data->Map = (HF_VAL **)POV_MALLOC(max_z*sizeof(HF_VAL *), "height field");
|
|
|
|
for (z = 0; z < max_z; z++)
|
|
{
|
|
Data->Map[z] = (HF_VAL *)POV_MALLOC(max_x*sizeof(HF_VAL), "height field");
|
|
}
|
|
|
|
/* Copy map. */
|
|
|
|
min_y = 65535L;
|
|
max_y = 0;
|
|
|
|
for (z = 0; z < max_z; z++)
|
|
{
|
|
for (x = 0; x < max_x; x++)
|
|
{
|
|
temp_y = image_height_at(image, x, max_z - z - 1);
|
|
|
|
Data->Map[z][x] = temp_y;
|
|
|
|
min_y = min(min_y, temp_y);
|
|
max_y = max(max_y, temp_y);
|
|
}
|
|
}
|
|
|
|
/* Resize bounding box. */
|
|
|
|
Data->min_y = min_y;
|
|
Data->max_y = max_y;
|
|
|
|
bounding_corner1[Y] = max((DBL)min_y, bounding_corner1[Y]) - HFIELD_OFFSET;
|
|
bounding_corner2[Y] = (DBL)max_y + HFIELD_OFFSET;
|
|
|
|
/* Compute smoothed height field. */
|
|
|
|
if (Test_Flag(this, SMOOTHED_FLAG))
|
|
{
|
|
smooth_height_field(max_x-1, max_z-1);
|
|
}
|
|
|
|
Data->max_x = max_x-2;
|
|
Data->max_z = max_z-2;
|
|
|
|
build_hfield_blocks();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* build_hfield_blocks
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Dieter Bayer
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Create the bounding block hierarchy used by the block traversal.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::build_hfield_blocks()
|
|
{
|
|
int x, z, nx, nz, wx, wz;
|
|
int i, j;
|
|
int xmin, xmax, zmin, zmax;
|
|
DBL y, ymin, ymax, water;
|
|
|
|
/* Get block size. */
|
|
|
|
nx = max(1, (int)(sqrt((DBL)Data->max_x)));
|
|
nz = max(1, (int)(sqrt((DBL)Data->max_z)));
|
|
|
|
/* Get dimensions of sub-block. */
|
|
|
|
wx = (int)ceil((DBL)(Data->max_x + 2) / (DBL)nx);
|
|
wz = (int)ceil((DBL)(Data->max_z + 2) / (DBL)nz);
|
|
|
|
/* Increase number of sub-blocks if necessary. */
|
|
|
|
if (nx * wx < Data->max_x + 2)
|
|
{
|
|
nx++;
|
|
}
|
|
|
|
if (nz * wz < Data->max_z + 2)
|
|
{
|
|
nz++;
|
|
}
|
|
|
|
if (!Test_Flag(this, HIERARCHY_FLAG) || ((nx == 1) && (nz == 1)))
|
|
{
|
|
/* We don't want a bounding hierarchy. Just use one block. */
|
|
|
|
Data->Block = (HFIELD_BLOCK **)POV_MALLOC(sizeof(HFIELD_BLOCK *), "height field blocks");
|
|
|
|
Data->Block[0] = (HFIELD_BLOCK *)POV_MALLOC(sizeof(HFIELD_BLOCK), "height field blocks");
|
|
|
|
Data->Block[0][0].xmin = 0;
|
|
Data->Block[0][0].xmax = Data->max_x;
|
|
Data->Block[0][0].zmin = 0;
|
|
Data->Block[0][0].zmax = Data->max_z;
|
|
|
|
Data->Block[0][0].ymin = bounding_corner1[Y];
|
|
Data->Block[0][0].ymax = bounding_corner2[Y];
|
|
|
|
Data->block_max_x = 1;
|
|
Data->block_max_z = 1;
|
|
|
|
Data->block_width_x = Data->max_x + 2;
|
|
Data->block_width_z = Data->max_y + 2;
|
|
|
|
// Debug_Info("Height field: %d x %d (1 x 1 blocks)\n", Data->max_x+2, Data->max_z+2);
|
|
|
|
return;
|
|
}
|
|
|
|
// Debug_Info("Height field: %d x %d (%d x %d blocks)\n", Data->max_x+2, Data->max_z+2, nx, nz);
|
|
|
|
/* Allocate memory for blocks. */
|
|
|
|
Data->Block = (HFIELD_BLOCK **)POV_MALLOC(nz*sizeof(HFIELD_BLOCK *), "height field blocks");
|
|
|
|
/* Store block information. */
|
|
|
|
Data->block_max_x = nx;
|
|
Data->block_max_z = nz;
|
|
|
|
Data->block_width_x = wx;
|
|
Data->block_width_z = wz;
|
|
|
|
water = bounding_corner1[Y];
|
|
|
|
for (z = 0; z < nz; z++)
|
|
{
|
|
Data->Block[z] = (HFIELD_BLOCK *)POV_MALLOC(nx*sizeof(HFIELD_BLOCK), "height field blocks");
|
|
|
|
for (x = 0; x < nx; x++)
|
|
{
|
|
/* Get block's borders. */
|
|
|
|
xmin = x * wx;
|
|
zmin = z * wz;
|
|
|
|
xmax = min((x + 1) * wx - 1, Data->max_x);
|
|
zmax = min((z + 1) * wz - 1, Data->max_z);
|
|
|
|
/* Find min. and max. height in current block. */
|
|
|
|
ymin = BOUND_HUGE;
|
|
ymax = -BOUND_HUGE;
|
|
|
|
for (i = xmin; i <= xmax+1; i++)
|
|
{
|
|
for (j = zmin; j <= zmax+1; j++)
|
|
{
|
|
y = Get_Height(i, j);
|
|
|
|
ymin = min(ymin, y);
|
|
ymax = max(ymax, y);
|
|
}
|
|
}
|
|
|
|
/* Store block's borders. */
|
|
|
|
Data->Block[z][x].xmin = xmin;
|
|
Data->Block[z][x].xmax = xmax;
|
|
Data->Block[z][x].zmin = zmin;
|
|
Data->Block[z][x].zmax = zmax;
|
|
|
|
Data->Block[z][x].ymin = max(ymin, water) - HFIELD_OFFSET;
|
|
Data->Block[z][x].ymax = ymax + HFIELD_OFFSET;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Translate_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Translate(const VECTOR, const TRANSFORM *tr)
|
|
{
|
|
Transform(tr);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Rotate_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Rotate(const VECTOR, const TRANSFORM *tr)
|
|
{
|
|
Transform(tr);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Scale_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Scale(const VECTOR, const TRANSFORM *tr)
|
|
{
|
|
Transform(tr);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Invert_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Invert()
|
|
{
|
|
Invert_Flag(this, INVERTED_FLAG);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Transform_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* -
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Transform(const TRANSFORM *tr)
|
|
{
|
|
Compose_Transforms(Trans, tr);
|
|
|
|
Compute_BBox();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Create_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Allocate and intialize a height field.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Modified to work with new intersection functions. [DB]
|
|
*
|
|
******************************************************************************/
|
|
|
|
HField::HField() : ObjectBase(HFIELD_OBJECT)
|
|
{
|
|
Trans = Create_Transform();
|
|
|
|
Make_Vector(bounding_corner1, -1.0, -1.0, -1.0);
|
|
Make_Vector(bounding_corner2, 1.0, 1.0, 1.0);
|
|
|
|
/* Allocate height field data. */
|
|
|
|
Data = (HFIELD_DATA *)POV_MALLOC(sizeof(HFIELD_DATA), "height field");
|
|
|
|
Data->References = 1;
|
|
|
|
Data->Normals_Height = 0;
|
|
|
|
Data->Map = NULL;
|
|
Data->Normals = NULL;
|
|
|
|
Data->max_x = 0;
|
|
Data->max_z = 0;
|
|
|
|
Data->block_max_x = 0;
|
|
Data->block_max_z = 0;
|
|
|
|
Data->block_width_x = 0;
|
|
Data->block_width_z = 0;
|
|
|
|
Set_Flag(this, HIERARCHY_FLAG);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Copy_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* NOTE: The height field data is not copied, only the number of references
|
|
* is counted, so that Destray_HField() knows if it can be destroyed.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
ObjectPtr HField::Copy()
|
|
{
|
|
HField *New = new HField();
|
|
|
|
POV_FREE (New->Data);
|
|
|
|
/* Copy height field. */
|
|
|
|
Destroy_Transform(New->Trans);
|
|
*New = *this;
|
|
New->Trans = Copy_Transform(Trans);
|
|
|
|
Assign_Vector(New->bounding_corner1, bounding_corner1);
|
|
Assign_Vector(New->bounding_corner2, bounding_corner2);
|
|
|
|
New->Data = Data;
|
|
New->Data->References++;
|
|
|
|
return(New);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Destroy_HField
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Doug Muir, David Buck, Drew Wells
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* NOTE: The height field data is destroyed if it's no longer
|
|
* used by any copy.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Modified to work with new intersection functions. [DB]
|
|
*
|
|
******************************************************************************/
|
|
|
|
HField::~HField()
|
|
{
|
|
int i;
|
|
|
|
Destroy_Transform(Trans);
|
|
|
|
if (--(Data->References) == 0)
|
|
{
|
|
if (Data->Map != NULL)
|
|
{
|
|
for (i = 0; i < Data->max_z+2; i++)
|
|
{
|
|
if (Data->Map[i] != NULL)
|
|
{
|
|
POV_FREE (Data->Map[i]);
|
|
}
|
|
}
|
|
|
|
POV_FREE (Data->Map);
|
|
}
|
|
|
|
if (Data->Normals != NULL)
|
|
{
|
|
for (i = 0; i < Data->Normals_Height; i++)
|
|
{
|
|
POV_FREE (Data->Normals[i]);
|
|
}
|
|
|
|
POV_FREE (Data->Normals);
|
|
}
|
|
|
|
if (Data->Block != NULL)
|
|
{
|
|
for (i = 0; i < Data->block_max_z; i++)
|
|
{
|
|
POV_FREE(Data->Block[i]);
|
|
}
|
|
|
|
POV_FREE(Data->Block);
|
|
}
|
|
|
|
POV_FREE (Data);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* Compute_HField_BBox
|
|
*
|
|
* INPUT
|
|
*
|
|
* HField - Height field
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* HField
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Dieter Bayer
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Calculate the bounding box of a height field.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Aug 1994 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void HField::Compute_BBox()
|
|
{
|
|
// [ABX 20.01.2004] Low_Left introduced to hide BCC 5.5 bug
|
|
BBOX_VECT& Low_Left = BBox.Lower_Left;
|
|
|
|
Assign_BBox_Vect(Low_Left, bounding_corner1);
|
|
|
|
VSub (BBox.Lengths, bounding_corner2, bounding_corner1);
|
|
|
|
if (Trans != NULL)
|
|
{
|
|
Recompute_BBox(&BBox, Trans);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* dda_traversal
|
|
*
|
|
* INPUT
|
|
*
|
|
* Ray - Current ray
|
|
* HField - Height field
|
|
* Start - Start point for the walk
|
|
* Block - Sub-block of the height field to traverse
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* int - true if intersection was found
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Dieter Bayer
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Traverse the grid cell of the height field using a modified DDA.
|
|
*
|
|
* Based on the following article:
|
|
*
|
|
* Musgrave, F. Kenton, "Grid Tracing: Fast Ray Tracing for Height
|
|
* Fields", Research Report YALEU-DCS-RR-39, Yale University, July 1988
|
|
*
|
|
* You should note that there are (n-1) x (m-1) grid cells in a height
|
|
* field of (image) size n x m. A grid cell (i,j), 0 <= i <= n-1,
|
|
* 0 <= j <= m-1, extends from x = i to x = i + 1 - epsilon and
|
|
* y = j to y = j + 1 -epsilon.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool HField::dda_traversal(const Ray &ray, const VECTOR Start, const HFIELD_BLOCK *Block, IStack &HField_Stack, const Ray &RRay, DBL mindist, DBL maxdist, TraceThreadData *Thread)
|
|
{
|
|
char *dda_msg = "Illegal grid value in dda_traversal().\n"
|
|
"The height field may contain dark spots. To eliminate them\n"
|
|
"moving the camera a tiny bit should help. For more information\n"
|
|
"read the user manual!";
|
|
int found;
|
|
int xmin, xmax, zmin, zmax;
|
|
int x, z, signx, signz;
|
|
DBL ymin, ymax, y1, y2;
|
|
DBL px, pz, dx, dy, dz;
|
|
DBL delta, error, x0, z0;
|
|
DBL neary, fary, deltay;
|
|
|
|
/* Setup DDA. */
|
|
|
|
found = false;
|
|
|
|
px = Start[X];
|
|
pz = Start[Z];
|
|
|
|
/* Get dimensions of current block. */
|
|
|
|
xmin = Block->xmin;
|
|
xmax = min(Block->xmax + 1, Data->max_x);
|
|
zmin = Block->zmin;
|
|
zmax = min(Block->zmax + 1, Data->max_z);
|
|
|
|
ymin = min(Start[Y], Block->ymin) - EPSILON;
|
|
ymax = max(Start[Y], Block->ymax) + EPSILON;
|
|
|
|
/* Check for illegal grid values (caused by numerical inaccuracies). */
|
|
|
|
if (px < (DBL)xmin)
|
|
{
|
|
if (px < (DBL)xmin - HFIELD_OFFSET)
|
|
{
|
|
;// TODO MESSAGE Warning(0, dda_msg);
|
|
|
|
return(false);
|
|
}
|
|
else
|
|
{
|
|
px = (DBL)xmin;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (px > (DBL)xmax + 1.0 - EPSILON)
|
|
{
|
|
if (px > (DBL)xmax + 1.0 + EPSILON)
|
|
{
|
|
;// TODO MESSAGE Warning(0, dda_msg);
|
|
|
|
return(false);
|
|
}
|
|
else
|
|
{
|
|
px = (DBL)xmax + 1.0 - EPSILON;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pz < (DBL)zmin)
|
|
{
|
|
if (pz < (DBL)zmin - HFIELD_OFFSET)
|
|
{
|
|
;// TODO MESSAGE Warning(0, dda_msg);
|
|
|
|
return(false);
|
|
}
|
|
else
|
|
{
|
|
pz = (DBL)zmin;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pz > (DBL)zmax + 1.0 - EPSILON)
|
|
{
|
|
if (pz > (DBL)zmax + 1.0 + EPSILON)
|
|
{
|
|
;// TODO MESSAGE Warning(0, dda_msg);
|
|
|
|
return(false);
|
|
}
|
|
else
|
|
{
|
|
pz = (DBL)zmax + 1.0 - EPSILON;
|
|
}
|
|
}
|
|
}
|
|
|
|
dx = ray.Direction[X];
|
|
dy = ray.Direction[Y];
|
|
dz = ray.Direction[Z];
|
|
|
|
/*
|
|
* Here comes the DDA algorithm.
|
|
*/
|
|
|
|
/* Choose algorithm depending on the driving axis. */
|
|
|
|
if (fabs(dx) >= fabs(dz))
|
|
{
|
|
/*
|
|
* X-axis is driving axis.
|
|
*/
|
|
|
|
delta = fabs(dz / dx);
|
|
|
|
x = (int)px;
|
|
z = (int)pz;
|
|
|
|
x0 = px - floor(px);
|
|
z0 = pz - floor(pz);
|
|
|
|
signx = sign(dx);
|
|
signz = sign(dz);
|
|
|
|
/* Get initial error. */
|
|
|
|
if (dx >= 0.0)
|
|
{
|
|
if (dz >= 0.0)
|
|
{
|
|
error = z0 + delta * (1.0 - x0) - 1.0;
|
|
}
|
|
else
|
|
{
|
|
error = -(z0 - delta * (1.0 - x0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dz >= 0.0)
|
|
{
|
|
error = z0 + delta * x0 - 1.0;
|
|
}
|
|
else
|
|
{
|
|
error = -(z0 - delta * x0);
|
|
}
|
|
}
|
|
|
|
/* Get y differential. */
|
|
|
|
deltay = dy / fabs(dx);
|
|
|
|
if (dx >= 0.0)
|
|
{
|
|
neary = Start[Y] - x0 * deltay;
|
|
|
|
fary = neary + deltay;
|
|
}
|
|
else
|
|
{
|
|
neary = Start[Y] - (1.0 - x0) * deltay;
|
|
|
|
fary = neary + deltay;
|
|
}
|
|
|
|
/* Step through the cells. */
|
|
|
|
do
|
|
{
|
|
if (neary < fary)
|
|
{
|
|
y1 = neary;
|
|
y2 = fary;
|
|
}
|
|
else
|
|
{
|
|
y1 = fary;
|
|
y2 = neary;
|
|
}
|
|
|
|
if (intersect_pixel(x, z, ray, y1, y2, HField_Stack, RRay, mindist, maxdist, Thread))
|
|
{
|
|
if (Type & IS_CHILD_OBJECT)
|
|
{
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
if (error > EPSILON)
|
|
{
|
|
z += signz;
|
|
|
|
if ((z < zmin) || (z > zmax))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (intersect_pixel(x, z, ray, y1, y2, HField_Stack, RRay, mindist, maxdist, Thread))
|
|
{
|
|
if (Type & IS_CHILD_OBJECT)
|
|
{
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
error--;
|
|
}
|
|
else
|
|
{
|
|
if (error > -EPSILON)
|
|
{
|
|
z += signz;
|
|
|
|
error--;
|
|
}
|
|
}
|
|
|
|
x += signx;
|
|
|
|
error += delta;
|
|
|
|
neary = fary;
|
|
|
|
fary += deltay;
|
|
}
|
|
while ((neary >= ymin) && (neary <= ymax) && (x >= xmin) && (x <= xmax) && (z >= zmin) && (z <= zmax));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Z-axis is driving axis.
|
|
*/
|
|
|
|
delta = fabs(dx / dz);
|
|
|
|
x = (int)px;
|
|
z = (int)pz;
|
|
|
|
x0 = px - floor(px);
|
|
z0 = pz - floor(pz);
|
|
|
|
signx = sign(dx);
|
|
signz = sign(dz);
|
|
|
|
/* Get initial error. */
|
|
|
|
if (dz >= 0.0)
|
|
{
|
|
if (dx >= 0.0)
|
|
{
|
|
error = x0 + delta * (1.0 - z0) - 1.0;
|
|
}
|
|
else
|
|
{
|
|
error = -(x0 - delta * (1.0 - z0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dx >= 0.0)
|
|
{
|
|
error = x0 + delta * z0 - 1.0;
|
|
}
|
|
else
|
|
{
|
|
error = -(x0 - delta * z0);
|
|
}
|
|
}
|
|
|
|
/* Get y differential. */
|
|
|
|
deltay = dy / fabs(dz);
|
|
|
|
if (dz >= 0.0)
|
|
{
|
|
neary = Start[Y] - z0 * deltay;
|
|
|
|
fary = neary + deltay;
|
|
}
|
|
else
|
|
{
|
|
neary = Start[Y] - (1.0 - z0) * deltay;
|
|
|
|
fary = neary + deltay;
|
|
}
|
|
|
|
/* Step through the cells. */
|
|
|
|
do
|
|
{
|
|
if (neary < fary)
|
|
{
|
|
y1 = neary;
|
|
y2 = fary;
|
|
}
|
|
else
|
|
{
|
|
y1 = fary;
|
|
y2 = neary;
|
|
}
|
|
|
|
if (intersect_pixel(x, z, ray, y1, y2, HField_Stack, RRay, mindist, maxdist, Thread))
|
|
{
|
|
if (Type & IS_CHILD_OBJECT)
|
|
{
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
if (error > EPSILON)
|
|
{
|
|
x += signx;
|
|
|
|
if ((x < xmin) || (x > xmax))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (intersect_pixel(x, z, ray, y1, y2, HField_Stack, RRay, mindist, maxdist, Thread))
|
|
{
|
|
if (Type & IS_CHILD_OBJECT)
|
|
{
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
error--;
|
|
}
|
|
else
|
|
{
|
|
if (error > -EPSILON)
|
|
{
|
|
x += signx;
|
|
|
|
error--;
|
|
}
|
|
}
|
|
|
|
z += signz;
|
|
|
|
error += delta;
|
|
|
|
neary = fary;
|
|
|
|
fary += deltay;
|
|
}
|
|
while ((neary >= ymin-EPSILON) && (neary <= ymax+EPSILON) &&
|
|
(x >= xmin) && (x <= xmax) &&
|
|
(z >= zmin) && (z <= zmax));
|
|
}
|
|
|
|
return(found);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* block_traversal
|
|
*
|
|
* INPUT
|
|
*
|
|
* Ray - Current ray
|
|
* HField - Height field
|
|
* Start - Start point for the walk
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* int - true if intersection was found
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Dieter Bayer
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Traverse the blocks of the height field.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Feb 1995 : Creation.
|
|
*
|
|
* Aug 1996 : Fixed bug as reported by Dean M. Phillips:
|
|
* "I found a bug in the height field code which resulted
|
|
* in "Illegal grid value in dda_traversal()." messages
|
|
* along with dark vertical lines in the height fields.
|
|
* This turned out to be caused by overlooking the units in
|
|
* which some boundary tests in two different places were
|
|
* made. It was easy to fix.
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool HField::block_traversal(const Ray &ray, const VECTOR Start, IStack &HField_Stack, const Ray &RRay, DBL mindist, DBL maxdist, TraceThreadData *Thread)
|
|
{
|
|
int xmax, zmax;
|
|
int x, z, nx, nz, signx, signz;
|
|
int found = false;
|
|
int dx_zero, dz_zero;
|
|
DBL px, pz, dx, dy, dz;
|
|
DBL maxdv;
|
|
DBL ymin, ymax, y1, y2;
|
|
DBL neary, fary;
|
|
DBL k1, k2, dist;
|
|
VECTOR nearP, farP;
|
|
HFIELD_BLOCK *Block;
|
|
|
|
px = Start[X];
|
|
pz = Start[Z];
|
|
|
|
dx = ray.Direction[X];
|
|
dy = ray.Direction[Y];
|
|
dz = ray.Direction[Z];
|
|
|
|
maxdv = (dx > dz) ? dx : dz;
|
|
|
|
/* First test for 'perpendicular' rays. */
|
|
|
|
if ((fabs(dx) < EPSILON) && (fabs(dz) < EPSILON))
|
|
{
|
|
x = (int)px;
|
|
z = (int)pz;
|
|
|
|
neary = Start[Y];
|
|
|
|
if (dy >= 0.0)
|
|
{
|
|
fary = 65536.0;
|
|
}
|
|
else
|
|
{
|
|
fary = 0.0;
|
|
}
|
|
|
|
return intersect_pixel(x, z, ray, min(neary, fary), max(neary, fary), HField_Stack, RRay, mindist, maxdist, Thread);
|
|
}
|
|
|
|
/* If we don't have blocks we just step through the grid. */
|
|
|
|
if ((Data->block_max_x <= 1) && (Data->block_max_z <= 1))
|
|
{
|
|
return dda_traversal(ray, Start, &Data->Block[0][0], HField_Stack, RRay, mindist, maxdist, Thread);
|
|
}
|
|
|
|
/* Get dimensions of grid. */
|
|
|
|
xmax = Data->block_max_x;
|
|
zmax = Data->block_max_z;
|
|
|
|
ymin = (DBL)Data->min_y - EPSILON;
|
|
ymax = (DBL)Data->max_y + EPSILON;
|
|
|
|
dx_zero = (fabs(dx) < EPSILON);
|
|
dz_zero = (fabs(dz) < EPSILON);
|
|
|
|
signx = sign(dx);
|
|
signz = sign(dz);
|
|
|
|
/* Walk on the block grid. */
|
|
|
|
px /= Data->block_width_x;
|
|
pz /= Data->block_width_z;
|
|
|
|
x = (int)px;
|
|
z = (int)pz;
|
|
|
|
Assign_Vector(nearP, Start);
|
|
|
|
neary = Start[Y];
|
|
|
|
/*
|
|
* Here comes the block walk algorithm.
|
|
*/
|
|
|
|
do
|
|
{
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Block_Tests]++;
|
|
#endif
|
|
|
|
/* Get current block. */
|
|
|
|
Block = &Data->Block[z][x];
|
|
|
|
/* Intersect ray with bounding planes. */
|
|
|
|
if (dx_zero)
|
|
{
|
|
k1 = BOUND_HUGE;
|
|
}
|
|
else
|
|
{
|
|
if (signx >= 0)
|
|
{
|
|
k1 = ((DBL)Block->xmax + 1.0 - ray.Origin[X]) / dx;
|
|
}
|
|
else
|
|
{
|
|
k1 = ((DBL)Block->xmin - ray.Origin[X]) / dx;
|
|
}
|
|
}
|
|
|
|
if (dz_zero)
|
|
{
|
|
k2 = BOUND_HUGE;
|
|
}
|
|
else
|
|
{
|
|
if (signz >= 0)
|
|
{
|
|
k2 = ((DBL)Block->zmax + 1.0 - ray.Origin[Z]) / dz;
|
|
}
|
|
else
|
|
{
|
|
k2 = ((DBL)Block->zmin - ray.Origin[Z]) / dz;
|
|
}
|
|
}
|
|
|
|
/* Figure out the indices of the next block. */
|
|
|
|
if (dz_zero || ((!dx_zero) && (k1<k2 - EPSILON / maxdv) && (k1>0.0)))
|
|
/* if ((k1 < k2 - EPSILON / maxdv) && (k1 > 0.0)) */
|
|
{
|
|
/* Step along the x-axis. */
|
|
|
|
dist = k1;
|
|
|
|
nx = x + signx;
|
|
nz = z;
|
|
}
|
|
else
|
|
{
|
|
if (dz_zero || ((!dx_zero) && (k1<k2 + EPSILON / maxdv) && (k1>0.0)))
|
|
/* if ((k1 < k2 + EPSILON / maxdv) && (k1 > 0.0)) */
|
|
{
|
|
/* Step along both axis (very rare case). */
|
|
|
|
dist = k1;
|
|
|
|
nx = x + signx;
|
|
nz = z + signz;
|
|
}
|
|
else
|
|
{
|
|
/* Step along the z-axis. */
|
|
|
|
dist = k2;
|
|
|
|
nx = x;
|
|
nz = z + signz;
|
|
}
|
|
}
|
|
|
|
/* Get point where ray leaves current block. */
|
|
|
|
VEvaluateRay(farP, ray.Origin, dist, ray.Direction);
|
|
|
|
fary = farP[Y];
|
|
|
|
if (neary < fary)
|
|
{
|
|
y1 = neary;
|
|
y2 = fary;
|
|
}
|
|
else
|
|
{
|
|
y1 = fary;
|
|
y2 = neary;
|
|
}
|
|
|
|
/* Can we hit current block at all? */
|
|
|
|
if ((y1 <= (DBL)Block->ymax + EPSILON) && (y2 >= (DBL)Block->ymin - EPSILON))
|
|
{
|
|
/* Test current block. */
|
|
|
|
#ifdef HFIELD_EXTRA_STATS
|
|
Thread->Stats()[Ray_HField_Block_Tests_Succeeded]++;
|
|
#endif
|
|
|
|
if (dda_traversal(ray, nearP, &Data->Block[z][x], HField_Stack, RRay, mindist, maxdist, Thread))
|
|
{
|
|
if (Type & IS_CHILD_OBJECT)
|
|
{
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Step to next block. */
|
|
|
|
x = nx;
|
|
z = nz;
|
|
|
|
Assign_Vector(nearP, farP);
|
|
|
|
neary = fary;
|
|
}
|
|
while ((x >= 0) && (x < xmax) && (z >= 0) && (z < zmax) && (neary >= ymin) && (neary <= ymax));
|
|
|
|
return(found);
|
|
}
|
|
|
|
}
|