[PloobsEngine] Tutorial 4 – Lights and Cameras


 

This tutorial will teach you about the PloobsEngine Lights and Camera System. (tutorials series here)

It is very important to say that in this version the Light system is exclusive of the Deferred Rendering technich. We implemented lights for Forward Render also, but it is not included in the PLoobsEngine (performance issues >.<), we plan in the near future to make a tutorial about Phong Shading using that code as example.

Camera System

The PloobsEngine camera system is quite simple and intuitive. In few words: we have an interface called ICamera where all cameras extends. The engine provides some build in implementation for convenience but everyone can make yours as needed. All IScenes must have at least one camera active. You add, remove, interpolate, change …  cameras using the IWorld property called CameraManger.

Avaliable build-in Cameras:

  • CameraFirstPerson (classic FPS camera without restriction, use mouse and keyboard WASD QZ to controls it)
  • CameraFollowObject (camera that follows a IObject)
  • CameraFollowPath (Camera that follows a previous recorded path)
  • CameraStatic (Camera that does not change its location, normally used as base camera when building others (instead of extending from ICamera, sometimes is easier to extend from CameraStatic))

You can have more than one camera added to the current IWorld’s CameraManager, but only one is active at time (=P)

The following example shows how to add more than one camera in the IWorld and smoothly navigate between them: (this part is very intutive, so we wont bother you with obvious comments)

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using PloobsEngine;
using PloobsEngine.Cameras;
using PloobsEngine.Commands;
using PloobsEngine.DataStructure;
using PloobsEngine.Input;
using PloobsEngine.Light;
using PloobsEngine.Material;
using PloobsEngine.Modelo;
using PloobsEngine.Physics;
using PloobsEngine.Physics.Bepu;
using PloobsEngine.SceneControl;
using PloobsEngine.Utils;
using PloobsEngine.Engine;

namespace IntroductionDemo4._0
{
    ///
    /// Camera Screen
    ///
    public class CameraScreens : IScene
    {
        BindKeyCommand bk;

        ///
        /// Circular list that holds the cameras (Circular list is a PloobsEngine build in data structure, we will talk about all of them in other tutorial)
        ///
        private CircularList camerasNames = new CircularList(3);

        protected override void SetWorldAndRenderTechnich(out IRenderTechnic renderTech, out IWorld world)
        {
            world = new IWorld(new BepuPhysicWorld(-9.8f, true), new SimpleCuller());

            DeferredRenderTechnicInitDescription desc = DeferredRenderTechnicInitDescription.Default();
            desc.UseFloatingBufferForLightMap = true;
            desc.BackGroundColor = Color.CornflowerBlue;
            renderTech = new DeferredRenderTechnic(desc);
        }

        protected override void LoadContent(PloobsEngine.Engine.GraphicInfo GraphicInfo, PloobsEngine.Engine.GraphicFactory factory, IContentManager contentManager)
        {
            base.LoadContent(GraphicInfo, factory, contentManager);                        

            #region Models

            ///Cria uma textura 1x1 com a cor branca
            Texture2D white = factory.CreateTexture2DColor(1,1, Color.White);
            {
                SimpleModel sm = new SimpleModel(factory, "..\\Content\\Model\\cubo");
                sm.SetTexture(white, TextureType.DIFFUSE);

                BoxObject pi = new BoxObject(new Vector3(100, 20, 0), 1,1,1, 5,new Vector3(100, 5, 100),Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
                DeferredNormalShader shader = new DeferredNormalShader();
                ///Setando alguns parametros do material
                shader.SpecularIntensity = 0.01f;
                shader.SpecularPower = 50;
                IMaterial mat = new DeferredMaterial(shader);
                IObject obj3 = new IObject(mat, sm, pi);
                this.World.AddObject(obj3);
            }

            {
                SimpleModel sm = new SimpleModel(factory,"..\\Content\\Model\\cubo");
                sm.SetTexture(white, TextureType.DIFFUSE);

                BoxObject pi = new BoxObject(new Vector3(90, 30, 0), 1,1,1, 10,new Vector3(1),Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
                DeferredNormalShader shader = new DeferredNormalShader();                

                IMaterial mat = new DeferredMaterial(shader);
                IObject obj3 = new IObject(mat, sm, pi);
                this.World.AddObject(obj3);
            }

            {
                SimpleModel sm = new SimpleModel(factory,"..\\Content\\Model\\cenario");
                IPhysicObject pi = new TriangleMeshObject(sm,Vector3.Zero,Matrix.Identity,Vector3.One,MaterialDescription.DefaultBepuMaterial());
                DeferredNormalShader shader = new DeferredNormalShader();
                IMaterial mat = new DeferredMaterial(shader);
                IObject obj3 = new IObject(mat, sm, pi);
                this.World.AddObject(obj3);
            }

            #endregion                                    

            #region NormalLight
            ///Conjunto de luzes direcionais
            DirectionalLightPE ld1 = new DirectionalLightPE(Vector3.Left, Color.White);
            DirectionalLightPE ld2 = new DirectionalLightPE(Vector3.Right, Color.White);
            DirectionalLightPE ld3 = new DirectionalLightPE(Vector3.Backward, Color.White);
            DirectionalLightPE ld4 = new DirectionalLightPE(Vector3.Forward, Color.White);
            DirectionalLightPE ld5 = new DirectionalLightPE(Vector3.Down, Color.White);
            float li = 0.4f;
            ld1.LightIntensity = li;
            ld2.LightIntensity = li;
            ld3.LightIntensity = li;
            ld4.LightIntensity = li;
            ld5.LightIntensity = li;
            this.World.AddLight(ld1);
            this.World.AddLight(ld2);
            this.World.AddLight(ld3);
            this.World.AddLight(ld4);
            this.World.AddLight(ld5);
            #endregion

            ///Creating the first Static Camera
            CameraStatic camx = new CameraStatic(new Vector3(130, 100, 700), Vector3.Zero);
            ///Naming it (To recover later)
            camx.Name = "default";
            camx.FarPlane = 3000;
            ///Adding to the manager (ITS NOT BEING ACTIVATED, JUST ADDED)
            this.World.CameraManager.AddCamera(camx, camx.Name);
            ///NOW ACTIVATING=P (If we add a camera using World.AddCamera(cam) we automatically set this camera as active)
            this.World.CameraManager.SetActiveCamera(camx.Name);
            ///Add to the circular list
            camerasNames.Value = camx.Name;
            camerasNames.Next();          

            ///Same for the second camera (this is not activated)
            CameraStatic cam2 = new CameraStatic(new Vector3(100, 100, 100), Vector3.Zero);
            cam2.Name = "StaticCamera";
            cam2.FarPlane = 3000;
            this.World.CameraManager.AddCamera(cam2, cam2.Name);
            camerasNames.Value = cam2.Name;
            camerasNames.Next();

            ///Again ...
            CameraStatic cam3 = new CameraStatic(new Vector3(500, 300, 300), Vector3.Zero);
            cam3.Name = "StaticCamera3";
            cam3.FarPlane = 3000;
            this.World.CameraManager.AddCamera(cam3, cam3.Name);
            camerasNames.Value = cam3.Name;
            camerasNames.Next();
            ///When pressing SPACE, a function will be called, WE COULD use the screen binding (but we did not ... no specific reason ....)
            SimpleConcreteKeyboardInputPlayable ikp = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.Space, KeyStateChange);
            bk = new BindKeyCommand(ikp, BindAction.ADD);
            CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bk);
        }

        protected override void Draw(GameTime gameTime, RenderHelper render)
        {
            base.Draw(gameTime, render);
            render.RenderTextComplete("Demo: Preset Cameras", new Vector2(GraphicInfo.Viewport.Width - 315, 15), Color.White,Matrix.Identity);
            render.RenderTextComplete("Space = Move to next camera", new Vector2(GraphicInfo.Viewport.Width - 315, 40), Color.White, Matrix.Identity);
        }

        ///
        /// When Escape is pressed
        ///
        ///
        void KeyStateChange(InputPlayableKeyBoard ipk)
        {
            ///TO CHANGE ONLY IN THE END OF THE INTERPOLATION
            //if (mundo.CameraManager.ActiveCameraType != State.INTERPOLATING)
            //{
                //camerasNames.Next();
                //mundo.CameraManager.SetActiveCamera(camerasNames.Value,InterpolationType.BYSTEP, 0.005f);
                //mundo.CameraManager.SetActiveCamera(camerasNames.Value, InterpolationType.BYTIME, 3);
            //}

            ///Advance on position in the circular list
            camerasNames.Next();
            ///Activate the current camera in the circular list
            ///There are two types of interpolator (take care of the camera transition smoothly). The first takes a fixed time (BYTIME( to make the transition and the second use a fixed speed (BYSTEP))
            this.World.CameraManager.SetActiveCamera(camerasNames.Value, InterpolationType.BYTIME, 3);
        }

        protected override void CleanUp(EngineStuff engine )
        {
            bk.BindAction = BindAction.REMOVE;
            CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bk);
        }

    }
}

Pretty easy =P In the IntroductionDemos you can find this sample and another one showing how to record a path and play it after.

Lights

As said in the beggining, Lights are only avaliable if you choose the DeferredRenderTechnich. We have the following Light Types avaliable: (we wont explain what each light is, good reference for this are: http://robertokoci.com/basics-of-light-in-3d-computer-graphics/ and in this marvelous book http://www.realtimerendering.com/book.html)

  • DirectionalLightPE
  • PointLightPE
  • SpotLightPE

The silly name DirectionalLightPE is used because in XNA 4.0, the xna already has a class called DirectionalLight, so to avoid confusion we changed the name (PE = Ploobs Engine =P)

We use Phong Equation to calculate each light contribuition. This blog post from Catalin Zilma shows some implementation details (if you want to play with any deferred rending light code, first look at that post) , we used a slightly diferent aproach.

The Directional Light and the SpotLight can cast Shadow (we will talk about it in the future =P).

You can create your own lights, but it is not simple, you need to extend ILight,extend IDeferredLightMap and create a shader that procces you light and create the light map for it. (we will talk about this in other tutorial).

The Introduction packages has some samples explaining how to add, remove and customize the behavior of the lights. We wont show them here cause this is very straightforward, instead we will show a simple but very interesting sample that integrates lights and physics.

Ball Throw Example

We will build a sample with the following behavior:

  • When left mouse button is clicked, a ball with a point light attached to it will be throw in the direction of the camera target vector
  • When right mouse button is clicked, a point light is placed in the current cameras position

This sample with show lots of things we already seen in last tutorials.

To begin, we need to extend the Point Light, cause we need to update its position every frame according to the Ball. The implementation is show bellow:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine.Light;
using PloobsEngine.Physics;

namespace AdvancedDemo4._0
{
    ///
    /// Point light that follow an object
    ///
    public class MoveablePointLight : PointLightPE
    {

        IPhysicObject ob;

        public MoveablePointLight(IPhysicObject obj, Color color, float lightRadius, float lightIntensity)
            : base(obj.Position, color, lightRadius, lightIntensity)
        {
            this.ob = obj;
        }

        public override Vector3 LightPosition
        {
            get
            {
                return ob.Position;
            }
            set
            {
                ob.Position = value;
            }
        }

    }
}

We just overrided the LightPosition property, return the object position instead of the light static position.

The following code shows how to bind the mouse buttons, create the ball and the point light.

using System;
using PloobsEngine.SceneControl;
using PloobsEngine;
using PloobsEngine.Input;
using Microsoft.Xna.Framework;
using PloobsEngine.Physics.Bepu;
using PloobsEngine.Modelo;
using PloobsEngine.Material;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine.Utils;
using PloobsEngine.Commands;
using Microsoft.Xna.Framework.Input;
using PloobsEngine.Light;
using PloobsEngine.Engine;
using PloobsEngine.Physics;

namespace EngineTestes
{
    public class LightThrowBepu
    {
        IWorld _mundo;
        Random rd = new Random();
        BindMouseCommand mm0 = null;
        BindMouseCommand mm1 = null;
        GraphicFactory factory;

        public void CleanUp()
        {
            mm0.BindAction = BindAction.REMOVE;
            CommandProcessor.getCommandProcessor().SendCommandAssyncronous(mm0);

            mm1.BindAction = BindAction.REMOVE;
            CommandProcessor.getCommandProcessor().SendCommandAssyncronous(mm1);
        }        

        public LightThrowBepu(IWorld mundo,GraphicFactory factory)
        {
            this.factory = factory;
            _mundo = mundo;
            {
                ///Register a function to be called when the the mouse is pressed
                InputPlaybleMouseBottom ip1 = new SimpleConcreteMouseBottomInputPlayable(StateKey.PRESS, EntityType.IOBJECT, MouseButtons.LeftButton, mousebuttonteste);
                mm0 = new BindMouseCommand(ip1, BindAction.ADD);
                CommandProcessor.getCommandProcessor().SendCommandAssyncronous(mm0);
            }
            {
                InputPlaybleMouseBottom ip = new SimpleConcreteMouseBottomInputPlayable(StateKey.PRESS, EntityType.IOBJECT, MouseButtons.RightButton, mousebuttontesteRight);
                mm1 = new BindMouseCommand(ip, BindAction.ADD);
                CommandProcessor.getCommandProcessor().SendCommandAssyncronous(mm1);
            }

        }

        public void mousebuttontesteRight(MouseState ms)
        {
            PointLightPE pl = new PointLightPE(_mundo.CameraManager.ActiveCamera.Position, StaticRandom.RandomColor(), 100, 5);
            pl.UsePointLightQuadraticAttenuation = true;
            _mundo.AddLight(pl);
        }

        int i = 0;
        public void mousebuttonteste(MouseState ms)
        {
            ///Create an object
            IObject physObj = SpawnPrimitive(_mundo.CameraManager.ActiveCamera.Position, Matrix.CreateRotationX(0.5f));
            physObj.PhysicObject.Velocity = (_mundo.CameraManager.ActiveCamera.Target - _mundo.CameraManager.ActiveCamera.Position) * 15.0f;

            ///Create a light that follow an object
            MoveablePointLight mvp = new MoveablePointLight(physObj.PhysicObject as SphereObject, new Color((float)rd.NextDouble(),(float) rd.NextDouble(),(float) rd.NextDouble()),25, 5);
            mvp.UsePointLightQuadraticAttenuation = true;
            ///Add them to the world
            _mundo.AddLight(mvp);
            physObj.Name = "FlyingBall " + ++i;
            _mundo.AddObject(physObj);

        }
        ///
        /// Create a simple Sphere object
        ///
        ///

        ///

        ///
        private IObject SpawnPrimitive(Vector3 pos, Matrix ori)
        {
            ///Load a Model with a custom texture
            SimpleModel sm2 = new SimpleModel(factory,"Model\\ball");
            sm2.SetTexture(factory.CreateTexture2DColor(1,1,Color.White,false), TextureType.DIFFUSE);
            DeferredNormalShader nd = new DeferredNormalShader();
            IMaterial m = new DeferredMaterial(nd);
            SphereObject  pi2 = new SphereObject(pos, 1,0.5f,1,MaterialDescription.DefaultBepuMaterial());
            IObject o = new IObject(m,sm2,pi2);
            return o;
        }

    }
}

Nothing new here =P, just the same stuff we learned from previous tutorials.

The code for this demo can be found in our Introduction Demos package (called LightThrowBepu.cs and MoveablePointLight.cs), you can download it here. (there are lots of others demos in this package from the previous tutorials =P).

Any doubts, critics, suggestions, pls go to our forum or leave a comment here.

See you guys =P

Links

 

, , , , , ,

  1. #1 by direktori bisnis indonesia on 29 de agosto de 2016 - 4:13 am

    hello!,I like your writing so a lot! share we keep up a correspondence extra
    approximately your post on AOL? I need a specialist in this area
    to resolve my problem. Maybe that’s you! Taking a look ahead
    to look you.

  2. #2 by offers on 29 de agosto de 2016 - 4:16 am

    I was recommended this web site by my cousin. I am not sure whether this post is written by him as nobody else know
    such detailed about my trouble. You’re wonderful! Thanks!

  3. #3 by male enhancement reviews on 29 de agosto de 2016 - 4:24 am

    There is evidently a lot to realize about this.
    I believe you made certain nice points in features also.

  4. #4 by coaching on 29 de agosto de 2016 - 4:26 am

    Your style is very unique compared to other people I’ve read
    stuff from. Thanks for posting when you have the opportunity, Guess I’ll just book mark this page.

  5. #5 by r4i gold 3ds on 29 de agosto de 2016 - 4:28 am

    Thanks for the post. Keep writing.

  6. #6 by Mincraft on 29 de agosto de 2016 - 4:39 am

    I was suggested this web site by way of my cousin. I am no
    longer certain whether this post is written by him as nobody else realize such unique approximately my problem.
    You’re amazing! Thank you!

  7. #7 by Fortress legends for pc on 29 de agosto de 2016 - 4:41 am

    Great post. I’m facing some of these issues as well..

  8. #8 by pnl ejercicios on 29 de agosto de 2016 - 4:48 am

    Hello there! I could have sworn I’ve been to this website before but after reading through some of the post I realized it’s new to me.
    Nonetheless, I’m definitely glad I found it and I’ll be bookmarking and checking back often!

  9. #9 by ley de atraccion on 29 de agosto de 2016 - 4:48 am

    Greetings! This is my 1st comment here so I just
    wanted to give a quick shout out and say I really enjoy reading
    through your blog posts. Can you recommend any other blogs/websites/forums that cover the same topics?
    Thank you so much!

  10. #10 by leijalautailu on 29 de agosto de 2016 - 5:10 am

    Keep on writing, great job!

  11. #11 by Mincraft on 29 de agosto de 2016 - 5:20 am

    Very nice post. I simply stumbled upon your weblog and wished to say that I’ve really enjoyed browsing
    your blog posts. After all I will be subscribing in your rss feed and I’m hoping you write again very soon!

  12. #12 by programacion neurolinguistica on 29 de agosto de 2016 - 5:24 am

    Hi! I could have sworn I’ve been to this site before but after browsing through some of
    the post I realized it’s new to me. Anyhow, I’m definitely happy I found
    it and I’ll be bookmarking and checking back frequently!

  13. #13 by toto hk pool on 29 de agosto de 2016 - 5:28 am

    Hey! I’m at work surfing around your blog from my new
    apple iphone! Just wanted to say I love reading your blog and look forward
    to all your posts! Carry on the superb work!

  14. #14 by leijalauta on 29 de agosto de 2016 - 5:43 am

    It’s perfect time to make some plans for the long run and it is time to be happy.
    I have read this publish and if I may just I desire to suggest you
    few attention-grabbing issues or suggestions. Perhaps you could write next articles regarding this
    article. I wish to learn more things about it!

  15. #15 by southerncoloradoonline.com on 29 de agosto de 2016 - 5:45 am

    Starkly marking the variation appropriate diet andd dwelling situations can mke right here is that 19th century English pupils
    at the Sandhurst Armed servbice Acadewmy (upper course) werre on common a whopping 8.seven inches (22
    cm) taller than their compatriots at the Marine Culture boys (lessen course).

  16. #16 by Marta on 29 de agosto de 2016 - 5:47 am

    This article is really a good one it assists new net viewers, who are wishing in favor of blogging.

  17. #17 by http://gas-on.co.kr/ on 29 de agosto de 2016 - 5:50 am

    Farr from isolating us in a digital entire world oof violence and gore, first-human being shkoters can develop a perception of lical community and
    solidarity that some people may be not agle to discover
    in their day-to-working dayy lives—and a perception of usefulness and management that may perhaps, inn transform, spill above into
    non-virtual everyday living.

1 619 620 621
(não será publicado)