Optimized vegetation branches rendering
This commit is contained in:
parent
9a096ec329
commit
159e0f7e81
11 changed files with 91 additions and 95 deletions
|
@ -3,9 +3,6 @@
|
||||||
#include "Vector3.h"
|
#include "Vector3.h"
|
||||||
#include "PackStream.h"
|
#include "PackStream.h"
|
||||||
|
|
||||||
CappedCylinder::CappedCylinder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
CappedCylinder::CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length)
|
CappedCylinder::CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length)
|
||||||
: InfiniteCylinder(InfiniteRay(base, direction), radius), length(length) {
|
: InfiniteCylinder(InfiniteRay(base, direction), radius), length(length) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace basics {
|
||||||
*/
|
*/
|
||||||
class BASICSSHARED_EXPORT CappedCylinder : public InfiniteCylinder {
|
class BASICSSHARED_EXPORT CappedCylinder : public InfiniteCylinder {
|
||||||
public:
|
public:
|
||||||
CappedCylinder();
|
CappedCylinder() = default;
|
||||||
CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length);
|
CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length);
|
||||||
|
|
||||||
inline double getLength() const {
|
inline double getLength() const {
|
||||||
|
|
|
@ -4,10 +4,8 @@
|
||||||
|
|
||||||
#define EPS 1E-8
|
#define EPS 1E-8
|
||||||
|
|
||||||
InfiniteCylinder::InfiniteCylinder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
InfiniteCylinder::InfiniteCylinder(const InfiniteRay &axis, double radius) : axis(axis), radius(radius) {
|
InfiniteCylinder::InfiniteCylinder(const InfiniteRay &axis, double radius) : axis(axis), radius(radius) {
|
||||||
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection,
|
int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection,
|
||||||
|
@ -16,11 +14,75 @@ int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *firs
|
||||||
* Original algorithm has been altered, because it didn't work with non-(0,0,0) axis origin.
|
* Original algorithm has been altered, because it didn't work with non-(0,0,0) axis origin.
|
||||||
* Maybe some optimizations could be made from this.
|
* Maybe some optimizations could be made from this.
|
||||||
*/
|
*/
|
||||||
|
Vector3 Q, G, AG, AQ;
|
||||||
|
double c0, c1, c2, discr, invC2, root0, root1, root;
|
||||||
|
|
||||||
Vector3 U, V, F = axis.getDirection(), P, B, Q, G, AG, AQ;
|
/* line */
|
||||||
|
Q = ray.getOrigin().sub(axis.getOrigin());
|
||||||
|
G = ray.getDirection();
|
||||||
|
|
||||||
|
/* compute A*G */
|
||||||
|
AG.x = A[0][0] * G.x + A[0][1] * G.y + A[0][2] * G.z;
|
||||||
|
AG.y = A[1][0] * G.x + A[1][1] * G.y + A[1][2] * G.z;
|
||||||
|
AG.z = A[2][0] * G.x + A[2][1] * G.y + A[2][2] * G.z;
|
||||||
|
|
||||||
|
/* compute A*Q */
|
||||||
|
AQ.x = A[0][0] * Q.x + A[0][1] * Q.y + A[0][2] * Q.z;
|
||||||
|
AQ.y = A[1][0] * Q.x + A[1][1] * Q.y + A[1][2] * Q.z;
|
||||||
|
AQ.z = A[2][0] * Q.x + A[2][1] * Q.y + A[2][2] * Q.z;
|
||||||
|
|
||||||
|
/* compute quadratic equation c0+c1*t+c2*t^2 = 0 */
|
||||||
|
c2 = G.x * AG.x + G.y * AG.y + G.z * AG.z;
|
||||||
|
c1 = 2.0f * (Q.x * AG.x + Q.y * AG.y + Q.z * AG.z);
|
||||||
|
c0 = Q.x * AQ.x + Q.y * AQ.y + Q.z * AQ.z + C;
|
||||||
|
|
||||||
|
/* solve for intersections */
|
||||||
|
int numIntersections;
|
||||||
|
discr = c1 * c1 - 4.0 * c0 * c2;
|
||||||
|
if (discr > EPS) {
|
||||||
|
numIntersections = 2;
|
||||||
|
discr = sqrt(discr);
|
||||||
|
invC2 = 1.0 / c2;
|
||||||
|
root0 = -0.5 * (c1 + discr) * invC2;
|
||||||
|
root1 = -0.5 * (c1 - discr) * invC2;
|
||||||
|
first_intersection->x = Q.x + root0 * G.x + ox;
|
||||||
|
first_intersection->y = Q.y + root0 * G.y + oy;
|
||||||
|
first_intersection->z = Q.z + root0 * G.z + oz;
|
||||||
|
second_intersection->x = Q.x + root1 * G.x + ox;
|
||||||
|
second_intersection->y = Q.y + root1 * G.y + oy;
|
||||||
|
second_intersection->z = Q.z + root1 * G.z + oz;
|
||||||
|
} else if (discr < -EPS) {
|
||||||
|
numIntersections = 0;
|
||||||
|
} else {
|
||||||
|
numIntersections = 1;
|
||||||
|
root = -0.5 * c1 / c2;
|
||||||
|
first_intersection->x = Q.x + root * G.x + ox;
|
||||||
|
first_intersection->y = Q.y + root * G.y + oy;
|
||||||
|
first_intersection->z = Q.z + root * G.z + oz;
|
||||||
|
}
|
||||||
|
|
||||||
|
return numIntersections;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfiniteCylinder::save(PackStream *stream) const {
|
||||||
|
axis.save(stream);
|
||||||
|
stream->write(&radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfiniteCylinder::load(PackStream *stream) {
|
||||||
|
axis.load(stream);
|
||||||
|
stream->read(&radius);
|
||||||
|
|
||||||
|
validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfiniteCylinder::validate() {
|
||||||
|
Vector3 U, V, F = axis.getDirection();
|
||||||
double length, invLength, prod;
|
double length, invLength, prod;
|
||||||
double R[3][3], A[3][3];
|
|
||||||
double e0, e1, C, c0, c1, c2, discr, invC2, root0, root1, root;
|
ox = axis.getOrigin().x;
|
||||||
|
oy = axis.getOrigin().y;
|
||||||
|
oz = axis.getOrigin().z;
|
||||||
|
|
||||||
/* choose U, V so that U,V,F is orthonormal set */
|
/* choose U, V so that U,V,F is orthonormal set */
|
||||||
|
|
||||||
|
@ -70,75 +132,6 @@ int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *firs
|
||||||
A[2][1] = R[0][2] * R[0][1] + R[1][2] * R[1][1];
|
A[2][1] = R[0][2] * R[0][1] + R[1][2] * R[1][1];
|
||||||
A[2][2] = R[0][2] * R[0][2] + R[1][2] * R[1][2];
|
A[2][2] = R[0][2] * R[0][2] + R[1][2] * R[1][2];
|
||||||
|
|
||||||
/* vector B */
|
|
||||||
P = Vector3(0.0, 0.0, 0.0);
|
|
||||||
B.x = -2.0 * P.x;
|
|
||||||
B.y = -2.0 * P.y;
|
|
||||||
B.z = -2.0 * P.z;
|
|
||||||
|
|
||||||
/* constant C */
|
/* constant C */
|
||||||
e0 = -2.0 * (R[0][0] * P.x + R[0][1] * P.y + R[0][2] * P.z);
|
C = -radius * radius;
|
||||||
e1 = -2.0 * (R[1][0] * P.x + R[1][1] * P.y + R[1][2] * P.z);
|
|
||||||
C = 0.25 * (e0 * e0 + e1 * e1) - radius * radius;
|
|
||||||
|
|
||||||
/* line */
|
|
||||||
Q = ray.getOrigin().sub(axis.getOrigin());
|
|
||||||
G = ray.getDirection();
|
|
||||||
|
|
||||||
/* compute A*G */
|
|
||||||
AG.x = A[0][0] * G.x + A[0][1] * G.y + A[0][2] * G.z;
|
|
||||||
AG.y = A[1][0] * G.x + A[1][1] * G.y + A[1][2] * G.z;
|
|
||||||
AG.z = A[2][0] * G.x + A[2][1] * G.y + A[2][2] * G.z;
|
|
||||||
|
|
||||||
/* compute A*Q */
|
|
||||||
AQ.x = A[0][0] * Q.x + A[0][1] * Q.y + A[0][2] * Q.z;
|
|
||||||
AQ.y = A[1][0] * Q.x + A[1][1] * Q.y + A[1][2] * Q.z;
|
|
||||||
AQ.z = A[2][0] * Q.x + A[2][1] * Q.y + A[2][2] * Q.z;
|
|
||||||
|
|
||||||
/* compute quadratic equation c0+c1*t+c2*t^2 = 0 */
|
|
||||||
c2 = G.x * AG.x + G.y * AG.y + G.z * AG.z;
|
|
||||||
c1 = B.x * G.x + B.y * G.y + B.z * G.z + 2.0f * (Q.x * AG.x + Q.y * AG.y + Q.z * AG.z);
|
|
||||||
c0 = Q.x * AQ.x + Q.y * AQ.y + Q.z * AQ.z + B.x * Q.x + B.y * Q.y + B.z * Q.z + C;
|
|
||||||
|
|
||||||
/* solve for intersections */
|
|
||||||
int numIntersections;
|
|
||||||
discr = c1 * c1 - 4.0 * c0 * c2;
|
|
||||||
if (discr > EPS) {
|
|
||||||
numIntersections = 2;
|
|
||||||
discr = sqrt(discr);
|
|
||||||
invC2 = 1.0 / c2;
|
|
||||||
root0 = -0.5 * (c1 + discr) * invC2;
|
|
||||||
root1 = -0.5 * (c1 - discr) * invC2;
|
|
||||||
first_intersection->x = Q.x + root0 * G.x;
|
|
||||||
first_intersection->y = Q.y + root0 * G.y;
|
|
||||||
first_intersection->z = Q.z + root0 * G.z;
|
|
||||||
second_intersection->x = Q.x + root1 * G.x;
|
|
||||||
second_intersection->y = Q.y + root1 * G.y;
|
|
||||||
second_intersection->z = Q.z + root1 * G.z;
|
|
||||||
|
|
||||||
*first_intersection = first_intersection->add(axis.getOrigin());
|
|
||||||
*second_intersection = second_intersection->add(axis.getOrigin());
|
|
||||||
} else if (discr < -EPS) {
|
|
||||||
numIntersections = 0;
|
|
||||||
} else {
|
|
||||||
numIntersections = 1;
|
|
||||||
root = -0.5 * c1 / c2;
|
|
||||||
first_intersection->x = Q.x + root * G.x;
|
|
||||||
first_intersection->y = Q.y + root * G.y;
|
|
||||||
first_intersection->z = Q.z + root * G.z;
|
|
||||||
|
|
||||||
*first_intersection = first_intersection->add(axis.getOrigin());
|
|
||||||
}
|
|
||||||
|
|
||||||
return numIntersections;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InfiniteCylinder::save(PackStream *stream) const {
|
|
||||||
axis.save(stream);
|
|
||||||
stream->write(&radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InfiniteCylinder::load(PackStream *stream) {
|
|
||||||
axis.load(stream);
|
|
||||||
stream->read(&radius);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace basics {
|
||||||
*/
|
*/
|
||||||
class BASICSSHARED_EXPORT InfiniteCylinder {
|
class BASICSSHARED_EXPORT InfiniteCylinder {
|
||||||
public:
|
public:
|
||||||
InfiniteCylinder();
|
InfiniteCylinder() = default;
|
||||||
InfiniteCylinder(const InfiniteRay &axis, double radius);
|
InfiniteCylinder(const InfiniteRay &axis, double radius);
|
||||||
|
|
||||||
inline const InfiniteRay &getAxis() const {
|
inline const InfiniteRay &getAxis() const {
|
||||||
|
@ -33,9 +33,21 @@ class BASICSSHARED_EXPORT InfiniteCylinder {
|
||||||
virtual void save(PackStream *stream) const;
|
virtual void save(PackStream *stream) const;
|
||||||
virtual void load(PackStream *stream);
|
virtual void load(PackStream *stream);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void validate();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
InfiniteRay axis;
|
InfiniteRay axis;
|
||||||
double radius;
|
double radius;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Stored equation factors, to speed up ray intersection
|
||||||
|
double R[3][3];
|
||||||
|
double A[3][3];
|
||||||
|
double C;
|
||||||
|
double ox;
|
||||||
|
double oy;
|
||||||
|
double oz;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
#include "InfiniteRay.h"
|
#include "InfiniteRay.h"
|
||||||
|
|
||||||
InfiniteRay::InfiniteRay() {
|
|
||||||
}
|
|
||||||
|
|
||||||
InfiniteRay::InfiniteRay(const Vector3 &origin, const Vector3 &direction)
|
InfiniteRay::InfiniteRay(const Vector3 &origin, const Vector3 &direction)
|
||||||
: origin(origin), direction(direction.normalize()) {
|
: origin(origin), direction(direction.normalize()) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace basics {
|
||||||
*/
|
*/
|
||||||
class BASICSSHARED_EXPORT InfiniteRay {
|
class BASICSSHARED_EXPORT InfiniteRay {
|
||||||
public:
|
public:
|
||||||
InfiniteRay();
|
InfiniteRay() = default;
|
||||||
InfiniteRay(const Vector3 &origin, const Vector3 &direction);
|
InfiniteRay(const Vector3 &origin, const Vector3 &direction);
|
||||||
|
|
||||||
static InfiniteRay fromPoints(const Vector3 &point1, const Vector3 &point2);
|
static InfiniteRay fromPoints(const Vector3 &point1, const Vector3 &point2);
|
||||||
|
|
|
@ -60,19 +60,19 @@ bool SpaceSegment::intersectBoundingBox(const SpaceSegment &bbox) const {
|
||||||
double tmax = min(min(max(t1, t2), max(t3, t4)), max(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
|
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
|
||||||
double t;
|
//double t;
|
||||||
if (tmax < 0.0) {
|
if (tmax < 0.0) {
|
||||||
t = tmax;
|
//t = tmax;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if tmin > tmax, ray doesn't intersect AABB
|
// if tmin > tmax, ray doesn't intersect AABB
|
||||||
if (tmin > tmax) {
|
if (tmin > tmax) {
|
||||||
t = tmax;
|
//t = tmax;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
t = tmin;
|
//t = tmin;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
#include "PackStream.h"
|
#include "PackStream.h"
|
||||||
#include "InfiniteRay.h"
|
#include "InfiniteRay.h"
|
||||||
|
|
||||||
Sphere::Sphere() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Sphere::Sphere(const Vector3 ¢er, double radius) : center(center), radius(radius) {
|
Sphere::Sphere(const Vector3 ¢er, double radius) : center(center), radius(radius) {
|
||||||
radius2 = radius * radius;
|
radius2 = radius * radius;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace basics {
|
||||||
*/
|
*/
|
||||||
class BASICSSHARED_EXPORT Sphere {
|
class BASICSSHARED_EXPORT Sphere {
|
||||||
public:
|
public:
|
||||||
Sphere();
|
Sphere() = default;
|
||||||
Sphere(const Vector3 ¢er, double radius);
|
Sphere(const Vector3 ¢er, double radius);
|
||||||
|
|
||||||
inline const Vector3 &getCenter() const {
|
inline const Vector3 &getCenter() const {
|
||||||
|
|
|
@ -5,6 +5,6 @@ VegetationInstance::VegetationInstance(const VegetationModelDefinition &model, c
|
||||||
: model(model), base(base), size(size), angle(angle) {
|
: model(model), base(base), size(size), angle(angle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
VegetationInstance VegetationInstance::displace(const Vector3 &location, const Vector3 &normal) const {
|
VegetationInstance VegetationInstance::displace(const Vector3 &location, const Vector3 &) const {
|
||||||
return VegetationInstance(model, location, size, angle);
|
return VegetationInstance(model, location, size, angle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,6 @@ double VegetationLayerDefinition::getMaxHeight() const {
|
||||||
return presence->getMaxHeight();
|
return presence->getMaxHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VegetationLayerDefinition::applyPreset(VegetationLayerPreset preset) {
|
void VegetationLayerDefinition::applyPreset(VegetationLayerPreset) {
|
||||||
model->randomize();
|
model->randomize();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue