From 80da1dac3f01a270ee22de46fac7cc084e8cbcbd Mon Sep 17 00:00:00 2001 From: "Huabing.Xu" Date: Wed, 27 Nov 2013 10:34:30 +0800 Subject: [PATCH] add Frustum, AABB, ViewTransform --- cocos/2d/renderer/Frustum.cpp | 390 ++++++++++++++++++++++++++++++++++ cocos/2d/renderer/Frustum.h | 96 +++++++++ 2 files changed, 486 insertions(+) create mode 100644 cocos/2d/renderer/Frustum.cpp create mode 100644 cocos/2d/renderer/Frustum.h diff --git a/cocos/2d/renderer/Frustum.cpp b/cocos/2d/renderer/Frustum.cpp new file mode 100644 index 0000000000..c590a50c4d --- /dev/null +++ b/cocos/2d/renderer/Frustum.cpp @@ -0,0 +1,390 @@ +#include "Frustum.h" +#include +#include "CCCommon.h" + +NS_CC_BEGIN + +ViewTransform::ViewTransform() +{ + _position = {0, 0, 0}; + _focus = {0, 0, -1}; + _up = {0, 1, 0 }; + _dirty = true; + kmMat4Identity(&_matrix); +} + +ViewTransform::~ViewTransform() +{ +} + +void ViewTransform::Init(const kmVec3 &pos, const kmVec3 &focus, const kmVec3 &up) +{ + _position = pos; + _focus = focus; + _up = up; + _dirty = true; +} + +void ViewTransform::LazyAdjust() const +{ + if(!_dirty) return; + kmVec3Subtract(&_adjustDir, &_focus, &_position); + kmVec3Normalize(&_adjustDir, &_adjustDir); + + kmVec3Cross(&_adjustRight, &_adjustDir, &_up); + kmVec3Normalize(&_adjustRight, &_adjustRight); + + kmVec3Cross(&_adjustUp, &_adjustRight, &_adjustDir); + kmVec3Normalize(&_adjustUp, &_adjustUp); + + _dirty = false; +} + +const kmVec3& ViewTransform::getDirection() const +{ + LazyAdjust(); + return _adjustDir; +} + +const kmVec3& ViewTransform::getRight() const +{ + LazyAdjust(); + return _adjustRight; +} + +const kmVec3& ViewTransform::getUp() const +{ + LazyAdjust(); + return _adjustUp; +} + +AABB::AABB(const kmVec3& min, const kmVec3& max) +{ + _min = min; + _max = max; + if(_min.x > _max.x) + { + CCLOG("_min.x is greater than _max.x, it will be swapped!"); + float temp = _min.x; _min.x = _max.x; _max.x = temp; + } + if(_min.y > _max.y) + { + CCLOG("_min.y is greater than _max.y, it will be swapped!"); + float temp = _min.y; _min.y = _max.y; _max.y = temp; + } + if(_min.z > _max.z) + { + CCLOG("_min.z is greater than _max.z, it will be swapped!"); + float temp = _min.z; _min.z = _max.z; _max.z = temp; + } +} + +AABB::~AABB() +{ +} + +kmVec3 AABB::getCenter() const +{ + kmVec3 result; + + kmVec3Add(&result, &_min, &_max); + kmVec3Scale(&result, &result, 0.5f); + return result; +} + +float AABB::getDimensionX() const +{ + return _max.x - _min.x; +} + +float AABB::getDimensionY() const +{ + return _max.y - _min.y; +} + +float AABB::getDimensionZ() const +{ + return _max.z - _min.z; +} + +kmVec3 AABB::getPositivePoint(const kmVec3& direction) const +{ + kmVec3 result = _max; + if( direction.x < 0 ) result.x = _min.x; + if( direction.y < 0 ) result.y = _min.y; + if( direction.z < 0 ) result.z = _min.z; + + return result; +} + +const AABB& AABB::expand(const kmVec3& point) +{ + if(point.x > _max.x) _max.x = point.x; + if(point.y > _max.y) _max.y = point.y; + if(point.z > _max.z) _max.z = point.z; + + if(point.x < _min.x) _min.x = point.x; + if(point.y < _min.y) _min.y = point.y; + if(point.z < _min.z) _min.z = point.z; + + return *this; +} + +kmVec3 AABB::getNegativePoint(const kmVec3& direction) const +{ + kmVec3 result = _min; + if( direction.x < 0 ) result.x = _max.x; + if( direction.y < 0 ) result.y = _max.y; + if( direction.z < 0 ) result.z = _max.z; + + return result; +} + +Frustum::Frustum() +{ +} + +Frustum::~Frustum() +{ +} + +void Frustum::setupProjectionOrthogonal(const cocos2d::ViewTransform &view, float width, float height, float near, float far) +{ + kmVec3 cc = view.getPosition(); + kmVec3 cDir = view.getDirection(); + kmVec3 cRight = view.getRight(); + kmVec3 cUp = view.getUp(); + + kmVec3Normalize(&cDir, &cDir); + kmVec3Normalize(&cRight, &cRight); + kmVec3Normalize(&cUp, &cUp); + + //near + { + kmVec3 point; + kmVec3 normal; + normal = cDir; + kmVec3Scale(&point, &cDir, near); + kmVec3Add(&point, &point, &cc); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::NEAR], &point, &normal); + } + + //far + { + kmVec3 point; + kmVec3 normal; + kmVec3Scale(&normal, &cDir, -1); + kmVec3Scale(&point, &cDir, far); + kmVec3Add(&point, &point, &cc); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::FAR], &point, &normal); + } + + //left + { + kmVec3 point; + kmVec3 normal; + normal = cRight; + kmVec3Scale(&point, &cRight, -width * 0.5); + kmVec3Add(&point, &point, &cc); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::LEFT], &point, &normal); + } + + //right + { + kmVec3 point; + kmVec3 normal; + kmVec3Scale(&normal, &cRight, -1); + kmVec3Scale(&point, &cRight, width * 0.5); + kmVec3Add(&point, &point, &cc); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::RIGHT], &point, &normal); + } + + //bottom + { + kmVec3 point; + kmVec3 normal; + normal = cUp; + kmVec3Scale(&point, &cUp, -height * 0.5); + kmVec3Add(&point, &point, &cc); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::BOTTOM], &point, &normal); + } + + //top + { + kmVec3 point; + kmVec3 normal; + kmVec3Scale(&normal, &cUp, -1); + kmVec3Scale(&point, &cUp, height * 0.5); + kmVec3Add(&point, &point, &cc); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::TOP], &point, &normal); + } +} + +void Frustum::setupProjectionPerspective(const ViewTransform& view, float left, float right, float top, float bottom, float near, float far) +{ + kmVec3 cc = view.getPosition(); + kmVec3 cDir = view.getDirection(); + kmVec3 cRight = view.getRight(); + kmVec3 cUp = view.getUp(); + + kmVec3Normalize(&cDir, &cDir); + kmVec3Normalize(&cRight, &cRight); + kmVec3Normalize(&cUp, &cUp); + + kmVec3 nearCenter; + kmVec3 farCenter; + + kmVec3Scale(&nearCenter, &cDir, near); + kmVec3Add(&nearCenter, &nearCenter, &cc); + + kmVec3Scale(&farCenter, &cDir, far); + kmVec3Add(&farCenter, &farCenter, &cc); + + //near + { + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::NEAR], &nearCenter, &cDir); + } + + //far + { + kmVec3 normal; + kmVec3Scale(&normal, &cDir, -1); + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::FAR], &farCenter, &normal); + } + + //left + { + kmVec3 point; + kmVec3Scale(&point, &cRight, left); + kmVec3Add(&point, &point, &nearCenter); + + kmVec3 normal; + kmVec3Subtract(&normal, &point, &cc); + kmVec3Cross(&normal, &normal, &cUp); + kmVec3Normalize(&normal, &normal); + + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::LEFT], &point, &normal); + } + + //right + { + kmVec3 point; + kmVec3Scale(&point, &cRight, right); + kmVec3Add(&point, &point, &nearCenter); + + kmVec3 normal; + kmVec3Subtract(&normal, &point, &cc); + kmVec3Cross(&normal, &cUp, &normal); + kmVec3Normalize(&normal, &normal); + + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::RIGHT], &point, &normal); + } + + //bottom + { + kmVec3 point; + kmVec3Scale(&point, &cUp, bottom); + kmVec3Add(&point, &point, &nearCenter); + + kmVec3 normal; + kmVec3Subtract(&normal, &point, &cc); + kmVec3Cross(&normal, &cRight, &normal); + kmVec3Normalize(&normal, &normal); + + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::BOTTOM], &point, &normal); + } + + //top + { + kmVec3 point; + kmVec3Scale(&point, &cUp, top); + kmVec3Add(&point, &point, &nearCenter); + + kmVec3 normal; + kmVec3Subtract(&normal, &point, &cc); + kmVec3Cross(&normal, &normal, &cRight); + kmVec3Normalize(&normal, &normal); + + kmPlaneFromPointNormal(&_frustumPlanes[FrustumPlane::TOP], &point, &normal); + } + +} + +void Frustum::setupProjectionPerspectiveFov(const ViewTransform& view, float fov, float ratio, float near, float far) +{ + float width = 2 * near * tan(fov * 0.5); + float height = width/ratio; + setupProjectionPerspective(view, -width/2, width/2, height/2, -height/2, near, far); +} + +void Frustum::setupFromMatrix(const kmMat4 &view, const kmMat4 &projection) +{ + kmMat4 mvp; + kmMat4Multiply(&mvp, &projection, &view); + + kmMat4ExtractPlane(&_frustumPlanes[FrustumPlane::NEAR], &mvp, KM_PLANE_NEAR); + kmMat4ExtractPlane(&_frustumPlanes[FrustumPlane::FAR], &mvp, KM_PLANE_FAR); + kmMat4ExtractPlane(&_frustumPlanes[FrustumPlane::LEFT], &mvp, KM_PLANE_LEFT); + kmMat4ExtractPlane(&_frustumPlanes[FrustumPlane::RIGHT], &mvp, KM_PLANE_RIGHT); + kmMat4ExtractPlane(&_frustumPlanes[FrustumPlane::BOTTOM], &mvp, KM_PLANE_BOTTOM); + kmMat4ExtractPlane(&_frustumPlanes[FrustumPlane::TOP], &mvp, KM_PLANE_TOP); +} + +Frustum::IntersectResult Frustum::intersectPoint(const kmVec3 &point) const +{ + int indexFirst = static_cast(FrustumPlane::NEAR); + int indexNumber = static_cast(FrustumPlane::NUMBER); + + for(int planeIndex = indexFirst; planeIndex < indexNumber; ++planeIndex) + { + if(kmPlaneDotCoord(&_frustumPlanes[static_cast(planeIndex)], &point) < 0) + return IntersectResult::OUTSIDE; + } + return IntersectResult::INSIDE; +} + +Frustum::IntersectResult Frustum::intersectAABB(const AABB& aabb) const +{ + IntersectResult result = IntersectResult::INSIDE; + int indexFirst = static_cast(FrustumPlane::NEAR); + int indexNumber = static_cast(FrustumPlane::NUMBER); + + for(int planeIndex = indexFirst; planeIndex < indexNumber; ++planeIndex) + { + kmPlane plane = _frustumPlanes[static_cast(planeIndex)]; + kmVec3 normal = {plane.a, plane.b, plane.c}; + kmVec3Normalize(&normal, &normal); + kmVec3 positivePoint = aabb.getPositivePoint(normal); + kmVec3 negativePoint = aabb.getNegativePoint(normal); + + if(kmPlaneDotCoord(&plane, &positivePoint) < 0) + return IntersectResult::OUTSIDE; + if(kmPlaneDotCoord(&plane, &negativePoint) < 0) + result = IntersectResult::INTERSECT; + } + + return result; +} + +Frustum::IntersectResult Frustum::intersectSphere(const kmVec3& center, float radius) const +{ + IntersectResult result = IntersectResult::INSIDE; + int indexFirst = static_cast(FrustumPlane::NEAR); + int indexNumber = static_cast(FrustumPlane::NUMBER); + + for(int planeIndex = indexFirst; planeIndex < indexNumber; ++planeIndex) + { + kmPlane plane = _frustumPlanes[static_cast(planeIndex)]; + kmVec3 normal = {plane.a, plane.b, plane.c}; + + float distance = kmPlaneDotCoord(&plane, ¢er); + distance = distance / kmVec3Length(&normal); + + if(distance < -radius) return IntersectResult::OUTSIDE; + if(distance <= radius && distance >= -radius) result = IntersectResult::INTERSECT; + } + + return result; +} + +NS_CC_END diff --git a/cocos/2d/renderer/Frustum.h b/cocos/2d/renderer/Frustum.h new file mode 100644 index 0000000000..d20bdf483a --- /dev/null +++ b/cocos/2d/renderer/Frustum.h @@ -0,0 +1,96 @@ +#ifndef __CC_FRUSTUM_H__ +#define __CC_FRUSTUM_H__ + +#include "CCPlatformMacros.h" +#include "math/kazmath/include/kazmath/kazmath.h" + +NS_CC_BEGIN + +class ViewTransform +{ +public: + ViewTransform(); + ~ViewTransform(); + void Init(const kmVec3& pos, const kmVec3& focus, const kmVec3& up); + + const kmVec3& getPosition() const { return _position; } + const kmVec3& getFocus() const { return _focus; } + const kmVec3& getDirection() const; + const kmVec3& getRight() const; + const kmVec3& getUp() const; + +private: + void LazyAdjust() const; +private: + kmVec3 _position; + kmVec3 _focus; + kmVec3 _up; + + mutable bool _dirty; + mutable kmMat4 _matrix; + mutable kmVec3 _adjustDir; + mutable kmVec3 _adjustRight; + mutable kmVec3 _adjustUp; +}; + +class AABB +{ +public: + AABB(const kmVec3& min, const kmVec3& max); + ~AABB(); + + kmVec3 getCenter() const; + + float getDimensionX() const; + float getDimensionY() const; + float getDimensionZ() const; + + kmVec3 getPositivePoint(const kmVec3& direction) const; + kmVec3 getNegativePoint(const kmVec3& direction) const; + + const AABB& expand(const kmVec3& point); +private: + kmVec3 _min; + kmVec3 _max; +}; + +class Frustum +{ +public: + enum class IntersectResult + { + OUTSIDE = 0, + INTERSECT = 1, + INSIDE = 2 + }; +public: + Frustum(); + ~Frustum(); + + void setupProjectionOrthogonal(const ViewTransform& view, float width, float height, float near, float far); + void setupProjectionPerspective(const ViewTransform& view, float left, float right, float top, float bottom, float near, float far); + void setupProjectionPerspectiveFov(const ViewTransform& view, float fov, float ratio, float near, float far); + + void setupFromMatrix(const kmMat4& view, const kmMat4& projection); + + IntersectResult intersectPoint(const kmVec3& point) const; + IntersectResult intersectAABB(const AABB& aabb) const; + IntersectResult intersectSphere(const kmVec3& center, float radius) const; + +private: + enum FrustumPlane + { + NEAR = 0, + FAR = 1, + BOTTOM = 2, + TOP = 3, + LEFT = 4, + RIGHT = 5, + NUMBER = 6 + }; + kmPlane _frustumPlanes[FrustumPlane::NUMBER]; +}; + +NS_CC_END + +#endif \ No newline at end of file