/// <summary> /// Test for visibility, return true if VISIBLE and cache result. /// </summary> /// <param name="camera"></param> /// <returns></returns> public bool CullTest(Camera camera) { Culled = true; if (renderables.Count > 0) { Frustum.CullResult cull = camera.Frustum.CullTest(Bounds); if (cull != Frustum.CullResult.TotallyOutside) { Culled = false; Debug.Assert(_disposes.Count == 0); Dictionary <byte, Renderable> .ValueCollection values = renderables.Values; foreach (Renderable renderable in values) { if (!renderable.CullTest(camera)) { _disposes.Add(renderable.Label); } } foreach (byte label in _disposes) { Dispose(label); } _disposes.Clear(); } } return(!Culled); }
public void CullCheck(Camera camera) { if (Bounds == null) { Culled = true; } else { Frustum.CullResult cull = camera.Frustum.CullTest(Bounds); Culled = cull == Frustum.CullResult.TotallyOutside; if (!Culled) { Dictionary <ushort, Renderable> .ValueCollection values = renderableDict.Values; if (cull == Frustum.CullResult.TotallyInside) { foreach (Renderable r in values) { r.Culled = false; } } else { foreach (Renderable r in values) { cull = camera.Frustum.CullTest(r.Bounds); r.Culled = cull == Frustum.CullResult.TotallyOutside; } } } } //Culled = false; // Debug hack to disable all culling, to separate fill from drawprim overhead }
/// <summary> /// Hierarchical cull check, with result cached. This is just /// so we don't have to recheck for each face pass. /// </summary> /// <param name="camera"></param> /// <param name="nodes"></param> /// <returns></returns> public Frustum.CullResult PreCull(Camera camera, List <Node> nodes) { #if MF_HOLDFRUSTUM GamePadInput pad = GamePadInput.GetGamePad0(); if (pad.ButtonY.IsPressed) { _holdFrustum = camera.Frustum.Clone(); } else if (pad.ButtonX.IsPressed) { _holdFrustum = null; } Frustum frustum = _holdFrustum == null ? camera.Frustum : _holdFrustum; #else // MF_HOLDFRUSTUM Frustum frustum = camera.Frustum; #endif // MF_HOLDFRUSTUM Cull = frustum.CullTest(Min, Max); if (Cull == Frustum.CullResult.PartiallyInside) { if (Left >= 0) { Debug.Assert(Right >= 0); nodes[Left].PreCull(camera, nodes); nodes[Right].PreCull(camera, nodes); } } return(Cull); }
/// <summary> /// Render this batch of geometry. No renderstate setup, just throw triangles at the card. /// </summary> /// <param name="camera"></param> public void Render(Camera camera) { #if MF_RENDERSOLO Frustum.CullResult cull = camera.Frustum.CullTest(Bounds); if (cull != Frustum.CullResult.TotallyOutside) #endif // !MF_RENDERSOLO { GraphicsDevice device = BokuGame.bokuGame.GraphicsDevice; device.SetVertexBuffer(data.Buffer); #if MF_KEYHACK if (!KeyboardInput.IsPressed(Keys.M) || !keyState.IsKeyDown(Keys.N)) { #endif // MF_KEYHACK device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, NumVerts, 0, NumTris); #if MF_KEYHACK } #endif // MF_KEYHACK } }
/// <summary> /// Set up a leaf node. /// </summary> /// <param name="batch"></param> /// <param name="bounds"></param> public Node(Int16 batch, AABB bounds) { this.Left = -1; this.Right = -1; this.Batch = batch; this.Min = bounds.Min; this.Max = bounds.Max; this.Cull = Frustum.CullResult.TotallyInside; }
/// <summary> /// Set up an interior node. /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <param name="bounds"></param> public Node(Int16 left, Int16 right, AABB bounds) { this.Left = left; this.Right = right; this.Batch = -1; this.Min = bounds.Min; this.Max = bounds.Max; this.Cull = Frustum.CullResult.TotallyInside; }
/// <summary> /// Render this section of road/wall /// </summary> /// <param name="camera"></param> public void Render(Camera camera) { if (RenderObj != null) { Frustum.CullResult cull = camera.Frustum.CullTest(Sphere); if (cull != Frustum.CullResult.TotallyOutside) { RenderObj.Render(camera, Road); } } }
/// <summary> /// Render this intersection of road/wall /// </summary> /// <param name="camera"></param> public void Render(Camera camera) { Frustum.CullResult cull = camera.Frustum.CullTest(Sphere); if (cull != Frustum.CullResult.TotallyOutside) { foreach (RenderObj fan in Fans) { fan.Render(camera, Road); } } }
/// <summary> /// Test for visibility, return true if VISIBLE and cache result. /// </summary> /// <param name="camera"></param> /// <returns></returns> public bool CullTest(Camera camera) { Water water = Water.FromLabel(Label); if (water != null) { Bounds.MaxZ = water.BaseHeight; Frustum.CullResult cull = camera.Frustum.CullTest(Bounds); Culled = (cull == Frustum.CullResult.TotallyOutside); return(true); } return(false); }
} // end of RenderObj c'tor public override void Render(Camera camera) { if (!parent.Visible) { return; } Frustum.CullResult cull = camera.Frustum.CullTest(parent.BoundingSphere.Center + parent.Movement.Position, parent.BoundingSphere.Radius); if (cull != Frustum.CullResult.TotallyOutside) { sro.PreRender = parent.PreRender; sro.Animators = parent.Animators; sro.RenderColor = parent.Classification.ColorRGBA; GameActor parentActor = parent as GameActor; if (parentActor != null) { sro.GlowEmissiveColor = parentActor.GlowEmissivity * parentActor.GlowColor; } Matrix local = Matrix.CreateScale(parent.ReScale) * parent.Movement.LocalMatrix; // Add in squashed transform if appropriate. if (parentActor.SquashScale != Vector3.One) { if (parent.CurrentState == GameThing.State.Squashed && false) { // Translate down to the ground. // TODO (****) should probably make this happen smoothly. float terrainHeight = Boku.SimWorld.Terra.Terrain.GetHeight(parent.Movement.Position); if (terrainHeight > 0) { Vector3 translation = local.Translation; translation.Z = terrainHeight; local.Translation = translation; parent.Movement.Altitude = translation.Z; } } local = Matrix.CreateScale(parentActor.SquashScale) * local; } sro.Render(camera, ref local, null); sro.PreRender = null; parent.DebugDisplay(camera); } }
public void Render(Camera camera, Road road) { if (Model != null) { BoundingSphere bound = Model.BoundingSphere; bound.Center = Vector3.Transform(bound.Center, localToWorld); Frustum.CullResult cull = camera.Frustum.CullTest(bound); if (cull != Frustum.CullResult.TotallyOutside) { if (Animator != null) { Animator.SetupActive(Model); } Model.DiffuseColor = road.Path.RGBColor; Model.Render(camera, ref localToWorld, null); } } }
} // end of HandleTouchInput() public override void Render(Camera camera) { // Don't bother if offscreen. Vector3 position = worldMatrix.Translation; float radius = 2.0f; Frustum.CullResult cullResult = camera.Frustum.CullTest(position, radius); if (cullResult == Frustum.CullResult.TotallyOutside) { return; } GraphicsDevice device = BokuGame.bokuGame.GraphicsDevice; Terrain terrain = BokuGame.bokuGame.inGame.Terrain; int materialIndex = Terrain.UISlotToMatIndex(uiSlot); Effect effect = terrain.EffectColor; EffectTechnique technique = TerrainMaterial.Get(materialIndex).TechniqueColor(TerrainMaterial.EffectTechs.TerrainColorPass); effect.CurrentTechnique = technique; // We need to push the near plane out more than is normal for UI // so that the material cubes don't render behind the terrain. Camera cam = camera; cam.NearClip = 6.0f; Matrix twistMatrix = Matrix.CreateRotationZ(worldMatrix.Translation.X + 0.5f); float scale = MathHelper.Clamp(2.0f / ((float)Math.Abs(worldMatrix.Translation.X) + 1.0f), 0.1f, 1.0f); Matrix scaleMatrix = Matrix.CreateScale(scale); Matrix world = twistMatrix * scaleMatrix * worldMatrix; Vector3 trans = world.Translation; trans.X *= scale + 0.5f; world.Translation = trans; hitWorld = world; // Compensate for verts being [0..1] instead of [-0.5..0.5] as we'd prefer. // They must be in [0..1] because UV mapping is implicit in local position. Matrix preTrans = Matrix.Identity; preTrans.Translation = new Vector3(-0.5f, -0.5f, -0.5f); world = preTrans * world; Matrix worldViewProjMatrix = world * cam.ViewProjectionMatrix; terrain.ParameterColor(Terrain.EffectParams.WorldMatrix).SetValue(worldMatrix); terrain.ParameterColor(Terrain.EffectParams.WorldViewProjMatrix).SetValue(worldViewProjMatrix); terrain.ParameterColor(Terrain.EffectParams.WarpCenter).SetValue(Vector4.Zero); terrain.ParameterEdit(Terrain.EffectParams.WorldMatrix).SetValue(worldMatrix); terrain.ParameterEdit(Terrain.EffectParams.WorldViewProjMatrix).SetValue(worldViewProjMatrix); terrain.ParameterEdit(Terrain.EffectParams.WarpCenter).SetValue(Vector4.Zero); #if NETFX_CORE // Note: Indexing into shaders doesn't work with MG. Apparently it // was some hack done in XNA related to the Effect code they used. // Anyway, instead of using this indexing we need to pick and set // the right technique which we do further down from here. #else if (BokuSettings.Settings.PreferReach) { //Select the VS based on the number of point-lights var lightNum = Boku.Fx.Luz.Count; if (lightNum > 6) { terrain.ParameterColor(Terrain.EffectParams.VSIndex).SetValue(4); terrain.ParameterEdit(Terrain.EffectParams.VSIndex).SetValue(4); } else if (lightNum > 4) { terrain.ParameterColor(Terrain.EffectParams.VSIndex).SetValue(3); terrain.ParameterEdit(Terrain.EffectParams.VSIndex).SetValue(3); } else if (lightNum > 2) { terrain.ParameterColor(Terrain.EffectParams.VSIndex).SetValue(2); terrain.ParameterEdit(Terrain.EffectParams.VSIndex).SetValue(2); } else if (lightNum > 0) { terrain.ParameterColor(Terrain.EffectParams.VSIndex).SetValue(1); terrain.ParameterEdit(Terrain.EffectParams.VSIndex).SetValue(1); } else { terrain.ParameterColor(Terrain.EffectParams.VSIndex).SetValue(0); terrain.ParameterEdit(Terrain.EffectParams.VSIndex).SetValue(0); } //Select the PS terrain.ParameterColor(Terrain.EffectParams.PSIndex).SetValue(0); terrain.ParameterEdit(Terrain.EffectParams.PSIndex).SetValue(0); } else // Shader Model v3 { //SM3 only uses one VS terrain.ParameterColor(Terrain.EffectParams.VSIndex).SetValue(5); terrain.ParameterEdit(Terrain.EffectParams.VSIndex).SetValue(5); //Select the PS terrain.ParameterColor(Terrain.EffectParams.PSIndex).SetValue(2); terrain.ParameterEdit(Terrain.EffectParams.PSIndex).SetValue(2); } #endif if (MaterialPicker.FabricMode) { var cubeSize = 1f; terrain.ParameterColor(Terrain.EffectParams.InvCubeSize).SetValue(new Vector3(cubeSize, 1.0f / cubeSize, cubeSize * 0.5f)); terrain.ParameterEdit(Terrain.EffectParams.InvCubeSize).SetValue(new Vector3(cubeSize, 1.0f / cubeSize, cubeSize * 0.5f)); #region Fabric device.SetVertexBuffer(vBuff_FA); device.Indices = iBuff_FA; terrain.SetGlobalParams_FA(); terrain.SetMaterialParams_FA((ushort)materialIndex, true); TerrainMaterial mat = TerrainMaterial.Get(materialIndex); #if NETFX_CORE int lightNum = Boku.Fx.Luz.Count; if (lightNum > 6) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L10_FA_SM2"]; } else if (lightNum > 4) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L6_FA_SM2"]; } else if (lightNum > 2) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L4_FA_SM2"]; } else if (lightNum > 0) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L2_FA_SM2"]; } else { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L0_FA_SM2"]; } #else effect.CurrentTechnique = mat.TechniqueColor(TerrainMaterial.EffectTechs_FA.TerrainColorPass_FA); #endif foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 37, 0, 56); } #endregion } else { #region Cube var cubeSize = 1.0f; terrain.ParameterColor(Terrain.EffectParams.InvCubeSize).SetValue(new Vector3(cubeSize, 1.0f / cubeSize, cubeSize * 0.5f)); terrain.ParameterEdit(Terrain.EffectParams.InvCubeSize).SetValue(new Vector3(cubeSize, 1.0f / cubeSize, cubeSize * 0.5f)); Tile.CheckIndices(new Point(32, 32)); if (Terrain.RenderMethod == Terrain.RenderMethods.FewerDraws) { device.SetVertexBuffer(vBuff); device.Indices = Tile.IndexBuffer_FD(); terrain.SetGlobalParams_FD(); terrain.SetMaterialParams_FD((ushort)materialIndex, true); } TerrainMaterial mat = TerrainMaterial.Get(materialIndex); #if NETFX_CORE int lightNum = Boku.Fx.Luz.Count; if (lightNum > 6) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L10_FD_SM2"]; } else if (lightNum > 4) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L6_FD_SM2"]; } else if (lightNum > 2) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L4_FD_SM2"]; } else if (lightNum > 0) { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L2_FD_SM2"]; } else { effect.CurrentTechnique = effect.Techniques["TerrainColorPass_L0_FD_SM2"]; } #else effect.CurrentTechnique = mat.TechniqueColor(TerrainMaterial.EffectTechs.TerrainColorPass); #endif foreach (EffectPass pass in effect.CurrentTechnique.Passes) { if (Terrain.RenderMethod == Terrain.RenderMethods.FewerDraws) { terrain.SetTopParams_FD((ushort)materialIndex, true); pass.Apply(); device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2); //20 verts, 10 triangles terrain.SetSideParams_FD((ushort)materialIndex, true); pass.Apply(); device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 4, 0, 16, 0, 8); //20 verts, 10 triangles } } #endregion } RenderLabel(camera, world); } // end of UIGridMaterialElement Render()
} // end of Render() // // // TODO (****) Need to verify that these all work! // // /// <summary> /// Test rectangle to see if it is visible in the camera. /// Note this is camera units (zoomed) not raw pixels. /// </summary> /// <param name="rect"></param> /// <returns></returns> public Frustum.CullResult CullTest(RectangleF rect) { // Get rect vertices. Vector2[] p = new Vector2[4]; p[0] = rect.Position; p[1] = p[0] + new Vector2(rect.Size.X, 0); p[2] = p[0] + new Vector2(0, rect.Size.Y); p[3] = p[0] + new Vector2(rect.Size.X, rect.Size.Y); // Transform into homogeneous coords. for (int i = 0; i < 4; i++) { p[i] = Vector2.Transform(p[i], ViewProjMatrix); } // Most common case is that all points are inside so look at that one first. Frustum.CullResult result = Frustum.CullResult.TotallyInside; for (int i = 0; i < 4; i++) { // Check if point is outside. if (p[i].X < -1 || p[i].X > 1 || p[i].Y < -1 || p[i].Y > 1) { // Found one that's outside, break; result = Frustum.CullResult.PartiallyInside; break; } } // All points are inside so we're done. if (result == Frustum.CullResult.TotallyInside) { return(result); } // See if all points are fully outside the same side of the screen. if (p[0].X < -1 && p[1].X < -1 && p[2].X < -1 && p[3].X < -1) { return(Frustum.CullResult.TotallyOutside); } if (p[0].X > 1 && p[1].X > 1 && p[2].X > 1 && p[3].X > 1) { return(Frustum.CullResult.TotallyOutside); } if (p[0].Y < -1 && p[1].Y < -1 && p[2].Y < -1 && p[3].Y < -1) { return(Frustum.CullResult.TotallyOutside); } if (p[0].Y > 1 && p[1].Y > 1 && p[2].Y > 1 && p[3].Y > 1) { return(Frustum.CullResult.TotallyOutside); } // TODO (****) Note this is not full correct. You can still have a // rect which is oriented diagonally with all its vertices off screen // but overlapping a corner of the screen. A proper, general solution // would probably be to do the fully inside test and then test each // line segment of the rect vs each edge of the screen. Bit too much // overkill for now. return(result); }