mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-10-31 07:25:22 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			450 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			450 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (C) 2002-2012 Nikolaus Gebhardt
 | |
| // This file is part of the "Irrlicht Engine".
 | |
| // For conditions of distribution and use, see copyright notice in irrlicht.h
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include "plane3d.h"
 | |
| #include "vector3d.h"
 | |
| #include "line3d.h"
 | |
| #include "aabbox3d.h"
 | |
| #include "matrix4.h"
 | |
| #include "IVideoDriver.h"
 | |
| 
 | |
| namespace irr
 | |
| {
 | |
| namespace scene
 | |
| {
 | |
| 
 | |
| //! Defines the view frustum. That's the space visible by the camera.
 | |
| /** The view frustum is enclosed by 6 planes. These six planes share
 | |
| eight points. A bounding box around these eight points is also stored in
 | |
| this structure.
 | |
| */
 | |
| struct SViewFrustum
 | |
| {
 | |
| 	enum VFPLANES
 | |
| 	{
 | |
| 		//! Far plane of the frustum. That is the plane furthest away from the eye.
 | |
| 		VF_FAR_PLANE = 0,
 | |
| 		//! Near plane of the frustum. That is the plane nearest to the eye.
 | |
| 		VF_NEAR_PLANE,
 | |
| 		//! Left plane of the frustum.
 | |
| 		VF_LEFT_PLANE,
 | |
| 		//! Right plane of the frustum.
 | |
| 		VF_RIGHT_PLANE,
 | |
| 		//! Bottom plane of the frustum.
 | |
| 		VF_BOTTOM_PLANE,
 | |
| 		//! Top plane of the frustum.
 | |
| 		VF_TOP_PLANE,
 | |
| 
 | |
| 		//! Amount of planes enclosing the view frustum. Should be 6.
 | |
| 		VF_PLANE_COUNT
 | |
| 	};
 | |
| 
 | |
| 	//! Default Constructor
 | |
| 	SViewFrustum() :
 | |
| 			BoundingRadius(0.f), FarNearDistance(0.f) {}
 | |
| 
 | |
| 	//! Copy Constructor
 | |
| 	SViewFrustum(const SViewFrustum &other);
 | |
| 
 | |
| 	//! This constructor creates a view frustum based on a projection and/or view matrix.
 | |
| 	//\param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style).
 | |
| 	SViewFrustum(const core::matrix4 &mat, bool zClipFromZero);
 | |
| 
 | |
| 	//! This constructor creates a view frustum based on a projection and/or view matrix.
 | |
| 	//\param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style).
 | |
| 	inline void setFrom(const core::matrix4 &mat, bool zClipFromZero);
 | |
| 
 | |
| 	//! transforms the frustum by the matrix
 | |
| 	/** \param mat: Matrix by which the view frustum is transformed.*/
 | |
| 	void transform(const core::matrix4 &mat);
 | |
| 
 | |
| 	//! returns the point which is on the far left upper corner inside the the view frustum.
 | |
| 	core::vector3df getFarLeftUp() const;
 | |
| 
 | |
| 	//! returns the point which is on the far left bottom corner inside the the view frustum.
 | |
| 	core::vector3df getFarLeftDown() const;
 | |
| 
 | |
| 	//! returns the point which is on the far right top corner inside the the view frustum.
 | |
| 	core::vector3df getFarRightUp() const;
 | |
| 
 | |
| 	//! returns the point which is on the far right bottom corner inside the the view frustum.
 | |
| 	core::vector3df getFarRightDown() const;
 | |
| 
 | |
| 	//! returns the point which is on the near left upper corner inside the the view frustum.
 | |
| 	core::vector3df getNearLeftUp() const;
 | |
| 
 | |
| 	//! returns the point which is on the near left bottom corner inside the the view frustum.
 | |
| 	core::vector3df getNearLeftDown() const;
 | |
| 
 | |
| 	//! returns the point which is on the near right top corner inside the the view frustum.
 | |
| 	core::vector3df getNearRightUp() const;
 | |
| 
 | |
| 	//! returns the point which is on the near right bottom corner inside the the view frustum.
 | |
| 	core::vector3df getNearRightDown() const;
 | |
| 
 | |
| 	//! returns a bounding box enclosing the whole view frustum
 | |
| 	const core::aabbox3d<f32> &getBoundingBox() const;
 | |
| 
 | |
| 	//! recalculates the bounding box and sphere based on the planes
 | |
| 	inline void recalculateBoundingBox();
 | |
| 
 | |
| 	//! get the bounding sphere's radius (of an optimized sphere, not the AABB's)
 | |
| 	float getBoundingRadius() const;
 | |
| 
 | |
| 	//! get the bounding sphere's radius (of an optimized sphere, not the AABB's)
 | |
| 	core::vector3df getBoundingCenter() const;
 | |
| 
 | |
| 	//! the cam should tell the frustum the distance between far and near
 | |
| 	void setFarNearDistance(float distance);
 | |
| 
 | |
| 	//! get the given state's matrix based on frustum E_TRANSFORMATION_STATE
 | |
| 	core::matrix4 &getTransform(video::E_TRANSFORMATION_STATE state);
 | |
| 
 | |
| 	//! get the given state's matrix based on frustum E_TRANSFORMATION_STATE
 | |
| 	const core::matrix4 &getTransform(video::E_TRANSFORMATION_STATE state) const;
 | |
| 
 | |
| 	//! clips a line to the view frustum.
 | |
| 	/** \return True if the line was clipped, false if not */
 | |
| 	bool clipLine(core::line3d<f32> &line) const;
 | |
| 
 | |
| 	//! the position of the camera
 | |
| 	core::vector3df cameraPosition;
 | |
| 
 | |
| 	//! all planes enclosing the view frustum.
 | |
| 	core::plane3d<f32> planes[VF_PLANE_COUNT];
 | |
| 
 | |
| 	//! bounding box around the view frustum
 | |
| 	core::aabbox3d<f32> boundingBox;
 | |
| 
 | |
| private:
 | |
| 	//! Hold a copy of important transform matrices
 | |
| 	enum E_TRANSFORMATION_STATE_FRUSTUM
 | |
| 	{
 | |
| 		ETS_VIEW = 0,
 | |
| 		ETS_PROJECTION = 1,
 | |
| 		ETS_COUNT_FRUSTUM
 | |
| 	};
 | |
| 
 | |
| 	//! recalculates the bounding sphere based on the planes
 | |
| 	inline void recalculateBoundingSphere();
 | |
| 
 | |
| 	//! Hold a copy of important transform matrices
 | |
| 	core::matrix4 Matrices[ETS_COUNT_FRUSTUM];
 | |
| 
 | |
| 	float BoundingRadius;
 | |
| 	float FarNearDistance;
 | |
| 	core::vector3df BoundingCenter;
 | |
| };
 | |
| 
 | |
| /*!
 | |
| 	Copy constructor ViewFrustum
 | |
| */
 | |
| inline SViewFrustum::SViewFrustum(const SViewFrustum &other)
 | |
| {
 | |
| 	cameraPosition = other.cameraPosition;
 | |
| 	boundingBox = other.boundingBox;
 | |
| 
 | |
| 	u32 i;
 | |
| 	for (i = 0; i < VF_PLANE_COUNT; ++i)
 | |
| 		planes[i] = other.planes[i];
 | |
| 
 | |
| 	for (i = 0; i < ETS_COUNT_FRUSTUM; ++i)
 | |
| 		Matrices[i] = other.Matrices[i];
 | |
| 
 | |
| 	BoundingRadius = other.BoundingRadius;
 | |
| 	FarNearDistance = other.FarNearDistance;
 | |
| 	BoundingCenter = other.BoundingCenter;
 | |
| }
 | |
| 
 | |
| inline SViewFrustum::SViewFrustum(const core::matrix4 &mat, bool zClipFromZero)
 | |
| {
 | |
| 	setFrom(mat, zClipFromZero);
 | |
| }
 | |
| 
 | |
| inline void SViewFrustum::transform(const core::matrix4 &mat)
 | |
| {
 | |
| 	for (u32 i = 0; i < VF_PLANE_COUNT; ++i)
 | |
| 		mat.transformPlane(planes[i]);
 | |
| 
 | |
| 	mat.transformVect(cameraPosition);
 | |
| 	recalculateBoundingBox();
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getFarLeftUp() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_FAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_TOP_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_LEFT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getFarLeftDown() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_FAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_BOTTOM_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_LEFT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getFarRightUp() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_FAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_TOP_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_RIGHT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getFarRightDown() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_FAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_BOTTOM_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_RIGHT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getNearLeftUp() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_NEAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_TOP_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_LEFT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getNearLeftDown() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_NEAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_BOTTOM_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_LEFT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getNearRightUp() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_NEAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_TOP_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_RIGHT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getNearRightDown() const
 | |
| {
 | |
| 	core::vector3df p;
 | |
| 	planes[scene::SViewFrustum::VF_NEAR_PLANE].getIntersectionWithPlanes(
 | |
| 			planes[scene::SViewFrustum::VF_BOTTOM_PLANE],
 | |
| 			planes[scene::SViewFrustum::VF_RIGHT_PLANE], p);
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| inline const core::aabbox3d<f32> &SViewFrustum::getBoundingBox() const
 | |
| {
 | |
| 	return boundingBox;
 | |
| }
 | |
| 
 | |
| inline void SViewFrustum::recalculateBoundingBox()
 | |
| {
 | |
| 	boundingBox.reset(getNearLeftUp());
 | |
| 	boundingBox.addInternalPoint(getNearRightUp());
 | |
| 	boundingBox.addInternalPoint(getNearLeftDown());
 | |
| 	boundingBox.addInternalPoint(getNearRightDown());
 | |
| 	boundingBox.addInternalPoint(getFarRightUp());
 | |
| 	boundingBox.addInternalPoint(getFarLeftDown());
 | |
| 	boundingBox.addInternalPoint(getFarRightDown());
 | |
| 	boundingBox.addInternalPoint(getFarLeftUp());
 | |
| 
 | |
| 	// Also recalculate the bounding sphere when the bbox changes
 | |
| 	recalculateBoundingSphere();
 | |
| }
 | |
| 
 | |
| inline float SViewFrustum::getBoundingRadius() const
 | |
| {
 | |
| 	return BoundingRadius;
 | |
| }
 | |
| 
 | |
| inline core::vector3df SViewFrustum::getBoundingCenter() const
 | |
| {
 | |
| 	return BoundingCenter;
 | |
| }
 | |
| 
 | |
| inline void SViewFrustum::setFarNearDistance(float distance)
 | |
| {
 | |
| 	FarNearDistance = distance;
 | |
| }
 | |
| 
 | |
| //! This constructor creates a view frustum based on a projection
 | |
| //! and/or view matrix.
 | |
| inline void SViewFrustum::setFrom(const core::matrix4 &mat, bool zClipFromZero)
 | |
| {
 | |
| 	// left clipping plane
 | |
| 	planes[VF_LEFT_PLANE].Normal.X = mat[3] + mat[0];
 | |
| 	planes[VF_LEFT_PLANE].Normal.Y = mat[7] + mat[4];
 | |
| 	planes[VF_LEFT_PLANE].Normal.Z = mat[11] + mat[8];
 | |
| 	planes[VF_LEFT_PLANE].D = mat[15] + mat[12];
 | |
| 
 | |
| 	// right clipping plane
 | |
| 	planes[VF_RIGHT_PLANE].Normal.X = mat[3] - mat[0];
 | |
| 	planes[VF_RIGHT_PLANE].Normal.Y = mat[7] - mat[4];
 | |
| 	planes[VF_RIGHT_PLANE].Normal.Z = mat[11] - mat[8];
 | |
| 	planes[VF_RIGHT_PLANE].D = mat[15] - mat[12];
 | |
| 
 | |
| 	// top clipping plane
 | |
| 	planes[VF_TOP_PLANE].Normal.X = mat[3] - mat[1];
 | |
| 	planes[VF_TOP_PLANE].Normal.Y = mat[7] - mat[5];
 | |
| 	planes[VF_TOP_PLANE].Normal.Z = mat[11] - mat[9];
 | |
| 	planes[VF_TOP_PLANE].D = mat[15] - mat[13];
 | |
| 
 | |
| 	// bottom clipping plane
 | |
| 	planes[VF_BOTTOM_PLANE].Normal.X = mat[3] + mat[1];
 | |
| 	planes[VF_BOTTOM_PLANE].Normal.Y = mat[7] + mat[5];
 | |
| 	planes[VF_BOTTOM_PLANE].Normal.Z = mat[11] + mat[9];
 | |
| 	planes[VF_BOTTOM_PLANE].D = mat[15] + mat[13];
 | |
| 
 | |
| 	// far clipping plane
 | |
| 	planes[VF_FAR_PLANE].Normal.X = mat[3] - mat[2];
 | |
| 	planes[VF_FAR_PLANE].Normal.Y = mat[7] - mat[6];
 | |
| 	planes[VF_FAR_PLANE].Normal.Z = mat[11] - mat[10];
 | |
| 	planes[VF_FAR_PLANE].D = mat[15] - mat[14];
 | |
| 
 | |
| 	// near clipping plane
 | |
| 	if (zClipFromZero) {
 | |
| 		planes[VF_NEAR_PLANE].Normal.X = mat[2];
 | |
| 		planes[VF_NEAR_PLANE].Normal.Y = mat[6];
 | |
| 		planes[VF_NEAR_PLANE].Normal.Z = mat[10];
 | |
| 		planes[VF_NEAR_PLANE].D = mat[14];
 | |
| 	} else {
 | |
| 		// near clipping plane
 | |
| 		planes[VF_NEAR_PLANE].Normal.X = mat[3] + mat[2];
 | |
| 		planes[VF_NEAR_PLANE].Normal.Y = mat[7] + mat[6];
 | |
| 		planes[VF_NEAR_PLANE].Normal.Z = mat[11] + mat[10];
 | |
| 		planes[VF_NEAR_PLANE].D = mat[15] + mat[14];
 | |
| 	}
 | |
| 
 | |
| 	// normalize normals
 | |
| 	u32 i;
 | |
| 	for (i = 0; i != VF_PLANE_COUNT; ++i) {
 | |
| 		const f32 len = -core::reciprocal_squareroot(
 | |
| 				planes[i].Normal.getLengthSQ());
 | |
| 		planes[i].Normal *= len;
 | |
| 		planes[i].D *= len;
 | |
| 	}
 | |
| 
 | |
| 	// make bounding box
 | |
| 	recalculateBoundingBox();
 | |
| }
 | |
| 
 | |
| /*!
 | |
| 	View Frustum depends on Projection & View Matrix
 | |
| */
 | |
| inline core::matrix4 &SViewFrustum::getTransform(video::E_TRANSFORMATION_STATE state)
 | |
| {
 | |
| 	u32 index = 0;
 | |
| 	switch (state) {
 | |
| 	case video::ETS_PROJECTION:
 | |
| 		index = SViewFrustum::ETS_PROJECTION;
 | |
| 		break;
 | |
| 	case video::ETS_VIEW:
 | |
| 		index = SViewFrustum::ETS_VIEW;
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	return Matrices[index];
 | |
| }
 | |
| 
 | |
| /*!
 | |
| 	View Frustum depends on Projection & View Matrix
 | |
| */
 | |
| inline const core::matrix4 &SViewFrustum::getTransform(video::E_TRANSFORMATION_STATE state) const
 | |
| {
 | |
| 	u32 index = 0;
 | |
| 	switch (state) {
 | |
| 	case video::ETS_PROJECTION:
 | |
| 		index = SViewFrustum::ETS_PROJECTION;
 | |
| 		break;
 | |
| 	case video::ETS_VIEW:
 | |
| 		index = SViewFrustum::ETS_VIEW;
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	return Matrices[index];
 | |
| }
 | |
| 
 | |
| //! Clips a line to the frustum
 | |
| inline bool SViewFrustum::clipLine(core::line3d<f32> &line) const
 | |
| {
 | |
| 	bool wasClipped = false;
 | |
| 	for (u32 i = 0; i < VF_PLANE_COUNT; ++i) {
 | |
| 		if (planes[i].classifyPointRelation(line.start) == core::ISREL3D_FRONT) {
 | |
| 			line.start = line.start.getInterpolated(line.end,
 | |
| 					1.f - planes[i].getKnownIntersectionWithLine(line.start, line.end));
 | |
| 			wasClipped = true;
 | |
| 		}
 | |
| 		if (planes[i].classifyPointRelation(line.end) == core::ISREL3D_FRONT) {
 | |
| 			line.end = line.start.getInterpolated(line.end,
 | |
| 					1.f - planes[i].getKnownIntersectionWithLine(line.start, line.end));
 | |
| 			wasClipped = true;
 | |
| 		}
 | |
| 	}
 | |
| 	return wasClipped;
 | |
| }
 | |
| 
 | |
| inline void SViewFrustum::recalculateBoundingSphere()
 | |
| {
 | |
| 	// Find the center
 | |
| 	const float shortlen = (getNearLeftUp() - getNearRightUp()).getLength();
 | |
| 	const float longlen = (getFarLeftUp() - getFarRightUp()).getLength();
 | |
| 
 | |
| 	const float farlen = FarNearDistance;
 | |
| 	const float fartocenter = (farlen + (shortlen - longlen) * (shortlen + longlen) / (4 * farlen)) / 2;
 | |
| 	const float neartocenter = farlen - fartocenter;
 | |
| 
 | |
| 	BoundingCenter = cameraPosition + -planes[VF_NEAR_PLANE].Normal * neartocenter;
 | |
| 
 | |
| 	// Find the radius
 | |
| 	core::vector3df dir[8];
 | |
| 	dir[0] = getFarLeftUp() - BoundingCenter;
 | |
| 	dir[1] = getFarRightUp() - BoundingCenter;
 | |
| 	dir[2] = getFarLeftDown() - BoundingCenter;
 | |
| 	dir[3] = getFarRightDown() - BoundingCenter;
 | |
| 	dir[4] = getNearRightDown() - BoundingCenter;
 | |
| 	dir[5] = getNearLeftDown() - BoundingCenter;
 | |
| 	dir[6] = getNearRightUp() - BoundingCenter;
 | |
| 	dir[7] = getNearLeftUp() - BoundingCenter;
 | |
| 
 | |
| 	u32 i = 0;
 | |
| 	float diam[8] = {0.f};
 | |
| 
 | |
| 	for (i = 0; i < 8; ++i)
 | |
| 		diam[i] = dir[i].getLengthSQ();
 | |
| 
 | |
| 	float longest = 0;
 | |
| 
 | |
| 	for (i = 0; i < 8; ++i) {
 | |
| 		if (diam[i] > longest)
 | |
| 			longest = diam[i];
 | |
| 	}
 | |
| 
 | |
| 	BoundingRadius = sqrtf(longest);
 | |
| }
 | |
| 
 | |
| } // end namespace scene
 | |
| } // end namespace irr
 |