Click here to Skip to main content
15,897,968 members
Articles / Desktop Programming / WPF

D3dScenePresenter - How to present and manipulate a 3D scene using MDX

Rate me:
Please Sign up or sign in to vote.
4.57/5 (14 votes)
15 Feb 2013CPOL14 min read 45.4K   1.6K   25  
This article shows how we can present a 3D scene and, perform common operations (zoom, rotate, move, zoom to specific region, adjust the camera to view the whole of the scene, and pick a 3D shape on a specific region on the rendered surface) on it, using Managed DirectX.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using MdxScene.Cameras;
using MdxScene.Lights;
using MdxScene.Shapes;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Threading.Tasks;

namespace MdxScene
{
    public class D3dScene
    {
        public D3dScene()
        {
            ClearColor = Color.Black;
            RenderShapesParallelly = false;
        }

        public virtual void Render(Device d3dDevice)
        {
            if (null == d3dDevice)
            {
                return;
            }

            InitDevice(d3dDevice);

            d3dDevice.BeginScene();

            // Render camera
            RenderCamera(d3dDevice);

            // Render lights
            RenderLights(d3dDevice);

            // Render shapes
            RenderShapes(d3dDevice);

            d3dDevice.EndScene();
        }

        protected virtual void InitDevice(Device d3dDevice)
        {
            d3dDevice.RenderState.ZBufferEnable = true;
            d3dDevice.RenderState.Lighting = Lights.Count > 0;
            d3dDevice.RenderState.CullMode = Camera.CoordinateSystem == CameraCoordinateSystem.RightHanded
                                                 ? Cull.Clockwise
                                                 : Cull.CounterClockwise;
            d3dDevice.RenderState.NormalizeNormals = true;
            d3dDevice.RenderState.DiffuseMaterialSource = ColorSource.Material;

            d3dDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, ClearColor, 1.0f, 0);
        }

        protected virtual void RenderCamera(Device d3dDevice)
        {
            Camera.Render(d3dDevice);
        }

        protected virtual void RenderLights(Device d3dDevice)
        {
            // Disable old scene lights.
            int lightsCount = d3dDevice.Lights.Count;
            for (int oldLightInx=0; oldLightInx<lightsCount; oldLightInx++)
            {
                d3dDevice.Lights[oldLightInx].Enabled = false;
            }

            // Render the enabled scene lights.
            int lightIndex = 0;
            foreach (D3dLight light in Lights)
            {
                if (null != light && light.Enabled)
                {
                    light.Index = lightIndex;
                    light.Render(d3dDevice);
                    lightIndex++;
                }
            }            
        }

        protected virtual void RenderShapes(Device d3dDevice)
        {
            ShapeEnvironmentData environmentData = GetShapeEnvironmentData();

            if (RenderShapesParallelly)
            {
                Parallel.ForEach(Shapes, s => RenderShape(s, environmentData, d3dDevice));
            }
            else
            {
                Shapes.ForEach(s => RenderShape(s, environmentData, d3dDevice));
            }
        }

        protected virtual ShapeEnvironmentData GetShapeEnvironmentData()
        {
            return new ShapeEnvironmentData
                       {
                           ViewMatrix = Camera.ViewMatrix,
                           ProjectionMatrix = Camera.ProjectionMatrix
                       };
        }

        protected virtual void RenderShape(D3dShape shape, ShapeEnvironmentData environmentData, Device d3dDevice)
        {
            if (null == shape)
            {
                return;
            }

            shape.RenderAlsoIfOutOfView = RenderShapesAlsoIfOutOfView;
            shape.EnvironmentData = environmentData;
            shape.Render(d3dDevice);
        }

        public D3dShape Pick(Device d3dDevice, float surfaceX, float surfaceY)
        {
            LastPickIntersections = GetPointIntersections(d3dDevice, surfaceX, surfaceY);

            // Get the closest shape.
            D3dShape res = GetLastPickClosestShape();

            // Get the pickable parent of the shape.
            while (null != res && !res.IsPickable)
            {
                res = res.Parent;
            }

            return res;
        }

        public List<IntersectResult> GetPointIntersections(Device d3dDevice, float surfaceX, float surfaceY)
        {
            Vector3 nearPlanePoint = new Vector3(surfaceX, surfaceY, 0);
            Vector3 farPlanePoint = new Vector3(surfaceX, surfaceY, 1);

            nearPlanePoint.Unproject(d3dDevice.Viewport, d3dDevice.Transform.Projection, d3dDevice.Transform.View,
                                     Matrix.Identity);
            farPlanePoint.Unproject(d3dDevice.Viewport, d3dDevice.Transform.Projection, d3dDevice.Transform.View,
                          Matrix.Identity);

            Vector3 rayDirection = Vector3.Subtract(farPlanePoint, nearPlanePoint);

            List<IntersectResult> res = new List<IntersectResult>();

            Parallel.ForEach(Shapes, shape => shape.AddRayIntersections(nearPlanePoint, rayDirection, res));

            return res;
        }

        public D3dShape GetLastPickClosestShape()
        {
            return LastPickIntersections.OrderBy(ir => ir.TriangleIntersections.Min(ti => ti.Dist)).
                Select(ir => ir.Shape).FirstOrDefault();
        }

        public void AdjustCameraView(bool keepAspectRatio = true)
        {
            Camera.AdjustView(Shapes.Where(s => s.IsVisible), keepAspectRatio);
        }

        #region Properties

        #region Camera
        private D3dCamera _camera;
        public D3dCamera Camera 
        {
            get { return _camera ?? (_camera = new D3dPerspectiveFovCamera()); }
            set { _camera = value; }
        }
        #endregion

        #region Lights
        private List<D3dLight> _lights;
        public List<D3dLight> Lights
        {
            get { return _lights ?? (_lights = new List<D3dLight>()); }
        }
        #endregion

        #region Shapes
        private List<D3dShape> _shapes;
        public List<D3dShape> Shapes
        {
            get { return _shapes ?? (_shapes = new List<D3dShape>()); }
        }
        #endregion

        public Color ClearColor { get; set; }

        #region LastPickIntersections
        private List<IntersectResult> _lastPickIntersections;
        public List<IntersectResult> LastPickIntersections
        {
            get { return _lastPickIntersections ?? (_lastPickIntersections = new List<IntersectResult>()); }
            protected set { _lastPickIntersections = value; }
        }
        #endregion

        public bool RenderShapesParallelly { get; set; }
        public bool RenderShapesAlsoIfOutOfView { get; set; }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions