'Intermediate Tutorial 3: Mouse Picking (3D Object Selection) and SceneQuery Masks'

Ported to VB.NET by [[User:Aeauseth|aeauseth]]

== Introduction ==

In this tutorial we will continue the work on the [[MOgre VB.NET Intermediate Tutorial 2|previous]] tutorial. We will be covering how to select any object on the screen using the mouse, and how to restrict what is selectable.

== Prerequisites ==

This tutorial will assume that you have gone through the previous tutorial. We will also assume you have downloaded and understand how to use [[MQuickGUI]].

== Getting Started ==

Create a VB.NET console application and paste the following code into Module1.vb:

<font color='blue'>Imports</font> Mogre

<font color='blue'>Module</font> Module1

<font color='blue'>Public</font> myKeyboard <font color='blue'>As</font> MOIS.Keyboard

<font color='blue'>Public</font> myMouse <font color='blue'>As</font> MOIS.Mouse

<font color='blue'>Public</font> myCamera <font color='blue'>As</font> Camera

<font color='blue'>Public</font> MyWindow <font color='blue'>As</font> RenderWindow

<font color='blue'>Public</font> myScene <font color='blue'>As</font> SceneManager

<font color='blue'>Public</font> myTranslation <font color='blue'>As</font> Vector3 = Vector3.ZERO

<font color='blue'>Public</font> Quitting <font color='blue'>As</font> <font color='blue'>Boolean</font> = <font color='blue'>False

</font> <font color='blue'>Public</font> myRightMouseDown <font color='blue'>As</font> <font color='blue'>Boolean</font> = <font color='blue'>False

</font> <font color='blue'>Public</font> myLeftMouseDown <font color='blue'>As</font> <font color='blue'>Boolean</font> = <font color='blue'>False

</font> <font color='blue'>Public</font> myCurrentObject <font color='blue'>As</font> SceneNode

<font color='blue'>Public</font> myRoot <font color='blue'>As</font> Root

<font color='blue'>Public</font> myMouseCursor <font color='blue'>As</font> MQuickGUI.MouseCursor

<font color='blue'>Public</font> myRobotMode <font color='blue'>As</font> <font color='blue'>Boolean</font> = <font color='blue'>True

</font> <font color='blue'>Public</font> <font color='blue'>Enum</font> QueryFlags <font color='blue'>As</font> <font color='blue'>UInteger

</font> NINJA_MASK = 1 << 0

ROBOT_MASK = 1 << 1

<font color='blue'>End</font> <font color='blue'>Enum

</font> <font color='blue'>Sub</font> Main()

<font color='blue'>Try

</font> <font color='green'>'Creating the Root Object

</font> myRoot = <font color='blue'>New</font> Root(<font color='darkred'>"Plugins.cfg"</font>, <font color='darkred'>"ogre.cfg"</font>, <font color='darkred'>"ogre.log"</font>)

<font color='green'>'Defining the Resources

</font> <font color='blue'>Dim</font> cf <font color='blue'>As</font> <font color='blue'>New</font> ConfigFile

cf.Load(<font color='darkred'>"resources.cfg"</font>, vbTab + <font color='darkred'>":="</font>, <font color='blue'>True</font>)

<font color='blue'>Dim</font> seci <font color='blue'>As</font> ConfigFile.SectionIterator = cf.GetSectionIterator

<font color='blue'>Dim</font> secName <font color='blue'>As</font> <font color='blue'>String</font>, typeName <font color='blue'>As</font> <font color='blue'>String</font>, archName <font color='blue'>As</font> <font color='blue'>String

</font> <font color='blue'>While</font> (seci.MoveNext())

secName = seci.CurrentKey

<font color='blue'>Dim</font> settings <font color='blue'>As</font> ConfigFile.SettingsMultiMap = seci.Current

<font color='blue'>For</font> <font color='blue'>Each</font> pair <font color='blue'>As</font> KeyValuePair(<font color='blue'>Of</font> <font color='blue'>String</font>, <font color='blue'>String</font>) <font color='blue'>In</font> settings

typeName = pair.Key

archName = pair.Value

<font color='blue'>Select</font> <font color='blue'>Case</font> typeName

<font color='blue'>Case</font> <font color='darkred'>"FileSystem"

</font> <font color='blue'>If</font> <font color='blue'>Not</font> IO.Directory.Exists(archName) <font color='blue'>Then

</font> <font color='blue'>If</font> IO.Directory.Exists(<font color='darkred'>"../../"</font> & archName) <font color='blue'>Then

</font> archName = <font color='darkred'>"../../"</font> & archName

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Case</font> <font color='darkred'>"Zip"

</font> <font color='blue'>If</font> <font color='blue'>Not</font> IO.File.Exists(archName) <font color='blue'>Then

</font> <font color='blue'>If</font> IO.File.Exists(<font color='darkred'>"../../"</font> & archName) <font color='blue'>Then

</font> archName = <font color='darkred'>"../../"</font> & archName

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>Select

</font> ResourceGroupManager.Singleton.AddResourceLocation(archName, typeName, secName)

<font color='blue'>Next

</font> <font color='blue'>End</font> <font color='blue'>While

</font> <font color='green'>'Setting up the RenderSystem

</font> <font color='blue'>If</font> <font color='blue'>Not</font> myRoot.RestoreConfig <font color='blue'>Then

</font> <font color='blue'>If</font> <font color='blue'>Not</font> myRoot.ShowConfigDialog <font color='blue'>Then

</font> <font color='blue'>Exit</font> <font color='blue'>Sub

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='green'>'Creating the Render Window

</font> MyWindow = myRoot.Initialise(<font color='blue'>True</font>, <font color='darkred'>"Ogre RenderWindow"</font>)

<font color='blue'>AddHandler</font> myRoot.FrameStarted, <font color='blue'>AddressOf</font> FrameStarted

<font color='green'>'Initializing Resource Groups

</font> TextureManager.Singleton.DefaultNumMipmaps = 5

ResourceGroupManager.Singleton.InitialiseAllResourceGroups()

<font color='green'>'Creating the Scene

</font> myScene = myRoot.CreateSceneManager(SceneType.ST_EXTERIOR_CLOSE)

<font color='green'>'Set the default lighting

</font> myScene.AmbientLight = <font color='blue'>New</font> ColourValue(0.5, 0.5, 0.5)

myScene.SetSkyDome(<font color='blue'>True</font>, <font color='darkred'>"Examples/CloudySky"</font>, 5, 8)

<font color='green'>'Camera

</font> myCamera = myScene.CreateCamera(<font color='darkred'>"Camera"</font>)

myCamera.SetPosition(40, 200, 580)

myCamera.Pitch(<font color='blue'>New</font> Degree(-30))

myCamera.Yaw(<font color='blue'>New</font> Degree(-45))

myCamera.NearClipDistance = 5

myRoot.AutoCreatedWindow.AddViewport(myCamera)

<font color='green'>'World geometry

</font> myScene.SetWorldGeometry(<font color='darkred'>"terrain.cfg"</font>)

<font color='green'>'Overlay

</font> <font color='blue'>Dim</font> myPanelOverlay <font color='blue'>As</font> Overlay = OverlayManager.Singleton.GetByName(<font color='darkred'>"Core/DebugOverlay"</font>)

myPanelOverlay.Show()

<font color='green'>'Input handler

</font> InputClass.Init()

<font color='green'>'The Render Loop

</font> myRoot.StartRendering()

<font color='green'>'Cleanup

</font> MyWindow.Dispose()

myRoot.Dispose()

<font color='blue'>Catch</font> ex <font color='blue'>As</font> System.Runtime.InteropServices.SEHException

<font color='blue'>If</font> OgreException.IsThrown <font color='blue'>Then

</font> MsgBox(OgreException.LastException.FullDescription, MsgBoxStyle.Critical, _

<font color='darkred'>"An Ogre SEHException has occured!"</font>)

<font color='blue'>Else

</font> MsgBox(ex.ToString, <font color='darkred'>"An error has occured"</font>)

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>Try

</font> <font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Public</font> <font color='blue'>Function</font> FrameStarted(<font color='blue'>ByVal</font> e <font color='blue'>As</font> FrameEvent) <font color='blue'>As</font> <font color='blue'>Boolean

</font> myMouse.Capture()

myKeyboard.Capture()

MQuickGUI.GUIManager.Singleton.injectTime(e.timeSinceLastFrame)

<font color='green'>'Handle player/camera movement

</font> InputClass.ProcessKeyboard()

<font color='green'>'Camera movement

</font> <font color='blue'>If</font> myTranslation <> Vector3.ZERO <font color='blue'>Then

</font> myCamera.Position += myCamera.Orientation * myTranslation * e.timeSinceLastFrame

<font color='green'>'Setup the scene query

</font> <font color='blue'>Dim</font> camPos <font color='blue'>As</font> Vector3 = myCamera.Position

<font color='blue'>Dim</font> cameraRay <font color='blue'>As</font> Ray = <font color='blue'>New</font> Ray(<font color='blue'>New</font> Vector3(camPos.x, 5000, camPos.z), Vector3.NEGATIVE_UNIT_Y)

<font color='blue'>Dim</font> myRaySceneQuery <font color='blue'>As</font> RaySceneQuery = myScene.CreateRayQuery(cameraRay)

<font color='blue'>Dim</font> results <font color='blue'>As</font> RaySceneQueryResult = myRaySceneQuery.Execute

<font color='blue'>For</font> <font color='blue'>Each</font> result <font color='blue'>As</font> RaySceneQueryResultEntry <font color='blue'>In</font> results

<font color='blue'>If</font> <font color='blue'>Not</font> result.worldFragment <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> <font color='blue'>Dim</font> terrainHeight <font color='blue'>As</font> <font color='blue'>Single</font> = result.worldFragment.singleIntersection.y

<font color='blue'>If</font> terrainHeight + 10 > camPos.y <font color='blue'>Then

</font> myCamera.SetPosition(camPos.x, terrainHeight + 10, camPos.z)

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Exit</font> <font color='blue'>For

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Next

</font> myRaySceneQuery.Dispose()

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='green'>'Now update the robot location if left mouse button is still down

</font> <font color='blue'>If</font> myLeftMouseDown <font color='blue'>Then

</font> <font color='green'>'Find location our mouse cursor is aiming at

</font> <font color='blue'>Dim</font> mouseRay <font color='blue'>As</font> Ray = myCamera.GetCameraToViewportRay((myMouseCursor.getPixelPosition.x + 15) / MyWindow.Width, (myMouseCursor.getPixelPosition.y + 15) / MyWindow.Height)

<font color='blue'>Dim</font> myRaySceneQuery <font color='blue'>As</font> RaySceneQuery = myScene.CreateRayQuery(mouseRay)

myRaySceneQuery.SetSortByDistance(<font color='blue'>True</font>)

<font color='blue'>Dim</font> results <font color='blue'>As</font> RaySceneQueryResult = myRaySceneQuery.Execute

<font color='blue'>For</font> <font color='blue'>Each</font> result <font color='blue'>As</font> RaySceneQueryResultEntry <font color='blue'>In</font> results

<font color='blue'>If</font> <font color='blue'>Not</font> result.worldFragment <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> myCurrentObject.Position = result.worldFragment.singleIntersection

<font color='blue'>Exit</font> <font color='blue'>For

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Next

</font> myRaySceneQuery.Dispose()

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='green'>'Debug Overlay

</font> <font color='blue'>Dim</font> myAvg <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/AverageFps"</font>)

<font color='blue'>Dim</font> myCurr <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/CurrFps"</font>)

<font color='blue'>Dim</font> myBest <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/BestFps"</font>)

<font color='blue'>Dim</font> myWorst <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/WorstFps"</font>)

<font color='blue'>Dim</font> myNumTris <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/NumTris"</font>)

<font color='blue'>Dim</font> myNumBatches <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/NumBatches"</font>)

<font color='blue'>Dim</font> myDebug <font color='blue'>As</font> OverlayElement = OverlayManager.Singleton.GetOverlayElement(<font color='darkred'>"Core/DebugText"</font>)

myAvg.Caption = <font color='darkred'>"Average FPS: "</font> & Mogre.StringConverter.ToString(MyWindow.AverageFPS)

myCurr.Caption = <font color='darkred'>"Current FPS: "</font> & Mogre.StringConverter.ToString(MyWindow.LastFPS)

myBest.Caption = <font color='darkred'>"Best FPS: "</font> & Mogre.StringConverter.ToString(MyWindow.BestFPS)

myWorst.Caption = <font color='darkred'>"Worst FPS: "</font> & Mogre.StringConverter.ToString(MyWindow.WorstFPS)

myNumTris.Caption = <font color='darkred'>"Triangle Count: "</font> & Mogre.StringConverter.ToString(MyWindow.TriangleCount)

myNumBatches.Caption = <font color='darkred'>"Batch Count: "</font> & Mogre.StringConverter.ToString(MyWindow.BatchCount)

<font color='blue'>If</font> myRobotMode <font color='blue'>Then

</font> myDebug.Caption = <font color='darkred'>"Robot Mode Enabled - Press Space to Toggle"

</font> <font color='blue'>Else

</font> myDebug.Caption = <font color='darkred'>"Ninja Mode Enabled - Press Space to Toggle"

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Return</font> <font color='blue'>Not</font> Quitting

<font color='blue'>End</font> <font color='blue'>Function

</font> <font color='blue'>Public</font> <font color='blue'>Class</font> InputClass

<font color='blue'>Const</font> TRANSLATE <font color='blue'>As</font> <font color='blue'>Single</font> = 200

<font color='blue'>Const</font> ROTATE <font color='blue'>As</font> <font color='blue'>Single</font> = 0.003

<font color='blue'>Shared</font> <font color='blue'>Sub</font> Init()

<font color='green'>'Keyboard

</font> <font color='blue'>Dim</font> windowHnd <font color='blue'>As</font> <font color='blue'>Integer

</font> MyWindow.GetCustomAttribute(<font color='darkred'>"WINDOW"</font>, windowHnd)

<font color='blue'>Dim</font> myInputManager <font color='blue'>As</font> MOIS.InputManager = MOIS.InputManager.CreateInputSystem(windowHnd)

myKeyboard = myInputManager.CreateInputObject(MOIS.Type.OISKeyboard, <font color='blue'>True</font>)

<font color='blue'>AddHandler</font> myKeyboard.KeyPressed, <font color='blue'>AddressOf</font> InputClass.KeyPressed

<font color='blue'>AddHandler</font> myKeyboard.KeyReleased, <font color='blue'>AddressOf</font> InputClass.KeyReleased

<font color='green'>'Mouse

</font> myMouse = myInputManager.CreateInputObject(MOIS.Type.OISMouse, <font color='blue'>True</font>)

<font color='blue'>Dim</font> mousestate <font color='blue'>As</font> MOIS.MouseState_NativePtr = myMouse.MouseState

mousestate.width = MyWindow.Width

mousestate.height = MyWindow.Height

<font color='blue'>AddHandler</font> myMouse.MouseMoved, <font color='blue'>AddressOf</font> InputClass.MouseMovedListener

<font color='blue'>AddHandler</font> myMouse.MousePressed, <font color='blue'>AddressOf</font> InputClass.MousePressedListener

<font color='blue'>AddHandler</font> myMouse.MouseReleased, <font color='blue'>AddressOf</font> InputClass.MouseReleasedListener

MQuickGUI.GUIManager.Singleton._notifyWindowDimensions(MyWindow.Width, MyWindow.Height)

myMouseCursor = MQuickGUI.GUIManager.Singleton.createMouseCursor(<font color='blue'>New</font> Vector2(30, 30), <font color='darkred'>"qgui.pointer"</font>)

SetMouseLocaton(<font color='blue'>New</font> Vector2(MyWindow.Width / 2, MyWindow.Height / 2))

<font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Shared</font> <font color='blue'>Sub</font> SetMouseLocaton(<font color='blue'>ByVal</font> loc <font color='blue'>As</font> Vector2)

<font color='blue'>Dim</font> axis <font color='blue'>As</font> MOIS.Axis_NativePtr = myMouse.MouseState.X

axis.abs = loc.x

axis = myMouse.MouseState.Y

axis.abs = loc.y

<font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Shared</font> <font color='blue'>Function</font> KeyPressed(<font color='blue'>ByVal</font> e <font color='blue'>As</font> MOIS.KeyEvent) <font color='blue'>As</font> <font color='blue'>Boolean

</font> <font color='green'>'Currently unused by this application

</font> <font color='blue'>If</font> e.key = MOIS.KeyCode.KC_SPACE <font color='blue'>Then

</font> myRobotMode = <font color='blue'>Not</font> myRobotMode

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Return</font> <font color='blue'>Nothing

</font> <font color='blue'>End</font> <font color='blue'>Function

</font> <font color='blue'>Shared</font> <font color='blue'>Function</font> KeyReleased(<font color='blue'>ByVal</font> e <font color='blue'>As</font> MOIS.KeyEvent) <font color='blue'>As</font> <font color='blue'>Boolean

</font> <font color='green'>'This function is just a placeholder

</font> <font color='green'>'It is unlikely you will ever use this

</font> <font color='green'>'Typically you either process unbuffered keyboard input (as in ProcessKeyboard)

</font> <font color='green'>'or you process buffered Keypress

</font> <font color='blue'>Return</font> <font color='blue'>Nothing

</font> <font color='blue'>End</font> <font color='blue'>Function

</font> <font color='blue'>Shared</font> <font color='blue'>Sub</font> ProcessKeyboard()

<font color='green'>'This Sub is typically called via the FrameStarted event.

</font> <font color='green'>'Clear previous translation

</font> myTranslation.z = 0

myTranslation.x = 0

myTranslation.y = 0

<font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_ESCAPE) <font color='blue'>Then

</font> Quitting = <font color='blue'>True

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_UP) <font color='blue'>Or</font> _

myKeyboard.IsKeyDown(MOIS.KeyCode.KC_W) <font color='blue'>Then

</font> myTranslation.z += -TRANSLATE

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_S) <font color='blue'>Or</font> _

myKeyboard.IsKeyDown(MOIS.KeyCode.KC_DOWN) <font color='blue'>Then

</font> myTranslation.z += TRANSLATE

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_A) <font color='blue'>Or</font> _

myKeyboard.IsKeyDown(MOIS.KeyCode.KC_LEFT) <font color='blue'>Then

</font> myTranslation.x += -TRANSLATE

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_D) <font color='blue'>Or</font> _

myKeyboard.IsKeyDown(MOIS.KeyCode.KC_RIGHT) <font color='blue'>Then

</font> myTranslation.x += TRANSLATE

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_Q) <font color='blue'>Or</font> _

myKeyboard.IsKeyDown(MOIS.KeyCode.KC_PGUP) <font color='blue'>Then

</font> myTranslation.y += TRANSLATE

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myKeyboard.IsKeyDown(MOIS.KeyCode.KC_Z) <font color='blue'>Or</font> _

myKeyboard.IsKeyDown(MOIS.KeyCode.KC_PGDOWN) <font color='blue'>Then

</font> myTranslation.y += -TRANSLATE

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Shared</font> <font color='blue'>Function</font> MouseMovedListener(<font color='blue'>ByVal</font> e <font color='blue'>As</font> MOIS.MouseEvent) <font color='blue'>As</font> <font color='blue'>Boolean

</font> <font color='green'>'Mouse Cursor Movement

</font> MQuickGUI.GUIManager.Singleton.injectMousePosition(myMouse.MouseState.X.abs, myMouse.MouseState.Y.abs)

<font color='green'>'Camera Rotate

</font> <font color='blue'>If</font> myRightMouseDown <font color='blue'>Then

</font> myCamera.Yaw(e.state.X.rel * -ROTATE)

myCamera.Pitch(e.state.Y.rel * -ROTATE)

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>Function

</font> <font color='blue'>Shared</font> <font color='blue'>Function</font> MousePressedListener(<font color='blue'>ByVal</font> e <font color='blue'>As</font> MOIS.MouseEvent, <font color='blue'>ByVal</font> id <font color='blue'>As</font> MOIS.MouseButtonID) <font color='blue'>As</font> <font color='blue'>Boolean

</font> <font color='blue'>If</font> e.state.ButtonDown(MOIS.MouseButtonID.MB_Right) <font color='blue'>Then

</font> onRightPressed()

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> e.state.ButtonDown(MOIS.MouseButtonID.MB_Left) <font color='blue'>Then

</font> onLeftPressed()

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>Function

</font> <font color='blue'>Shared</font> <font color='blue'>Sub</font> onLeftPressed()

myLeftMouseDown = <font color='blue'>True

</font> <font color='green'>'Inform GUI of mouse status

</font> MQuickGUI.GUIManager.Singleton.injectMouseButtonDown(MOIS.MouseButtonID.MB_Left)

<font color='green'>'Turn off bounding box

</font> <font color='blue'>If</font> <font color='blue'>Not</font> myCurrentObject <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> myCurrentObject.ShowBoundingBox = <font color='blue'>False

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='green'>'Find location our mouse cursor is aiming at

</font> <font color='blue'>Dim</font> mouseRay <font color='blue'>As</font> Ray = myCamera.GetCameraToViewportRay((myMouseCursor.getPixelPosition.x + 15) / MyWindow.Width, (myMouseCursor.getPixelPosition.y + 15) / MyWindow.Height)

<font color='blue'>Dim</font> myRaySceneQuery <font color='blue'>As</font> RaySceneQuery = myScene.CreateRayQuery(mouseRay)

myRaySceneQuery.SetSortByDistance(<font color='blue'>True</font>)

<font color='blue'>If</font> myRobotMode <font color='blue'>Then

</font> myRaySceneQuery.QueryMask = QueryFlags.ROBOT_MASK

<font color='blue'>Else

</font> myRaySceneQuery.QueryMask = QueryFlags.NINJA_MASK

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Dim</font> results <font color='blue'>As</font> RaySceneQueryResult = myRaySceneQuery.Execute

<font color='blue'>For</font> <font color='blue'>Each</font> result <font color='blue'>As</font> RaySceneQueryResultEntry <font color='blue'>In</font> results

<font color='green'>'Movable object?

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.movable <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.movable.Name.Contains(<font color='darkred'>"tile["</font>) <font color='blue'>Then

</font> myCurrentObject = result.movable.ParentSceneNode

<font color='blue'>Exit</font> <font color='blue'>For

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.worldFragment <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> CreateEntityAt(result.worldFragment.singleIntersection)

<font color='blue'>Exit</font> <font color='blue'>For

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Next

</font> myRaySceneQuery.Dispose()

<font color='green'>'Turn on bounding box

</font> <font color='blue'>If</font> <font color='blue'>Not</font> myCurrentObject <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> myCurrentObject.ShowBoundingBox = <font color='blue'>True

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='green'>'Hide mouse cursor while we are holding down the left mouse button

</font> myMouseCursor.hide()

<font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Shared</font> <font color='blue'>Sub</font> CreateEntityAt(<font color='blue'>ByVal</font> Loc <font color='blue'>As</font> Vector3)

<font color='blue'>Static</font> iCount <font color='blue'>As</font> <font color='blue'>Integer</font> = 0

iCount += 1

<font color='blue'>Dim</font> entName <font color='blue'>As</font> <font color='blue'>String

</font> <font color='blue'>Dim</font> myEntity <font color='blue'>As</font> Entity

<font color='blue'>If</font> myRobotMode <font color='blue'>Then

</font> entName = <font color='darkred'>"Robot"</font> & iCount

myEntity = myScene.CreateEntity(entName, <font color='darkred'>"robot.mesh"</font>)

myEntity.QueryFlags = QueryFlags.ROBOT_MASK

<font color='blue'>Else

</font> entName = <font color='darkred'>"Ninja"</font> & iCount

myEntity = myScene.CreateEntity(entName, <font color='darkred'>"ninja.mesh"</font>)

myEntity.QueryFlags = QueryFlags.NINJA_MASK

<font color='blue'>End</font> <font color='blue'>If

</font> myCurrentObject = myScene.RootSceneNode.CreateChildSceneNode(entName & <font color='darkred'>"Node"</font>, Loc)

myCurrentObject.AttachObject(myEntity)

myCurrentObject.SetScale(0.1, 0.1, 0.1)

<font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Shared</font> <font color='blue'>Sub</font> onRightPressed()

myRightMouseDown = <font color='blue'>True

</font> myMouseCursor.hide()

<font color='green'>'Inform GUI of mouse status

</font> MQuickGUI.GUIManager.Singleton.injectMouseButtonDown(MOIS.MouseButtonID.MB_Right)

<font color='blue'>End</font> <font color='blue'>Sub

</font> <font color='blue'>Shared</font> <font color='blue'>Function</font> MouseReleasedListener(<font color='blue'>ByVal</font> e <font color='blue'>As</font> MOIS.MouseEvent, <font color='blue'>ByVal</font> id <font color='blue'>As</font> MOIS.MouseButtonID) <font color='blue'>As</font> <font color='blue'>Boolean

</font> <font color='blue'>If</font> myRightMouseDown <font color='blue'>And</font> <font color='blue'>Not</font> e.state.ButtonDown(MOIS.MouseButtonID.MB_Right) <font color='blue'>Then

</font> myRightMouseDown = <font color='blue'>False

</font> MQuickGUI.GUIManager.Singleton.injectMouseButtonUp(MOIS.MouseButtonID.MB_Left)

SetMouseLocaton(<font color='blue'>New</font> Vector2(MyWindow.Width / 2, MyWindow.Height / 2))

MQuickGUI.GUIManager.Singleton.injectMousePosition(myMouse.MouseState.X.abs, myMouse.MouseState.Y.abs)

myMouseCursor.show()

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> myLeftMouseDown <font color='blue'>And</font> <font color='blue'>Not</font> e.state.ButtonDown(MOIS.MouseButtonID.MB_Left) <font color='blue'>Then

</font> myLeftMouseDown = <font color='blue'>False

</font> MQuickGUI.GUIManager.Singleton.injectMouseButtonUp(MOIS.MouseButtonID.MB_Right)

myMouseCursor.show()

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>Function

</font> <font color='blue'>End</font> <font color='blue'>Class

End</font> <font color='blue'>Module</font>

Be sure you can compile and run this code before continuing.

== Showing Which Object is Selected ==

In this tutorial we will be making it so that you can "pick up" and move objects after you have placed them. We would like to have a way for the user to know which object she's currently manipulating. In a game, we would probably like to create a special way of highlighting the object, but for our tutorial (and for your applications before they are release-ready), you can use the showBoundingBox method to create a box around objects.

Our basic idea is to disable the bounding box on the old current object when the mouse is first clicked, then enable the bounding box as soon as we have the new object. Note we have already added the following code that unselect the currentObject whenever we press the onLeftPressed:

<font color='green'>'Turn off bounding box</font>

<font color='blue'>If</font> <font color='blue'>Not</font> myCurrentObject <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then</font>

myCurrentObject.ShowBoundingBox = <font color='blue'>False</font>

<font color='blue'>End</font>

Just before we exit the onLeftPressed we set the bounding box to true:

<font color='green'>'Turn on bounding box</font>

<font color='blue'>If</font> <font color='blue'>Not</font> myCurrentObject <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then</font>

myCurrentObject.ShowBoundingBox = <font color='blue'>True</font>

<font color='blue'>End</font>

Now the myCurrentObject is always highlighted on the screen.

== Adding Ninjas ==

We also added some code that creates Ninja's. Pressing the space key will toggle the mode, and we will display a message to the user which mode they are in.


The code to accomplish this is pretty straight forward so I'll spare you the details. You can always look thru the code to see what is going on.

== Selecting Objects ==

Now we are going to dive into the meat of this tutorial: using RaySceneQueries to select objects on the screen. Before we start making changes to the code I will first explain a [http://www.ogre3d.org/docs/api/html/structOgre_1_1RaySceneQueryResultEntry.html RaySceneQueryResultEntry] in more detail. (Please follow the link and look at the struct briefly.)

The RaySceneQueryResult returns an iterator of RaySceneQueryResultEntry structs. This struct contains three variables. The distance variable tells you how far away the object is along the ray. One of the other two variables will be non-null. The movable variable will contain a [http://www.ogre3d.org/docs/api/html/classOgre_1_1MovableObject.html MovableObject] if the Ray intersected one. The worldFragment will contain a [http://www.ogre3d.org/docs/api/html/structOgre_1_1SceneQuery_1_1WorldFragment.html WorldFragment] object if it hit a world fragment (like the terrain).

MovableObjects are basically any object you would attach to a SceneNode (such as Entities, Lights, etc). See the inheritance tree on [http://www.ogre3d.org/docs/api/html/classOgre_1_1MovableObject.html this page] to find out what type of objects would be returned. Most normal applications of RaySceneQueries will involve selecting and manipulating either the MovableObject you have clicked on, or the SceneNodes they are attached to. To get the name of the MovableObject, call the getName method. To get the SceneNode (or Node) the object is attached to, call getParentSceneNode (or getParentNode). The movable variable in a RaySceneQueryResultEntry will be equal to NULL if the result is not a MovableObject.

The WorldFragment is a different beast all together. When the worldFragment member of a RaySceneQueryResult is set, it means that the result is part of the world geometry created by the SceneManager. The type of world fragment that is returned is based on the SceneManager. The way this is implemented is WorldFragment struct contains the [http://www.ogre3d.org/docs/api/html/classOgre_1_1SceneQuery.html#Ogre_1_1TerrainRaySceneQuerys5 fragmentType] variable which specifies the type of world fragment it contains. Based on the fragmentType variable, one of the other variables will be set ([http://www.ogre3d.org/docs/api/html/structOgre_1_1SceneQuery_1_1WorldFragment.html singleIntersection, planes, geometry, or renderOp]). Generally speaking, RaySceneQueries only return WFT_SINGLE_INTERSECTION WorldFragments. The singleIntersection variable is simply a Vector3 reporting the location of the intersection. Other types of world fragments are beyond the scope of this tutorial.

Now lets look at an example. Lets say we wanted to print out a list of results after a RaySceneQuery. The following code would do this.

<font color='green'>'Do not add this code to the program, just read along:

</font> <font color='blue'>Dim</font> results <font color='blue'>As</font> RaySceneQueryResult = myRaySceneQuery.Execute

<font color='blue'>For</font> <font color='blue'>Each</font> result <font color='blue'>As</font> RaySceneQueryResultEntry <font color='blue'>In</font> results

<font color='green'>'Is this a result a WorldFragment?

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.worldFragment <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> <font color='blue'>Dim</font> location <font color='blue'>As</font> Vector3 = result.worldFragment.singleIntersection

Debug.WriteLine(<font color='darkred'>"WorldFragment: ("</font> & location.x & <font color='darkred'>", "</font> & location.y & <font color='darkred'>", "</font> & location.z & <font color='darkred'>")"</font>)

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='green'>'Is this a MovableObject?

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.movable <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> Debug.WriteLine(<font color='darkred'>"MovableObject: "</font> & result.movable.Name)

<font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Next</font>

This would print out the names of all MovableObjects that the ray intersects, and it would print the location of where it intersected the world geometry (if it did hit it). Note that this can sometimes act in strange ways. For example, if you are using the TerrainSceneManager, the origin of the Ray you fire must be over the Terrain or the intersection query will not register it as a hit. Different scene managers implement RaySceneQueries in different ways. Be sure to experiment with it when you use it with a new SceneManager.

You may recall that in the previous tutoraial we just assumed that the first result was our terrain. This is bad, since we cannot be sure that the TerrainSceneManager will always return the world geometry first. We need to loop through the results to make sure we are finding what we are looking for. Another thing that we want to do is to "pick up" and drag objects that have already been placed. Currently if you click on an object that has already been placed, the program ignores it and places a robot behind it. You will note the following code changes:

<font color='green'>'Find location our mouse cursor is aiming at

</font> <font color='blue'>Dim</font> mouseRay <font color='blue'>As</font> Ray = myCamera.GetCameraToViewportRay((myMouseCursor.getPixelPosition.x + 15) / MyWindow.Width, (myMouseCursor.getPixelPosition.y + 15) / MyWindow.Height)

<font color='blue'>Dim</font> myRaySceneQuery <font color='blue'>As</font> RaySceneQuery = myScene.CreateRayQuery(mouseRay)

myRaySceneQuery.SetSortByDistance(<font color='blue'>True</font>)

<font color='blue'>Dim</font> results <font color='blue'>As</font> RaySceneQueryResult = myRaySceneQuery.Execute

<font color='blue'>For</font> <font color='blue'>Each</font> result <font color='blue'>As</font> RaySceneQueryResultEntry <font color='blue'>In</font> results

<font color='green'>'Movable object?

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.movable <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.movable.Name.Contains(<font color='darkred'>"tile["</font>) <font color='blue'>Then

</font> myCurrentObject = result.movable.ParentSceneNode

<font color='blue'>Exit</font> <font color='blue'>For

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>If</font> <font color='blue'>Not</font> result.worldFragment <font color='blue'>Is</font> <font color='blue'>Nothing</font> <font color='blue'>Then

</font> CreateEntityAt(result.worldFragment.singleIntersection)

<font color='blue'>Exit</font> <font color='blue'>For

</font> <font color='blue'>End</font> <font color='blue'>If

</font> <font color='blue'>Next

</font> myRaySceneQuery.Dispose()

First we check if the first intersection is a MovableObject, if so we'll assign mCurrentObject to be its parent SceneNode. There is a catch though. The TerrainSceneManager creates MovableObjects for the terrain itself, so we might actually be intersecting one of the tiles. In order to fix that, I check the name of the object to make sure that it does not resemble a terrain tile name; a sample tile name would be "tile[0][0,2]". Finally, notice the 'Exit For' statement. We only need to act on the first object, so as soon as we find a valid one we need to get out of the for loop altogether.

Compile and play with the code. Now we create the correct type of object when we click on terrain, and when we click on an object we will see the bounding box (no dragging it around right now, this comes in the next Step). One valid question is, since we only want the first intersection, and since we sorted by depth, why not just use an if statement? The main reason is we could actually have a fall through if there the first returned object is one of those pesky tiles. We have to loop until we find something other than a tile or we hit the end of the list.

== Query Masks ==

Notice that no matter what mode we are in we can select either object. Our RaySceneQuery will return either Robots or Ninjas, whichever is in front. It doesn't have to be this way though. All MovableObjects allow you to set a mask value for them, and SceneQueries allow you to filter your results based on this mask. All masks are done using the binary AND operation, so if you are unfamiliar with this, you should [http://atrevida.comprenica.com/atrtut02.html brush up on it] before continuing.

The first thing we are going to do is create the mask values. Go to the very beginning of the MouseQueryListener class and add this after the public statement:

<font color='blue'>Public</font> <font color='blue'>Enum</font> QueryFlags <font color='blue'>As</font> <font color='blue'>UInteger

</font> NINJA_MASK = 1 << 0

ROBOT_MASK = 1 << 1

<font color='blue'>End</font> <font color='blue'>Enum</font>

This creates an enum with two values, which in binary are 0001 and 0010. Now, every time we create a Robot entity, we call its "setMask" function to set the query flags to be ROBOT_MASK. Every time we create a Ninja entity we call its "setMask" function and use NINJA_MASK instead. Now, when we are in Ninja mode, we will make the RaySceneQuery only consider objects with the NINJA_MASK flag, and when we are in Robot mode we will make it only consider ROBOT_MASK.

<font color='blue'>If</font> myRobotMode <font color='blue'>Then

</font> entName = <font color='darkred'>"Robot"</font> & iCount

myEntity = myScene.CreateEntity(entName, <font color='darkred'>"robot.mesh"</font>)

myEntity.QueryFlags = QueryFlags.ROBOT_MASK

<font color='blue'>Else

</font> entName = <font color='darkred'>"Ninja"</font> & iCount

myEntity = myScene.CreateEntity(entName, <font color='darkred'>"ninja.mesh"</font>)

myEntity.QueryFlags = QueryFlags.NINJA_MASK

<font color='blue'>End</font> <font color='blue'>If</font>

We still need to make it so that when we are in a mode, we can only click and drag objects of that type. We need to set the query flags so that only the correct object type can be selected. We accomplish this by setting the query mask in the RaySceneQuery to be the ROBOT_MASK in Robot mode, and set it to NINJA_MASK in Ninja mode.

<font color='blue'>If</font> myRobotMode <font color='blue'>Then

</font> myRaySceneQuery.QueryMask = QueryFlags.ROBOT_MASK

<font color='blue'>Else

</font> myRaySceneQuery.QueryMask = QueryFlags.NINJA_MASK

<font color='blue'>End</font> <font color='blue'>If</font>

Compile and run the tutorial. We now select only the objects we are looking for. All rays that pass through other objects go through them and hit the correct object. We are now finished working on this code. The next section will not be modifying it.

== Query Type Masks ==

There's one more thing to consider when using scene queries. Suppose you added a billboardset or a particle system to your scene above, and you want to move it around. You will find that the query never returns the billboardset that you click on. This is because the SceneQuery has another mask, the QueryTypeMask, that limits you to selecting only the type specified as the flag. By default when you do a query, it returns only objects of entity type.

In your code, if you want your query to return BillboardSets or ParticleSystems, you'll have to do this first before executing your query:

myRaySceneQuery.QueryTypeMask = SceneManager.FX_TYPE_MASK

Now the query will only return BillboardSets or ParticleSystems as results.

There are 6 types of QueryTypeMask defined in the SceneManager class as static members:

WORLD_GEOMETRY_TYPE_MASK <font color='green'>'Returns world geometry..</font>

ENTITY_TYPE_MASK <font color='green'>'Returns entities..</font>

FX_TYPE_MASK <font color='green'>'Returns billboardsets / particle systems..</font>

STATICGEOMETRY_TYPE_MASK <font color='green'>'Returns static geometry..</font>

LIGHT_TYPE_MASK <font color='green'>'Returns lights..</font>

USER_TYPE_MASK_LIMIT <font color='green'>'User type mask limit.</font>

The default QueryTypeMask when the property is not set manually is ENTITY_TYPE_MASK.

== More on Masks ==

Our mask example is very simple, so I would like to go through a few more complex examples.

Setting a MovableObject's Mask

Every time we want to create a new mask, the binary representation must contain only one 1 in it. That is, these are valid masks:

00000001

00000010

00000100

00001000

00010000

00100000

01000000

10000000

And so on. We can very easily create these values by taking 1 and bitshifting them by a position value. That is:

00000001 = 1<<0

00000010 = 1<<1

00000100 = 1<<2

00001000 = 1<<3

00010000 = 1<<4

00100000 = 1<<5

01000000 = 1<<6

10000000 = 1<<7

All the way up to 1<<31. This gives us 32 distinct masks we can use for MovableObjects.

Querying for Multiple Masks

We can query for multiple masks by using the bitwise OR operator. Let say we have three different groups of objects in a game:

<font color='blue'>Enum</font> QueryFlags <font color='blue'>As</font> <font color='blue'>Integer</font>

FRIENDLY_CHARACTERS = 1 << 0

ENEMY_CHARACTERS = 1 << 1

STATIONARY_OBJECTS = 1 << 2

<font color='blue'>End</font> <font color='blue'>Enum</font>

Now, if we wanted to query for only friendly characters we could do:

myRaySceneQuery.QueryTypeMask = QueryFlags.FRIENDLY_CHARACTERS

If we want the query to return both enemy characters and stationary objects, we would use:

myRaySceneQuery.QueryTypeMask = QueryFlags.ENEMY_CHARACTERS <font color='blue'>Or</font> QueryFlags.STATIONARY_OBJECTS

If you use a lot of these types of queries, you might want to define this in the enum:

OBJECTS_ENEMIES = QueryFlags.ENEMY_CHARACTERS <font color='blue'>Or</font> QueryFlags.STATIONARY_OBJECTS

And then simply use OBJECTS_ENEMIES to query.

Querying for Everything but a Mask

You can also query for anything other than a mask using the bit inversion operator, like so:

myRaySceneQuery.QueryTypeMask = <font color='blue'>Not</font> QueryFlags.FRIENDLY_CHARACTERS

Which will return everything other than friendly characters. You can also do this for multiple masks:

myRaySceneQuery.QueryTypeMask = <font color='blue'>Not</font> (QueryFlags.FRIENDLY_CHARACTERS <font color='blue'>Or </font> _

QueryFlags.STATIONARY_OBJECTS)

Which would return everything other than friendly characters and stationary objects.

Selecting all Objects or No Objects

You can do some very interesting stuff with masks. The thing to remember is, if you set the query mask QM for a SceneQuery, it will match all MovableObjects that have the mask OM if QM & OM contains at least one 1. Thus, setting the query mask for a SceneQuery to 0 will make it return no MovableObjects. Setting the query mask to ~0 (0xFFFFF...) will make it return all MovableObjects that do not have a 0 query mask.

Using a query mask of 0 can be highly useful in some situations. For example, the TerrainSceneManager does not use QueryMasks when it returns a worldFragment. By doing this:

myRaySceneQuery.QueryTypeMask = 0

You will get ONLY the worldFragment in your RaySceneQueries for that SceneManager. This can be very useful if you have a lot of objects on screen and you do not want to waste time looping through all of them if you only need to look for the Terrain intersection.

== Exercises ==

Easy Exercises

  1. The TerrainSceneManager creates tiles with a default mask of ~0 (all queries select it). We fixed this problem by testing to see if the name of the movable object equaled "tile[0][0,2]". Even though it's not implemented yet, the TerrainSceneManager supports multiple pages, and if there were more things than just "tile[0][0,2]" this would cause our code to break down. Instead of making the test in the loop, fix the problem properly by setting all of the tile objects created by the TerrainSceneManager to have a unique mask. (Hint: The TerrainSceneManager creates a SceneNode called "Terrain" which contains all of these tiles. Loop through them and set the attached object's masks to something of your choosing.)


Intermediate Exercises

  1. Our program delt with two things, Robots and Ninjas. If we were going to implement a scene editor, we would want to place any number of different object types. Generalize this code to allow the placement of any type of object from a predefined list. Create an overlay with the list of objects you want the editor to have (such as Ninjas, Robots, Knots, Ships, etc), and have the SceneQueries only select that type of object.
  2. Since we are using multiple types of objects now, use the [http://www.google.com/search?q=factory+pattern Factory Pattern] to properly create the SceneNodes and Entities.


Advanced Exercises

  1. Generalize the previous exercises to read in all of the meshes that Ogre knows about (IE everything that was parsed in from the Media directory), and give the ability to place them. Note that there should not be a limit as to how many types of objects ogre can place. Since you only have 32 unique query masks to use, you may need to come up with a way to quickly change all of the query flags for objects on the screen.
  2. You might have noticed that when you click on an object, the object is "lifted" from the bottom of the bounding box. To see this, click on the top of any character and move him. He will be transported instantly elsewhere. Modify the program to fix this problem.


Exercises for Further Study

  1. Add a way to select multiple objects to the program such that when you hold the Ctrl key and click multiple objects are highlighted. When you move these objects, move all of them as a group.
  2. Many scene editing programs allow you to group objects so that they are always moved together. Implement this in the program.


;Proceed to [[MOgre VB.NET Intermediate Tutorial 4]] 'Volume Selection and Basic Manual Objects'

<HR>
Creative Commons Copyright -- Some rights reserved.


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

  • "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  • "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  • "Licensor" means the individual or entity that offers the Work under the terms of this License.
  • "Original Author" means the individual or entity who created the Work.
  • "Work" means the copyrightable work of authorship offered under the terms of this License.
  • "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  • "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  • to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  • to create and reproduce Derivative Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  • For the avoidance of doubt, where the work is a musical composition:
    • Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
    • Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
    • Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).


The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  • You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  • You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  • If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

  • This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  • Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  • Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  • Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  • If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  • No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  • This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.