paysages3d/src/render/software/VegetationModelRenderer.cpp

129 lines
4.1 KiB
C++

#include "VegetationModelRenderer.h"
#include "Color.h"
#include "SurfaceMaterial.h"
#include "SpaceSegment.h"
#include "InfiniteRay.h"
#include "Disk.h"
#include "SoftwareRenderer.h"
#include "LightComponent.h"
#include "VegetationModelDefinition.h"
#include "VegetationResult.h"
VegetationModelRenderer::VegetationModelRenderer(SoftwareRenderer* parent, const VegetationModelDefinition* model):
parent(parent), model(model)
{
}
VegetationModelRenderer::~VegetationModelRenderer()
{
}
VegetationResult VegetationModelRenderer::getResult(const SpaceSegment &segment, bool only_hit) const
{
InfiniteRay ray(segment.getStart(), segment.getDirection());
int intersections;
Color result = COLOR_TRANSPARENT;
bool hit = false;
Vector3 location, normal;
double distance, nearest, maximal;
maximal = segment.getLength();
nearest = maximal;
for (const auto &branch: model->getSolidVolumes())
{
Vector3 near, far;
if (branch.checkRayIntersection(ray, &near, &far))
{
distance = ray.getCursor(near);
if (distance >= 0.0 and distance <= maximal)
{
// Got a branch hit
if (only_hit)
{
return VegetationResult(true);
}
if (distance < nearest)
{
result = Color(0.2, 0.15, 0.15);
nearest = distance;
hit = true;
location = near;
normal = near.sub(branch.getAxis().getOrigin()).crossProduct(branch.getAxis().getDirection()).normalize();
normal = branch.getAxis().getDirection().crossProduct(normal).normalize();
}
}
}
}
for (const auto &foliage: model->getFoliageGroups())
{
Vector3 near, far;
intersections = foliage.checkRayIntersection(ray, &near, &far);
if (intersections == 2)
{
InfiniteRay subray(ray.getOrigin().sub(foliage.getCenter()).scale(1.0 / foliage.getRadius()), ray.getDirection());
for (const auto &leaf: model->getFoliageItems())
{
Disk sized_leaf(leaf.getPoint(), leaf.getNormal(), leaf.getRadius() * leaf.getRadius() / foliage.getRadius());
if (sized_leaf.checkRayIntersection(subray, &near) == 1)
{
near = near.scale(foliage.getRadius()).add(foliage.getCenter());
distance = ray.getCursor(near);
if (distance >= 0.0 and distance <= maximal)
{
// Got a foliage hit
if (only_hit)
{
return VegetationResult(true);
}
if (distance < nearest)
{
result = Color(0.3, 0.5, 0.3);
nearest = distance;
hit = true;
location = near;
normal = sized_leaf.getNormal();
if (normal.dotProduct(location.sub(segment.getStart())) > 0.0)
{
// We look at backside
result = Color(0.3, 0.4, 0.3);
normal = normal.scale(-1.0);
}
}
}
}
}
}
}
if (hit)
{
SurfaceMaterial material(result);
material.reflection = 0.003;
material.shininess = 3.0;
material.hardness = 0.3;
material.validate();
// FIXME Can't use reference to temporary material
return VegetationResult(location, normal, material);
}
else
{
return VegetationResult();
}
}
bool VegetationModelRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
{
return getResult(SpaceSegment(at, light.direction.scale(-2.0)), true).isHit();
}