/// <summary> /// Scale a box. /// </summary> /// <param name="box">The box to scale.</param> /// <param name="scale">The amount to scale in each direction.</param> /// <returns>The scaled box.</returns> public static Box3F Scale(ref Box3F box, ref Vector3 scale) { Vector3 center = 0.5f * (box.Max + box.Min); Vector3 extent = 0.5f * (box.Max - box.Min); extent.X *= scale.X; extent.Y *= scale.Y; extent.Z *= scale.Z; Box3F outBox = new Box3F(); outBox.Min = center - extent; outBox.Max = center + extent; return outBox; }
/// <summary> /// Copies a render instance's fields into this render instance. /// </summary> /// <param name="src">The render instance to copy.</param> public void Copy(RenderInstance src) { VertexBuffer = src.VertexBuffer; IndexBuffer = src.IndexBuffer; VertexDeclaration = src.VertexDeclaration; Material = src.Material; ObjectTransform = src.ObjectTransform; WorldBox = src.WorldBox; UTextureAddressMode = src.UTextureAddressMode; VTextureAddressMode = src.VTextureAddressMode; VertexSize = src.VertexSize; PrimitiveType = src.PrimitiveType; BaseVertex = src.BaseVertex; VertexCount = src.VertexCount; StartIndex = src.StartIndex; PrimitiveCount = src.PrimitiveCount; Type = src.Type; SortPoint = src.SortPoint; IsSortPointSet = src.IsSortPointSet; Opacity = src.Opacity; }
public bool IsObjectObscured(Vector3 camPos, Box3F bounds) { // do a fast radius check to see if any part of the object could be // within the fog far distance _objBoundsMin = bounds.Min; _objBoundsMax = bounds.Max; float radius = Math.Max(_objBoundsMax.X - _objBoundsMin.X, Math.Max(_objBoundsMax.Y - _objBoundsMin.Y, _objBoundsMax.Z - _objBoundsMin.Z)); if (Vector3.Distance(camPos, bounds.Center) - radius < _fogFarDistance) return false; return true; }
public bool IsObjectFogged(Vector3 camPos, Box3F bounds) { // do a fast radius check to see if any part of the object could be completely // inside the unfogged area close to the camera _objBoundsMin = bounds.Min; _objBoundsMax = bounds.Max; float radius = Math.Max(_objBoundsMax.X - _objBoundsMin.X, Math.Max(_objBoundsMax.Y - _objBoundsMin.Y, _objBoundsMax.Z - _objBoundsMin.Z)); if (Vector3.Distance(camPos, bounds.Center) + radius < _fogNearDistance) return false; return true; }
/// <summary> /// Transforms a box with a specified transform. /// </summary> /// <param name="inBox">The box to transform.</param> /// <param name="transform">The transform.</param> /// <returns>The transformed box.</returns> public static Box3F Transform(ref Box3F inBox, ref Matrix transform) { Vector3 center = 0.5f * (inBox.Min + inBox.Max); Vector3 extent = inBox.Max - center; Vector3 newExtent = new Vector3( extent.X * Math.Abs(transform.M11) + extent.Y * Math.Abs(transform.M21) + extent.Z * Math.Abs(transform.M31), extent.X * Math.Abs(transform.M12) + extent.Y * Math.Abs(transform.M22) + extent.Z * Math.Abs(transform.M32), extent.X * Math.Abs(transform.M13) + extent.Y * Math.Abs(transform.M23) + extent.Z * Math.Abs(transform.M33)); center = Vector3.Transform(center, transform); Box3F outBox = new Box3F(center - newExtent, center + newExtent); #if extra_strict_check // extra strict check { // Cycle through all the corners of the box and transform them. Then // extend test box by transformed corners. This box should match the // one we computed above except for precision errors. Box3F testBox= new Box3F(10E10f,10E10f,10E10f,-10E10f,-10E10f,-10E10f); center = 0.5f * (inBox.Min + inBox.Max); for (int i = 0; i < 8; i++) { Vector3 corner = center; corner.X += ((i & 1) == 0) ? extent.X : -extent.X; corner.Y += ((i & 2) == 0) ? extent.Y : -extent.Y; corner.Z += ((i & 4) == 0) ? extent.Z : -extent.Z; corner = Vector3.Transform(corner, transform); testBox.Extend(corner); } Assert.Fatal(Math.Abs(testBox.Min.X - outBox.Min.X) < 0.001f && Math.Abs(testBox.Min.Y - outBox.Min.Y) < 0.001f && Math.Abs(testBox.Min.Z - outBox.Min.Z) < 0.001f, "doh"); Assert.Fatal(Math.Abs(testBox.Max.X - outBox.Max.X) < 0.001f && Math.Abs(testBox.Max.Y - outBox.Max.Y) < 0.001f && Math.Abs(testBox.Max.Z - outBox.Max.Z) < 0.001f, "doh"); } #endif return outBox; }
/// <summary> /// Check to see if another box overlaps this box. /// </summary> /// <param name="inBox">The other box to check.</param> /// <returns>Returns true if the specified box overlaps this box.</returns> public bool Overlaps(ref Box3F inBox) { if (inBox.Min.X > Max.X || inBox.Min.Y > Max.Y || inBox.Min.Z > Max.Z) return false; if (inBox.Max.X < Min.X || inBox.Max.Y < Min.Y || inBox.Max.Z < Min.Z) return false; return true; }
/// <summary> /// Returns whether or not a box intersects with the frustum. /// </summary> /// <param name="box">The box to test.</param> /// <param name="mat">The object to world space matrix. Use identity if the box is already in world space.</param> /// <param name="radius">The radius of the box.</param> /// <returns>True if the box intersects the frustum.</returns> public bool Intersects(Box3F box, Matrix mat, float radius) { // Note: use the long form of vector operations because they get inlined whereas operator version does not #if EXTRA_DIAGNOSTICS _stats.totalChecks++; #endif // Vector3 objPos = 0.5f * (box.Max + box.Min) - _cameraPos; Vector3 objPos; Vector3.Add(ref box.Max, ref box.Min, out objPos); Vector3.Multiply(ref objPos, 0.5f, out objPos); Vector3.Subtract(ref objPos, ref _cameraPos, out objPos); float dist; Vector3.Dot(ref objPos, ref _cameraY, out dist); if (dist < -radius || dist > _farDist + radius) { // sphere behind near plane or beyond far plane #if EXTRA_DIAGNOSTICS _stats.sphereFarNearReject++; #endif return false; } // Short form of code which doesn't get inlined: // Vector3 conePos = -radius * _rejectInvSin * _camearY; // Vector3 offset = objPos - conePos; // float dy = Vector3.Dot(offset,_cameraY); // offset -= dy * _cameraY; // float dxSq = offset.LengthSquared(); // Long form of code which does get inlined: float dy, dxSq; Vector3 conePos, offset, offsetY; Vector3.Multiply(ref _cameraY, -radius * _rejectInvSin, out conePos); Vector3.Subtract(ref objPos, ref conePos, out offset); Vector3.Dot(ref offset, ref _cameraY, out dy); Vector3.Multiply(ref _cameraY, dy, out offsetY); Vector3.Subtract(ref offset, ref offsetY, out offset); dxSq = offset.X * offset.X + offset.Y * offset.Y + offset.Z * offset.Z; if (dy * dy < _rejectCos * _rejectCos * (dxSq + dy * dy)) { // early rejection, outside outer cone... #if EXTRA_DIAGNOSTICS _stats.sphereConeReject++; #endif return false; } Vector3.Multiply(ref _cameraY, -radius * _acceptInvSin, out conePos); Vector3.Subtract(ref objPos, ref conePos, out offset); Vector3.Dot(ref offset, ref _cameraY, out dy); Vector3.Multiply(ref _cameraY, dy, out offsetY); Vector3.Subtract(ref offset, ref offsetY, out offset); dxSq = offset.X * offset.X + offset.Y * offset.Y + offset.Z * offset.Z; if (dy * dy > _acceptCos * _acceptCos * (dxSq + dy * dy)) { // early accept, inside inner cone...check far plane or near plane against box? #if EXTRA_DIAGNOSTICS _stats.sphereConeAccept++; #endif return true; } // Compare bounds to each of the frustum planes in turn. float boxDistance; #if EXTRA_DIAGNOSTICS _stats.checkLeft++; #endif if (!MathUtil.Collision.TestOBBPlane(box, mat, _leftPlaneNormal, _leftPlaneDist, out boxDistance) && boxDistance > 0.0f) { // fully outside this plane #if EXTRA_DIAGNOSTICS _stats.outsideLeft++; #endif return false; } #if EXTRA_DIAGNOSTICS _stats.checkRight++; #endif if (!MathUtil.Collision.TestOBBPlane(box, mat, _rightPlaneNormal, _rightPlaneDist, out boxDistance) && boxDistance > 0.0f) { // fully outside this plane #if EXTRA_DIAGNOSTICS _stats.outsideRight++; #endif return false; } #if EXTRA_DIAGNOSTICS _stats.checkTop++; #endif if (!MathUtil.Collision.TestOBBPlane(box, mat, _topPlaneNormal, _topPlaneDist, out boxDistance) && boxDistance > 0.0f) { // fully outside this plane #if EXTRA_DIAGNOSTICS _stats.outsideTop++; #endif return false; } #if EXTRA_DIAGNOSTICS _stats.checkBottom++; #endif if (!MathUtil.Collision.TestOBBPlane(box, mat, _bottomPlaneNormal, _bottomPlaneDist, out boxDistance) && boxDistance > 0.0f) { // fully outside this plane #if EXTRA_DIAGNOSTICS _stats.outsideBottom++; #endif return false; } #if EXTRA_DIAGNOSTICS _stats.checkNear++; #endif if (!MathUtil.Collision.TestOBBPlane(box, mat, -_cameraY, _nearPlaneDist, out boxDistance) && boxDistance > 0.0f) { // fully outside this plane #if EXTRA_DIAGNOSTICS _stats.outsideNear++; #endif return false; } #if EXTRA_DIAGNOSTICS _stats.checkFar++; #endif if (!MathUtil.Collision.TestOBBPlane(box, mat, _cameraY, _farPlaneDist, out boxDistance) && boxDistance > 0.0f) { // fully outside this plane #if EXTRA_DIAGNOSTICS _stats.outsideFar++; #endif return false; } // passed all the tests, so must be inside frustum #if EXTRA_DIAGNOSTICS _stats.passAllChecks++; #endif return true; }
/// <summary> /// Generate the frustum. /// </summary> /// <param name="nearDist">The distance to the near plane.</param> /// <param name="farDist">The distance to the far plane.</param> /// <param name="fov">The field of view of the frustum.</param> /// <param name="aspectRatio">The aspect ratio.</param> /// <param name="camToWorld">The camera transform.</param> public void SetFrustum(float nearDist, float farDist, float fov, float aspectRatio, Matrix camToWorld) { _nearDist = nearDist; _farDist = farDist; float left = -farDist * (float)Math.Tan(fov * 0.5f) * aspectRatio; float right = -left; float bottom = left / aspectRatio; float top = -bottom; Vector3 farPosLeftUp = new Vector3(left, farDist, top); Vector3 farPosLeftDown = new Vector3(left, farDist, bottom); Vector3 farPosRightUp = new Vector3(right, farDist, top); Vector3 farPosRightDown = new Vector3(right, farDist, bottom); _cameraPos = camToWorld.Translation; _cameraY = MatrixUtil.MatrixGetRow(1, ref camToWorld); farPosLeftUp = MatrixUtil.MatMulP(ref farPosLeftUp, ref camToWorld); farPosLeftDown = MatrixUtil.MatMulP(ref farPosLeftDown, ref camToWorld); farPosRightUp = MatrixUtil.MatMulP(ref farPosRightUp, ref camToWorld); farPosRightDown = MatrixUtil.MatMulP(ref farPosRightDown, ref camToWorld); _bounds = new Box3F(_cameraPos, _cameraPos); _bounds.Extend(farPosLeftUp); _bounds.Extend(farPosLeftDown); _bounds.Extend(farPosRightUp); _bounds.Extend(farPosRightDown); _leftPlaneNormal = Vector3.Cross(farPosLeftUp - _cameraPos, farPosLeftDown - _cameraPos); _rightPlaneNormal = Vector3.Cross(farPosRightDown - _cameraPos, farPosRightUp - _cameraPos); _topPlaneNormal = Vector3.Cross(farPosRightUp - _cameraPos, farPosLeftUp - _cameraPos); _bottomPlaneNormal = Vector3.Cross(farPosLeftDown - _cameraPos, farPosRightDown - _cameraPos); _leftPlaneNormal.Normalize(); _rightPlaneNormal.Normalize(); _topPlaneNormal.Normalize(); _bottomPlaneNormal.Normalize(); _leftPlaneDist = Vector3.Dot(_leftPlaneNormal, _cameraPos); _rightPlaneDist = Vector3.Dot(_rightPlaneNormal, _cameraPos); _topPlaneDist = Vector3.Dot(_topPlaneNormal, _cameraPos); _bottomPlaneDist = Vector3.Dot(_bottomPlaneNormal, _cameraPos); _nearPlaneDist = -nearDist - Vector3.Dot(_cameraY, _cameraPos); _farPlaneDist = farDist + Vector3.Dot(_cameraY, _cameraPos); // precompute sin for quick object accept float minr = Math.Min(right, top); Assert.Fatal(minr > 0.0f, "doh!"); _acceptCos = _farDist / (float)Math.Sqrt(_farDist * _farDist + minr * minr); _acceptInvSin = 1.0f / (float)Math.Sqrt(1.0f - _acceptCos * _acceptCos); // precompute sin for object culling float maxr = (float)Math.Sqrt(right * right + bottom * bottom); // anything outside this cone rejected _rejectCos = _farDist / (float)Math.Sqrt(_farDist * _farDist + maxr * maxr); _rejectInvSin = 1.0f / (float)Math.Sqrt(1.0f - _rejectCos * _rejectCos); }
/// <summary> /// Sets the material information for the draw primitive. /// </summary> /// <param name="material">The material to use.</param> /// <param name="bounds">The bounding box of the mesh being rendered.</param> /// <param name="srs">The scene render state.</param> public static void SetMaterial(ref Material material, ref Box3F bounds, SceneRenderState srs) { // perform name mapping to replace materials with custom materials if (material.Name != null && material._renderMaterial == null) { String baseName = Shape.CurrentFilePath + @"\" + material.Name; material._renderMaterial = MaterialManager.Lookup(baseName); if (material._renderMaterial == null) { // if no material mapped, then use a default material if (baseName != null) { LightingMaterial effect = new LightingMaterial(); effect.LightingMode = LightingMode.PerPixel; effect.TextureFilename = baseName; effect.IsTranslucent = (material.Flags & MaterialFlags.Translucent) != 0; material._renderMaterial = MaterialManager.Add(baseName, effect); } // if we didn't find it this time, don't look it up again if (material._renderMaterial == null) material.Name = null; } } // material info _curRenderInst.Material = material._renderMaterial; bool translucent = (material.Flags & MaterialFlags.Translucent) != 0; _curRenderInst.Type = translucent ? RenderInstance.RenderInstanceType.Translucent2D : RenderInstance.RenderInstanceType.Mesh3D; bool sWrap = (material.Flags & MaterialFlags.S_Wrap) != 0; _curRenderInst.UTextureAddressMode = sWrap ? TextureAddressMode.Wrap : TextureAddressMode.Clamp; bool tWrap = (material.Flags & MaterialFlags.T_Wrap) != 0; _curRenderInst.VTextureAddressMode = tWrap ? TextureAddressMode.Wrap : TextureAddressMode.Clamp; // transform info Matrix transform = srs.World.Top; _curRenderInst.WorldBox = Box3F.Transform(ref bounds, ref transform); _curRenderInst.ObjectTransform = transform; }
/// <summary> /// Sets the material information for the draw primitive. This will do nothing if the same material /// index was used with the previous call. /// </summary> /// <param name="matIndex">The index of the material to use.</param> /// <param name="matList">The material list to look up the material in.</param> /// <param name="bounds">The bounding box of the mesh being rendered.</param> /// <param name="srs">The scene render state.</param> public static void SetMaterial(uint matIndex, Material[] matList, ref Box3F bounds, SceneRenderState srs) { if (matIndex == _matIndex) return; _matIndex = matIndex; SetMaterial(ref matList[matIndex], ref bounds, srs); }
// Test OBB against plane. Return 0.0f if collide, + dist if outside plane, - dist if inside plane public static bool TestOBBPlane(Box3F b, Matrix mat, Vector3 planeNormal, float planeDist, out float boxDistance) { // Compute OBB center and extents Vector3 c0 = new Vector3(0.5f * (b.Max.X + b.Min.X), 0.5f * (b.Max.Y + b.Min.Y), 0.5f * (b.Max.Z + b.Min.Z)); Vector3 c; Vector3.Transform(ref c0, ref mat, out c); Vector3 e = new Vector3(0.5f * (b.Max.X - b.Min.X), 0.5f * (b.Max.Y - b.Min.Y), 0.5f * (b.Max.Z - b.Min.Z)); // Project box extents onto planeNormal Vector3 x, y, z; MatrixUtil.GetX(ref mat, out x); MatrixUtil.GetY(ref mat, out y); MatrixUtil.GetZ(ref mat, out z); float xdot = planeNormal.X * x.X + planeNormal.Y * x.Y + planeNormal.Z * x.Z; float ydot = planeNormal.X * y.X + planeNormal.Y * y.Y + planeNormal.Z * y.Z; float zdot = planeNormal.X * z.X + planeNormal.Y * z.Y + planeNormal.Z * z.Z; float cdot = planeNormal.X * c.X + planeNormal.Y * c.Y + planeNormal.Z * c.Z; float rad = e.X * Math.Abs(xdot) + e.Y * Math.Abs(ydot) + e.Z * Math.Abs(zdot); boxDistance = cdot - planeDist; if (Math.Abs(boxDistance) <= rad) { boxDistance = 0.0f; return true; } if (boxDistance < 0.0f) boxDistance += rad; else boxDistance -= rad; return false; }
// From the book "Real Time Collision Detection" by Christer Ericson, pp. 164. public static bool TestAABBPlane(Box3F b, Vector3 planeNormal, float planeDist, out float boxDistance) { // These two lines not necessary with a (center, extents) AABB representation Vector3 c = (b.Max + b.Min) * 0.5f; // Compute AABB center Vector3 e = b.Max - c; // Compute positive extents // Compute the projection interval radius of b onto L(t) = b.c + t * p.n float r = e.X * Math.Abs(planeNormal.X) + e.Y * Math.Abs(planeNormal.Y) + e.Z * Math.Abs(planeNormal.Z); // Compute distance of box center from plane boxDistance = Vector3.Dot(planeNormal, c) - planeDist; // Intersection occurs when distance s falls within [-r,+r] interval if (Math.Abs(boxDistance) <= r) { boxDistance = 0.0f; return true; } if (boxDistance < 0.0f) boxDistance += r; else boxDistance -= r; return false; }
/// <summary> /// Gets the list of lights in the scene that would have the most affect on an object /// inside of a box, up to a maximum number. /// </summary> /// <param name="worldBox">The box to get lights for.</param> /// <param name="maxLights">The maximum number of lights to return.</param> /// <returns></returns> public List<Light> GetLights(Box3F worldBox, int maxLights) { _objectLights.Clear(); _lightComparer.WorldBox = worldBox; _lightList.Sort(_lightComparer); int count = _lightList.Count; for (int i = 0; i < count && i < maxLights; i++) { Light light = _lightList[i]; if (light.Owner != null) _objectLights.Add(light); } return _objectLights; }