Moving the camera with the keyboard

Irrlicht.Net is no longer developed or supported, Irrlicht.Net Cross Platform is a much more complete wrapper. Please ask your C# related questions on their forums first.
Locked
Idunno
Posts: 2
Joined: Tue Aug 22, 2006 9:43 pm

Moving the camera with the keyboard

Post by Idunno »

Using the keyboard to move the camera 'WASD' or the arrow keys. I cant figure out how to do it.

Can someone tell me what Im doing wrong? Or should I just get a job serving up french fries?

Im writing this line which is probably all wrong

Code: Select all

            
if (KeyBoard.KeyState[(int)Keys.Left] || KeyBoard.KeyState[(int)Keys.A])//left arrow key
            {
                camera.Position = new Vector3D(-100, 50, CameraMove = CameraMove + 1);
            }

What should I be writing? Please keep it simple. Im a irrlicht virgin.
When I move the position like this, the camera spins off into nowhereshell.

the complete program sample is this;


Code: Select all

using System;
using System.Collections; 
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Runtime.InteropServices;
using System.Threading; 
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Irrlicht;
using Irrlicht.Video;
using Irrlicht.Core;
using Irrlicht.Scene;

namespace irrlicht14
{
public partial class Form1 : Form
    {            
        public Form1()
        {
            InitializeComponent();    
        }

# region keyboard code

     public class KeyBoard
    {
        public static bool[] KeyState;

        static KeyBoard()
        {
            // Watch for keyboard activity
            KeyboardListener.s_KeyEventHandler += new EventHandler(KeyboardListener_s_KeyEventHandler);
            KeyState = new bool[256];
        }

        public static event KeyPressEventHandler OnKeyChanged;

        static void KeyboardListener_s_KeyEventHandler(object sender, EventArgs e)
        {
            KeyboardListener.UniversalKeyEventArgs eventArgs = (KeyboardListener.UniversalKeyEventArgs)e;
            if (eventArgs.m_Msg == 256)
            {
                KeyBoard.KeyState[eventArgs.m_Key] = true;
                OnKeyChanged(null, null);
            }
            else
            {
                KeyBoard.KeyState[eventArgs.m_Key] = false;
                OnKeyChanged(null, null);
            }
        }

    }


    /// <summary>
    /// The KeyboardListener is a static class that allows registering a number
    /// of event handlers that you want to get called in case some keyboard key is pressed
    /// or released. The nice thing is that this KeyboardListener is also active in case
    /// the parent application is running in the back.
    /// </summary>
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "SkipVerification")]
    public class KeyboardListener
    {
        #region Private declarations

        /// <summary>
        /// The Window that intercepts Keyboard messages
        /// </summary>
        private static ListeningWindow s_Listener;

        #endregion

        #region Private methods
        /// <summary>
        /// The function that will handle all keyboard activity signaled by the ListeningWindow.
        /// In this context handling means calling all registered subscribers for every key pressed / released.
        /// </summary>
        /// <remarks>
        /// Inside this method the events could also be fired by calling
        /// s_KeyEventHandler(null,new KeyEventArgs(key,msg)) However, in case one of the registered
        /// subscribers throws an exception, execution of the non-executed subscribers is cancelled.
        /// </remarks>
        /// <param name="key"></param>
        /// <param name="msg"></param>
        private static void KeyHandler(ushort key, uint msg)
        {
            if (s_KeyEventHandler != null)
            {
                Delegate[] delegates = s_KeyEventHandler.GetInvocationList();

                foreach (Delegate del in delegates)
                {
                    EventHandler sink = (EventHandler)del;

                    try
                    {
                        // This is a static class, therefore null is passed as the object reference
                        sink(null, new UniversalKeyEventArgs(key, msg));
                    }

                    // You can add some meaningful code to this catch block.
                    catch { };
                }
            }
        }
        #endregion

        #region Public declarations

        /// <summary>
        /// An instance of this class is passed when Keyboard events are fired by the KeyboardListener.
        /// </summary>
        public class UniversalKeyEventArgs : KeyEventArgs
        {
            public readonly uint m_Msg;
            public readonly ushort m_Key;

            public UniversalKeyEventArgs(ushort aKey, uint aMsg)
                : base((Keys)aKey)
            {
                m_Msg = aMsg;
                m_Key = aKey;
            }
        }

        /// <summary>
        /// For every application thread that is interested in keyboard events
        /// an EventHandler can be added to this variable
        /// </summary>
        public static event EventHandler s_KeyEventHandler;

        #endregion

        #region Public methods

        static KeyboardListener()
        {
            ListeningWindow.KeyDelegate aKeyDelegate = new ListeningWindow.KeyDelegate(KeyHandler);
            s_Listener = new ListeningWindow(aKeyDelegate);
        }

        #endregion

        #region Definition ListeningWindow class
        /// <summary>
        /// A ListeningWindow object is a Window that intercepts Keyboard events.
        /// </summary>
        private class ListeningWindow : NativeWindow
        {
            #region Declarations
            public delegate void KeyDelegate(ushort key, uint msg);

            private const int
                WS_CLIPCHILDREN = 0x02000000,
                WM_INPUT = 0x00FF,
                RIDEV_INPUTSINK = 0x00000100,
                RID_INPUT = 0x10000003,
                RIM_TYPEKEYBOARD = 1;

            private uint m_PrevMessage = 0;
            private ushort m_PrevControlKey = 0;
            private KeyDelegate m_KeyHandler = null;
            #endregion

            #region Unsafe types
            internal unsafe struct RAWINPUTDEV
            {
                public ushort usUsagePage;
                public ushort usUsage;
                public uint dwFlags;
                public void* hwndTarget;
            };

            internal unsafe struct RAWINPUTHEADER
            {
                public uint dwType;
                public uint dwSize;
                public void* hDevice;
                public void* wParam;
            };

            internal unsafe struct RAWINPUTHKEYBOARD
            {
                public RAWINPUTHEADER header;
                public ushort MakeCode;
                public ushort Flags;
                public ushort Reserved;
                public ushort VKey;
                public uint Message;
                public uint ExtraInformation;

            };
            #endregion

            public ListeningWindow(KeyDelegate keyHandlerFunction)
            {
                m_KeyHandler = keyHandlerFunction;

                CreateParams cp = new CreateParams();

                // Fill in the CreateParams details.
                cp.Caption = "Hidden window";
                cp.ClassName = null;
                cp.X = 0x7FFFFFFF;
                cp.Y = 0x7FFFFFFF;
                cp.Height = 0;
                cp.Width = 0;
                //cp.Parent = parent.Handle;
                cp.Style = WS_CLIPCHILDREN;

                // Create the actual invisible window
                this.CreateHandle(cp);

                // Register for Keyboard notification
                unsafe
                {
                    try
                    {
                        RAWINPUTDEV myRawDevice = new RAWINPUTDEV();
                        myRawDevice.usUsagePage = 0x01;
                        myRawDevice.usUsage = 0x06;
                        myRawDevice.dwFlags = RIDEV_INPUTSINK;
                        myRawDevice.hwndTarget = this.Handle.ToPointer();

                        if (RegisterRawInputDevices(&myRawDevice, 1, (uint)sizeof(RAWINPUTDEV)) == false)
                        {
                            int err = Marshal.GetLastWin32Error();
                            throw new Win32Exception(err, "ListeningWindow::RegisterRawInputDevices");
                        }
                    }

                    catch { throw; }
                }
            }


            #region Private methods
            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_INPUT:
                        {
                            try
                            {
                                unsafe
                                {
                                    uint dwSize, receivedBytes;
                                    uint sizeof_RAWINPUTHEADER = (uint)(sizeof(RAWINPUTHEADER));

                                    // Find out the size of the buffer we have to provide
                                    int res = GetRawInputData(m.LParam.ToPointer(), RID_INPUT, null, &dwSize, sizeof_RAWINPUTHEADER);

                                    if (res == 0)
                                    {
                                        // Allocate a buffer and ...
                                        byte* lpb = stackalloc byte[(int)dwSize];

                                        // ... get the data
                                        receivedBytes = (uint)GetRawInputData((RAWINPUTHKEYBOARD*)(m.LParam.ToPointer()), RID_INPUT, lpb, &dwSize, sizeof_RAWINPUTHEADER);
                                        if (receivedBytes == dwSize)
                                        {
                                            RAWINPUTHKEYBOARD* keybData = (RAWINPUTHKEYBOARD*)lpb;

                                            // Finally, analyze the data
                                            if (keybData->header.dwType == RIM_TYPEKEYBOARD)
                                            {
                                                if ((m_PrevControlKey != keybData->VKey) || (m_PrevMessage != keybData->Message))
                                                {
                                                    m_PrevControlKey = keybData->VKey;
                                                    m_PrevMessage = keybData->Message;

                                                    // Call the delegate in case data satisfies
                                                    m_KeyHandler(keybData->VKey, keybData->Message);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            string errMsg = string.Format("WndProc::GetRawInputData (2) received {0} bytes while expected {1} bytes", receivedBytes, dwSize);
                                            throw new Exception(errMsg);
                                        }
                                    }
                                    else
                                    {
                                        string errMsg = string.Format("WndProc::GetRawInputData (1) returned non zero value ({0})", res);
                                        throw new Exception(errMsg);
                                    }
                                }
                            }

                            catch { throw; }
                        }
                        break;
                }

                // In case you forget this you will run into problems
                base.WndProc(ref m);
            }

            #endregion

            #region Private external methods

            // In case you want to have a comprehensive overview of calling conventions follow the next link:
            // http://www.codeproject.com/cpp/calling_conventions_demystified.asp

            [DllImport("User32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            internal static extern unsafe bool RegisterRawInputDevices(RAWINPUTDEV* rawInputDevices, uint numDevices, uint size);

            [DllImport("User32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.I4)]
            internal static extern unsafe int GetRawInputData(void* hRawInput,
                uint uiCommand,
                byte* pData,
                uint* pcbSize,
                uint cbSizeHeader
                );

            #endregion
        }
    }
        #endregion
# endregion


        private void Main_Load(object sender, EventArgs e) 
        {
        
            this.Show();
            Main_Run(pictureBox1);
        }

        private void Main_Run(Control c)        
        {
            IrrlichtDevice device = new IrrlichtDevice(Irrlicht.Video.DriverType.DIRECT3D9,
                new Dimension2D(c.Width, c.Height), 
                32, false, false, false, true, c.Handle);

       //setup *****************************************************************
            string TextViewer = "Viewer";
            bool ShowFPS = true;
            int CameraMove = -150;
        
 




            //ripped from example 07

            string path = "C:/irrlicht/media/";

            ISceneManager smgr = device.SceneManager;
            IVideoDriver driver = device.VideoDriver;

            device.FileSystem.AddZipFileArchive(path + "map-20kdm2.pk3");

            IAnimatedMesh q3levelmesh = smgr.GetMesh("20kdm2.bsp");
            ISceneNode q3node = null;
            if (q3levelmesh != null)
                q3node = smgr.AddOctTreeSceneNode(q3levelmesh.GetMesh(0), null, 0);

            ITriangleSelector selector = null;
            if (q3node != null)
            {
                q3node.Position = new Vector3D(-1370, -130, -1400);
                selector = smgr.CreateOctTreeTriangleSelector(
                    q3levelmesh.GetMesh(0), q3node, 128);
                // not implemented but not necessary 
                //q3node.TriangleSelector=selector; 
            }

            //ICameraSceneNode camera = smgr.AddCameraSceneNodeFPS(null, 100, 300, 0);
            ICameraSceneNode camera = smgr.AddCameraSceneNode(null, new Vector3D(0, 0, 0), new Vector3D(0, 0, 0), 0);
            camera.Position = new Vector3D(-100, 50, CameraMove);

            ISceneNodeAnimator anim = smgr.CreateCollisionResponseAnimator(
                selector, //specifies how the world, against collision detection is done looks like
                camera,   //object, you
                new Vector3D(30, 50, 30),//how big the object is, it is the radius of an ellipsoid
                new Vector3D(0, -3, 0), //direction and speed of gravity. (0,0,0) will disable it
                new Vector3D(0, 50, 0), //translation: humans have eyes not in the middle of our body so we raise it by 50
                0);
            camera.AddAnimator(anim);

            //device.CursorControl.Visible = false;

            // add billboard for drawing where we found an intersection
            IBillboardSceneNode bill = smgr.AddBillboardSceneNode(
                null, new Dimension2Df(20, 20), new Vector3D(), 0);
            bill.SetMaterialType(MaterialType.TRANSPARENT_ADD_COLOR);
            bill.SetMaterialTexture(0, driver.GetTexture(
                path + "particle.bmp"));
            bill.SetMaterialFlag(MaterialFlag.LIGHTING, false);
            bill.SetMaterialFlag(MaterialFlag.ZBUFFER, false);
            Material material = new Material();
            material.Texture1 = driver.GetTexture(
                path + "faerie2.bmp");
            material.Lighting = true;

            IAnimatedMeshSceneNode node = null;
            IAnimatedMesh faerie = smgr.GetMesh(
                path + "faerie.md2");
            if (faerie != null)
            {
                node = smgr.AddAnimatedMeshSceneNode(faerie, null, 0);
                node.Position = new Vector3D(-70, 0, -90);
                node.SetMD2Animation(MD2AnimationType.RUN);
                node.SetMaterial(0, material);

                node = smgr.AddAnimatedMeshSceneNode(faerie, null, 0);
                node.Position = new Vector3D(-70, 0, -30);
                node.SetMD2Animation(MD2AnimationType.SALUTE);
                node.SetMaterial(0, material);

                node = smgr.AddAnimatedMeshSceneNode(faerie, null, 0);
                node.Position = new Vector3D(-70, 0, -60);
                node.SetMD2Animation(MD2AnimationType.JUMP);
                node.SetMaterial(0, material);
            }

            material.Texture1 = null;
            material.Lighting = false;

            //Add a light 
            smgr.AddLightSceneNode(null, new Vector3D(-60, 100, 400),
                new Colorf(1.0f, 1.0f, 1.0f, 1.0f), 600, 0);

            /*For not making it too complicated, I'm doing picking inside the drawing 
              loop. We take two pointers for storing the current and the last selected 
              scene node and start the loop.*/
            ISceneNode selectedSceneNode = null;
            ISceneNode lastSelectedSceneNode = null; 

            

       //end setup *************************************************************
            
            int fps = -1;
            //while (device.Run() && c.Enabled)
            while (device.Run())
            {
                device.VideoDriver.BeginScene(true, true, new Irrlicht.Video.Color(0, 100, 100, 100));
                device.SceneManager.DrawAll();
                //device.GUIEnvironment.DrawAll();

                //draw a line from the camera out and ask colision manager
                //Does it hit a triange?
                //if yes then we draw the billboard at that position
                Line3D line = new Line3D();
                line.start = camera.Position;
                line.end = line.start +
                    (camera.Target - line.start).Normalize() * 1000;
                Vector3D intersection;
                Triangle3D tri;
                if (smgr.SceneCollisionManager.GetCollisionPoint(
                    line, selector, out intersection, out tri))
                {
                    bill.Position = intersection;

                    driver.SetTransform(TransformationState.WORLD, new Matrix4());
                    driver.SetMaterial(material);
                    driver.Draw3DTriangle(tri, new Irrlicht.Video.Color(0, 255, 0, 0));
                }


                //this illustates scene node picking based on bounding boxes
                selectedSceneNode = smgr.SceneCollisionManager.GetSceneNodeFromCameraBB(camera, 0, false);

                if (lastSelectedSceneNode != null)
                    lastSelectedSceneNode.SetMaterialFlag(
                        MaterialFlag.LIGHTING, true);

                if (selectedSceneNode == q3node ||
                    selectedSceneNode == bill)
                    selectedSceneNode = null;

                if (selectedSceneNode != null)
                    selectedSceneNode.SetMaterialFlag(
                        MaterialFlag.LIGHTING, false);
                lastSelectedSceneNode = selectedSceneNode;


                
            if (KeyBoard.KeyState[(int)Keys.Left] || KeyBoard.KeyState[(int)Keys.A])//left arrow key
            {
                camera.Position = new Vector3D(-100, 50, CameraMove = CameraMove + 1);
            }


                device.VideoDriver.EndScene();
                System.Windows.Forms.Application.DoEvents();
                if (ShowFPS == true)// display (or dont) the FPS
                {
                    // display frames per second value
                    if (fps != device.VideoDriver.FPS)
                    {
                        fps = device.VideoDriver.FPS;
                        Text = (TextViewer + " FPS " + fps);
                    }// end if
                }// end if
            }// end while loop

        }// end method runIrrlichtInWindowsFormTest

 

    }// end class
}// end namespace

If anybody would be so kind as to tell this old boy what he is doing wrong and how to make it right would be sorely appreciated.


Sincerely
No good deed goes unpunished.
exal
Posts: 60
Joined: Tue Feb 24, 2004 9:05 am

Post by exal »

you are creating a new vector in your code. But you need to add it to your camera vector or matrix if it's a translation to make it have any effect.

If you do remeber to not just set it you need to add it. Otherwise the camera will stay in the same place all the time (the new place)
Idunno
Posts: 2
Joined: Tue Aug 22, 2006 9:43 pm

and how

Post by Idunno »

Ok so your saying im on the right track?

That is encouraging I must admit.

How do you add it?

Im having such a time getting my head around this.

Everything in Irrlicht is easy and promoted as easy. I cant seem to understand why a simple task like 'movement' is so difficult to comprehend LOL


Sincerely
No good deed goes unpunished.
exal
Posts: 60
Joined: Tue Feb 24, 2004 9:05 am

Post by exal »

Irrlicht IS very easy compared to other engines.

But the concepts around 3D math and matrices etc. are certainly not.

Asume you have a vector

V1 ( a point) 2,0,0
You have a vector V2 (1,0,0) pointing to the right
If you add these vectors V1 + V2 the resulting vector will be V3 = V1 + V2 = (3,0,0)

The output will be a dot 3 meters to the right instead of 2 meters to the right. This is a translation of the vector point.

You could be satisfied with this and just accept it
------------------
But it could also be a vector with the 3,0,0 length increased to 3 instead of 2 along the x axis.

This could mean that the speed of your object has increased since it's moving with 3 units instead of 2 units.

Confusing isn't it?

The conclusion is
A vector can be a point. Adding another vector will move the point (Translate)
It can be a direction. Adding another vector can give it another direction and/or increasing/decreasing it's length (magnitude) which can be interpreted as speed in some cases.
Locked