Improved vegetation rendering
- Added early check on terrain height range during iteration - Added test render of vegetation model - Tweaked basic tree model
This commit is contained in:
parent
cd006e1093
commit
68945111d1
16 changed files with 338 additions and 37 deletions
|
@ -2,10 +2,6 @@
|
|||
|
||||
#include "PackStream.h"
|
||||
|
||||
Disk::Disk()
|
||||
{
|
||||
}
|
||||
|
||||
Disk::Disk(const Vector3 &point, const Vector3 &normal, double radius):
|
||||
InfinitePlane(point, normal), radius(radius)
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace basics {
|
|||
class BASICSSHARED_EXPORT Disk: public InfinitePlane
|
||||
{
|
||||
public:
|
||||
Disk();
|
||||
Disk() = default;
|
||||
Disk(const Vector3 &point, const Vector3 &normal, double radius);
|
||||
|
||||
inline double getRadius() const {return radius;}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <climits>
|
||||
#include "SpaceGridIterator.h"
|
||||
using namespace std;
|
||||
|
||||
SpaceSegment::SpaceSegment(const Vector3& start, const Vector3& end):
|
||||
start(start), end(end)
|
||||
|
@ -58,6 +59,44 @@ bool SpaceSegment::intersectYInterval(double ymin, double ymax)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SpaceSegment::intersectBoundingBox(const SpaceSegment &bbox) const
|
||||
{
|
||||
Vector3 dir = getDirection();
|
||||
// r.dir is unit direction vector of ray
|
||||
double dfx = 1.0 / dir.x;
|
||||
double dfy = 1.0 / dir.y;
|
||||
double dfz = 1.0 / dir.z;
|
||||
// lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
|
||||
// r.org is origin of ray
|
||||
double t1 = (bbox.start.x - start.x) * dfx;
|
||||
double t2 = (bbox.end.x - start.x) * dfx;
|
||||
double t3 = (bbox.start.y - start.y) * dfy;
|
||||
double t4 = (bbox.end.y - start.y) * dfy;
|
||||
double t5 = (bbox.start.z - start.z) * dfz;
|
||||
double t6 = (bbox.end.z - start.z) * dfz;
|
||||
|
||||
double tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6));
|
||||
double tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6));
|
||||
|
||||
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
|
||||
double t;
|
||||
if (tmax < 0.0)
|
||||
{
|
||||
t = tmax;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if tmin > tmax, ray doesn't intersect AABB
|
||||
if (tmin > tmax)
|
||||
{
|
||||
t = tmax;
|
||||
return false;
|
||||
}
|
||||
|
||||
t = tmin;
|
||||
return true;
|
||||
}
|
||||
|
||||
SpaceSegment SpaceSegment::projectedOnXPlane(double x) const
|
||||
{
|
||||
return SpaceSegment(Vector3(x, start.y, start.z), Vector3(x, end.y, end.z));
|
||||
|
|
|
@ -33,6 +33,11 @@ public:
|
|||
*/
|
||||
bool intersectYInterval(double ymin, double ymax);
|
||||
|
||||
/**
|
||||
* Return true if the segment intersects a bounding box, represented by another segment (crossing diagonal).
|
||||
*/
|
||||
bool intersectBoundingBox(const SpaceSegment &bbox) const;
|
||||
|
||||
/**
|
||||
* Return a version of this segment, projected on a X plane.
|
||||
*/
|
||||
|
|
|
@ -119,7 +119,6 @@ static void addBranchRecurse(std::vector<CappedCylinder> &branches, const Vector
|
|||
if (length > 0.1)
|
||||
{
|
||||
int split_count = 3;
|
||||
Vector3 new_base = base.add(direction.scale(length));
|
||||
Matrix4 pivot1 = Matrix4::newRotateAxis(randomizeValue(1.0 - 0.6 * length, 0.9, 1.1), VECTOR_EAST);
|
||||
Vector3 new_direction = pivot1.multPoint(direction);
|
||||
for (int i = 0; i < split_count; i++)
|
||||
|
@ -127,7 +126,8 @@ static void addBranchRecurse(std::vector<CappedCylinder> &branches, const Vector
|
|||
Matrix4 pivot2 = Matrix4::newRotateAxis(randomizeValue(M_PI * 2.0 / (double)split_count, 0.9, 1.1), direction);
|
||||
new_direction = pivot2.multPoint(new_direction);
|
||||
|
||||
addBranchRecurse(branches, new_base, new_direction, randomizeValue(radius, 0.65, 0.75), randomizeValue(length, 0.55, 0.65));
|
||||
Vector3 new_base = base.add(direction.scale(randomizeValue(length, 0.4, 1.0)));
|
||||
addBranchRecurse(branches, new_base, new_direction, randomizeValue(radius, 0.45, 0.6), randomizeValue(length, 0.55, 0.85));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ void VegetationModelDefinition::randomize()
|
|||
foliage_items.clear();
|
||||
|
||||
// Add trunk and branches
|
||||
addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, 0.05, 0.5);
|
||||
addBranchRecurse(solid_volumes, VECTOR_ZERO, VECTOR_UP, 0.04, 0.5);
|
||||
|
||||
// Add foliage groups
|
||||
for (const auto &branch: solid_volumes)
|
||||
|
@ -150,7 +150,7 @@ void VegetationModelDefinition::randomize()
|
|||
{
|
||||
double radius = length * 0.5;
|
||||
Vector3 center = branch.getAxis().getOrigin().add(branch.getAxis().getDirection().scale(radius));
|
||||
foliage_groups.push_back(Sphere(center, radius * 5.0));
|
||||
foliage_groups.push_back(Sphere(center, radius * 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ void VegetationModelDefinition::randomize()
|
|||
{
|
||||
double radius = 0.15;
|
||||
Vector3 dir = Vector3::randomInSphere(1.0 - radius);
|
||||
Vector3 normal = dir.add(Vector3::randomInSphere(0.4)).add(Vector3(0.0, 0.6, 0.0)).normalize();
|
||||
Vector3 normal = dir.add(Vector3::randomInSphere(0.4)).add(Vector3(0.0, 0.3, 0.0)).normalize();
|
||||
Disk leaf(dir, normal, randomizeValue(radius, 0.8, 1.0));
|
||||
foliage_items.push_back(leaf);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@
|
|||
#include "LightFilter.h"
|
||||
#include "GodRaysSampler.h"
|
||||
#include "Rasterizer.h"
|
||||
#include "SpaceSegment.h"
|
||||
#include "OverlayRasterizer.h"
|
||||
#include "VegetationModelDefinition.h"
|
||||
#include "VegetationInstance.h"
|
||||
#include "VegetationRenderer.h"
|
||||
#include "RayCastingResult.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
@ -251,6 +257,50 @@ static void testSunNearHorizon()
|
|||
}
|
||||
}
|
||||
|
||||
static void testVegetationModels()
|
||||
{
|
||||
class TestRasterizer: public OverlayRasterizer
|
||||
{
|
||||
public:
|
||||
TestRasterizer(SoftwareCanvasRenderer *renderer, const VegetationModelDefinition &model):
|
||||
OverlayRasterizer(renderer, renderer->getProgressHelper()),
|
||||
instance(model, VECTOR_ZERO),
|
||||
vegetation(renderer->getVegetationRenderer())
|
||||
{
|
||||
}
|
||||
|
||||
virtual Color processPixel(int, int, double relx, double rely) const override
|
||||
{
|
||||
relx *= 0.75;
|
||||
rely *= 0.75;
|
||||
SpaceSegment segment(Vector3(relx, rely + 0.5, -5.0), Vector3(relx, rely + 0.5, 5.0));
|
||||
RayCastingResult result = vegetation->renderInstance(segment, instance, false, true);
|
||||
return result.hit ? result.hit_color : Color(0.6, 0.7, 0.9);
|
||||
}
|
||||
|
||||
VegetationInstance instance;
|
||||
VegetationRenderer *vegetation;
|
||||
};
|
||||
|
||||
Scenery scenery;
|
||||
scenery.autoPreset(1);
|
||||
scenery.getClouds()->clear();
|
||||
scenery.getTerrain()->propWaterHeight()->setValue(1.0);
|
||||
scenery.getCamera()->setTarget(VECTOR_ZERO);
|
||||
scenery.getCamera()->setLocation(Vector3(0.0, 0.0, -5.0));
|
||||
int width = 800;
|
||||
int height = 800;
|
||||
|
||||
SoftwareCanvasRenderer renderer(&scenery);
|
||||
renderer.setSize(width, height);
|
||||
renderer.setQuality(0.5);
|
||||
|
||||
VegetationModelDefinition model(NULL);
|
||||
renderer.setSoloRasterizer(new TestRasterizer(&renderer, model));
|
||||
|
||||
startTestRender(&renderer, "vegetation_model_basic");
|
||||
}
|
||||
|
||||
void runTestSuite()
|
||||
{
|
||||
testGroundShadowQuality();
|
||||
|
@ -260,4 +310,5 @@ void runTestSuite()
|
|||
testNearFrustum();
|
||||
testCloudsNearGround();
|
||||
testSunNearHorizon();
|
||||
testVegetationModels();
|
||||
}
|
||||
|
|
52
src/render/software/OverlayRasterizer.cpp
Normal file
52
src/render/software/OverlayRasterizer.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include "OverlayRasterizer.h"
|
||||
|
||||
#include "Color.h"
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "CameraDefinition.h"
|
||||
#include "CanvasFragment.h"
|
||||
|
||||
OverlayRasterizer::OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress):
|
||||
Rasterizer(renderer, progress, 0, COLOR_WHITE)
|
||||
{
|
||||
}
|
||||
|
||||
int OverlayRasterizer::prepareRasterization()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void OverlayRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
|
||||
{
|
||||
double width = (double)renderer->render_camera->getWidth();
|
||||
double height = (double)renderer->render_camera->getHeight();
|
||||
Vector3 topleft = renderer->unprojectPoint(Vector3(height, 0.0, 1.0));
|
||||
Vector3 bottomleft = renderer->unprojectPoint(Vector3(0.0, 0.0, 1.0));
|
||||
Vector3 topright = renderer->unprojectPoint(Vector3(height, width, 1.0));
|
||||
Vector3 bottomright = renderer->unprojectPoint(Vector3(0.0, width, 1.0));
|
||||
|
||||
pushQuad(canvas, topleft, bottomleft, bottomright, topright);
|
||||
}
|
||||
|
||||
Color OverlayRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const
|
||||
{
|
||||
double width = (double)renderer->render_camera->getWidth() - 1.0;
|
||||
double height = (double)renderer->render_camera->getHeight() - 1.0;
|
||||
double relx;
|
||||
double rely;
|
||||
double x = floor(fragment.getPixel().x);
|
||||
double y = floor(fragment.getPixel().y);
|
||||
|
||||
if (width > height)
|
||||
{
|
||||
relx = 2.0 * ((x - (width - height) * 0.5) / height - 0.5);
|
||||
rely = 2.0 * (y / height - 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
relx = 2.0 * (x / height - 0.5);
|
||||
rely = 2.0 * ((y - (height - width) * 0.5) / height - 0.5);
|
||||
}
|
||||
|
||||
return processPixel((int)x, (int)y, relx, rely);
|
||||
}
|
||||
|
35
src/render/software/OverlayRasterizer.h
Normal file
35
src/render/software/OverlayRasterizer.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef OVERLAYRASTERIZER_H
|
||||
#define OVERLAYRASTERIZER_H
|
||||
|
||||
#include "software_global.h"
|
||||
|
||||
#include "Rasterizer.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
/**
|
||||
* Base class for overlay rasterizer.
|
||||
*
|
||||
* It's a rasterizer that puts a single quad in front of camera, in order to apply a shader on each pixel.
|
||||
*/
|
||||
class SOFTWARESHARED_EXPORT OverlayRasterizer: public Rasterizer
|
||||
{
|
||||
public:
|
||||
OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress);
|
||||
|
||||
/**
|
||||
* Abstract method to implement to shade each pixel.
|
||||
*/
|
||||
virtual Color processPixel(int x, int y, double relx, double rely) const = 0;
|
||||
|
||||
private:
|
||||
virtual int prepareRasterization();
|
||||
virtual void rasterizeToCanvas(CanvasPortion *canvas);
|
||||
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OVERLAYRASTERIZER_H
|
|
@ -220,3 +220,18 @@ bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainRenderer::estimateMinMaxHeight(double x1, double z1, double x2, double z2, double *ymin, double *ymax)
|
||||
{
|
||||
double y1 = getHeight(x1, z1, true);
|
||||
double y2 = getHeight(x2, z2, true);
|
||||
|
||||
// TODO Add quality factor
|
||||
// TODO Use all 4 corners
|
||||
// TODO Apply max slope
|
||||
// TODO Estimate displacement
|
||||
|
||||
std::pair<double, double>minmax = std::minmax(y1, y2);
|
||||
*ymin = minmax.first;
|
||||
*ymax = minmax.second;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@ public:
|
|||
virtual Color getFinalColor(const Vector3 &location, double precision);
|
||||
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
|
||||
|
||||
/**
|
||||
* Estimate a probable range of altitudes, given a rectangle area.
|
||||
*/
|
||||
void estimateMinMaxHeight(double x1, double z1, double x2, double z2, double *ymin, double *ymax);
|
||||
|
||||
private:
|
||||
SoftwareRenderer *parent;
|
||||
TerrainRayWalker *walker_ray;
|
||||
|
|
|
@ -113,7 +113,6 @@ VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment,
|
|||
material.shininess = 3.0;
|
||||
material.hardness = 0.3;
|
||||
material.validate();
|
||||
// FIXME Can't use reference to temporary material
|
||||
return VegetationResult(location, normal, material);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
class VegetationGridIterator: public SpaceGridIterator
|
||||
{
|
||||
public:
|
||||
VegetationGridIterator(const SpaceSegment &segment, VegetationRenderer *renderer, VegetationLayerDefinition *layer, bool only_hit):
|
||||
segment(segment), renderer(renderer), layer(layer), only_hit(only_hit)
|
||||
VegetationGridIterator(const SpaceSegment &segment, VegetationRenderer *renderer, bool only_hit):
|
||||
segment(segment), renderer(renderer), only_hit(only_hit)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -30,24 +30,12 @@ public:
|
|||
|
||||
virtual bool onCell(int x, int, int z) override
|
||||
{
|
||||
std::vector<VegetationInstance> instances;
|
||||
|
||||
layer->getPresence()->collectInstances(&instances, *layer->getModel(), x - 0.5, z - 0.5, x + 0.5, z + 0.5);
|
||||
|
||||
for (auto &instance: instances)
|
||||
{
|
||||
result = renderer->renderInstance(segment, instance, only_hit);
|
||||
if (result.hit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
result = renderer->getBoundResult(segment, (double)x, (double)z, only_hit);
|
||||
return not result.hit;
|
||||
}
|
||||
private:
|
||||
const SpaceSegment &segment;
|
||||
VegetationRenderer *renderer;
|
||||
VegetationLayerDefinition *layer;
|
||||
RayCastingResult result;
|
||||
bool only_hit;
|
||||
};
|
||||
|
@ -97,17 +85,11 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool
|
|||
{
|
||||
if (enabled)
|
||||
{
|
||||
VegetationDefinition *vegetation = parent->getScenery()->getVegetation();
|
||||
int n = vegetation->count();
|
||||
// TODO Don't stop at first layer, find the nearest hit
|
||||
for (int i = 0; i < n; i++)
|
||||
// Find instances potentially crossing the segment
|
||||
VegetationGridIterator it(segment, this, only_hit);
|
||||
if (not segment.projectedOnYPlane().iterateOnGrid(it))
|
||||
{
|
||||
// Find instances potentially crossing the segment
|
||||
VegetationGridIterator it(segment, this, vegetation->getVegetationLayer(i), only_hit);
|
||||
if (not segment.projectedOnYPlane().iterateOnGrid(it))
|
||||
{
|
||||
return it.getResult();
|
||||
}
|
||||
return it.getResult();
|
||||
}
|
||||
return RayCastingResult();
|
||||
}
|
||||
|
@ -117,6 +99,42 @@ RayCastingResult VegetationRenderer::getResult(const SpaceSegment &segment, bool
|
|||
}
|
||||
}
|
||||
|
||||
RayCastingResult VegetationRenderer::getBoundResult(const SpaceSegment &segment, double x, double z, bool only_hit, double xsize, double zsize)
|
||||
{
|
||||
// Early check if we may cross any vegetation
|
||||
double ymin, ymax;
|
||||
double vegetation_max_height = 0.0; // TODO
|
||||
parent->getTerrainRenderer()->estimateMinMaxHeight(x, z, x + xsize, z + zsize, &ymin, &ymax);
|
||||
ymax += vegetation_max_height;
|
||||
SpaceSegment bbox(Vector3(x, ymin, z), Vector3(x + xsize, ymax, z + zsize));
|
||||
if (not segment.intersectBoundingBox(bbox)) {
|
||||
return RayCastingResult();
|
||||
}
|
||||
|
||||
// Iterate all layers and instances
|
||||
VegetationDefinition *vegetation = parent->getScenery()->getVegetation();
|
||||
int n = vegetation->count();
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
VegetationLayerDefinition *layer = vegetation->getVegetationLayer(i);
|
||||
|
||||
std::vector<VegetationInstance> instances;
|
||||
layer->getPresence()->collectInstances(&instances, *layer->getModel(), x, z, x + xsize, z + zsize);
|
||||
|
||||
for (auto &instance: instances)
|
||||
{
|
||||
RayCastingResult result = renderInstance(segment, instance, only_hit);
|
||||
if (result.hit)
|
||||
{
|
||||
// TODO Don't stop at first hit, find the nearest one
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RayCastingResult();
|
||||
}
|
||||
|
||||
bool VegetationRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
||||
{
|
||||
if (enabled)
|
||||
|
|
|
@ -34,6 +34,11 @@ public:
|
|||
*/
|
||||
RayCastingResult getResult(const SpaceSegment &segment, bool only_hit=false);
|
||||
|
||||
/**
|
||||
* Perform ray casting on a squared region.
|
||||
*/
|
||||
RayCastingResult getBoundResult(const SpaceSegment &segment, double x, double z, bool only_hit=false, double xsize=1.0, double zsize=1.0);
|
||||
|
||||
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace software {
|
|||
class WaterRenderer;
|
||||
|
||||
class Rasterizer;
|
||||
class OverlayRasterizer;
|
||||
class SkyRasterizer;
|
||||
class TerrainRasterizer;
|
||||
|
||||
|
|
65
src/tests/OverlayRasterizer_Test.cpp
Normal file
65
src/tests/OverlayRasterizer_Test.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "BaseTestCase.h"
|
||||
#include "OverlayRasterizer.h"
|
||||
|
||||
#include <vector>
|
||||
#include "Scenery.h"
|
||||
#include "SoftwareCanvasRenderer.h"
|
||||
#include "Color.h"
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
double relx;
|
||||
double rely;
|
||||
} PixelCall;
|
||||
|
||||
std::vector<PixelCall> calls;
|
||||
|
||||
class MockOverlayRasterizer: public OverlayRasterizer
|
||||
{
|
||||
public:
|
||||
MockOverlayRasterizer(SoftwareCanvasRenderer *renderer):
|
||||
OverlayRasterizer(renderer, renderer->getProgressHelper())
|
||||
{
|
||||
}
|
||||
|
||||
virtual Color processPixel(int x, int y, double relx, double rely) const override
|
||||
{
|
||||
PixelCall call = {x, y, relx, rely};
|
||||
calls.push_back(call);
|
||||
return COLOR_BLUE;
|
||||
}
|
||||
};
|
||||
|
||||
void checkCall(const PixelCall &call, int x, int y, double relx, double rely)
|
||||
{
|
||||
EXPECT_EQ(x, call.x);
|
||||
EXPECT_EQ(y, call.y);
|
||||
EXPECT_DOUBLE_EQ(relx, call.relx);
|
||||
EXPECT_DOUBLE_EQ(rely, call.rely);
|
||||
}
|
||||
|
||||
TEST(OverlayRasterizer, pixelProcessing)
|
||||
{
|
||||
calls.clear();
|
||||
|
||||
Scenery scenery;
|
||||
SoftwareCanvasRenderer renderer(&scenery);
|
||||
renderer.setSize(4, 3);
|
||||
renderer.setSoloRasterizer(new MockOverlayRasterizer(&renderer));
|
||||
renderer.render();
|
||||
|
||||
ASSERT_EQ(12, (int)calls.size());
|
||||
checkCall(calls[0], 0, 0, -1.5, -1.0);
|
||||
checkCall(calls[1], 0, 2, -1.5, 1.0);
|
||||
checkCall(calls[2], 2, 0, 0.5, -1.0);
|
||||
checkCall(calls[3], 2, 2, 0.5, 1.0);
|
||||
checkCall(calls[4], 0, 1, -1.5, 0.0);
|
||||
checkCall(calls[5], 1, 0, -0.5, -1.0);
|
||||
checkCall(calls[6], 1, 1, -0.5, 0.0);
|
||||
checkCall(calls[7], 1, 2, -0.5, 1.0);
|
||||
checkCall(calls[8], 2, 1, 0.5, 0.0);
|
||||
checkCall(calls[9], 3, 0, 1.5, -1.0);
|
||||
checkCall(calls[10], 3, 1, 1.5, 0.0);
|
||||
checkCall(calls[11], 3, 2, 1.5, 1.0);
|
||||
}
|
|
@ -59,3 +59,18 @@ TEST(SpaceSegment, iterateOnGrid_Negative)
|
|||
ASSERT_EQ(1, (int)it.locations.size());
|
||||
EXPECT_VECTOR3_COORDS(it.locations[0], -9.0, -9.0, -9.0);
|
||||
}
|
||||
|
||||
TEST(SpaceSegment, intersectBoundingBox)
|
||||
{
|
||||
SpaceSegment bbox(Vector3(-1.0, -1.0, -1.0), Vector3(1.0, 4.0, 1.0));
|
||||
|
||||
EXPECT_TRUE(SpaceSegment(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0)).intersectBoundingBox(bbox));
|
||||
|
||||
EXPECT_TRUE(SpaceSegment(Vector3(-5.0, 0.0, 0.0), Vector3(5.0, 0.0, 0.0)).intersectBoundingBox(bbox));
|
||||
EXPECT_FALSE(SpaceSegment(Vector3(-5.0, -2.0, 0.0), Vector3(5.0, -2.0, 0.0)).intersectBoundingBox(bbox));
|
||||
|
||||
EXPECT_TRUE(SpaceSegment(Vector3(-5.0, 0.0, -5.0), Vector3(5.0, 0.0, 5.0)).intersectBoundingBox(bbox));
|
||||
EXPECT_FALSE(SpaceSegment(Vector3(-5.0, -2.0, 0.0), Vector3(5.0, -2.0, 5.0)).intersectBoundingBox(bbox));
|
||||
|
||||
EXPECT_FALSE(SpaceSegment(Vector3(-10.0, 0.0, 0.0), Vector3(10.0, -6.0, 0.0)).intersectBoundingBox(bbox));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue