Browse Source

Finishing up work on Joint initialization refactor

BearishSun 9 years ago
parent
commit
8d394ebd50
37 changed files with 2120 additions and 1763 deletions
  1. 89 93
      Documentation/Manuals/Native/gui.md
  2. 146 127
      Documentation/Manuals/Native/scripting.md
  3. 2 1
      Doxyfile
  4. 3 0
      Source/BansheeCore/Include/BsCJoint.h
  5. 11 6
      Source/BansheeCore/Include/BsCJointRTTI.h
  6. 1 1
      Source/BansheeCore/Include/BsSphericalJoint.h
  7. 1 1
      Source/BansheeCore/Source/BsCD6Joint.cpp
  8. 26 18
      Source/BansheeCore/Source/BsCJoint.cpp
  9. 1 1
      Source/BansheeEngine/Source/BsGUIToggle.cpp
  10. 46 60
      Source/MBansheeEngine/Physics/D6Joint.cs
  11. 219 219
      Source/MBansheeEngine/Physics/DistanceJoint.cs
  12. 18 18
      Source/MBansheeEngine/Physics/FixedJoint.cs
  13. 30 24
      Source/MBansheeEngine/Physics/HingeJoint.cs
  14. 128 77
      Source/MBansheeEngine/Physics/Joint.cs
  15. 137 118
      Source/MBansheeEngine/Physics/NativeD6Joint.cs
  16. 102 87
      Source/MBansheeEngine/Physics/NativeDistanceJoint.cs
  17. 21 21
      Source/MBansheeEngine/Physics/NativeFixedJoint.cs
  18. 83 70
      Source/MBansheeEngine/Physics/NativeHingeJoint.cs
  19. 105 90
      Source/MBansheeEngine/Physics/NativeJoint.cs
  20. 66 54
      Source/MBansheeEngine/Physics/NativeSliderJoint.cs
  21. 50 44
      Source/MBansheeEngine/Physics/NativeSphericalJoint.cs
  22. 81 72
      Source/MBansheeEngine/Physics/SliderJoint.cs
  23. 85 76
      Source/MBansheeEngine/Physics/SphericalJoint.cs
  24. 18 1
      Source/SBansheeEngine/Include/BsScriptDistanceJoint.h
  25. 14 0
      Source/SBansheeEngine/Include/BsScriptJoint.h
  26. 134 110
      Source/SBansheeEngine/Source/BsScriptD6Joint.cpp
  27. 20 1
      Source/SBansheeEngine/Source/BsScriptD6Joint.h
  28. 92 78
      Source/SBansheeEngine/Source/BsScriptDistanceJoint.cpp
  29. 33 30
      Source/SBansheeEngine/Source/BsScriptFixedJoint.cpp
  30. 1 1
      Source/SBansheeEngine/Source/BsScriptFixedJoint.h
  31. 81 70
      Source/SBansheeEngine/Source/BsScriptHingeJoint.cpp
  32. 15 1
      Source/SBansheeEngine/Source/BsScriptHingeJoint.h
  33. 115 91
      Source/SBansheeEngine/Source/BsScriptJoint.cpp
  34. 66 56
      Source/SBansheeEngine/Source/BsScriptSliderJoint.cpp
  35. 13 1
      Source/SBansheeEngine/Source/BsScriptSliderJoint.h
  36. 54 44
      Source/SBansheeEngine/Source/BsScriptSphericalJoint.cpp
  37. 13 1
      Source/SBansheeEngine/Source/BsScriptSphericalJoint.h

+ 89 - 93
Documentation/Manuals/Native/gui.md

@@ -1,93 +1,89 @@
-Creating custom GUI elements						{#customGUI}
-===============
-[TOC]
-
-Sometimes the built-in GUI elements are just not enough, for example when you need a very specific GUI element for your game (e.g. for displaying graphs, charts), want to use a special material or in general just need some more customization options. While in some cases you could construct such elements from the provided GUI elements, it is often more efficient to create a brand new customized type. New GUI elements can be created relatively easily and allow you to fully customize the look and interaction with your element.
-
-Before staring you should familiarize yourself how Banshee's GUI works from the users perspective. Make sure to read [GUI manual](TODOLINK) to understand the basics (although the manual is for scripting, the C++ versions of these methods are essentially equivalent and the same concepts apply).
-
-All GUI elements derive from the base GUIElementBase type. The elements can be categorized into two major groups:
- - GUILayout: They derive from GUILayout and do not have any graphics, but instead they control the placement of all elements attached to them.
- - GUIElement: These are your standard GUI elements like buttons, input boxes, sliders and such.
- 
-You will almost certainly be interested in GUIElement, and are unlikely to need to create custom GUILayout types.
-
-# Basic GUI element # {#customGUI_a}
-When creating a custom GUI element you will need to override the GUIElement type. The minimum set of methods you need to implement for your custom elements are:
- - _getOptimalSize() - Return the optimal size of your GUI element. This is used for controlling the size of the element in a layout (if it's not fixed). For example if your element was displaying a texture 64x64 in size, then the optimal size should probably return 64x64. If you element is displaying text you can use GUIHelper to help you calculate the bounds of the text. If displaying something else then it's up to you to determine what constitutes an optimal size.
- - _getNumRenderElements() - Return the number of separate elements that your GUIElement consists of. Each element has its own mesh, material and texture. In most cases there is only one element.
- - _getNumQuads() - Returns the number of quads for the mesh of the render element at the specified index (e.g. one quad if the GUI element displays just one texture). This allows external systems how big of a buffer to allocate for element's mesh.
- - _fillBuffer() - This is the "meat" of the GUIElement override. It allows you to fill a buffer with vertices, uv coordinates and indices for a mesh for the specified render element. This allows you to fully control the look of your GUI element by providing customized geometry.
- - _getMaterial() - Here you should return a material that will be applied to the mesh output in the previous method. You can choose from a few pre-built material types and provide a texture and a color. If you wish to add a new custom material, check later in this guide.
- - updateRenderElementsInternal() - Called whenever the element's size or style changes. In this method you should rebuild the element's mesh. (While you could build the mesh in _fillBuffer it is inefficient as _fillBuffer will get called much more often than updateRenderElementsInternal due to mesh batching).
- 
-In order to help you with creating GUI meshes and materials make sure to take a look at ImageSprite and TextSprite classes. ImageSprite can easily generate image geometry of specified size, whether a simple quad or a Scale9grid image. And TextSprite will take a text string, font and additional options as input, and output a set of quads required for text rendering.
-
-When constructing the GUIElement you will also need to provide a style-name (see next chapter) and dimensions to the GUIElement constructor. The dimensions determine the initial size of the element. To use the default dimensions just provide the constructor with return value from GUIDimensions::create(). GUIElement will internally use dimensions provided by its style, but providing custom GUIDimensions allows you to override the dimensions in the style. For that reason you should provide the user with a constructor that accepts GUIOptions object, which will contain optional dimension overrides the user can specify. Then you can create a GUIDimension by providing the GUIOptions object as a parameter.
-
-It is highly suggested you check out an implementation of GUITexture (in BsGUITexture.cpp) for a simple implementation of everything mentioned.
-
-# Style # {#customGUI_b}
-
-Every GUIElement has an access to a specific style (GUIElementStyle). You can access the style by calling _getStyle(). The style controls the look of your elements, including its texture, text style and margins. When creating a custom GUIElement you normally don't need to worry about style options like margins or padding, but you should take the rest into account when creating your mesh & material.
-
-You might also need to override styleUpdated() method that will trigger when the user changes the GUIElement's style, depending on your needs. Although in most cases it will be not necessary as a style change will trigger a call to updateRenderElementsInternal() anyway.
-
-You should also provide a style-name for your element. This is a string that will be used for looking up the default style for the element, and you must provide it to the GUIElement constructor. You should also allow the user to provide a custom style name if needed (e.g. sometimes its nice to be able to style a GUIToggle element so it looks like a GUIButton).
-
-See the GUIWidget chapter for more information about styles.
-
-# Input # {#customGUI_c}
-
-If you implemented everything so far you should have a functional GUIElement that has a custom look, but it still doesn't accept any input. In order to accept input you must override some or all of the following methods:
- - _mouseEvent - Triggered when the mouse moves and/or is clicked
- - _textInputEvent - Triggered when the user inputs some text on the keyboard
- - _virtualButtonEvent - Triggered when a certain virtual button is pressed (see [Input guide](TODOLINK) if you wish to know about virtual buttons)
- - _commandEvent - Triggers when some kind of a specialized event happens, like losing/gainging focus, deleting text, selecting text, forcing redraw and similar. See GUICommandEventType for an explanation of what each does.
- 
-Check out _mouseEvent implementation in GUIButtonBase for a simple implementation of a mouse hover on/off and click functionality. Check GUIInputBox for a more complex implementation of input.
-
-If working with text input also take a look at GUIInputCaret and GUIInputSelection, as they help with input caret movement, and text selection. Instances of these classes can be accessed from GUIManager with getInputCaretTool() and getInputSelectionTool(). This is completely optional and you may choose to handle these manually. See GUIInputBox for example usage.
-
-# Updating # {#customGUI_c}
-
-If your element allows the user to update certain contents of it, you need to notify the GUI system so it knows when to rebuild the element. For example if you GUI element displays a text string you might provide a setText method that allows the user to change what is displayed. When that is done the GUI system will need to update the batched GUI mesh, update the GUI elements mesh and/or update the layout position/size of the element.
-
-There are three ways to notify the GUI system the element is dirty, each more expensive in performance than the other:
- - _markMeshDirty() - Causes the batched GUI mesh to be re-assembled, will cause a call to GUIElement::_fillBuffer. Call this if element's size didn't change (e.g. when its position, or depth changes). Batched GUI mesh is a mesh managed by the GUI system that consists of multiple GUI elements sharing the same material properties.
- - _markContentDirty() - Has the same effect as _markMeshDirty(), but will also trigger an GUIElement::updateRenderElementsInternal() call so the GUI element mesh will be fully rebuilt. Call this when contents of the GUI element change, but not its size (e.g. changing a texture but it is the same dimensions as the old one.)
- - _markLayoutDirty() - Has the same effect as _markContentDirty(), but will also cause the layout system to recalculate the element's position and size. This can be very expensive as this will generally trigger layout updates for all siblings and children, potentially even parent GUI elements. Call this when element changes and no other dirty calls are appropriate.
-
-When setting GUI contents consider using the provided GUIContent type, in order to stay consistent with the built-in elements. It allows you to specify an image, text and a tooltip all in one. But this is completely optional.
- 
-# Various # {#customGUI_d}
- 
-## GUIWidget ## {#customGUI_d_a}
- 
-Every GUIElement is internally part of a specific GUIWidget. A GUIWidget is a Component (CGUIWidget) that can be attached to a scene object, and transformed like one. This allows you to perform arbitrary transformations on GUI elements.
-
-You attach elements to a GUIWidget by adding them to the widgets GUIPanel. Each widget has a GUIPanel which is a GUILayout object that allows you to manually position GUI elements within it. Every GUI element's top level parent must be such a panel or they will not be rendered.
-
-GUIWidget also allows you to control to which camera should the GUI elements within it be rendered. The camera provides the dimensions of the widget, and in turn the available area for all child GUI elements.
-
-Every style that a GUIElement uses is retrieved from its parent GUIWidget. GUIWidget contain a GUISkin object which has a key-value map where style-names are mapped to particular GUI element styles. Changing the GUISkin, or moving a GUIElement to a widget with a different style will trigger the GUI element rebuild, so it can apply the new style options.
-
-## GUIManager ## {#customGUI_d_b}
-
-GUIManager is the top level GUI manager that handles almost everything GUI related. It keeps tracks of all GUIWidgets and GUIElements. It updates layout when element properties change, rebuilds the GUI meshes, sets up GUI materials and ultimately renders the GUI. It also dispatches all GUI input events.
-
-If you need to do some very deep changes to the GUI system this is where you do it.
-
-And every GUIWidget has a GUISkin which contains a list of all the GUI element styles. The style for a specific element will depend on the GUIWidget it is assigned to, and the GUISkin assigned to the widget. (See below for information on GUIWidgets). 
-
-## Text ##  {#customGUI_d_c}
-
-If you ever require to render text completely manually (perhaps even outside of the GUI system), take a look at TextData. It will take a text string with a font and optional bounds as input, and output (optionally) word wrapped text in the form of words and lines, from which you can easily retrieve a mesh for rendering.
- 
-## Scripting integration ## {#customGUI_d_d}
-
-Once you have a custom GUI element you might want to expose it to the scripting API. Take a look at the [scripting interop guide](TODOLINK) to learn how to create scripting API objects. 
-
-Every GUI scripting interop object must implement TScriptGUIElement, but other than that creating GUI interop objects is the same as the general case described in the guide above. See ScriptGUITexture for an example.
-
-Creating managed GUI objects is again the same as the general case. Take a look at the managed GUITexture.
+Creating custom GUI elements						{#customGUI}
+===============
+[TOC]
+
+Sometimes the built-in GUI elements are just not enough, for example when you need a very specific GUI element for your game (e.g. for displaying graphs, charts), want to use a special material or in general just need some more customization options. While in some cases you could construct such elements from the provided GUI elements, it is often more efficient to create a brand new customized type. New GUI elements can be created relatively easily and allow you to fully customize the look and interaction with your element.
+
+Before staring you should familiarize yourself how Banshee's GUI works from the users perspective. Make sure to read [GUI manual](@ref TODOLINK) to understand the basics (although the manual is for scripting, the C++ versions of these methods are essentially equivalent and the same concepts apply).
+
+All GUI elements derive from the base @ref BansheeEngine::GUIElementBase "GUIElementBase" type. The elements can be categorized into two major groups:
+ - GUILayout: They derive from @ref BansheeEngine::GUILayout "GUILayout" and do not have any graphics, but instead they control the placement of all elements attached to them.
+ - GUIElement: They derive from @ref BansheeEngine::GUIElement "GUIElement", and they're standard GUI elements like buttons, input boxes, sliders and such.
+ 
+You will almost certainly be interested in `GUIElement`, and are unlikely to need to create custom `GUILayout` types.
+
+# Basic GUI element # {#customGUI_a}
+When creating a custom GUI element you will need to override the @ref BansheeEngine::GUIElement "GUIElement" type. The minimum set of methods you need to implement for your custom elements are:
+ - @ref BansheeEngine::GUIElement::_getOptimalSize "_getOptimalSize()" - Return the optimal size of your GUI element. This is used for controlling the size of the element in a layout (if it's not fixed). For example if your element was displaying a texture 64x64 in size, then the optimal size should probably return 64x64. If you element is displaying text you can use @ref BansheeEngine::GUIHelper "GUIHelper" to help you calculate the bounds of the text. If displaying something else then it's up to you to determine what constitutes an optimal size.
+ - @ref BansheeEngine::GUIElement::_getNumRenderElements() "_getNumRenderElements()" - Return the number of separate elements that your GUIElement consists of. Each element has its own mesh, material and texture. In most cases there is only one element.
+ - @ref BansheeEngine::GUIElement::_getNumQuads() "_getNumQuads()" - Returns the number of quads for the mesh of the render element at the specified index (e.g. one quad if the GUI element displays just one texture). This allows external systems to know how big of a buffer to allocate for the element's mesh.
+ - @ref BansheeEngine::GUIElement::_fillBuffer() "_fillBuffer()" - This is the meat of the `GUIElement` override. It allows you to fill a buffer with vertices, uv coordinates and indices for a mesh for the specified render element. This allows you to fully control the look of your GUI element by providing customized geometry.
+ - @ref BansheeEngine::GUIElement::_getMaterial() "_getMaterial()" - Here you should return a material that will be applied to the mesh output in the previous method. You can choose from a few pre-built material types and provide a texture and a color. If you wish to add a new custom material, check later in this guide.
+ - @ref BansheeEngine::GUIElement::updateRenderElementsInternal() "updateRenderElementsInternal()" - Called whenever the element's size or style changes. In this method you should rebuild the element's mesh. (While you could build the mesh in `_fillBuffer` it is inefficient as `_fillBuffer` will get called much more often than `updateRenderElementsInternal` due to mesh batching).
+ 
+In order to help you with creating GUI meshes and materials make sure to take a look at @ref BansheeEngine::ImageSprite "ImageSprite" and @ref BansheeEngine::TextSprite "TextSprite" classes. `ImageSprite` can easily generate image geometry of specified size, whether a simple quad or a scale-9-grid image. And `TextSprite` will take a text string, font and additional options as input, and output a set of quads required for text rendering.
+
+When constructing the `GUIElement` you will also need to provide a style-name (see next chapter) and dimensions to the `GUIElement` constructor. The dimensions determine the initial size of the element. To use the default dimensions just provide the constructor with return value from @ref BansheeEngine::GUIDimensions::create() "GUIDimensions::create()". `GUIElement` will internally use dimensions provided by its style, but providing custom `GUIDimensions` allows you to override the dimensions of the style. For that reason you should provide the user with a constructor that accepts @ref BansheeEngine::GUIOptions "GUIOptions" object, which will contain optional dimension overrides the user can specify. Then you can create a `GUIDimension` by providing the `GUIOptions` object as a parameter.
+
+It is highly suggested you check out an implementation of @ref BansheeEngine::GUITexture "GUITexture" (in *BsGUITexture.cpp*) for a simple implementation of everything mentioned.
+
+# Style # {#customGUI_b}
+
+Every `GUIElement` has an access to a specific style (@ref BansheeEngine::GUIElementStyle "GUIElementStyle"). You can access the style by calling @ref BansheeEngine::GUIElement::_getStyle() "GUIElement::_getStyle()". The style controls the look of your elements, including its texture, text style and margins. When creating a custom `GUIElement` you normally don't need to worry about style options like margins or padding, but you should take the rest into account when creating your mesh and material. You could also choose to ignore the style at the consequence of not having a skinnable `GUIElement`.
+
+You might also need to override @ref BansheeEngine::GUIElement::styleUpdated() "GUIElement::styleUpdated()" method that will trigger when the user changes the `GUIElement`'s style, depending on your needs. Although in most cases it will be not necessary as a style change will trigger a call to `updateRenderElementsInternal()`, so you can retrieve the new style information there, as well as rebuild any contents as needed.
+
+You should also provide a style-name for your element. This is a string that will be used for looking up the default style for the element, and you must provide it to the `GUIElement` constructor. This name will be used for looking up the exact `GUIElementStyle` in the currently active `GUISkin` (see later for infomation about GUI skins). You might also allow the user to provide a custom style name if needed (e.g. sometimes its nice to be able to style a `GUIToggle` element so it looks like a `GUIButton`).
+
+# Input # {#customGUI_c}
+
+If you implemented everything so far you should have a functional GUIElement that has a custom look, but it still doesn't accept any input. In order to accept input you must override some or all of the following methods:
+ - @ref BansheeEngine::GUIElement::_mouseEvent "GUIElement::_mouseEvent" - Triggered when the mouse moves and/or is clicked
+ - @ref BansheeEngine::GUIElement::_textInputEvent "GUIElement::_textInputEvent" - Triggered when the user inputs some text on the keyboard
+ - @ref BansheeEngine::GUIElement::_virtualButtonEvent "GUIElement::_virtualButtonEvent" - Triggered when a certain virtual button is pressed (see [Input guide](@ref input) if you wish to know about virtual buttons)
+ - @ref BansheeEngine::GUIElement::_commandEvent "GUIElement::_commandEvent" - Triggers when some kind of a specialized event happens, like losing/gainging focus, deleting text, selecting text, forcing redraw and similar. See @ref BansheeEngine::GUICommandEventType "GUICommandEventType" for an explanation of what each does.
+ 
+Check out `_mouseEvent` implementation in @ref BansheeEngine::GUIButtonBase "GUIButtonBase" for a simple implementation of a mouse hover on/off and click functionality. Check @ref BansheeEngine::GUIInputBox "GUIInputBox" for a more complex implementation of input.
+
+If working with text input also take a look at @ref BansheeEngine::GUIInputCaret "GUIInputCaret" and @ref BansheeEngine::GUIInputSelection "GUIInputSelection", as they help with input caret movement, and text selection. Instances of these classes can be accessed from @ref BansheeEngine::GUIManager "GUIManager" with @ref BansheeEngine::GUIManager::getInputCaretTool() "getInputCaretTool()" and @ref BansheeEngine::GUIManager::getInputSelectionTool() "getInputSelectionTool()". This is completely optional and you may choose to handle these manually. See @ref BansheeEngine::GUIInputBox "GUIInputBox" for example usage.
+
+# Updating # {#customGUI_d}
+
+If your element allows the user to update certain contents of it after creation (i.e. it's not static), you need to notify the GUI system so it knows when to rebuild the element. For example if your GUI element displays a text string you might provide a `setText` method that allows the user to change what is displayed. When that is done the GUI system will need to update the batched GUI mesh, update the GUI elements mesh and/or update the layout position/size of the element.
+
+There are three ways to notify the GUI system the element is dirty, each more expensive in performance than the previous:
+ - @ref BansheeEngine::GUIElement::_markMeshAsDirty() "_markMeshAsDirty()" - Causes the batched GUI mesh to be re-assembled, will cause a call to `_fillBuffer`. Call this if element's size didn't change (e.g. when its position, or depth changes). Batched GUI mesh is a mesh managed by the GUI system that consists of multiple GUI elements sharing the same material properties.
+ - @ref BansheeEngine::GUIElement::_markContentAsDirty() "_markContentAsDirty()" - Has the same effect as `_markMeshDirty()`, but will also trigger an `updateRenderElementsInternal` call so the GUI element mesh will be fully rebuilt. Call this when contents of the GUI element change, but not its size (e.g. changing a texture but it is the same dimensions as the old one.)
+ - @ref BansheeEngine::GUIElement::_markLayoutAsDirty() "_markLayoutAsDirty()" - Has the same effect as `_markContentDirty()`, but will also cause the layout system to recalculate the element's position and size. This can be very expensive as this will generally trigger layout updates for all siblings and children, potentially even parent GUI elements. Call this when element changes and no other dirty calls are appropriate. Normally you have to call this whenever the element's size changes.
+
+When updating your GUI element with new contents consider using the provided @ref BansheeEngine::GUIContent "GUIContent" type, in order to stay consistent with the built-in elements. It allows you to specify an image, text and a tooltip all in one. But this is completely optional.
+ 
+# Various # {#customGUI_e}
+ 
+## GUIWidget ## {#customGUI_d_a}
+ 
+Every `GUIElement` is internally part of a specific `GUIWidget`. A `GUIWidget` is a @ref BansheeEngine::Component "Component" (@ref BansheeEngine::CGUIWidget "CGUIWidget") that can be attached to a scene object, and transformed like one. This allows you to perform arbitrary transformations on GUI elements.
+
+You attach elements to a `GUIWidget` by adding them to the widget's @ref BansheeEngine::GUIPanel "GUIPanel". Each widget has a `GUIPanel` which is a `GUILayout` object that allows you to manually position GUI elements within it. Every GUI element's top level parent must be such a panel or they will not be rendered.
+
+`GUIWidget` also allows you to control to which camera should the GUI elements within it be rendered. The camera provides the dimensions of the widget, and in turn the available area for all child GUI elements.
+
+Every style that a `GUIElement` uses is retrieved from its parent `GUIWidget`. GUIWidget contains a @ref BansheeEngine::GUISkin "GUISkin" object which has a key-value map where style-names are mapped to particular GUI element styles. Changing the `GUISkin`, or moving a `GUIElement` to a widget with a different style will trigger the GUI element rebuild, so it can apply the new style options.
+
+## GUIManager ## {#customGUI_d_b}
+
+@ref BansheeEngine::GUIManager "GUIManager" is the top level GUI manager that handles almost everything GUI related. It keeps tracks of all `GUIWidget`s and `GUIElement`s. It updates layout when element properties change, rebuilds the GUI meshes, sets up GUI materials and ultimately renders the GUI. It also dispatches all GUI input events.
+
+If you need to do some very deep changes to the GUI system this is where you do it.
+
+## Text ##  {#customGUI_d_c}
+
+If you ever require to render text completely manually (perhaps even outside of the GUI system), take a look at @ref BansheeEngine::TextData "TextData". It will take a text string with a font and optional bounds as input, and output (optionally) word wrapped text in the form of words and lines, from which you can easily retrieve a mesh for rendering.
+ 
+## Scripting integration ## {#customGUI_d_d}
+
+Once you have a custom GUI element you might want to expose it to the scripting API. Take a look at the [scripting interop guide](@ref scripting) to learn how to create scripting API objects. 
+
+Every GUI scripting interop object must implement @ref BansheeEngine::TScriptGUIElement "TScriptGUIElement", but other than that creating GUI interop objects is the same as the general case described in the guide above. See @ref BansheeEngine::ScriptGUITexture "ScriptGUITexture" for an example.
+
+Creating managed GUI objects is again the same as the general case. Take a look at the managed GUITexture implementation for an example.

+ 146 - 127
Documentation/Manuals/Native/scripting.md

@@ -4,95 +4,97 @@ Scripting									{#scripting}
 
 Often when you extend the native portion of the engine in some way you might want to expose that functionality to the managed (i.e. scripting) code. This guide will show you how to create C++ objects that communicate with C# code and vice versa. 
 
-Before we delve into the specifics of Banshees scripting you should understand how the scripting system works in general. All C# script code is ran from the C++ part of the engine using the Mono runtime. Mono runtime allows you to communicate with C# code (and for the C# code to communicate with C++), query class/method/field information and pass data between the two languages.
+Before we delve into the specifics of Banshee's scripting you should understand how the scripting system works in general. All C# script code is ran from the C++ part of the engine using the Mono runtime. Mono runtime allows you to communicate with C# code (and for the C# code to communicate with C++), query class/method/field information and pass data between the two languages.
 
 # Scripting system #	{#scripting_a}
 
-Because using Mono directly is complex because its lack of documentation, Banshee provides a set of easy to use wrappers for almost all of Mono related functionality. 
+Because using Mono directly is complex (mostly due to its lack of documentation), Banshee provides a set of easy to use wrappers for almost all of Mono related functionality. 
 
-BansheeMono is a plugin that wraps the functionality of the Mono runtime. Here is where the Mono runtime is started and assemblies are loaded (and unloaded), while also giving you detailed information about all loaded classes and a way to communicate with managed code.
+@ref BansheeMono is a plugin that wraps the functionality of the Mono runtime. Here is where the Mono runtime is started and assemblies are loaded (and unloaded), while also giving you detailed information about all loaded classes and a way to communicate with managed code.
 
-The main entry point is the MonoManager class which allows you to start the runtime and load assemblies. The most important method here is MonoManager::loadAssembly. It takes as assembly path to load and returns a MonoAssembly object. 
+The main entry point is the @ref BansheeEngine::MonoManager "MonoManager" class which allows you to start the runtime and load assemblies. The most important method here is @ref BansheeEngine::MonoManager::loadAssembly "MonoManager::loadAssembly". It takes as assembly path to load and returns a @ref BansheeEngine::MonoAssembly "MonoAssembly" object. 
 
 ## MonoAssembly 	{#scripting_a_a}
-MonoAssembly gives you access to all classes in the assembly. You can retrieve all clases using MonoAssembly::getAllClasses, or retrieve a specific one by calling MonoAssembly::getClass(namespace, typename). Both of these methods return a MonoClass object.
+@ref BansheeEngine::MonoAssembly "MonoAssembly" gives you access to all classes in the assembly. You can retrieve all clases using @ref BansheeEngine::MonoAssembly::getAllClasses "MonoAssembly::getAllClasses", or retrieve a specific one by calling @ref BansheeEngine::MonoAssembly::getClass(const String&, const String&) "MonoAssembly::getClass(namespace, typename)". Both of these methods return a @ref BansheeEngine::MonoClass "MonoClass" object.
 
 ## MonoClass 	{#scripting_a_b}
-MonoClass gives you access to all methods, fields, properties and attributes of a specific class. It also allows you to register “internal” methods. These methods allow the managed code to call C++ code, and we’ll go into them later.
+@ref BansheeEngine::MonoClass "MonoClass" gives you access to all methods, fields, properties and attributes of a specific class. It also allows you to register "internal" methods. These methods allow the managed code to call C++ code, and we'll go into them later.
 
-Classes also allow you to create object instances of their type. Use MonoClass::createInstance to create a new object instance. All managed objects are referenced using a MonoObject type (more on that later), which is returned from the MonoClass::createInstance call. When creating an instance you may choose whether to construct it or not, and to provide constructor signature if you need a specific one.
+Classes also allow you to create object instances of their type. Use @ref BansheeEngine::MonoClass::createInstance "MonoClass::createInstance" to create a new object instance. All managed objects are referenced using a `MonoObject` type (more on that later), which is returned from the @ref BansheeEngine::MonoClass::createInstance "MonoClass:createInstance" call. When creating an instance you may choose whether to construct it or not, and to provide constructor signature if you need a specific one.
 
-To retrieve a method from a class call MonoClass::getMethod(), accepting a name (without parameter types) and a number of parameters. If your method is overloaded you can use MonoClass::getMethodExact which accepts a method name, and a comma separated list of parameter types. You may also use MonoClass::getAllMethods to retrieve all methods in a class.
+To retrieve a method from a class call @ref BansheeEngine::MonoClass::getMethod() "MonoClass::getMethod()", accepting a name (without parameter types) and a number of parameters. If your method is overloaded you can use @ref BansheeEngine::MonoClass::getMethodExact "MonoClass:getMethodExact()" which accepts a method name, and a comma separated list of parameter types. You may also use @ref BansheeEngine::MonoClass::getAllMethods "MonoClass::getAllMethods" to retrieve all methods in a class.
 
-All the above methods return a MonoMethod object.
+All the above methods return a @ref BansheeEngine::MonoMethod "MonoMethod" object.
 
 ## MonoMethod {#scripting_a_c}
 This class provides information about about a managed method, as well as giving you multiple ways of invoking it.
 
 To invoke a method you may use multiple approaches:
- - MonoMethod::invoke - Calls the exact method on a specific managed object, with the provided parameters. Well take about how managed objects are referenced in native code later, as well as how passing data between C++ and managed code works.
- - MonoMethod::invokeVirtual - Calls the method polymorphically. Determines the actual type of the provided managed object instance and calls an overriden method if available.
- - MonoMethod::getThunk - Returns a C++ function pointer accepting a managed object, zero or more parameters and an exception object. You can then call the function pointer like you would a C++ function. This is equivalent to MonoMethod::invoke but is significantly faster. A helper method MonoUtil::invokeThunk is provided - it is suggested you use it instead of calling thunks manually (it handles exceptions without you needing to worry about it).
+ - @ref BansheeEngine::MonoMethod::invoke "MonoMethod::invoke" - Calls the exact method on a specific managed object, with the provided parameters. We'll take about how managed objects are referenced in native code later, as well as how passing data between C++ and managed code works.
+ - @ref BansheeEngine::MonoMethod::invokeVirtual "MonoMethod::invokeVirtual" - Calls the method polymorphically. Determines the actual type of the provided managed object instance and calls an overriden method if available.
+ - @ref BansheeEngine::MonoMethod::getThunk "MonoMethod::getThunk" - Returns a C++ function pointer accepting a managed object, zero or more parameters and an exception object. You can then call the function pointer like you would a C++ function. This is equivalent to @ref BansheeEngine::MonoMethod::invoke "MonoMethod::invoke" but is significantly faster. A helper method @ref BansheeEngine::MonoUtil::invokeThunk "MonoUtil::invokeThunk" is provided - it is suggested you use it instead of calling thunks manually (it handles exceptions internally).
 
 When calling static methods you should provide a null value for the managed object instance.
 
 ## MonoField {#scripting_a_d}
-Similar to methods, field information can be retrieved from MonoClass by calling MonoClass::getField or MonoClass::getAllFields. The returned value is a MonoField which provides information about the field and allows you to retrieve and set values in the field using MonoField::getValue/MonoField::setValue. This works similar to how methods are invoked and is explained in more detail later.
+Similar to methods, field information can be retrieved from @ref BansheeEngine::MonoClass "MonoClass" by calling @ref BansheeEngine::MonoClass::getField "MonoClass::getField" or @ref BansheeEngine::MonoClass::getAllFields "MonoClass::getAllFields". The returned value is a @ref BansheeEngine::MonoField "MonoField" which provides information about the field and allows you to retrieve and set values in the field using @ref BansheeEngine::MonoField::getValue "MonoField::getValue" / @ref BansheeEngine::MonoField::setValue "MonoField::setValue". This works similar to how methods are invoked and is explained in more detail later.
 
 ## MonoProperty {#scripting_a_e}
-Properties are very similar to fields, retrieved from MonoClass by calling MonoClass::getProperty or MonoClass::getAllProperties. The returned value is a MonoProperty which provides information about the property and allows you to retrieve and set values in it. The main difference is that properties in C# can be indexed (like arrays) and therefore a two set of set/get methods are provided, one accepting an index and other one not. Its up to the user to know which one to call. The methods are MonoPropety::get/MonoProperty::set and MonoProperty::getIndexed/MonoProperty::setIndexed.
+Properties are very similar to fields, retrieved from @ref BansheeEngine::MonoClass "MonoClass" by calling @ref BansheeEngine::MonoClass::getProperty "MonoClass::getProperty" or @ref BansheeEngine::MonoClass::getAllProperties "MonoClass::getAllProperties". The returned value is a @ref BansheeEngine::MonoProperty "MonoProperty" which provides information about the property and allows you to retrieve and set values in it. The main difference is that properties in C# can be indexed (like arrays) and therefore a two set of set/get methods are provided, one accepting an index and other one not. It's up to the user to know which one to call. The methods are @ref BansheeEngine::MonoProperty::get "MonoProperty::get" / @ref BansheeEngine::MonoProperty::set "MonoProperty::set" and @ref BansheeEngine::MonoProperty::getIndexed "MonoProperty::getIndexed" / @ref BansheeEngine::MonoProperty::setIndexed "MonoProperty::setIndexed".
 
 ## Attributes {#scripting_a_f}
-Attributes provide data about a class, method or field provided at runtime, and allows them to be customized in more detail. Attributes don’t have their own wrapper, because they are esentially normal managed objects and you can work with them as such.
+Attributes provide data about a class, method or field provided at runtime, which usually allows such objects to be specialized in some regard. Attributes don't have their own wrapper, because they are esentially normal managed objects and you can work with them as such.
 
-To retrieve a list of attributes from a class use: MonoClass::getAllAttributes(), which returns a list of MonoClass objects that identify the attribute types. To get the actual object instance of the attribute you may call MonoClass::getAttribute with the wanted attribute MonoClass. After that you can call methods, work with field values and other, same as you would with a normal object.
+To retrieve a list of attributes from a class use @ref BansheeEngine::MonoClass::getAllAttributes() "MonoClass::getAllAttributes", which returns a list of @ref BansheeEngine::MonoClass "MonoClass" objects that identify the attribute types. To get the actual object instance of the attribute you may call @ref BansheeEngine::MonoClass::getAttribute "MonoClass::getAttribute" with the wanted attribute @ref BansheeEngine::MonoClass "MonoClass". After that you can call methods, work with field values and other, same as you would with a normal object.
 
-Attributes can also be retrieved from MonoMethod using MonoMethod::getAttribute, or from MonoField using MonoField::getAttribute.
+Attributes can also be retrieved from a @ref BansheeEngine::MonoMethod "MonoMethod" using @ref BansheeEngine::MonoMethod::getAttribute "MonoMethod::getAttribute", or from @ref BansheeEngine::MonoField "MonoField" using @ref BansheeEngine::MonoField::getAttribute "MonoField::getAttribute".
 
 ## Managed objects {#scripting_a_g}
-So far we have talked about invoking methods and retrieving field values, but we havent yet explained how to access managed object instances in C++ code. All managed objects in Mono are represented by a MonoObject.
+So far we have talked about invoking methods and retrieving field values, but we haven't yet explained how to access managed object instances in C++ code. All managed objects in Mono are represented by a `MonoObject`.
 
-For example, when calling a non-static method the first parameter provided to MonoMethod::invoke is a MonoObject pointer. Same goes for retrieving or setting values on fields, properties. Attributes are also a MonoObject.
+For example, when calling a non-static method the first parameter provided to @ref BansheeEngine::MonoMethod::invoke "MonoMethod::invoke" is a `MonoObject` pointer. Same goes for retrieving or setting values on fields, properties. Attributes are also a `MonoObject`.
 
-Mono also provides two more specialized types: MonoArray for managed arrays, and MonoString for managed strings, but they are both still a MonoObject.
+Mono also provides two more specialized types: `MonoArray` for managed arrays, and `MonoString` for managed strings, but they are both still a `MonoObject`.
 
-Be aware that all managed objects are garbage collected. This means you should not keep a reference to them unless you are sure they are alive. Just having a pointer to a MonoObject will not keep the object alive and it may go out of scope as soon as the control returns to managed code. A good way to deal with this issue is:
- - Call a native method in the object’s finalizer (~MyObject()) which will notify you when the object is no longer valid. Be aware that finalizer may be called after the object is unusable.
- - Require the user to manually destroy the object by calling a custom Destroy method or similar.
- - Force the garbage collector to keep the object alive by calling “mono_gchandle_new” which will return a handle to the object. The handle will keep the object alive until you release it by calling “mono_gchandle_free”. Be aware if an assembly the object belongs to is unloaded all objects will be destroyed regardless of the kept handles.
+Be aware that all managed objects are garbage collected. This means you should not keep a reference to them unless you are sure they are alive. Just having a pointer to a `MonoObject` will not keep the object alive and it may go out of scope as soon as the control returns to managed code. A good way to deal with this issue is:
+ - Call a native method in the object's finalizer (`~MyObject()`) which will notify you when the object is no longer valid. Be aware that finalizer may be called after the object is unusable.
+ - Require the user to manually destroy the object by calling a custom `Destroy` method or similar.
+ - Force the garbage collector to keep the object alive by calling `mono_gchandle_new` which will return a handle to the object. The handle will keep the object alive until you release it by calling `mono_gchandle_free`. Be aware if an assembly the object belongs to is unloaded all objects will be destroyed regardless of the kept handles.
  
 ## Marshalling data {#scripting_a_h}
 Mono does not perform automatic marshalling of data when calling managed code from C++ (or vice versa). This is important when calling methods, retrieving/setting field/property values, and responding to calls from managed code, because you need to know in what format to expect the data.
 
 The rules are:
- - All primitive types are passed as is. e.g. an int in C# will be a 4 byte integer in C++, a float will be a float, a bool will be a bool.
- - All reference types (“class” in C#) are passed as a MonoObject*. Strings and arrays are handled specially, where strings are passed as MonoString*, and arrays as MonoArray*.
-   - If a reference type parameter in a method is prefixed with an “out” modifier, then the received parameters are MonoObject**, MonoString**, MonoArray** and your method is expected to populate those values.
- - Structs (non-primitive value types) are provided as raw memory. Make sure that all structs in C# have a ”[StructLayout(LayoutKind.Sequential)]” attribute, which ensures they have the same memory layout as C++ structs. This way you can just accept the raw C++ structure and read it with no additional conversion.
-  - It is suggested you never pass structures by value, it is known to cause problems in Mono. Instead pass all structures by prefixing them with “ref” which will give you a pointer to the structure in managed code (e.g. MyStruct*). If you need to output a struct use the “out” modifier which you will give you a double pointer (e.g. MyStruct**).
-  - In cases where it is not possible to avoid passing structures by value (e.g. when retrieving them from a field, use the MonoField::getValueBoxed method instead MonoField::getValue, which will return a struct in the form of a MonoObject. You can then retrieve the raw struct value by calling mono_object_unbox.
-  - Everything above applies only when managed code is calling C++. When calling into managed code from C++, all structs need to be boxed (i.e. converted to MonoObject). Use mono_value_box to convert a C++ struct into a MonoObject*. See ScriptVector3 for an example implementation.
+ - All primitive types are passed as is. e.g. an `int` in C# will be a 4 byte integer in C++, a `float` will be a float, a `bool` will be a bool.
+ - All reference types (`class` in C#) are passed as a `MonoObject*`. Strings and arrays are handled specially, where strings are passed as `MonoString*`, and arrays as `MonoArray*`.
+   - If a reference type parameter in a method is prefixed with an `out` modifier, then the received parameters are `MonoObject**`, `MonoString**`, `MonoArray**` and your method is expected to populate those values.
+ - Structs (non-primitive value types) are provided as raw memory. Make sure that all structs in C# that require marshalling have a `[StructLayout(LayoutKind.Sequential)]` attribute, which ensures they have the same memory layout as C++ structs. This way you can just accept the raw C++ structure and read it with no additional conversion.
+  - It is suggested you never pass structures by value, it is known to cause problems in Mono. Instead pass all structures by prefixing them with `ref` which will give you a pointer to the structure in managed code (e.g. `MyStruct*`). If you need to output a struct use the `out` modifier which you will give you a double pointer (e.g. `MyStruct**`).
+  - In cases where it is not possible to avoid passing structures by value (e.g. when retrieving them from a field, use the @ref BansheeEngine::MonoField::getValueBoxed "MonoField::getValueBoxed" method instead @ref BansheeEngine::MonoField::getValue "MonoField::getValue", which will return a struct in the form of a `MonoObject`. You can then retrieve the raw struct value by calling `mono_object_unbox`.
+  - Everything above applies only when managed code is calling C++. When calling into managed code from C++, all structs need to be boxed (i.e. converted to `MonoObject`). Use `mono_value_box` to convert a C++ struct into a `MonoObject*`. See @ref BansheeEngine::ScriptVector3 "ScriptVector3" for an example implementation.
   
 Banshee provides a helper code to assist with marshalling string:
- - MonoUtil::toWString/MonoUtil::toString - Converts a MonoString* to a native string
- - MonoUtil::fromWString/MonoUtil::fromString - Converts a native string into a MonoString*
+ - @ref BansheeEngine::MonoUtil::monoToWString "MonoUtil::monoToWString" / @ref BansheeEngine::MonoUtil::monoToString "MonoUtil::monoToString" - Converts a `MonoString*` to a native string
+ - @ref BansheeEngine::MonoUtil::wstringToMono "MonoUtil::wstringToMono" / @ref BansheeEngine::MonoUtil::stringToMono "MonoUtil::stringToMono" - Converts a native string into a `MonoString*`
 
-ScriptArray allows you to construct new arrays and read managed arrays, easily. 
+@ref BansheeEngine::ScriptArray "ScriptArray" allows you to construct new arrays and read managed arrays, easily. 
 
-To create a new arrays call ScriptArray<Type>::create. Type can be a primitive type like int, float, a native string or a Script* object (more about Script* objects later). You can then fill the array by calling ScriptArray::set and retrieve the managed MonoArray* by calling ScriptArray::getInternal.
+To create a new arrays call @ref BansheeEngine::ScriptArray::create "ScriptArray<Type>::create". Type can be a primitive type like `int`, `float`, a native string or a `Script*` object (more about `Script*` objects later). You can then fill the array by calling @ref BansheeEngine::ScriptArray::set "ScriptArray::set" and retrieve the managed `MonoArray*` by calling @ref BansheeEngine::ScriptArray::getInternal "ScriptArray::getInternal".
 
-To more easily read existing arrays create a new ScriptArray by providing it with a MonoArray* as its parameters. Then you can easily retrieve the size of the array using ScriptArray::size(), and the value of its elements by calling ScriptArray::get<Type>. 
+To more easily read existing arrays create a new @ref BansheeEngine::ScriptArray "ScriptArray" by providing it with a `MonoArray*` as its parameters. Then you can easily retrieve the size of the array using @ref BansheeEngine::ScriptArray::size() "ScriptArray::size()", and the value of its elements by calling @ref BansheeEngine::ScriptArray::get "ScriptArray::get<Type>"". 
 
 ## Internal methods {#scripting_a_i}
 So far we have talked about calling managed code, and retrieving information about managed types, but we have yet to show how managed code calls C++ code. This is accomplished using native methods.
 
 The first step is to define a stub method in managed code, like so:
-	[MethodImpl(MethodImplOptions.InternalCall)]
-	private static extern float Internal_GetSomeValue(MyObject obj);
-
-You then hook up this method with managed code by calling MonoClass:addInternalMethod. In this specific case it would be myClass->ddInternalMethod(“Internal_GetSomeValue”, &myNativeFunction), assuming myClass is a MonoClass of the type that contains the stub method. After this call any call to the managed stub method will call the provide native function myNativeFunction. You should take care to properly handle parameter passing as described above.
+~~~~~~~~~~~~~{.cs}
+[MethodImpl(MethodImplOptions.InternalCall)]
+private static extern float Internal_GetSomeValue(MyObject obj);
+~~~~~~~~~~~~~
+	
+You then hook up this method with managed code by calling @ref BansheeEngine::MonoClass::addInternalMethod "MonoClass::addInternalMethod". In this specific case it would be `myClass->addInternalMethod("Internal_GetSomeValue", &myNativeFunction)`, assuming `myClass` is a @ref BansheeEngine::MonoClass "MonoClass" of the type that contains the stub method. After this call any call to the managed stub method will call the provided native function `myNativeFunction`. You should take care to properly handle parameter passing as described above.
 
-Take a look at ScriptGUISkin implementation for a simple example of how exactly does this work. 
+Take a look at @ref BansheeEngine::ScriptGUISkin "ScriptGUISkin" implementation for a simple example of how exactly does this work. 
 
 # Script objects {#scripting_b}
 As you can see interaction between the two languages can get a bit cumbersome. For that reason Banshee implements a higher level system built on the functionality shown so far. It provides an universal interface all script objects must implement. It primarily ensures that native and managed code is always linked by keeping a pointer to each other's objects, as well as gracefully handling managed object destruction and handling assembly refresh (due to script hot-swap).
@@ -103,135 +105,152 @@ When creating a new class available for scripting you need to add two things:
 
 ## Native interop object ## {#scripting_b_a}
 
-All native interop objects implement the ScriptObject interface. A basic implementation of such an interface is:
-	class ScriptMyObject : public ScriptObject <ScriptMyObject>
-	{
-	public:
-		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "MyObject")
+All native interop objects implement the @ref BansheeEngine::ScriptObject "ScriptObject" interface. A basic implementation of such an interface can be:
+~~~~~~~~~~~~~{.cpp}
+class ScriptMyObject : public ScriptObject <ScriptMyObject>
+{
+public:
+	SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "MyObject")
 
-	private:
-		ScriptMyObject(MonoObject* instance);
-		
-		static void internal_CreateInstance(MonoObject* obj);
-		static float internal_GetSomeValue(MonoObject* obj);
-	};
+private:
+	ScriptMyObject(MonoObject* instance);
 	
-All ScriptObject must begin with a SCRIPT_OBJ macro. The macro accepts (in order): 
- - the name of the assembly (.dll) the manager script object is in, this is either ENGINE_ASSEMBLY or EDITOR_ASSEMBLY
+	static void internal_CreateInstance(MonoObject* obj);
+	static float internal_GetSomeValue(MonoObject* obj);
+};
+~~~~~~~~~~~~~	
+	
+All `ScriptObject`s must begin with a `SCRIPT_OBJ` macro. The macro accepts (in order): 
+ - the name of the assembly (.dll) the manager script object is in, this is either `ENGINE_ASSEMBLY` or `EDITOR_ASSEMBLY`
  - the namespace the type is in
  - the name of the managed type
 
-SCRIPT_OBJ macro also defines a static initRuntimeData() method you need to implement (more on that later).
+`SCRIPT_OBJ` macro also defines a static `initRuntimeData()` method you need to implement (more on that later).
 
-When constructing a ScriptObject you must also provide a pointer to the managed object that wraps it (note the constructor). If the ScriptObject is used for a class that is static then the constructor is of no consequence as the ScriptObject itself never needs to be instantiated (all of its method will be static). 
+When constructing a `ScriptObject` you must also provide a pointer to the managed object that wraps it (note the constructor). If the `ScriptObject` is used for a class that is static then the constructor is of no consequence as the `ScriptObject` itself never needs to be instantiated (all of its methods will be static). 
  
 The two last method definitions are called from C# (via an internal call, see the section about internal methods earlier).
 
 ### initRuntimeData ### {#scripting_b_a_a}
-initRuntimeData is a static method that every ScriptObject needs to implement. It takes care of hooking up managed internal methods to C++ functions. It gets called automatically whenever the assembly containing the related managed class is loaded. 
+`initRuntimeData` is a static method that every `ScriptObject` needs to implement. It takes care of hooking up managed internal methods to C++ functions. It gets called automatically whenever the assembly containing the related managed class is loaded. 
 
-Every ScriptObject provides a static "metaData" structure you can use for retrieving the MonoClass of the related managed class. You can use that MonoClass to register internal methods to it (as described earlier). For example a basic initRuntimeData() might look like so:
-	void ScriptMyObject::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptFont::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_GetSomeValue", &ScriptFont::internal_GetSomeValue);
-	}
+Every `ScriptObject` provides a static @ref BansheeEngine::ScriptObject<Type, Base>::metaData "metaData" structure you can use for retrieving the @ref BansheeEngine::MonoClass "MonoClass" of the related managed class. You can use that @ref BansheeEngine::MonoClass "MonoClass" to register internal methods to it (as described earlier). For example a basic `initRuntimeData()` might look like so:
+~~~~~~~~~~~~~{.cpp}
+void ScriptMyObject::initRuntimeData()
+{
+	metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptFont::internal_CreateInstance);
+	metaData.scriptClass->addInternalCall("Internal_GetSomeValue", &ScriptFont::internal_GetSomeValue);
+}
+~~~~~~~~~~~~~
 
-initRuntimeData is also a good spot to retrieve MonoMethod (or thunks) for managed methods that needed to be called by the script interop object, if any.
+`initRuntimeData` is also a good spot to retrieve @ref BansheeEngine::MonoMethod "MonoMethod" (or thunks) for managed methods that needed to be called by the script interop object, if any.
 
 ### Creating script object instances ### {#scripting_b_a_b}	
-If your class is not static you will need to eventually create an instance of the script object. This can be done either from C# or C++, depending on what is needed. For example script interop objects for GUI will be created from managed code because user can add GUI elements himself, but a resource like Font will have its script interop object (and managed instanced) created purely from C++ because such an object cannot be created directly in managed code.
-
-For the first case you should set up an internal method that accepts the managed object instance, and is called in the managed constructor (internal_CreateInstance in above example). This way the method gets called whenever the managed object gets created and you can create the related script interop object. A simple implementation would look like so:
-
-	void ScriptMyObject::internal_createInstance(MonoObject* obj)
-	{
-		bs_new<ScriptMyObject>(obj);
-	}
+If your class is not static you will need to eventually create an instance of the script object. This can be done either from C# or C++, depending on what is needed. For example script interop objects for GUI will be created from managed code because user can add GUI elements himself, but a resource like @ref BansheeEngine::Font "Font" will have its script interop object (and managed instanced) created purely from C++ because such an object cannot be created directly in managed code.
+
+For the first case you should set up an internal method that accepts the managed object instance, and is called in the managed constructor (`internal_CreateInstance` in the above example). This way the method gets called whenever the managed object gets created and you can create the related script interop object. A simple implementation would look like so:
+~~~~~~~~~~~~~{.cpp}
+void ScriptMyObject::internal_createInstance(MonoObject* obj)
+{
+	bs_new<ScriptMyObject>(obj);
+}
+~~~~~~~~~~~~~
 	
-Note that you don't actually need to store the created object anywhere. The ScriptObject constructor ensures that the pointer to the script interop object is stored in the managed object.
-
-For the second case where you want to create the interop object from C++ you can create a static create() method like so:
-    MonoObject* ScriptMyObject::create()
-	{
-		MonoObject* managedObj = metaData.scriptClass->createInstance();
-		bs_new<ScriptMyObject>(managedObj);
-		
-		return managedObj;
-	}
+Note that you don't actually need to store the created object anywhere. The `ScriptObject` constructor ensures that the pointer to the script interop object is stored in the managed object.
+
+For the second case where you want to create the interop object from C++ you can create a static `create()` method like so:
+~~~~~~~~~~~~~{.cpp}
+MonoObject* ScriptMyObject::create()
+{
+	MonoObject* managedObj = metaData.scriptClass->createInstance();
+	bs_new<ScriptMyObject>(managedObj);
+	
+	return managedObj;
+}
+~~~~~~~~~~~~~
 	
 In this case the method calls a parameterless constructor but you may specify parameters as needed.
 
-If you have a MonoObject* but need to retrieve its ScriptObject use toNative(MonoObject*) static method. e.g. ScriptMyObject::toNative(). Within the interop object instance you can use ScriptObject::getManagedInstance() to retrieve the managed object.
+If you have a `MonoObject*` but need to retrieve its `ScriptObject` use @ref BansheeEngine::ScriptObject::toNative(MonoObject*) "toNative(MonoObject*)" static method. Within the interop object instance you can use @ref BansheeEngine::ScriptObject::getManagedInstance() "ScriptObject::getManagedInstance()" to retrieve the managed object.
 
 ### Destroying script object instances ### {#scripting_b_a_c}
-When the managed object is destroyed (e.g. goes out of scope and gets garbage collected) the system will automatically take care of freeing the related ScriptObject. If you need to add onto or replace that functionality you can override ScriptObject::_onManagedInstanceDeleted method.
+When the managed object is destroyed (e.g. goes out of scope and gets garbage collected) the system will automatically take care of freeing the related ScriptObject. If you need to add onto or replace that functionality you can override @ref BansheeEngine::ScriptObject::_onManagedInstanceDeleted "ScriptObject::_onManagedInstanceDeleted" method.
 
 ## Managed wrapper object ## {#scripting_b_b}
 Creating the script interop object is one half of the job done. You also need to create the managed counterpart, however that is significantly simpler.
 
 Every managed script object must implement the ScriptObject interface. For example a C# version of the class we're using in this example would look like:
-	namespace BansheeEngine
+~~~~~~~~~~~~~{.cs}
+namespace BansheeEngine
+{
+	public class MyObject : ScriptObject
 	{
-		public class MyObject : ScriptObject
+		public MyObject()
+		{
+			Internal_CreateInstance(this)
+		}
+		
+		public float SomeValue
 		{
-			public MyObject()
-			{
-				Internal_CreateInstance(this)
-			}
-			
-			public float SomeValue
-			{
-				get { return Internal_GetSomeValue(this); }
-			}
-			
-			[MethodImpl(MethodImplOptions.InternalCall)]
-			private static extern void Internal_CreateInstance(MyObject obj);
-			
-			[MethodImpl(MethodImplOptions.InternalCall)]
-			private static extern float Internal_GetSomeValue(MyObject obj);
+			get { return Internal_GetSomeValue(this); }
 		}
+		
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_CreateInstance(MyObject obj);
+		
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern float Internal_GetSomeValue(MyObject obj);
 	}
+}
+~~~~~~~~~~~~~
 
-That's all that needs to be done. You can now create the object in C# and use its property to retrieve the value from C++ code. All ScriptObjects provide a GetCachedPtr method which returns an IntPtr which points to the script interop object described in previous sections.
+That's all that needs to be done. You can now create the object in C# and use its property to retrieve the value from C++ code. All managed `ScriptObjects` provide a `GetCachedPtr` method which returns an `IntPtr` which points to the script interop object described in previous sections.
 
 ## Assembly refresh ##
 What has been shown so far is enough to create a class exposed to script, however object of such a class will not survive assembly refresh. Assembly refresh happens when scripts are recompiled and managed assemblies are unloaded and reloaded. This is something that generally happens only in the editor.
 
-If you don't care about your object surving the refresh, you do not need to implement what is described here. For example GUI elements don't persist refresh, because they're just rebuilt from the managed code every time the refresh happens. However objects like resources, scene objects and components are persistent - we don't wish to reload the entire scene and all resources every time assembly refresh happens.
+If you don't care about your object surviving the refresh, you do not need to implement what is described here. For example GUI elements don't persist refresh, because they're just rebuilt from the managed code every time the refresh happens. However objects like resources, scene objects and components are persistent - we don't wish to reload the entire scene and all resources every time assembly refresh happens.
 
-A persistent script object need to inherit a variation of ScriptObject like so:
-	class MyScriptObject : public ScriptObject<MyScriptObject, PersistentScriptObjectBase>
+A persistent script object need to inherit a variation of `ScriptObject` like so:
+~~~~~~~~~~~~~{.cpp}
+class MyScriptObject : public ScriptObject<MyScriptObject, PersistentScriptObjectBase>
+~~~~~~~~~~~~~	
 	
 This ensures that your object is treated properly during assembly refresh. Persistent object then needs to handle four different actions, represented by overrideable methods. These methods are called in order specified during assembly refresh.
- - ScriptObject::beginRefresh() - Called just before the refresh starts. The object is still alive here and you can perform needed actions (e.g. saving managed object's contents). You can choose to to override this method.
- - ScriptObject::_onManagedInstanceDeleted - Called after assembly unload happened and the managed object was destroyed. You should override this to prevent the ScriptObject itself from being deleted if the assembly refresh is in progress (which is what the default implementation does). If assembly refresh is not in progress this method should delete the ScriptObject normals because it likely got called due to normal reasons (managed object went out of scope).
- - ScriptObject::createManagedInstance - Creates the managed instance after new assemblies are loaded. You should override this if your managed class is constructed using a constructor with parameters. By default this will call MonoClass::createInstance using the parameterless constructor.
- - ScriptObject::endRefresh() - Called after all assemblies are loaded, and after all script interop objects were either destroyed (non-persistent) or had their managed instances created (persistent). Allows you to restore data stored in beginRefresh(), but that is optional.
+ - @ref BansheeEngine::ScriptObject::beginRefresh() "ScriptObject::beginRefresh()" - Called just before the refresh starts. The object is still alive here and you can perform needed actions (e.g. saving managed object's contents).
+ - @ref BansheeEngine::ScriptObject::_onManagedInstanceDeleted "ScriptObject::_onManagedInstanceDeleted()" - Called after assembly unload happened and the managed object was destroyed. You should override this to prevent the `ScriptObject` itself from being deleted if the assembly refresh is in progress (which is what the default implementation does). If assembly refresh is not in progress this method should delete the `ScriptObject` as normal because it likely got called due to standard reasons (managed object went out of scope).
+ - @ref BansheeEngine::ScriptObject::createManagedInstance "ScriptObject::createManagedInstance()" - Creates the managed instance after new assemblies are loaded. You should override this if your managed class is constructed using a constructor with parameters. By default this will call @ref BansheeEngine::MonoClass::createInstance "MonoClass::createInstance()" using the parameterless constructor.
+ - @ref BansheeEngine::ScriptObject::endRefresh() "ScriptObject::endRefresh()" - Called after all assemblies are loaded, and after all script interop objects were either destroyed (non-persistent) or had their managed instances created (persistent). Allows you to restore data stored in `beginRefresh()`, but that is optional.
  
-See ScriptSceneObject and its base class ScriptGameObjectBase for example implementations of these methods.
+See @ref BansheeEngine::ScriptSceneObject "ScriptSceneObject" and its base class @ref BansheeEngine::ScriptGameObjectBase "ScriptGameObjectBase" for example implementations of these methods.
 
 ## Deriving from ScriptObject ##
-Sometimes script objects are polymorphic. For example a GUIElement is derived from ScriptObject in managed code, and GUIButton from GUIElement, however they both have script interop objects of their own.
-
-Due to the nature our script interop objects are defined we cannot follow the same simple chain of inheritance in C++ code. For example class definition script interop object for GUIElement would be:
-	class ScriptGUIElement : public ScriptObject<ScriptGUIElement>
-	{
-	public:
-		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "GUIElement")
-	...
-	}
+Sometimes script objects are polymorphic. For example a `GUIElement` is derived from `ScriptObject` in managed code, and `GUIButton` from `GUIElement`, however they both have script interop objects of their own.
+
+Due to the nature our script interop objects are defined we cannot follow the same simple chain of inheritance in C++ code. For example class definition script interop object for `GUIElement` would be:
+~~~~~~~~~~~~~{.cpp}
+class ScriptGUIElement : public ScriptObject<ScriptGUIElement>
+{
+public:
+	SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "GUIElement")
+...
+}
+~~~~~~~~~~~~~	
 	
-But what would it be for GUIButton?	It also needs to implement ScriptObject with its own SCRIPT_OBJ macro so we cannot just inherit from ScriptGUIElement directly as it would clash.
+But what would it be for `GUIButton`? It also needs to implement `ScriptObject` with its own `SCRIPT_OBJ` macro so we cannot just inherit from `ScriptGUIElement` directly as it would clash.
 
-The solution is to create a third class that will serve as a base for both. This third class will be a base class for ScriptObject (it's second template parameter allows us to override its default ScriptObjectBase base class). The third class will need to inherit ScriptObjectBase and can implement any functionality common to all GUI elements (e.g. it might store a pointer to a native GUIElement*).
+The solution is to create a third class that will serve as a base for both. This third class will be a base class for `ScriptObject` (its second template parameter allows us to override its default `ScriptObjectBase` base class). The third class will need to inherit @ref BansheeEngine::ScriptObjectBase "ScriptObjectBase" and can implement any functionality common to all GUI elements (e.g. it might store a pointer to a native `GUIElement*`).
 
 Then we can define script interop object for GUI element as:
-	class ScriptGUIElement : public ScriptObject<ScriptGUIElement, ScriptGUIElementBase>
-	
-Where ScriptGUIElementBase is our third class. Interop object for GUIButton would then be:
-	class ScriptGUIButton : public ScriptObject<ScriptGUIButton, ScriptGUIElementBase>
+~~~~~~~~~~~~~{.cpp}
+class ScriptGUIElement : public ScriptObject<ScriptGUIElement, ScriptGUIElementBase>
+~~~~~~~~~~~~~
+
+Where `ScriptGUIElementBase` is our third (common) class. Interop object for `GUIButton` would then be:
+~~~~~~~~~~~~~{.cpp}
+class ScriptGUIButton : public ScriptObject<ScriptGUIButton, ScriptGUIElementBase>
+~~~~~~~~~~~~~	
 	
-This ensures that all GUI elements can now be accessed through the common ScriptGUIElementBase interface. Which is important if GUIElement provides some internal method calls shared between all GUI element types, otherwise we wouldn't know what to cast the interop object held by its managed object to.
+This ensures that all GUI elements can now be accessed through the common `ScriptGUIElementBase` interface. Which is important if `GUIElement` provides some internal method calls shared between all GUI element types, otherwise we wouldn't know what to cast the interop object held by its managed object to.
 
-See ScriptGUIElement and ScriptGUIButton for an example.
+See @ref BansheeEngine::ScriptGUIElement "ScriptGUIElement" and @ref BansheeEngine::ScriptGUIButton "ScriptGUIButton" for an example.

+ 2 - 1
Doxyfile

@@ -791,7 +791,8 @@ INPUT                  = Source/BansheeUtility/Include \
                          Source/BansheeSL/Include \
                          Source/RenderBeast/Include \
                          Source/SBansheeEngine/Include \
-                         Source/SBansheeEditor/Include
+                         Source/SBansheeEditor/Include \
+                         Documentation/Manuals/Native
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 3 - 0
Source/BansheeCore/Include/BsCJoint.h

@@ -104,6 +104,9 @@ namespace BansheeEngine
 		/** Notifies the joint that one of the attached rigidbodies moved and that its transform needs updating. */
 		void notifyRigidbodyMoved(const HRigidbody& body);
 
+		/** Checks can the provided rigidbody be used for initializing the joint. */
+		bool isBodyValid(const HRigidbody& body);
+
 		/** Calculates the local position/rotation that needs to be applied to the particular joint body. */
 		void getLocalTransform(JointBody body, Vector3& position, Quaternion& rotation);
 

+ 11 - 6
Source/BansheeCore/Include/BsCJointRTTI.h

@@ -24,9 +24,14 @@ namespace BansheeEngine
 		BS_PLAIN_MEMBER_NAMED(mRotationA, mRotations[0]);
 		BS_PLAIN_MEMBER_NAMED(mRotationB, mRotations[1]);
 
-		BS_PLAIN_MEMBER_NAMED(mBreakForce, mDesc.breakForce)
-		BS_PLAIN_MEMBER_NAMED(mBreakTorque, mDesc.breakTorque)
-		BS_PLAIN_MEMBER_NAMED(mEnableCollision, mDesc.enableCollision)
+		float& getBreakForce(OwnerType* obj) { return obj->mDesc.breakForce; }	
+		void setBreakForce(OwnerType* obj, float& val) { obj->mDesc.breakForce = val; }
+
+		float& getBreakTorque(OwnerType* obj) { return obj->mDesc.breakTorque; }
+		void setBreakTorque(OwnerType* obj, float& val) { obj->mDesc.breakTorque = val; }
+
+		bool& getEnableCollision(OwnerType* obj) { return obj->mDesc.enableCollision; }
+		void setEnableCollision(OwnerType* obj, bool& val) { obj->mDesc.enableCollision = val; }
 
 	public:
 		CJointRTTI()
@@ -37,9 +42,9 @@ namespace BansheeEngine
 			BS_ADD_PLAIN_FIELD(mPositionB, 3);
 			BS_ADD_PLAIN_FIELD(mRotationA, 4);
 			BS_ADD_PLAIN_FIELD(mRotationB, 5);
-			BS_ADD_PLAIN_FIELD(mBreakForce, 6);
-			BS_ADD_PLAIN_FIELD(mBreakTorque, 7);
-			BS_ADD_PLAIN_FIELD(mEnableCollision, 8);
+			BS_ADD_PLAIN_FIELD(BreakForce, 6);
+			BS_ADD_PLAIN_FIELD(BreakTorque, 7);
+			BS_ADD_PLAIN_FIELD(EnableCollision, 8);
 		}
 
 		const String& getRTTIName() override

+ 1 - 1
Source/BansheeCore/Include/BsSphericalJoint.h

@@ -28,7 +28,7 @@ namespace BansheeEngine
 		};
 
 	public:
-		SphericalJoint(const SPHERICAL_JOINT_DESC& desc);
+		SphericalJoint(const SPHERICAL_JOINT_DESC& desc) { }
 		virtual ~SphericalJoint() { }
 
 		/** 

+ 1 - 1
Source/BansheeCore/Source/BsCD6Joint.cpp

@@ -166,7 +166,7 @@ namespace BansheeEngine
 
 	SPtr<Joint> CD6Joint::createInternal()
 	{
-		SPtr<Joint> joint = D6Joint::create();
+		SPtr<Joint> joint = D6Joint::create(mDesc);
 
 		joint->_setOwner(PhysicsOwnerType::Component, this);
 		return joint;

+ 26 - 18
Source/BansheeCore/Source/BsCJoint.cpp

@@ -43,7 +43,7 @@ namespace BansheeEngine
 		// If joint already exists, destroy it if we removed all bodies, otherwise update its transform
 		if(mInternal != nullptr)
 		{
-			if (mBodies[0] == nullptr && mBodies[1] == nullptr)
+			if (!isBodyValid(mBodies[0]) && !isBodyValid(mBodies[1]))
 				destroyInternal();
 			else
 			{
@@ -58,7 +58,7 @@ namespace BansheeEngine
 		else // If joint doesn't exist, check if we can create it
 		{
 			// Must be an active component and at least one of the bodies must be non-null
-			if (SO()->getActive() && mBodies[0] != nullptr || mBodies[1] != nullptr)
+			if (SO()->getActive() && (isBodyValid(mBodies[0]) || isBodyValid(mBodies[1])))
 			{
 				restoreInternal();
 			}
@@ -160,7 +160,7 @@ namespace BansheeEngine
 
 	void CJoint::onEnabled()
 	{
-		if(mBodies[0] != nullptr || mBodies[1] != nullptr)
+		if(isBodyValid(mBodies[0]) || isBodyValid(mBodies[1]))
 			restoreInternal();
 	}
 
@@ -184,25 +184,22 @@ namespace BansheeEngine
 
 	void CJoint::restoreInternal()
 	{
-		if (mInternal == nullptr)
-		{
-			if (mBodies[0] != nullptr)
-				mDesc.bodies[0].body = mBodies[0]->_getInternal();
-			else
-				mDesc.bodies[0].body = nullptr;
+		if (mBodies[0] != nullptr)
+			mDesc.bodies[0].body = mBodies[0]->_getInternal();
+		else
+			mDesc.bodies[0].body = nullptr;
 
-			if (mBodies[1] != nullptr)
-				mDesc.bodies[1].body = mBodies[1]->_getInternal();
-			else
-				mDesc.bodies[1].body = nullptr;
+		if (mBodies[1] != nullptr)
+			mDesc.bodies[1].body = mBodies[1]->_getInternal();
+		else
+			mDesc.bodies[1].body = nullptr;
 
-			getLocalTransform(JointBody::A, mDesc.bodies[0].position, mDesc.bodies[0].rotation);
-			getLocalTransform(JointBody::B, mDesc.bodies[1].position, mDesc.bodies[1].rotation);
+		getLocalTransform(JointBody::A, mDesc.bodies[0].position, mDesc.bodies[0].rotation);
+		getLocalTransform(JointBody::B, mDesc.bodies[1].position, mDesc.bodies[1].rotation);
 
-			mInternal = createInternal();
+		mInternal = createInternal();
 
-			mInternal->onJointBreak.connect(std::bind(&CJoint::triggerOnJointBroken, this));
-		}
+		mInternal->onJointBreak.connect(std::bind(&CJoint::triggerOnJointBroken, this));
 	}
 
 	void CJoint::destroyInternal()
@@ -229,6 +226,17 @@ namespace BansheeEngine
 			assert(false); // Not allowed to happen
 	}
 
+	bool CJoint::isBodyValid(const HRigidbody& body)
+	{
+		if (body == nullptr)
+			return false;
+
+		if (body->_getInternal() == nullptr)
+			return false;
+
+		return true;
+	}
+
 	void CJoint::updateTransform(JointBody body)
 	{
 		Vector3 localPos;

+ 1 - 1
Source/BansheeEngine/Source/BsGUIToggle.cpp

@@ -72,7 +72,7 @@ namespace BansheeEngine
 
 	SPtr<GUIToggleGroup> GUIToggle::createToggleGroup(bool allowAllOff)
 	{
-		SPtr<GUIToggleGroup> toggleGroup = bs_shared_ptr_new<GUIToggleGroup>(allowAllOff);
+		SPtr<GUIToggleGroup> toggleGroup = bs_shared_ptr<GUIToggleGroup>(new (bs_alloc<GUIToggleGroup>()) GUIToggleGroup(allowAllOff));
 		toggleGroup->initialize(toggleGroup);
 
 		return toggleGroup;

+ 46 - 60
Source/MBansheeEngine/Physics/D6Joint.cs

@@ -11,23 +11,7 @@ namespace BansheeEngine
     public sealed class D6Joint : Joint
     {
         [SerializeField]
-        private LimitLinear linearLimit = new LimitLinear();
-        [SerializeField]
-        private LimitAngularRange twistLimit = new LimitAngularRange();
-        [SerializeField]
-        private LimitConeRange swingLimit = new LimitConeRange();
-        [SerializeField]
-        private D6JointMotion[] motion = new D6JointMotion[(int)D6JointAxis.Count];
-        [SerializeField]
-        private D6JointDrive[] drives = new D6JointDrive[(int)D6JointDriveType.Count];
-        [SerializeField]
-        private Vector3 drivePosition = Vector3.Zero;
-        [SerializeField]
-        private Quaternion driveRotation = Quaternion.Identity;
-        [SerializeField]
-        private Vector3 driveLinearVelocity = Vector3.Zero;
-        [SerializeField]
-        private Vector3 driveAngularVelocity = Vector3.Zero;
+        private SerializableData data = new SerializableData();
 
         /// <summary>
         /// Returns the current rotation of the joint around the X axis.
@@ -76,13 +60,13 @@ namespace BansheeEngine
         /// </summary>
         public LimitLinear LimitLinear
         {
-            get { return linearLimit; }
+            get { return data.@internal.linearLimit; }
             set
             {
-                if (linearLimit == value)
+                if (data.@internal.linearLimit == value)
                     return;
 
-                linearLimit = value;
+                data.@internal.linearLimit = value;
 
                 if (Native != null)
                     Native.LimitLinear = value;
@@ -94,13 +78,13 @@ namespace BansheeEngine
         /// </summary>
         public LimitAngularRange LimitTwist
         {
-            get { return twistLimit; }
+            get { return data.@internal.twistLimit; }
             set
             {
-                if (twistLimit == value)
+                if (data.@internal.twistLimit == value)
                     return;
 
-                twistLimit = value;
+                data.@internal.twistLimit = value;
 
                 if (Native != null)
                     Native.LimitTwist = value;
@@ -112,13 +96,13 @@ namespace BansheeEngine
         /// </summary>
         public LimitConeRange LimitSwing
         {
-            get { return swingLimit; }
+            get { return data.@internal.swingLimit; }
             set
             {
-                if (swingLimit == value)
+                if (data.@internal.swingLimit == value)
                     return;
 
-                swingLimit = value;
+                data.@internal.swingLimit = value;
 
                 if (Native != null)
                     Native.LimitSwing = value;
@@ -131,13 +115,13 @@ namespace BansheeEngine
         /// </summary>
         public Vector3 DrivePosition
         {
-            get { return drivePosition; }
+            get { return d[email protected]rivePosition; }
             set
             {
-                if (drivePosition == value)
+                if (d[email protected]rivePosition == value)
                     return;
 
-                drivePosition = value;
+                d[email protected]rivePosition = value;
 
                 if (Native != null)
                     Native.DrivePosition = value;
@@ -150,13 +134,13 @@ namespace BansheeEngine
         /// </summary>
         public Quaternion DriveRotation
         {
-            get { return driveRotation; }
+            get { return d[email protected]riveRotation; }
             set
             {
-                if (driveRotation == value)
+                if (d[email protected]riveRotation == value)
                     return;
 
-                driveRotation = value;
+                d[email protected]riveRotation = value;
 
                 if (Native != null)
                     Native.DriveRotation = value;
@@ -168,13 +152,13 @@ namespace BansheeEngine
         /// </summary>
         public Vector3 DriveLinearVelocity
         {
-            get { return driveLinearVelocity; }
+            get { return d[email protected]riveLinearVelocity; }
             set
             {
-                if (driveLinearVelocity == value)
+                if (d[email protected]riveLinearVelocity == value)
                     return;
 
-                driveLinearVelocity = value;
+                d[email protected]riveLinearVelocity = value;
 
                 if (Native != null)
                     Native.DriveLinearVelocity = value;
@@ -186,13 +170,13 @@ namespace BansheeEngine
         /// </summary>
         public Vector3 DriveAngularVelocity
         {
-            get { return driveAngularVelocity; }
+            get { return d[email protected]riveAngularVelocity; }
             set
             {
-                if (driveAngularVelocity == value)
+                if (d[email protected]riveAngularVelocity == value)
                     return;
 
-                driveAngularVelocity = value;
+                d[email protected]riveAngularVelocity = value;
 
                 if (Native != null)
                     Native.DriveAngularVelocity = value;
@@ -206,7 +190,7 @@ namespace BansheeEngine
         /// <returns>Motion constrain type for the axis.</returns>
         public D6JointMotion GetMotion(D6JointAxis axis)
         {
-            return motion[(int) axis];
+            return data.@internal.motion[(int) axis];
         }
 
         /// <summary>
@@ -230,10 +214,10 @@ namespace BansheeEngine
         /// <param name="motion">Type of motion for the axis.</param>
         public void SetMotion(D6JointAxis axis, D6JointMotion motion)
         {
-            if (this.motion[(int)axis] == motion)
+            if (data.@internal.motion[(int)axis] == motion)
                 return;
 
-            this.motion[(int)axis] = motion;
+            data.@internal.motion[(int)axis] = motion;
 
             if (Native != null)
                 Native.SetMotion(axis, motion);
@@ -246,7 +230,7 @@ namespace BansheeEngine
         /// <returns>Properties for the requested drive type.</returns>
         public D6JointDrive GetDrive(D6JointDriveType type)
         {
-            return drives[(int) type];
+            return d[email protected]rives[(int) type];
         }
 
         /// <summary>
@@ -256,10 +240,10 @@ namespace BansheeEngine
         /// <param name="drive">Drive properties.</param>
         public void SetDrive(D6JointDriveType type, D6JointDrive drive)
         {
-            if (this.drives[(int)type] == drive)
+            if (data.@internal.drives[(int)type] == drive)
                 return;
 
-            this.drives[(int)type] = drive;
+            data.@internal.drives[(int)type] = drive;
 
             if (Native != null)
                 Native.SetDrive(type, drive);
@@ -276,25 +260,27 @@ namespace BansheeEngine
         /// <inheritdoc/>
         internal override NativeJoint CreateNative()
         {
-            NativeD6Joint joint = new NativeD6Joint();
-
-            // TODO - Apply this all at once to avoid all the individual interop function calls
-            joint.LimitLinear = linearLimit;
-            joint.LimitTwist = twistLimit;
-            joint.LimitSwing = swingLimit;
+            NativeD6Joint joint = new NativeD6Joint(commonData.@internal, data.@internal);
 
-            for (int i = 0; i < (int) D6JointAxis.Count; i++)
-                joint.SetMotion((D6JointAxis) i, motion[i]);
-
-            for (int i = 0; i < (int)D6JointDriveType.Count; i++)
-                joint.SetDrive((D6JointDriveType)i, drives[i]);
+            return joint;
+        }
 
-            joint.DrivePosition = drivePosition;
-            joint.DriveRotation = driveRotation;
-            joint.DriveLinearVelocity = driveLinearVelocity;
-            joint.DriveAngularVelocity = driveAngularVelocity;
+        /// <summary>
+        /// Holds all data the joint component needs to persist through serialization.
+        /// </summary>
+        [SerializeObject]
+        internal new class SerializableData
+        {
+            public ScriptD6JointData @internal;
 
-            return joint;
+            public SerializableData()
+            {
+                @internal.linearLimit = new LimitLinear();
+                @internal.twistLimit = new LimitAngularRange();
+                @internal.swingLimit = new LimitConeRange();
+                @internal.motion = new D6JointMotion[(int)D6JointAxis.Count];
+                @internal.drives = new D6JointDrive[(int)D6JointDriveType.Count];
+            }
         }
     }
 }

+ 219 - 219
Source/MBansheeEngine/Physics/DistanceJoint.cs

@@ -1,219 +1,219 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// A joint that maintains an upper or lower (or both) bound on the distance between two bodies.
-    /// </summary>
-    public sealed class DistanceJoint : Joint
-    {
-        /// <summary>
-        /// Flags to control distance joint behaviour.
-        /// </summary>
-        [Flags]
-        private enum Flag
-        {
-            MinDistanceLimit = 0x01,
-            MaxDistanceLimit = 0x02,
-            Spring = 0x04
-        }
-
-        [SerializeField]
-        private float minDistance = 0.0f;
-        [SerializeField]
-        private float maxDistance = 0.0f;
-        [SerializeField]
-        private float tolerance = 0.25f;
-        [SerializeField]
-        private Spring spring;
-        [SerializeField]
-        private Flag flags = 0;
-
-        /// <summary>
-        /// Returns the current distance between the two joint bodies.
-        /// </summary>
-        public float Distance
-        {
-            get
-            {
-                if(Native != null)
-                    return Native.Distance;
-
-                return 0.0f;
-            }
-        }
-
-        /// <summary>
-        /// Determines the minimum distance the bodies are allowed to be at, they will get no closer. You must enable
-        /// <see cref="EnableMinDistanceLimit"/> in order for this to be enforced.
-        /// </summary>
-        public float MinDistance
-        {
-            get { return minDistance; }
-            set
-            {
-                if (minDistance == value)
-                    return;
-
-                minDistance = value;
-
-                if (Native != null)
-                    Native.MinDistance = value;
-            }
-        }
-
-        /// <summary>
-        /// Determines the maximum distance the bodies are allowed to be at, they will get no further. You must enable
-        /// <see cref="EnableMaxDistanceLimit"/> in order for this to be enforced.
-        /// </summary>
-        public float MaxDistance
-        {
-            get { return maxDistance; }
-            set
-            {
-                if (maxDistance == value)
-                    return;
-
-                maxDistance = value;
-
-                if (Native != null)
-                    Native.MaxDistance = value;
-            }
-        }
-
-        /// <summary>
-        /// Determines the error tolerance of the joint at which the joint becomes active. This value slightly extends the
-        /// lower and upper limit for precision reasons.
-        /// </summary>
-        public float Tolerance
-        {
-            get { return tolerance; }
-            set
-            {
-                if (tolerance == value)
-                    return;
-
-                tolerance = value;
-
-                if (Native != null)
-                    Native.Tolerance = value;
-            }
-        }
-
-        /// <summary>
-        /// Returns a spring that controls how the joint responds when a limit is reached. You must enable 
-        /// <see cref="EnableSpring"/> in order for the spring to be applied.
-        /// </summary>
-        public Spring Spring
-        {
-            get { return spring; }
-            set
-            {
-                if (spring == value)
-                    return;
-
-                spring = value;
-
-                if (Native != null)
-                    Native.Spring = value;
-            }
-        }
-
-        /// <summary>
-        /// Enables or disables the limit that causes joint objects to maintain a minimum distance between themselves.
-        /// </summary>
-        public bool EnableMinDistanceLimit
-        {
-            get { return (flags & Flag.MinDistanceLimit) != 0; }
-            set
-            {
-                if (!SetFlag(Flag.MinDistanceLimit, value))
-                    return;
-
-                if (Native != null)
-                    Native.EnableMinDistanceLimit = value;
-            }
-        }
-
-        /// <summary>
-        /// Enables or disables the limit that causes joint objects to maintain a maximum distance between themselves.
-        /// </summary>
-        public bool EnableMaxDistanceLimit
-        {
-            get { return (flags & Flag.MaxDistanceLimit) != 0; }
-            set
-            {
-                if (!SetFlag(Flag.MaxDistanceLimit, value))
-                    return;
-
-                if (Native != null)
-                    Native.EnableMinDistanceLimit = value;
-            }
-        }
-
-        /// <summary>
-        /// Enables or disables the spring that controls how the joint reacts when the limit is reached.
-        /// </summary>
-        public bool EnableSpring
-        {
-            get { return (flags & Flag.Spring) != 0; }
-            set
-            {
-                if (!SetFlag(Flag.Spring, value))
-                    return;
-
-                if (Native != null)
-                    Native.EnableSpring = value;
-            }
-        }
-
-        /// <summary>
-        /// Toggles a specific distance joint flag on or off.
-        /// </summary>
-        /// <param name="flag">Flag to toggle.</param>
-        /// <param name="enabled">Should the flag be turned on or off.</param>
-        /// <returns>True if the new newly set flag state was different from the previous one.</returns>
-        private bool SetFlag(Flag flag, bool enabled)
-        {
-            Flag newFlags = flags;
-
-            if (enabled)
-                newFlags |= flag;
-            else
-                newFlags &= ~flag;
-
-            if (newFlags == flags)
-                return false;
-
-            flags = newFlags;
-            return true;
-        }
-
-        /// <summary>
-        /// Returns the native joint wrapped by this component.
-        /// </summary>
-        private NativeDistanceJoint Native
-        {
-            get { return (NativeDistanceJoint)native; }
-        }
-
-        /// <inheritdoc/>
-        internal override NativeJoint CreateNative()
-        {
-            NativeDistanceJoint joint = new NativeDistanceJoint();
-
-            // TODO - Apply this all at once to avoid all the individual interop function calls
-            joint.MinDistance = minDistance;
-            joint.MaxDistance = maxDistance;
-            joint.Tolerance = tolerance;
-            joint.Spring = spring;
-            joint.EnableMinDistanceLimit = EnableMinDistanceLimit;
-            joint.EnableMaxDistanceLimit = EnableMaxDistanceLimit;
-            joint.EnableSpring = EnableSpring;
-
-            return joint;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// A joint that maintains an upper or lower (or both) bound on the distance between two bodies.
+    /// </summary>
+    public sealed class DistanceJoint : Joint
+    {
+        /// <summary>
+        /// Flags to control distance joint behaviour.
+        /// </summary>
+        [Flags]
+        internal enum Flag // Note: Must match C++ enum DistanceJoint::Flag
+        {
+            MinDistanceLimit = 0x01,
+            MaxDistanceLimit = 0x02,
+            Spring = 0x04
+        }
+
+        [SerializeField]
+        private SerializableData data = new SerializableData();
+
+        /// <summary>
+        /// Returns the current distance between the two joint bodies.
+        /// </summary>
+        public float Distance
+        {
+            get
+            {
+                if(Native != null)
+                    return Native.Distance;
+
+                return 0.0f;
+            }
+        }
+
+        /// <summary>
+        /// Determines the minimum distance the bodies are allowed to be at, they will get no closer. You must enable
+        /// <see cref="EnableMinDistanceLimit"/> in order for this to be enforced.
+        /// </summary>
+        public float MinDistance
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.MinDistance = value;
+            }
+        }
+
+        /// <summary>
+        /// Determines the maximum distance the bodies are allowed to be at, they will get no further. You must enable
+        /// <see cref="EnableMaxDistanceLimit"/> in order for this to be enforced.
+        /// </summary>
+        public float MaxDistance
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.MaxDistance = value;
+            }
+        }
+
+        /// <summary>
+        /// Determines the error tolerance of the joint at which the joint becomes active. This value slightly extends the
+        /// lower and upper limit for precision reasons.
+        /// </summary>
+        public float Tolerance
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.Tolerance = value;
+            }
+        }
+
+        /// <summary>
+        /// Returns a spring that controls how the joint responds when a limit is reached. You must enable 
+        /// <see cref="EnableSpring"/> in order for the spring to be applied.
+        /// </summary>
+        public Spring Spring
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.Spring = value;
+            }
+        }
+
+        /// <summary>
+        /// Enables or disables the limit that causes joint objects to maintain a minimum distance between themselves.
+        /// </summary>
+        public bool EnableMinDistanceLimit
+        {
+            get { return ([email protected] & Flag.MinDistanceLimit) != 0; }
+            set
+            {
+                if (!SetFlag(Flag.MinDistanceLimit, value))
+                    return;
+
+                if (Native != null)
+                    Native.EnableMinDistanceLimit = value;
+            }
+        }
+
+        /// <summary>
+        /// Enables or disables the limit that causes joint objects to maintain a maximum distance between themselves.
+        /// </summary>
+        public bool EnableMaxDistanceLimit
+        {
+            get { return ([email protected] & Flag.MaxDistanceLimit) != 0; }
+            set
+            {
+                if (!SetFlag(Flag.MaxDistanceLimit, value))
+                    return;
+
+                if (Native != null)
+                    Native.EnableMinDistanceLimit = value;
+            }
+        }
+
+        /// <summary>
+        /// Enables or disables the spring that controls how the joint reacts when the limit is reached.
+        /// </summary>
+        public bool EnableSpring
+        {
+            get { return ([email protected] & Flag.Spring) != 0; }
+            set
+            {
+                if (!SetFlag(Flag.Spring, value))
+                    return;
+
+                if (Native != null)
+                    Native.EnableSpring = value;
+            }
+        }
+
+        /// <summary>
+        /// Toggles a specific distance joint flag on or off.
+        /// </summary>
+        /// <param name="flag">Flag to toggle.</param>
+        /// <param name="enabled">Should the flag be turned on or off.</param>
+        /// <returns>True if the new newly set flag state was different from the previous one.</returns>
+        private bool SetFlag(Flag flag, bool enabled)
+        {
+            Flag newFlags = [email protected];
+
+            if (enabled)
+                newFlags |= flag;
+            else
+                newFlags &= ~flag;
+
+            if (newFlags == [email protected])
+                return false;
+
+            [email protected] = newFlags;
+            return true;
+        }
+
+        /// <summary>
+        /// Returns the native joint wrapped by this component.
+        /// </summary>
+        private NativeDistanceJoint Native
+        {
+            get { return (NativeDistanceJoint)native; }
+        }
+
+        /// <inheritdoc/>
+        internal override NativeJoint CreateNative()
+        {
+            NativeDistanceJoint joint = new NativeDistanceJoint(commonData.@internal, data.@internal);
+
+            return joint;
+        }
+
+        /// <summary>
+        /// Holds all data the joint component needs to persist through serialization.
+        /// </summary>
+        [SerializeObject]
+        internal new class SerializableData
+        {
+            public ScriptDistanceJointData @internal;
+
+            public SerializableData()
+            {
+                @internal.minDistance = 0.0f;
+                @internal.maxDistance = 0.0f;
+                @internal.tolerance = 0.25f;
+                @internal.flags = 0;
+            }
+        }
+    }
+}

+ 18 - 18
Source/MBansheeEngine/Physics/FixedJoint.cs

@@ -1,18 +1,18 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Physics joint that will maintain a fixed distance and orientation between its two attached bodies.
-    /// </summary>
-    public sealed class FixedJoint : Joint
-    {
-        /// <inheritdoc/>
-        internal override NativeJoint CreateNative()
-        {
-            NativeFixedJoint joint = new NativeFixedJoint();
-
-            return joint;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Physics joint that will maintain a fixed distance and orientation between its two attached bodies.
+    /// </summary>
+    public sealed class FixedJoint : Joint
+    {
+        /// <inheritdoc/>
+        internal override NativeJoint CreateNative()
+        {
+            NativeFixedJoint joint = new NativeFixedJoint(commonData.@internal);
+
+            return joint;
+        }
+    }
+}

+ 30 - 24
Source/MBansheeEngine/Physics/HingeJoint.cs

@@ -14,18 +14,14 @@ namespace BansheeEngine
         /// Flags to control hinge joint behaviour.
         /// </summary>
         [Flags]
-        private enum Flag
+        internal enum Flag // Note: Must match C++ enum HingeJoint::Flag
         {
             Limit = 0x01,
             Drive = 0x02,
         }
 
         [SerializeField]
-        private LimitAngularRange limit = new LimitAngularRange();
-        [SerializeField]
-        private HingeJointDrive drive = new HingeJointDrive();
-        [SerializeField]
-        private Flag flags = 0;
+        private SerializableData data = new SerializableData();
 
         /// <summary>
         /// Returns the current angle between the two attached bodes.
@@ -61,13 +57,13 @@ namespace BansheeEngine
         /// </summary>
         public LimitAngularRange Limit
         {
-            get { return limit; }
+            get { return data.@internal.limit; }
             set
             {
-                if (limit == value)
+                if (data.@internal.limit == value)
                     return;
 
-                limit = value;
+                data.@internal.limit = value;
 
                 if (Native != null)
                     Native.Limit = value;
@@ -80,13 +76,13 @@ namespace BansheeEngine
         /// </summary>
         public HingeJointDrive Drive
         {
-            get { return drive; }
+            get { return d[email protected]rive; }
             set
             {
-                if (drive == value)
+                if (d[email protected]rive == value)
                     return;
 
-                drive = value;
+                d[email protected]rive = value;
 
                 if (Native != null)
                     Native.Drive = value;
@@ -98,7 +94,7 @@ namespace BansheeEngine
         /// </summary>
         public bool EnableLimit
         {
-            get { return (flags & Flag.Limit) != 0; }
+            get { return (data.@internal.flags & Flag.Limit) != 0; }
             set
             {
                 if (!SetFlag(Flag.Limit, value))
@@ -114,7 +110,7 @@ namespace BansheeEngine
         /// </summary>
         public bool EnableDrive
         {
-            get { return (flags & Flag.Drive) != 0; }
+            get { return (data.@internal.flags & Flag.Drive) != 0; }
             set
             {
                 if (!SetFlag(Flag.Drive, value))
@@ -133,17 +129,17 @@ namespace BansheeEngine
         /// <returns>True if the new newly set flag state was different from the previous one.</returns>
         private bool SetFlag(Flag flag, bool enabled)
         {
-            Flag newFlags = flags;
+            Flag newFlags = data.@internal.flags;
 
             if (enabled)
                 newFlags |= flag;
             else
                 newFlags &= ~flag;
 
-            if (newFlags == flags)
+            if (newFlags == data.@internal.flags)
                 return false;
 
-            flags = newFlags;
+            data.@internal.flags = newFlags;
             return true;
         }
 
@@ -158,15 +154,25 @@ namespace BansheeEngine
         /// <inheritdoc/>
         internal override NativeJoint CreateNative()
         {
-            NativeHingeJoint joint = new NativeHingeJoint();
-
-            // TODO - Apply this all at once to avoid all the individual interop function calls
-            joint.Limit = limit;
-            joint.Drive = drive;
-            joint.EnableLimit = EnableLimit;
-            joint.EnableDrive = EnableDrive;
+            NativeHingeJoint joint = new NativeHingeJoint(commonData.@internal, data.@internal);
 
             return joint;
         }
+
+        /// <summary>
+        /// Holds all data the joint component needs to persist through serialization.
+        /// </summary>
+        [SerializeObject]
+        internal new class SerializableData
+        {
+            public ScriptHingeJointData @internal;
+
+            public SerializableData()
+            {
+                @internal.limit = new LimitAngularRange();
+                @internal.drive = new HingeJointDrive();
+                @internal.flags = 0;
+            }
+        }
     }
 }

+ 128 - 77
Source/MBansheeEngine/Physics/Joint.cs

@@ -15,7 +15,7 @@ namespace BansheeEngine
         internal NativeJoint native;
 
         [SerializeField]
-        internal SerializableData serializableData = new SerializableData();
+        internal SerializableData commonData = new SerializableData();
 
         /// <summary>
         /// Triggered when the joint's break force or torque is exceeded.
@@ -27,13 +27,13 @@ namespace BansheeEngine
         /// </summary>
         public float BreakForce
         {
-            get { return serializableData.breakForce; }
+            get { return commonData.@internal.breakForce; }
             set
             {
-                if (serializableData.breakForce == value)
+                if (commonData.@internal.breakForce == value)
                     return;
 
-                serializableData.breakForce = value;
+                commonData.@internal.breakForce = value;
 
                 if (native != null)
                     native.BreakForce = value;
@@ -46,13 +46,13 @@ namespace BansheeEngine
         /// </summary>
         public float BreakTorque
         {
-            get { return serializableData.breakTorque; }
+            get { return commonData.@internal.breakTorque; }
             set
             {
-                if (serializableData.breakTorque == value)
+                if (commonData.@internal.breakTorque == value)
                     return;
 
-                serializableData.breakTorque = value;
+                commonData.@internal.breakTorque = value;
 
                 if (native != null)
                     native.BreakTorque = value;
@@ -64,13 +64,13 @@ namespace BansheeEngine
         /// </summary>
         public bool EnableCollision
         {
-            get { return serializableData.enableCollision; }
+            get { return commonData.@internal.enableCollision; }
             set
             {
-                if (serializableData.enableCollision == value)
+                if (commonData.@internal.enableCollision == value)
                     return;
 
-                serializableData.enableCollision = value;
+                commonData.@internal.enableCollision = value;
 
                 if (native != null)
                     native.EnableCollision = value;
@@ -84,7 +84,7 @@ namespace BansheeEngine
         /// <returns>Rigidbody managed by the joint, or null if none.</returns>
         public Rigidbody GetRigidbody(JointBody body)
         {
-            return serializableData.bodies[(int) body];
+            return commonData.bodies[(int) body];
         }
 
         /// <summary>
@@ -96,21 +96,35 @@ namespace BansheeEngine
         ///                         and <see cref="SetRotation"/>.</param>
         public void SetRigidbody(JointBody body, Rigidbody rigidbody)
         {
-            if (serializableData.bodies[(int)body] == rigidbody)
+            if (commonData.bodies[(int)body] == rigidbody)
                 return;
 
-            if (serializableData.bodies[(int)body] != null)
-                serializableData.bodies[(int)body].SetJoint(null);
+            if (commonData.bodies[(int)body] != null)
+                commonData.bodies[(int)body].SetJoint(null);
 
-            serializableData.bodies[(int)body] = rigidbody;
+            commonData.bodies[(int)body] = rigidbody;
 
             if (rigidbody != null)
-                serializableData.bodies[(int)body].SetJoint(this);
+                commonData.bodies[(int)body].SetJoint(this);
 
+            // If joint already exists, destroy it if we removed all bodies, otherwise update its transform
             if (native != null)
             {
-                native.SetRigidbody(body, rigidbody);
-                UpdateTransform(body);
+                if (!IsBodyValid(commonData.bodies[0]) && !IsBodyValid(commonData.bodies[0]))
+                    DestroyNative();
+                else
+                {
+                    native.SetRigidbody(body, rigidbody);
+                    UpdateTransform(body);
+                }
+            }
+            else // If joint doesn't exist, check if we can create it
+            {
+                // Must be an active component and at least one of the bodies must be non-null
+                if (SceneObject.Active && (IsBodyValid(commonData.bodies[0]) || IsBodyValid(commonData.bodies[0])))
+                {
+                    RestoreNative();
+                }
             }
         }
 
@@ -121,7 +135,7 @@ namespace BansheeEngine
         /// <returns>Position relative to the body.</returns>
         public Vector3 GetPosition(JointBody body)
         {
-            return serializableData.positions[(int)body];
+            return commonData.positions[(int)body];
         }
 
         /// <summary>
@@ -131,10 +145,10 @@ namespace BansheeEngine
         /// <param name="position">Position relative to the body.</param>
         public void SetPosition(JointBody body, Vector3 position)
         {
-            if (serializableData.positions[(int)body] == position)
+            if (commonData.positions[(int)body] == position)
                 return;
 
-            serializableData.positions[(int) body] = position;
+            commonData.positions[(int) body] = position;
 
             if (native != null)
                 UpdateTransform(body);
@@ -147,7 +161,7 @@ namespace BansheeEngine
         /// <returns>Rotation relative to the body.</returns>
         public Quaternion GetRotation(JointBody body)
         {
-            return serializableData.rotations[(int)body];
+            return commonData.rotations[(int)body];
         }
 
         /// <summary>
@@ -157,10 +171,10 @@ namespace BansheeEngine
         /// <param name="rotation">Rotation relative to the body.</param>
         public void SetRotation(JointBody body, Quaternion rotation)
         {
-            if (serializableData.rotations[(int)body] == rotation)
+            if (commonData.rotations[(int)body] == rotation)
                 return;
 
-            serializableData.rotations[(int)body] = rotation;
+            commonData.rotations[(int)body] = rotation;
 
             if (native != null)
                 UpdateTransform(body);
@@ -181,13 +195,16 @@ namespace BansheeEngine
         /// <param name="body">Rigidbody that moved.</param>
 	    internal void NotifyRigidbodyMoved(Rigidbody body)
 	    {
+            if (native == null)
+                return;
+
 		    // If physics update is in progress do nothing, as its the joint itself that's probably moving the body
 		    if (Physics.IsUpdateInProgress)
 			    return;
 
-		    if (serializableData.bodies[0] == body)
+		    if (commonData.bodies[0] == body)
 			    UpdateTransform(JointBody.A);
-		    else if (serializableData.bodies[1] == body)
+		    else if (commonData.bodies[1] == body)
 			    UpdateTransform(JointBody.B);
 	    }
 
@@ -204,7 +221,8 @@ namespace BansheeEngine
 
         private void OnEnable()
         {
-            RestoreNative();
+            if(IsBodyValid(commonData.bodies[0]) || IsBodyValid(commonData.bodies[1]))
+                RestoreNative();
         }
 
         private void OnDisable()
@@ -214,18 +232,18 @@ namespace BansheeEngine
 
         private void OnDestroy()
         {
-            if (serializableData.bodies[0] != null)
-                serializableData.bodies[0].SetJoint(null);
+            if (commonData.bodies[0] != null)
+                commonData.bodies[0].SetJoint(null);
 
-            if (serializableData.bodies[1] != null)
-                serializableData.bodies[1].SetJoint(null);
+            if (commonData.bodies[1] != null)
+                commonData.bodies[1].SetJoint(null);
 
             DestroyNative();
         }
 
         private void OnTransformChanged(TransformChangedFlags flags)
         {
-            if (!SceneObject.Active)
+            if (native == null)
                 return;
 
             // We're ignoring this during physics update because it would cause problems if the joint itself was moved by physics
@@ -246,32 +264,28 @@ namespace BansheeEngine
         /// </summary>
         private void RestoreNative()
 	    {
+            [email protected][0] = IntPtr.Zero;
+            [email protected][1] = IntPtr.Zero;
+
+            if (commonData.bodies[0] != null)
+            {
+                NativeRigidbody nativeBody = commonData.bodies[0].native;
+                if (nativeBody != null)
+                    [email protected][0] = nativeBody.GetCachedPtr();
+            }
+
+            if (commonData.bodies[1] != null)
+            {
+                NativeRigidbody nativeBody = commonData.bodies[1].native;
+                if (nativeBody != null)
+                    [email protected][1] = nativeBody.GetCachedPtr();
+            }
+
+            GetLocalTransform(JointBody.A, out [email protected][0], out [email protected][0]);
+            GetLocalTransform(JointBody.B, out [email protected][1], out [email protected][1]);
+
             native = CreateNative();
             native.Component = this;
-
-            // Note: Merge into one call to avoid many virtual function calls
-            Rigidbody[] bodies = new Rigidbody[2];
-
-		    if (serializableData.bodies[0] != null)
-			    bodies[0] = serializableData.bodies[0];
-		    else
-			    bodies[0] = null;
-
-		    if (serializableData.bodies[1] != null)
-			    bodies[1] = serializableData.bodies[1];
-		    else
-			    bodies[1] = null;
-
-		    native.SetRigidbody(JointBody.A, bodies[0]);
-		    native.SetRigidbody(JointBody.B, bodies[1]);
-		    native.BreakForce = serializableData.breakForce;
-            native.BreakTorque = serializableData.breakTorque;
-            native.EnableCollision = serializableData.enableCollision;
-		    native.BreakTorque = serializableData.breakTorque;
-            native.EnableCollision = serializableData.enableCollision;
-
-		    UpdateTransform(JointBody.A);
-		    UpdateTransform(JointBody.B);
 	    }
 
         /// <summary>
@@ -286,6 +300,50 @@ namespace BansheeEngine
 	        }
 	    }
 
+        /// <summary>
+        /// Checks can the provided rigidbody be used for initializing the joint.
+        /// </summary>
+        /// <param name="body">Body to check.</param>
+        /// <returns>True if the body can be used for initializing the joint, false otherwise.</returns>
+        private bool IsBodyValid(Rigidbody body)
+        {
+            if (body == null)
+                return false;
+
+            if (body.native == null)
+                return false;
+
+            return true;
+        }
+
+        /// <summary>
+        /// Calculates the local position/rotation that needs to be applied to the particular joint body.
+        /// </summary>
+        /// <param name="body">Body to calculate the transform for.</param>
+        /// <param name="position">Output position for the body.</param>
+        /// <param name="rotation">Output rotation for the body</param>
+        private void GetLocalTransform(JointBody body, out Vector3 position, out Quaternion rotation)
+        {
+            position = commonData.positions[(int)body];
+            rotation = commonData.rotations[(int)body];
+
+            // Transform to world space of the related body
+            Rigidbody rigidbody = commonData.bodies[(int)body];
+            if (rigidbody != null)
+            {
+                Quaternion worldRot = rigidbody.SceneObject.Rotation;
+
+                rotation = worldRot * rotation;
+                position = worldRot.Rotate(position) + rigidbody.SceneObject.Position;
+            }
+
+            // Transform to space local to the joint
+            Quaternion invRotation = SceneObject.Rotation.Inverse;
+
+            position = invRotation.Rotate(position - SceneObject.Position);
+            rotation = invRotation * rotation;
+        }
+
         /// <summary>
         /// Updates the local transform for the specified body attached to the joint.
         /// </summary>
@@ -295,26 +353,10 @@ namespace BansheeEngine
 		    Vector3 localPos;
 		    Quaternion localRot;
 
-		    localPos = serializableData.positions[(int)body];
-		    localRot = serializableData.rotations[(int)body];
-
-		    // Transform to world space of the related body
-		    Rigidbody rigidbody = serializableData.bodies[(int)body];
-		    if (rigidbody != null)
-		    {
-		        Quaternion worldRot = rigidbody.SceneObject.Rotation;
-
-			    localRot = worldRot * localRot;
-			    localPos = worldRot.Rotate(localPos) + rigidbody.SceneObject.Position;
-		    }
+            GetLocalTransform(body, out localPos, out localRot);
 
-		    // Transform to space local to the joint
-		    Quaternion invRotation = SceneObject.Rotation.Inverse;
 
-		    localPos = invRotation.Rotate(localPos - SceneObject.Position);
-		    localRot = invRotation * localRot;
-
-		    native.SetPosition(body, localPos);
+            native.SetPosition(body, localPos);
             native.SetRotation(body, localRot);
 	    }
 
@@ -324,12 +366,21 @@ namespace BansheeEngine
         [SerializeObject]
         internal class SerializableData
         {
+            public ScriptCommonJointData @internal;
+
+            public SerializableData()
+            {
+                @internal.bodies = new IntPtr[2];
+                @internal.positions = new Vector3[2];
+                @internal.rotations = new Quaternion[2];
+                @internal.breakForce = float.MaxValue;
+                @internal.breakTorque = float.MaxValue;
+                @internal.enableCollision = false;
+            }
+
             public Rigidbody[] bodies = new Rigidbody[2];
             public Vector3[] positions = new Vector3[2];
             public Quaternion[] rotations = new Quaternion[2];
-            public float breakForce = float.MaxValue;
-            public float breakTorque = float.MaxValue;
-            public bool enableCollision = false;
         }
     }
 

+ 137 - 118
Source/MBansheeEngine/Physics/NativeD6Joint.cs

@@ -1,118 +1,137 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native D6Joint class.
-    /// <see cref="D6Joint"/>
-    /// </summary>
-    internal class NativeD6Joint : NativeJoint
-    {
-        public Radian Twist
-        {
-            get { return new Radian(Internal_GetTwist(mCachedPtr)); }
-        }
-
-        public Radian SwingY
-        {
-            get { return new Radian(Internal_GetSwingY(mCachedPtr)); }
-        }
-
-        public Radian SwingZ
-        {
-            get { return new Radian(Internal_GetSwingZ(mCachedPtr)); }
-        }
-
-        public LimitLinear LimitLinear
-        {
-            set { Internal_SetLimitLinear(mCachedPtr, value); }
-        }
-
-        public LimitAngularRange LimitTwist
-        {
-            set { Internal_SetLimitTwist(mCachedPtr, value); }
-        }
-
-        public LimitConeRange LimitSwing
-        {
-            set { Internal_SetLimitSwing(mCachedPtr, value); }
-        }
-
-        public Vector3 DrivePosition
-        {
-            set { Internal_SetDrivePosition(mCachedPtr, ref value); }
-        }
-
-        public Quaternion DriveRotation
-        {
-            set { Internal_SetDriveRotation(mCachedPtr, ref value); }
-        }
-
-        public Vector3 DriveLinearVelocity
-        {
-            set { Internal_SetDriveLinearVelocity(mCachedPtr, ref value); }
-        }
-
-        public Vector3 DriveAngularVelocity
-        {
-            set { Internal_SetDriveAngularVelocity(mCachedPtr, ref value); }
-        }
-
-        public void SetMotion(D6JointAxis axis, D6JointMotion motion)
-        {
-            Internal_SetMotion(mCachedPtr, axis, motion);
-        }
-
-        public void SetDrive(D6JointDriveType type, D6JointDrive drive)
-        {
-            Internal_SetDrive(mCachedPtr, type, drive);
-        }
-
-        public NativeD6Joint()
-        {
-            Internal_CreateInstance(this);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(NativeD6Joint instance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetMotion(IntPtr thisPtr, D6JointAxis axis, D6JointMotion motion);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetTwist(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetSwingY(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetSwingZ(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetLimitLinear(IntPtr thisPtr, LimitLinear limit);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetLimitTwist(IntPtr thisPtr, LimitAngularRange limit);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetLimitSwing(IntPtr thisPtr, LimitConeRange limit);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDrive(IntPtr thisPtr, D6JointDriveType type, D6JointDrive drive);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDrivePosition(IntPtr thisPtr, ref Vector3 position);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDriveRotation(IntPtr thisPtr, ref Quaternion rotation);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDriveLinearVelocity(IntPtr thisPtr, ref Vector3 velocity);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDriveAngularVelocity(IntPtr thisPtr, ref Vector3 velocity);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native D6Joint class.
+    /// <see cref="D6Joint"/>
+    /// </summary>
+    internal class NativeD6Joint : NativeJoint
+    {
+        public Radian Twist
+        {
+            get { return new Radian(Internal_GetTwist(mCachedPtr)); }
+        }
+
+        public Radian SwingY
+        {
+            get { return new Radian(Internal_GetSwingY(mCachedPtr)); }
+        }
+
+        public Radian SwingZ
+        {
+            get { return new Radian(Internal_GetSwingZ(mCachedPtr)); }
+        }
+
+        public LimitLinear LimitLinear
+        {
+            set { Internal_SetLimitLinear(mCachedPtr, value); }
+        }
+
+        public LimitAngularRange LimitTwist
+        {
+            set { Internal_SetLimitTwist(mCachedPtr, value); }
+        }
+
+        public LimitConeRange LimitSwing
+        {
+            set { Internal_SetLimitSwing(mCachedPtr, value); }
+        }
+
+        public Vector3 DrivePosition
+        {
+            set { Internal_SetDrivePosition(mCachedPtr, ref value); }
+        }
+
+        public Quaternion DriveRotation
+        {
+            set { Internal_SetDriveRotation(mCachedPtr, ref value); }
+        }
+
+        public Vector3 DriveLinearVelocity
+        {
+            set { Internal_SetDriveLinearVelocity(mCachedPtr, ref value); }
+        }
+
+        public Vector3 DriveAngularVelocity
+        {
+            set { Internal_SetDriveAngularVelocity(mCachedPtr, ref value); }
+        }
+
+        public void SetMotion(D6JointAxis axis, D6JointMotion motion)
+        {
+            Internal_SetMotion(mCachedPtr, axis, motion);
+        }
+
+        public void SetDrive(D6JointDriveType type, D6JointDrive drive)
+        {
+            Internal_SetDrive(mCachedPtr, type, drive);
+        }
+
+        public NativeD6Joint(ScriptCommonJointData commonData, ScriptD6JointData data)
+        {
+            Internal_CreateInstance(this, ref commonData, ref data);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeD6Joint instance, ref ScriptCommonJointData commonData,
+            ref ScriptD6JointData data);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMotion(IntPtr thisPtr, D6JointAxis axis, D6JointMotion motion);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetTwist(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetSwingY(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetSwingZ(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLimitLinear(IntPtr thisPtr, LimitLinear limit);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLimitTwist(IntPtr thisPtr, LimitAngularRange limit);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLimitSwing(IntPtr thisPtr, LimitConeRange limit);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDrive(IntPtr thisPtr, D6JointDriveType type, D6JointDrive drive);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDrivePosition(IntPtr thisPtr, ref Vector3 position);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDriveRotation(IntPtr thisPtr, ref Quaternion rotation);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDriveLinearVelocity(IntPtr thisPtr, ref Vector3 velocity);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDriveAngularVelocity(IntPtr thisPtr, ref Vector3 velocity);
+    }
+
+    /// <summary>
+    /// Used for passing D6Joint initialization data between native and managed code.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct ScriptD6JointData // Note: Must match C++ struct ScriptD6JointData
+    {
+        public LimitLinear linearLimit;
+        public LimitAngularRange twistLimit;
+        public LimitConeRange swingLimit;
+        public D6JointMotion[] motion;
+        public D6JointDrive[] drives;
+        public Vector3 drivePosition;
+        public Quaternion driveRotation;
+        public Vector3 driveLinearVelocity;
+        public Vector3 driveAngularVelocity;
+    }
+}

+ 102 - 87
Source/MBansheeEngine/Physics/NativeDistanceJoint.cs

@@ -1,87 +1,102 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-
-using System;
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native DistanceJoint class.
-    /// <see cref="DistanceJoint"/>
-    /// </summary>
-    internal class NativeDistanceJoint : NativeJoint
-    {
-        public float Distance
-        {
-            get { return Internal_GetDistance(mCachedPtr); }
-        }
-
-        public float MinDistance
-        {
-            set { Internal_SetMinDistance(mCachedPtr, value); }
-        }
-
-        public float MaxDistance
-        {
-            set { Internal_SetMaxDistance(mCachedPtr, value); }
-        }
-
-        public float Tolerance
-        {
-            set { Internal_SetTolerance(mCachedPtr, value); }
-        }
-
-        public Spring Spring
-        {
-            set { Internal_SetSpring(mCachedPtr, ref value); }
-        }
-
-        public bool EnableMinDistanceLimit
-        {
-            set { Internal_SetEnableMinDistanceLimit(mCachedPtr, value); }
-        }
-
-        public bool EnableMaxDistanceLimit
-        {
-            set { Internal_SetEnableMaxDistanceLimit(mCachedPtr, value); }
-        }
-
-        public bool EnableSpring
-        {
-            set { Internal_SetEnableSpring(mCachedPtr, value); }
-        }
-
-        public NativeDistanceJoint()
-        {
-            Internal_CreateInstance(this);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(NativeDistanceJoint instance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetDistance(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetMinDistance(IntPtr thisPtr, float value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetMaxDistance(IntPtr thisPtr, float value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetTolerance(IntPtr thisPtr, float value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetSpring(IntPtr thisPtr, ref Spring value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableMinDistanceLimit(IntPtr thisPtr, bool value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableMaxDistanceLimit(IntPtr thisPtr, bool value);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableSpring(IntPtr thisPtr, bool value);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native DistanceJoint class.
+    /// <see cref="DistanceJoint"/>
+    /// </summary>
+    internal class NativeDistanceJoint : NativeJoint
+    {
+        public float Distance
+        {
+            get { return Internal_GetDistance(mCachedPtr); }
+        }
+
+        public float MinDistance
+        {
+            set { Internal_SetMinDistance(mCachedPtr, value); }
+        }
+
+        public float MaxDistance
+        {
+            set { Internal_SetMaxDistance(mCachedPtr, value); }
+        }
+
+        public float Tolerance
+        {
+            set { Internal_SetTolerance(mCachedPtr, value); }
+        }
+
+        public Spring Spring
+        {
+            set { Internal_SetSpring(mCachedPtr, ref value); }
+        }
+
+        public bool EnableMinDistanceLimit
+        {
+            set { Internal_SetEnableMinDistanceLimit(mCachedPtr, value); }
+        }
+
+        public bool EnableMaxDistanceLimit
+        {
+            set { Internal_SetEnableMaxDistanceLimit(mCachedPtr, value); }
+        }
+
+        public bool EnableSpring
+        {
+            set { Internal_SetEnableSpring(mCachedPtr, value); }
+        }
+
+        public NativeDistanceJoint(ScriptCommonJointData commonData, ScriptDistanceJointData data)
+        {
+            Internal_CreateInstance(this, ref commonData, ref data);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeDistanceJoint instance, ref ScriptCommonJointData commonData,
+           ref ScriptDistanceJointData data);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetDistance(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMinDistance(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMaxDistance(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetTolerance(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetSpring(IntPtr thisPtr, ref Spring value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableMinDistanceLimit(IntPtr thisPtr, bool value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableMaxDistanceLimit(IntPtr thisPtr, bool value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableSpring(IntPtr thisPtr, bool value);
+    }
+
+    /// <summary>
+    /// Used for passing DistanceJoint initialization data between native and managed code.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct ScriptDistanceJointData // Note: Must match C++ struct ScriptDistanceJointData
+    {
+        public float minDistance;
+        public float maxDistance;
+        public float tolerance;
+        public Spring spring;
+        public DistanceJoint.Flag flags;
+    }
+}

+ 21 - 21
Source/MBansheeEngine/Physics/NativeFixedJoint.cs

@@ -1,21 +1,21 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native FixedJoint class.
-    /// <see cref="FixedJoint"/>
-    /// </summary>
-    internal class NativeFixedJoint : NativeJoint
-    {
-        public NativeFixedJoint()
-        {
-            Internal_CreateInstance(this);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(NativeFixedJoint instance);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native FixedJoint class.
+    /// <see cref="FixedJoint"/>
+    /// </summary>
+    internal class NativeFixedJoint : NativeJoint
+    {
+        public NativeFixedJoint(ScriptCommonJointData commonData)
+        {
+            Internal_CreateInstance(this, ref commonData);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeFixedJoint instance, ref ScriptCommonJointData commonData);
+    }
+}

+ 83 - 70
Source/MBansheeEngine/Physics/NativeHingeJoint.cs

@@ -1,70 +1,83 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native HingeJoint class.
-    /// <see cref="HingeJoint"/>
-    /// </summary>
-    internal class NativeHingeJoint : NativeJoint
-    {
-        public Radian Angle
-        {
-            get { return new Radian(Internal_GetAngle(mCachedPtr)); }
-        }
-
-        public float Speed
-        {
-            get { return Internal_GetSpeed(mCachedPtr); }
-        }
-
-        public LimitAngularRange Limit
-        {
-            set { Internal_SetLimit(mCachedPtr, value); }
-        }
-
-        public HingeJointDrive Drive
-        {
-            set { Internal_SetDrive(mCachedPtr, value); }
-        }
-
-        public bool EnableLimit
-        {
-            set { Internal_SetEnableLimit(mCachedPtr, value); }
-        }
-
-        public bool EnableDrive
-        {
-            set { Internal_SetEnableDrive(mCachedPtr, value); }
-        }
-
-        public NativeHingeJoint()
-        {
-            Internal_CreateInstance(this);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(NativeHingeJoint instance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetAngle(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetSpeed(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetLimit(IntPtr thisPtr, LimitAngularRange limit);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDrive(IntPtr thisPtr, HingeJointDrive drive);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableLimit(IntPtr thisPtr, bool enable);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableDrive(IntPtr thisPtr, bool enable);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native HingeJoint class.
+    /// <see cref="HingeJoint"/>
+    /// </summary>
+    internal class NativeHingeJoint : NativeJoint
+    {
+        public Radian Angle
+        {
+            get { return new Radian(Internal_GetAngle(mCachedPtr)); }
+        }
+
+        public float Speed
+        {
+            get { return Internal_GetSpeed(mCachedPtr); }
+        }
+
+        public LimitAngularRange Limit
+        {
+            set { Internal_SetLimit(mCachedPtr, value); }
+        }
+
+        public HingeJointDrive Drive
+        {
+            set { Internal_SetDrive(mCachedPtr, value); }
+        }
+
+        public bool EnableLimit
+        {
+            set { Internal_SetEnableLimit(mCachedPtr, value); }
+        }
+
+        public bool EnableDrive
+        {
+            set { Internal_SetEnableDrive(mCachedPtr, value); }
+        }
+
+        public NativeHingeJoint(ScriptCommonJointData commonData, ScriptHingeJointData data)
+        {
+            Internal_CreateInstance(this, ref commonData, ref data);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeHingeJoint instance, ref ScriptCommonJointData commonData,
+            ref ScriptHingeJointData data);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetAngle(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetSpeed(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLimit(IntPtr thisPtr, LimitAngularRange limit);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDrive(IntPtr thisPtr, HingeJointDrive drive);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableLimit(IntPtr thisPtr, bool enable);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableDrive(IntPtr thisPtr, bool enable);
+    }
+
+    /// <summary>
+    /// Used for passing HingeJoint initialization data between native and managed code.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct ScriptHingeJointData // Note: Must match C++ struct ScriptHingeJointData
+    {
+        public LimitAngularRange limit;
+        public HingeJointDrive drive;
+        public HingeJoint.Flag flags;
+    }
+}

+ 105 - 90
Source/MBansheeEngine/Physics/NativeJoint.cs

@@ -1,90 +1,105 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native Joint class.
-    /// <see cref="Joint"/>
-    /// </summary>
-    internal class NativeJoint : ScriptObject
-    {
-        private Joint component;
-
-        /// <summary>
-        /// Component that owns the native joint object.
-        /// </summary>
-        public Joint Component
-        {
-            get { return component; }
-            set { component = value; }
-        }
-
-        public float BreakForce
-        {
-            set { Internal_SetBreakForce(mCachedPtr, value); }
-        }
-
-        public float BreakTorque
-        {
-            set { Internal_SetBreakTorque(mCachedPtr, value); }
-        }
-
-        public bool EnableCollision
-        {
-            set { Internal_SetEnableCollision(mCachedPtr, value); }
-        }
-
-        public void SetRigidbody(JointBody body, Rigidbody rigidbody)
-        {
-            IntPtr rigidbodyPtr = IntPtr.Zero;
-            if (rigidbody != null)
-                rigidbodyPtr = rigidbody.native.GetCachedPtr();
-
-            Internal_SetBody(mCachedPtr, body, rigidbodyPtr);
-        }
-
-        public void SetPosition(JointBody body, Vector3 position)
-        {
-            Internal_SetPosition(mCachedPtr, body, ref position);
-        }
-
-        public void SetRotation(JointBody body, Quaternion rotation)
-        {
-            Internal_SetRotation(mCachedPtr, body, ref rotation);
-        }
-        
-        public void Destroy()
-        {
-            Internal_Destroy(mCachedPtr);
-        }
-
-        private void Internal_DoOnJointBreak()
-        {
-            Component.DoOnJointBreak();
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_Destroy(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetBody(IntPtr thisPtr, JointBody body, IntPtr rigidbody);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetPosition(IntPtr thisPtr, JointBody body, ref Vector3 position);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetRotation(IntPtr thisPtr, JointBody body, ref Quaternion rotation);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetBreakForce(IntPtr thisPtr, float force);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetBreakTorque(IntPtr thisPtr, float torque);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableCollision(IntPtr thisPtr, bool value);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native Joint class.
+    /// <see cref="Joint"/>
+    /// </summary>
+    internal class NativeJoint : ScriptObject
+    {
+        private Joint component;
+
+        /// <summary>
+        /// Component that owns the native joint object.
+        /// </summary>
+        public Joint Component
+        {
+            get { return component; }
+            set { component = value; }
+        }
+
+        public float BreakForce
+        {
+            set { Internal_SetBreakForce(mCachedPtr, value); }
+        }
+
+        public float BreakTorque
+        {
+            set { Internal_SetBreakTorque(mCachedPtr, value); }
+        }
+
+        public bool EnableCollision
+        {
+            set { Internal_SetEnableCollision(mCachedPtr, value); }
+        }
+
+        public void SetRigidbody(JointBody body, Rigidbody rigidbody)
+        {
+            IntPtr rigidbodyPtr = IntPtr.Zero;
+            if (rigidbody != null)
+                rigidbodyPtr = rigidbody.native.GetCachedPtr();
+
+            Internal_SetBody(mCachedPtr, body, rigidbodyPtr);
+        }
+
+        public void SetPosition(JointBody body, Vector3 position)
+        {
+            Internal_SetPosition(mCachedPtr, body, ref position);
+        }
+
+        public void SetRotation(JointBody body, Quaternion rotation)
+        {
+            Internal_SetRotation(mCachedPtr, body, ref rotation);
+        }
+        
+        public void Destroy()
+        {
+            Internal_Destroy(mCachedPtr);
+        }
+
+        private void Internal_DoOnJointBreak()
+        {
+            Component.DoOnJointBreak();
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Destroy(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetBody(IntPtr thisPtr, JointBody body, IntPtr rigidbody);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetPosition(IntPtr thisPtr, JointBody body, ref Vector3 position);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetRotation(IntPtr thisPtr, JointBody body, ref Quaternion rotation);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetBreakForce(IntPtr thisPtr, float force);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetBreakTorque(IntPtr thisPtr, float torque);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableCollision(IntPtr thisPtr, bool value);
+    }
+
+    /// <summary>
+    /// Used for passing common Joint initialization data between native and managed code.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct ScriptCommonJointData // Note: Must match C++ struct ScriptCommonJointData
+    {
+        public IntPtr[] bodies;
+        public Vector3[] positions;
+        public Quaternion[] rotations;
+        public float breakForce;
+        public float breakTorque;
+        public bool enableCollision;
+    }
+}

+ 66 - 54
Source/MBansheeEngine/Physics/NativeSliderJoint.cs

@@ -1,54 +1,66 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native SliderJoint class.
-    /// <see cref="SliderJoint"/>
-    /// </summary>
-    internal class NativeSliderJoint : NativeJoint
-    {
-        public float Position
-        {
-            get { return Internal_GetPosition(mCachedPtr); }
-        }
-
-        public float Speed
-        {
-            get { return Internal_GetSpeed(mCachedPtr); }
-        }
-
-        public LimitLinearRange Limit
-        {
-            set { Internal_SetLimit(mCachedPtr, value); }
-        }
-
-        public bool EnableLimit
-        {
-            set { Internal_SetEnableLimit(mCachedPtr, value); }
-        }
-
-        public NativeSliderJoint()
-        {
-            Internal_CreateInstance(this);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(NativeSliderJoint instance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetPosition(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetSpeed(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetLimit(IntPtr thisPtr, LimitLinearRange limit);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableLimit(IntPtr thisPtr, bool enable);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native SliderJoint class.
+    /// <see cref="SliderJoint"/>
+    /// </summary>
+    internal class NativeSliderJoint : NativeJoint
+    {
+        public float Position
+        {
+            get { return Internal_GetPosition(mCachedPtr); }
+        }
+
+        public float Speed
+        {
+            get { return Internal_GetSpeed(mCachedPtr); }
+        }
+
+        public LimitLinearRange Limit
+        {
+            set { Internal_SetLimit(mCachedPtr, value); }
+        }
+
+        public bool EnableLimit
+        {
+            set { Internal_SetEnableLimit(mCachedPtr, value); }
+        }
+
+        public NativeSliderJoint(ScriptCommonJointData commonData, ScriptSliderJointData data)
+        {
+            Internal_CreateInstance(this, ref commonData, ref data);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeSliderJoint instance, ref ScriptCommonJointData commonData,
+            ref ScriptSliderJointData data);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetPosition(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetSpeed(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLimit(IntPtr thisPtr, LimitLinearRange limit);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableLimit(IntPtr thisPtr, bool enable);
+    }
+
+    /// <summary>
+    /// Used for passing SliderJoint initialization data between native and managed code.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct ScriptSliderJointData // Note: Must match C++ struct ScriptSliderJointData
+    {
+        public LimitLinearRange limit;
+        public bool enableLimit;
+    }
+}

+ 50 - 44
Source/MBansheeEngine/Physics/NativeSphericalJoint.cs

@@ -1,44 +1,50 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Wrapper around the native SphericalJoint class.
-    /// <see cref="SphericalJoint"/>
-    /// </summary>
-    internal class NativeSphericalJoint : NativeJoint
-    {
-        public LimitConeRange Limit
-        {
-            set { Internal_SetLimit(mCachedPtr, value); }
-        }
-
-        public bool EnableLimit
-        {
-            set { Internal_SetEnableLimit(mCachedPtr, value); }
-        }
-
-        public NativeSphericalJoint()
-        {
-            Internal_CreateInstance(this);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(NativeSphericalJoint instance);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetPosition(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetSpeed(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetLimit(IntPtr thisPtr, LimitConeRange limit);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetEnableLimit(IntPtr thisPtr, bool enable);
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native SphericalJoint class.
+    /// <see cref="SphericalJoint"/>
+    /// </summary>
+    internal class NativeSphericalJoint : NativeJoint
+    {
+        public LimitConeRange Limit
+        {
+            set { Internal_SetLimit(mCachedPtr, value); }
+        }
+
+        public bool EnableLimit
+        {
+            set { Internal_SetEnableLimit(mCachedPtr, value); }
+        }
+
+        public NativeSphericalJoint(ScriptCommonJointData commonData, ScriptSphericalJointData data)
+        {
+            Internal_CreateInstance(this, ref commonData, ref data);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeSphericalJoint instance, 
+            ref ScriptCommonJointData commonData, ref ScriptSphericalJointData data);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLimit(IntPtr thisPtr, LimitConeRange limit);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEnableLimit(IntPtr thisPtr, bool enable);
+    }
+
+    /// <summary>
+    /// Used for passing SphericalJoint initialization data between native and managed code.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct ScriptSphericalJointData // Note: Must match C++ struct ScriptSphericalJointData
+    {
+        public LimitConeRange limit;
+        public bool enableLimit;
+    }
+}

+ 81 - 72
Source/MBansheeEngine/Physics/SliderJoint.cs

@@ -1,72 +1,81 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Joint that removes all but a single translational degree of freedom. Bodies are allowed to move along a single axis.
-    /// </summary>
-    public sealed class SliderJoint : Joint
-    {
-        [SerializeField]
-        private LimitLinearRange limit = new LimitLinearRange();
-        [SerializeField]
-        private bool enableLimit;
-
-        /// <summary>
-        /// Determines the limit that constrains the movement of the joint to a specific minimum and maximum distance. You
-        /// must enable <see cref="EnableLimit"/> for this to be enforced.
-        /// </summary>
-        public LimitLinearRange Limit
-        {
-            get { return limit; }
-            set
-            {
-                if (limit == value)
-                    return;
-
-                limit = value;
-
-                if (Native != null)
-                    Native.Limit = value;
-            }
-        }
-
-        /// <summary>
-        /// Enables or disables the limit that clamps the movement of the joint.
-        /// </summary>
-        public bool EnableLimit
-        {
-            get { return enableLimit; }
-            set
-            {
-                if (enableLimit == value)
-                    return;
-
-                enableLimit = value;
-
-                if (Native != null)
-                    Native.EnableLimit = value;
-            }
-        }
-
-        /// <summary>
-        /// Returns the native joint wrapped by this component.
-        /// </summary>
-        private NativeSliderJoint Native
-        {
-            get { return (NativeSliderJoint)native; }
-        }
-
-        /// <inheritdoc/>
-        internal override NativeJoint CreateNative()
-        {
-            NativeSliderJoint joint = new NativeSliderJoint();
-
-            // TODO - Apply this all at once to avoid all the individual interop function calls
-            joint.Limit = limit;
-            joint.EnableLimit = enableLimit;
-
-            return joint;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Joint that removes all but a single translational degree of freedom. Bodies are allowed to move along a single axis.
+    /// </summary>
+    public sealed class SliderJoint : Joint
+    {
+        [SerializeField]
+        private SerializableData data = new SerializableData();
+
+        /// <summary>
+        /// Determines the limit that constrains the movement of the joint to a specific minimum and maximum distance. You
+        /// must enable <see cref="EnableLimit"/> for this to be enforced.
+        /// </summary>
+        public LimitLinearRange Limit
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.Limit = value;
+            }
+        }
+
+        /// <summary>
+        /// Enables or disables the limit that clamps the movement of the joint.
+        /// </summary>
+        public bool EnableLimit
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.EnableLimit = value;
+            }
+        }
+
+        /// <summary>
+        /// Returns the native joint wrapped by this component.
+        /// </summary>
+        private NativeSliderJoint Native
+        {
+            get { return (NativeSliderJoint)native; }
+        }
+
+        /// <inheritdoc/>
+        internal override NativeJoint CreateNative()
+        {
+            NativeSliderJoint joint = new NativeSliderJoint(commonData.@internal, data.@internal);
+
+            return joint;
+        }
+
+        /// <summary>
+        /// Holds all data the joint component needs to persist through serialization.
+        /// </summary>
+        [SerializeObject]
+        internal new class SerializableData
+        {
+            public ScriptSliderJointData @internal;
+
+            public SerializableData()
+            {
+                @internal.limit = new LimitLinearRange();
+                @internal.enableLimit = false;
+            }
+        }
+    }
+}

+ 85 - 76
Source/MBansheeEngine/Physics/SphericalJoint.cs

@@ -1,76 +1,85 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// A spherical joint removes all translational degrees of freedom but allows all rotational degrees of freedom. 
-    /// Essentially this ensures that the anchor points of the two bodies are always coincident. Bodies are allowed to
-    /// rotate around the anchor points, and their rotatation can be limited by an elliptical cone.
-    /// </summary>
-    public sealed class SphericalJoint : Joint
-    {
-        [SerializeField]
-        private LimitConeRange limit = new LimitConeRange();
-        [SerializeField]
-        private bool enableLimit;
-        
-        /// <summary>
-        /// Determines the limit that clamps the rotation of the joint inside an eliptical angular cone. You must enable 
-        /// <see cref="EnableLimit"/> for this to be enforced.
-        /// </summary>
-        public LimitConeRange Limit
-        {
-            get { return limit; }
-            set
-            {
-                if (limit == value)
-                    return;
-
-                limit = value;
-
-                if (Native != null)
-                    Native.Limit = value;
-            }
-        }
-        
-        /// <summary>
-        /// Enables or disables the limit that clamps the rotation of the joint.
-        /// </summary>
-        public bool EnableLimit
-        {
-            get { return enableLimit; }
-            set
-            {
-                if (enableLimit == value)
-                    return;
-
-                enableLimit = value;
-
-                if (Native != null)
-                    Native.EnableLimit = value;
-            }
-        }
-
-        /// <summary>
-        /// Returns the native joint wrapped by this component.
-        /// </summary>
-        private NativeSphericalJoint Native
-        {
-            get { return (NativeSphericalJoint)native; }
-        }
-
-        /// <inheritdoc/>
-        internal override NativeJoint CreateNative()
-        {
-            NativeSphericalJoint joint = new NativeSphericalJoint();
-
-            // TODO - Apply this all at once to avoid all the individual interop function calls
-            joint.Limit = limit;
-            joint.EnableLimit = enableLimit;
-
-            return joint;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// A spherical joint removes all translational degrees of freedom but allows all rotational degrees of freedom. 
+    /// Essentially this ensures that the anchor points of the two bodies are always coincident. Bodies are allowed to
+    /// rotate around the anchor points, and their rotatation can be limited by an elliptical cone.
+    /// </summary>
+    public sealed class SphericalJoint : Joint
+    {
+        [SerializeField]
+        private SerializableData data = new SerializableData();
+
+        /// <summary>
+        /// Determines the limit that clamps the rotation of the joint inside an eliptical angular cone. You must enable 
+        /// <see cref="EnableLimit"/> for this to be enforced.
+        /// </summary>
+        public LimitConeRange Limit
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.Limit = value;
+            }
+        }
+        
+        /// <summary>
+        /// Enables or disables the limit that clamps the rotation of the joint.
+        /// </summary>
+        public bool EnableLimit
+        {
+            get { return [email protected]; }
+            set
+            {
+                if ([email protected] == value)
+                    return;
+
+                [email protected] = value;
+
+                if (Native != null)
+                    Native.EnableLimit = value;
+            }
+        }
+
+        /// <summary>
+        /// Returns the native joint wrapped by this component.
+        /// </summary>
+        private NativeSphericalJoint Native
+        {
+            get { return (NativeSphericalJoint)native; }
+        }
+
+        /// <inheritdoc/>
+        internal override NativeJoint CreateNative()
+        {
+            NativeSphericalJoint joint = new NativeSphericalJoint(commonData.@internal, data.@internal);
+
+            return joint;
+        }
+
+        /// <summary>
+        /// Holds all data the joint component needs to persist through serialization.
+        /// </summary>
+        [SerializeObject]
+        internal new class SerializableData
+        {
+            public ScriptSphericalJointData @internal;
+
+            public SerializableData()
+            {
+                @internal.limit = new LimitConeRange();
+                @internal.enableLimit = false;
+            }
+        }
+    }
+}

+ 18 - 1
Source/SBansheeEngine/Include/BsScriptDistanceJoint.h

@@ -5,6 +5,7 @@
 #include "BsScriptEnginePrerequisites.h"
 #include "BsScriptCollider.h"
 #include "BsScriptJoint.h"
+#include "BsDistanceJoint.h"
 
 namespace BansheeEngine
 {
@@ -12,6 +13,8 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	struct ScriptDistanceJointData;
+
 	/** Interop class between C++ & CLR for DistanceJoint. */
 	class BS_SCR_BE_EXPORT ScriptDistanceJoint : public TScriptJoint<ScriptDistanceJoint>
 	{
@@ -26,7 +29,8 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_CreateInstance(MonoObject* instance);
+		static void internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, 
+			ScriptDistanceJointData* data);
 		static float internal_GetDistance(ScriptDistanceJoint* thisPtr);
 		static void internal_SetMinDistance(ScriptDistanceJoint* thisPtr, float value);
 		static void internal_SetMaxDistance(ScriptDistanceJoint* thisPtr, float value);
@@ -37,5 +41,18 @@ namespace BansheeEngine
 		static void internal_SetEnableSpring(ScriptDistanceJoint* thisPtr, bool value);
 	};
 
+	/** Used for passing common Joint initialization data between native and managed code. */
+	struct ScriptDistanceJointData // Note: Must match C# struct ScriptDistanceJointData
+	{
+		float minDistance;
+		float maxDistance;
+		float tolerance;
+		Spring spring;
+		DistanceJoint::Flag flags;
+
+		/** Converts this structure into a descriptor used for initializing a joint. */
+		void toDesc(DISTANCE_JOINT_DESC& desc) const;
+	};
+
 	/** @} */
 }

+ 14 - 0
Source/SBansheeEngine/Include/BsScriptJoint.h

@@ -80,5 +80,19 @@ namespace BansheeEngine
 		static OnJointBreakThunkDef onJointBreakThunk;
 	};
 
+	/** Used for passing common Joint initialization data between native and managed code. */
+	struct ScriptCommonJointData // Note: Must match C# struct ScriptCommonJointData
+	{
+		MonoArray* bodies;
+		MonoArray* positions;
+		MonoArray* rotations;
+		float breakForce;
+		float breakTorque;
+		bool enableCollision;
+
+		/** Converts this structure into a descriptor used for initializing a joint. */
+		void toDesc(JOINT_DESC& desc) const;
+	};
+
 	/** @} */
 }

+ 134 - 110
Source/SBansheeEngine/Source/BsScriptD6Joint.cpp

@@ -1,111 +1,135 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptD6Joint.h"
-#include "BsScriptJointCommon.h"
-
-namespace BansheeEngine
-{
-	ScriptD6Joint::ScriptD6Joint(MonoObject* instance, const SPtr<Joint>& joint)
-		:TScriptJoint(instance, joint)
-	{
-
-	}
-
-	void ScriptD6Joint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptD6Joint::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_SetMotion", &ScriptD6Joint::internal_SetMotion);
-		metaData.scriptClass->addInternalCall("Internal_GetTwist", &ScriptD6Joint::internal_GetTwist);
-		metaData.scriptClass->addInternalCall("Internal_GetSwingY", &ScriptD6Joint::internal_GetSwingY);
-		metaData.scriptClass->addInternalCall("Internal_GetSwingZ", &ScriptD6Joint::internal_GetSwingZ);
-		metaData.scriptClass->addInternalCall("Internal_SetLimitLinear", &ScriptD6Joint::internal_SetLimitLinear);
-		metaData.scriptClass->addInternalCall("Internal_SetLimitTwist", &ScriptD6Joint::internal_SetLimitTwist);
-		metaData.scriptClass->addInternalCall("Internal_SetLimitSwing", &ScriptD6Joint::internal_SetLimitSwing);
-		metaData.scriptClass->addInternalCall("Internal_SetDrive", &ScriptD6Joint::internal_SetDrive);
-		metaData.scriptClass->addInternalCall("Internal_SetDrivePosition", &ScriptD6Joint::internal_SetDrivePosition);
-		metaData.scriptClass->addInternalCall("Internal_SetDriveRotation", &ScriptD6Joint::internal_SetDriveRotation);
-		metaData.scriptClass->addInternalCall("Internal_SetDriveLinearVelocity", &ScriptD6Joint::internal_SetDriveLinearVelocity);
-		metaData.scriptClass->addInternalCall("Internal_SetDriveAngularVelocity", &ScriptD6Joint::internal_SetDriveAngularVelocity);
-	}
-
-	D6Joint* ScriptD6Joint::getD6Joint() const
-	{
-		return static_cast<D6Joint*>(mJoint.get());
-	}
-
-	void ScriptD6Joint::internal_CreateInstance(MonoObject* instance)
-	{
-		SPtr<D6Joint> joint = D6Joint::create();
-		joint->_setOwner(PhysicsOwnerType::Script, instance);
-
-		ScriptD6Joint* scriptJoint = new (bs_alloc<ScriptD6Joint>()) ScriptD6Joint(instance, joint);
-	}
-
-	void ScriptD6Joint::internal_SetMotion(ScriptD6Joint* thisPtr, D6Joint::Axis axis, D6Joint::Motion motion)
-	{
-		thisPtr->getD6Joint()->setMotion(axis, motion);
-	}
-
-	float ScriptD6Joint::internal_GetTwist(ScriptD6Joint* thisPtr)
-	{
-		return thisPtr->getD6Joint()->getTwist().valueRadians();
-	}
-
-	float ScriptD6Joint::internal_GetSwingY(ScriptD6Joint* thisPtr)
-	{
-		return thisPtr->getD6Joint()->getSwingY().valueRadians();
-	}
-
-	float ScriptD6Joint::internal_GetSwingZ(ScriptD6Joint* thisPtr)
-	{
-		return thisPtr->getD6Joint()->getSwingZ().valueRadians();
-	}
-
-	void ScriptD6Joint::internal_SetLimitLinear(ScriptD6Joint* thisPtr, MonoObject* limit)
-	{
-		LimitLinear nativeLimit = ScriptLimitLinear::convert(limit);
-		thisPtr->getD6Joint()->setLimitLinear(nativeLimit);
-	}
-
-	void ScriptD6Joint::internal_SetLimitTwist(ScriptD6Joint* thisPtr, MonoObject* limit)
-	{
-		LimitAngularRange nativeLimit = ScriptLimitAngularRange::convert(limit);
-		thisPtr->getD6Joint()->setLimitTwist(nativeLimit);
-	}
-
-	void ScriptD6Joint::internal_SetLimitSwing(ScriptD6Joint* thisPtr, MonoObject* limit)
-	{
-		LimitConeRange nativeLimit = ScriptLimitConeRange::convert(limit);
-		thisPtr->getD6Joint()->setLimitSwing(nativeLimit);
-	}
-
-	void ScriptD6Joint::internal_SetDrive(ScriptD6Joint* thisPtr, D6Joint::DriveType type, MonoObject* drive)
-	{
-		D6Joint::Drive nativeDrive = ScriptD6JointDrive::convert(drive);
-		thisPtr->getD6Joint()->setDrive(type, nativeDrive);
-	}
-
-	void ScriptD6Joint::internal_SetDrivePosition(ScriptD6Joint* thisPtr, Vector3* position)
-	{
-		Quaternion rotation = thisPtr->getD6Joint()->getDriveRotation();
-		thisPtr->getD6Joint()->setDriveTransform(*position, rotation);
-	}
-
-	void ScriptD6Joint::internal_SetDriveRotation(ScriptD6Joint* thisPtr, Quaternion* rotation)
-	{
-		Vector3 position = thisPtr->getD6Joint()->getDrivePosition();
-		thisPtr->getD6Joint()->setDriveTransform(position, *rotation);
-	}
-
-	void ScriptD6Joint::internal_SetDriveLinearVelocity(ScriptD6Joint* thisPtr, Vector3* velocity)
-	{
-		Vector3 angularVelocity = thisPtr->getD6Joint()->getDriveAngularVelocity();
-		thisPtr->getD6Joint()->setDriveVelocity(*velocity, angularVelocity);
-	}
-
-	void ScriptD6Joint::internal_SetDriveAngularVelocity(ScriptD6Joint* thisPtr, Vector3* velocity)
-	{
-		Vector3 linearVelocity = thisPtr->getD6Joint()->getDriveLinearVelocity();
-		thisPtr->getD6Joint()->setDriveVelocity(linearVelocity, *velocity);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptD6Joint.h"
+#include "BsScriptJointCommon.h"
+#include "BsMonoUtil.h"
+
+namespace BansheeEngine
+{
+	ScriptD6Joint::ScriptD6Joint(MonoObject* instance, const SPtr<Joint>& joint)
+		:TScriptJoint(instance, joint)
+	{
+
+	}
+
+	void ScriptD6Joint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptD6Joint::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_SetMotion", &ScriptD6Joint::internal_SetMotion);
+		metaData.scriptClass->addInternalCall("Internal_GetTwist", &ScriptD6Joint::internal_GetTwist);
+		metaData.scriptClass->addInternalCall("Internal_GetSwingY", &ScriptD6Joint::internal_GetSwingY);
+		metaData.scriptClass->addInternalCall("Internal_GetSwingZ", &ScriptD6Joint::internal_GetSwingZ);
+		metaData.scriptClass->addInternalCall("Internal_SetLimitLinear", &ScriptD6Joint::internal_SetLimitLinear);
+		metaData.scriptClass->addInternalCall("Internal_SetLimitTwist", &ScriptD6Joint::internal_SetLimitTwist);
+		metaData.scriptClass->addInternalCall("Internal_SetLimitSwing", &ScriptD6Joint::internal_SetLimitSwing);
+		metaData.scriptClass->addInternalCall("Internal_SetDrive", &ScriptD6Joint::internal_SetDrive);
+		metaData.scriptClass->addInternalCall("Internal_SetDrivePosition", &ScriptD6Joint::internal_SetDrivePosition);
+		metaData.scriptClass->addInternalCall("Internal_SetDriveRotation", &ScriptD6Joint::internal_SetDriveRotation);
+		metaData.scriptClass->addInternalCall("Internal_SetDriveLinearVelocity", &ScriptD6Joint::internal_SetDriveLinearVelocity);
+		metaData.scriptClass->addInternalCall("Internal_SetDriveAngularVelocity", &ScriptD6Joint::internal_SetDriveAngularVelocity);
+	}
+
+	D6Joint* ScriptD6Joint::getD6Joint() const
+	{
+		return static_cast<D6Joint*>(mJoint.get());
+	}
+
+	void ScriptD6Joint::internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptD6JointData* data)
+	{
+		D6_JOINT_DESC desc;
+		commonData->toDesc(desc);
+		data->toDesc(desc);
+
+		SPtr<D6Joint> joint = D6Joint::create(desc);
+		joint->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptD6Joint* scriptJoint = new (bs_alloc<ScriptD6Joint>()) ScriptD6Joint(instance, joint);
+	}
+
+	void ScriptD6Joint::internal_SetMotion(ScriptD6Joint* thisPtr, D6Joint::Axis axis, D6Joint::Motion motion)
+	{
+		thisPtr->getD6Joint()->setMotion(axis, motion);
+	}
+
+	float ScriptD6Joint::internal_GetTwist(ScriptD6Joint* thisPtr)
+	{
+		return thisPtr->getD6Joint()->getTwist().valueRadians();
+	}
+
+	float ScriptD6Joint::internal_GetSwingY(ScriptD6Joint* thisPtr)
+	{
+		return thisPtr->getD6Joint()->getSwingY().valueRadians();
+	}
+
+	float ScriptD6Joint::internal_GetSwingZ(ScriptD6Joint* thisPtr)
+	{
+		return thisPtr->getD6Joint()->getSwingZ().valueRadians();
+	}
+
+	void ScriptD6Joint::internal_SetLimitLinear(ScriptD6Joint* thisPtr, MonoObject* limit)
+	{
+		LimitLinear nativeLimit = ScriptLimitLinear::convert(limit);
+		thisPtr->getD6Joint()->setLimitLinear(nativeLimit);
+	}
+
+	void ScriptD6Joint::internal_SetLimitTwist(ScriptD6Joint* thisPtr, MonoObject* limit)
+	{
+		LimitAngularRange nativeLimit = ScriptLimitAngularRange::convert(limit);
+		thisPtr->getD6Joint()->setLimitTwist(nativeLimit);
+	}
+
+	void ScriptD6Joint::internal_SetLimitSwing(ScriptD6Joint* thisPtr, MonoObject* limit)
+	{
+		LimitConeRange nativeLimit = ScriptLimitConeRange::convert(limit);
+		thisPtr->getD6Joint()->setLimitSwing(nativeLimit);
+	}
+
+	void ScriptD6Joint::internal_SetDrive(ScriptD6Joint* thisPtr, D6Joint::DriveType type, MonoObject* drive)
+	{
+		D6Joint::Drive nativeDrive = ScriptD6JointDrive::convert(drive);
+		thisPtr->getD6Joint()->setDrive(type, nativeDrive);
+	}
+
+	void ScriptD6Joint::internal_SetDrivePosition(ScriptD6Joint* thisPtr, Vector3* position)
+	{
+		Quaternion rotation = thisPtr->getD6Joint()->getDriveRotation();
+		thisPtr->getD6Joint()->setDriveTransform(*position, rotation);
+	}
+
+	void ScriptD6Joint::internal_SetDriveRotation(ScriptD6Joint* thisPtr, Quaternion* rotation)
+	{
+		Vector3 position = thisPtr->getD6Joint()->getDrivePosition();
+		thisPtr->getD6Joint()->setDriveTransform(position, *rotation);
+	}
+
+	void ScriptD6Joint::internal_SetDriveLinearVelocity(ScriptD6Joint* thisPtr, Vector3* velocity)
+	{
+		Vector3 angularVelocity = thisPtr->getD6Joint()->getDriveAngularVelocity();
+		thisPtr->getD6Joint()->setDriveVelocity(*velocity, angularVelocity);
+	}
+
+	void ScriptD6Joint::internal_SetDriveAngularVelocity(ScriptD6Joint* thisPtr, Vector3* velocity)
+	{
+		Vector3 linearVelocity = thisPtr->getD6Joint()->getDriveLinearVelocity();
+		thisPtr->getD6Joint()->setDriveVelocity(linearVelocity, *velocity);
+	}
+
+	void ScriptD6JointData::toDesc(D6_JOINT_DESC& desc) const
+	{
+		ScriptArray motionArray(motion);
+
+		for (UINT32 i = 0; i < (UINT32)D6Joint::Axis::Count; i++)
+			desc.motion[i] = motionArray.get<D6Joint::Motion>(i);
+
+		for (UINT32 i = 0; i < (UINT32)D6Joint::DriveType::Count; i++)
+			desc.drive[i] = ScriptD6JointDrive::convert(motionArray.get<MonoObject*>(i));
+
+		desc.limitLinear = ScriptLimitLinear::convert(linearLimit);
+		desc.limitTwist = ScriptLimitAngularRange::convert(twistLimit);
+		desc.limitSwing = ScriptLimitConeRange::convert(swingLimit);
+		desc.drivePosition = drivePosition;
+		desc.driveRotation = driveRotation;
+		desc.driveLinearVelocity = driveLinearVelocity;
+		desc.driveAngularVelocity = driveAngularVelocity;
+	}
 }

+ 20 - 1
Source/SBansheeEngine/Source/BsScriptD6Joint.h

@@ -13,6 +13,8 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	struct ScriptD6JointData;
+
 	/** Interop class between C++ & CLR for D6Joint. */
 	class BS_SCR_BE_EXPORT ScriptD6Joint : public TScriptJoint<ScriptD6Joint>
 	{
@@ -27,7 +29,7 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_CreateInstance(MonoObject* instance);
+		static void internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptD6JointData* data);
 		static void internal_SetMotion(ScriptD6Joint* thisPtr, D6Joint::Axis axis, D6Joint::Motion motion);
 		static float internal_GetTwist(ScriptD6Joint* thisPtr);
 		static float internal_GetSwingY(ScriptD6Joint* thisPtr);
@@ -42,5 +44,22 @@ namespace BansheeEngine
 		static void internal_SetDriveAngularVelocity(ScriptD6Joint* thisPtr, Vector3* velocity);
 	};
 
+	/** Used for passing common Joint initialization data between native and managed code. */
+	struct ScriptD6JointData // Note: Must match C# struct ScriptD6JointData
+	{
+		MonoObject* linearLimit;
+		MonoObject* twistLimit;
+		MonoObject* swingLimit;
+		MonoArray* motion;
+		MonoArray* drives;
+		Vector3 drivePosition;
+		Quaternion driveRotation;
+		Vector3 driveLinearVelocity;
+		Vector3 driveAngularVelocity;
+
+		/** Converts this structure into a descriptor used for initializing a joint. */
+		void toDesc(D6_JOINT_DESC& desc) const;
+	};
+
 	/** @} */
 }

+ 92 - 78
Source/SBansheeEngine/Source/BsScriptDistanceJoint.cpp

@@ -1,79 +1,93 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptDistanceJoint.h"
-#include "BsDistanceJoint.h"
-
-namespace BansheeEngine
-{
-	ScriptDistanceJoint::ScriptDistanceJoint(MonoObject* instance, const SPtr<Joint>& joint)
-		:TScriptJoint(instance, joint)
-	{
-
-	}
-
-	void ScriptDistanceJoint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptDistanceJoint::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_GetDistance", &ScriptDistanceJoint::internal_GetDistance);
-		metaData.scriptClass->addInternalCall("Internal_SetMinDistance", &ScriptDistanceJoint::internal_SetMinDistance);
-		metaData.scriptClass->addInternalCall("Internal_SetMaxDistance", &ScriptDistanceJoint::internal_SetMaxDistance);
-		metaData.scriptClass->addInternalCall("Internal_SetTolerance", &ScriptDistanceJoint::internal_SetTolerance);
-		metaData.scriptClass->addInternalCall("Internal_SetSpring", &ScriptDistanceJoint::internal_SetSpring);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableMinDistanceLimit", &ScriptDistanceJoint::internal_SetEnableMinDistanceLimit);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableMaxDistanceLimit", &ScriptDistanceJoint::internal_SetEnableMaxDistanceLimit);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableSpring", &ScriptDistanceJoint::internal_SetEnableSpring);
-	}
-
-	DistanceJoint* ScriptDistanceJoint::getDistanceJoint() const
-	{
-		return static_cast<DistanceJoint*>(mJoint.get());
-	}
-
-	void ScriptDistanceJoint::internal_CreateInstance(MonoObject* instance)
-	{
-		SPtr<DistanceJoint> joint = DistanceJoint::create();
-		joint->_setOwner(PhysicsOwnerType::Script, instance);
-
-		ScriptDistanceJoint* scriptJoint = new (bs_alloc<ScriptDistanceJoint>()) ScriptDistanceJoint(instance, joint);
-	}
-
-	float ScriptDistanceJoint::internal_GetDistance(ScriptDistanceJoint* thisPtr)
-	{
-		return thisPtr->getDistanceJoint()->getDistance();
-	}
-
-	void ScriptDistanceJoint::internal_SetMinDistance(ScriptDistanceJoint* thisPtr, float value)
-	{
-		thisPtr->getDistanceJoint()->setMinDistance(value);
-	}
-
-	void ScriptDistanceJoint::internal_SetMaxDistance(ScriptDistanceJoint* thisPtr, float value)
-	{
-		thisPtr->getDistanceJoint()->setMaxDistance(value);
-	}
-
-	void ScriptDistanceJoint::internal_SetTolerance(ScriptDistanceJoint* thisPtr, float value)
-	{
-		thisPtr->getDistanceJoint()->setTolerance(value);
-	}
-
-	void ScriptDistanceJoint::internal_SetSpring(ScriptDistanceJoint* thisPtr, Spring* value)
-	{
-		thisPtr->getDistanceJoint()->setSpring(*value);
-	}
-
-	void ScriptDistanceJoint::internal_SetEnableMinDistanceLimit(ScriptDistanceJoint* thisPtr, bool value)
-	{
-		thisPtr->getDistanceJoint()->setFlag(DistanceJoint::Flag::MinDistance, value);
-	}
-
-	void ScriptDistanceJoint::internal_SetEnableMaxDistanceLimit(ScriptDistanceJoint* thisPtr, bool value)
-	{
-		thisPtr->getDistanceJoint()->setFlag(DistanceJoint::Flag::MaxDistance, value);
-	}
-
-	void ScriptDistanceJoint::internal_SetEnableSpring(ScriptDistanceJoint* thisPtr, bool value)
-	{
-		thisPtr->getDistanceJoint()->setFlag(DistanceJoint::Flag::Spring, value);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptDistanceJoint.h"
+#include "BsDistanceJoint.h"
+
+namespace BansheeEngine
+{
+	ScriptDistanceJoint::ScriptDistanceJoint(MonoObject* instance, const SPtr<Joint>& joint)
+		:TScriptJoint(instance, joint)
+	{
+
+	}
+
+	void ScriptDistanceJoint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptDistanceJoint::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetDistance", &ScriptDistanceJoint::internal_GetDistance);
+		metaData.scriptClass->addInternalCall("Internal_SetMinDistance", &ScriptDistanceJoint::internal_SetMinDistance);
+		metaData.scriptClass->addInternalCall("Internal_SetMaxDistance", &ScriptDistanceJoint::internal_SetMaxDistance);
+		metaData.scriptClass->addInternalCall("Internal_SetTolerance", &ScriptDistanceJoint::internal_SetTolerance);
+		metaData.scriptClass->addInternalCall("Internal_SetSpring", &ScriptDistanceJoint::internal_SetSpring);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableMinDistanceLimit", &ScriptDistanceJoint::internal_SetEnableMinDistanceLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableMaxDistanceLimit", &ScriptDistanceJoint::internal_SetEnableMaxDistanceLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableSpring", &ScriptDistanceJoint::internal_SetEnableSpring);
+	}
+
+	DistanceJoint* ScriptDistanceJoint::getDistanceJoint() const
+	{
+		return static_cast<DistanceJoint*>(mJoint.get());
+	}
+
+	void ScriptDistanceJoint::internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData,
+		ScriptDistanceJointData* data)
+	{
+		DISTANCE_JOINT_DESC desc;
+		commonData->toDesc(desc);
+		data->toDesc(desc);
+
+		SPtr<DistanceJoint> joint = DistanceJoint::create(desc);
+		joint->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptDistanceJoint* scriptJoint = new (bs_alloc<ScriptDistanceJoint>()) ScriptDistanceJoint(instance, joint);
+	}
+
+	float ScriptDistanceJoint::internal_GetDistance(ScriptDistanceJoint* thisPtr)
+	{
+		return thisPtr->getDistanceJoint()->getDistance();
+	}
+
+	void ScriptDistanceJoint::internal_SetMinDistance(ScriptDistanceJoint* thisPtr, float value)
+	{
+		thisPtr->getDistanceJoint()->setMinDistance(value);
+	}
+
+	void ScriptDistanceJoint::internal_SetMaxDistance(ScriptDistanceJoint* thisPtr, float value)
+	{
+		thisPtr->getDistanceJoint()->setMaxDistance(value);
+	}
+
+	void ScriptDistanceJoint::internal_SetTolerance(ScriptDistanceJoint* thisPtr, float value)
+	{
+		thisPtr->getDistanceJoint()->setTolerance(value);
+	}
+
+	void ScriptDistanceJoint::internal_SetSpring(ScriptDistanceJoint* thisPtr, Spring* value)
+	{
+		thisPtr->getDistanceJoint()->setSpring(*value);
+	}
+
+	void ScriptDistanceJoint::internal_SetEnableMinDistanceLimit(ScriptDistanceJoint* thisPtr, bool value)
+	{
+		thisPtr->getDistanceJoint()->setFlag(DistanceJoint::Flag::MinDistance, value);
+	}
+
+	void ScriptDistanceJoint::internal_SetEnableMaxDistanceLimit(ScriptDistanceJoint* thisPtr, bool value)
+	{
+		thisPtr->getDistanceJoint()->setFlag(DistanceJoint::Flag::MaxDistance, value);
+	}
+
+	void ScriptDistanceJoint::internal_SetEnableSpring(ScriptDistanceJoint* thisPtr, bool value)
+	{
+		thisPtr->getDistanceJoint()->setFlag(DistanceJoint::Flag::Spring, value);
+	}
+
+	void ScriptDistanceJointData::toDesc(DISTANCE_JOINT_DESC& desc) const
+	{
+		desc.minDistance = minDistance;
+		desc.maxDistance = maxDistance;
+		desc.tolerance = tolerance;
+		desc.spring = spring;
+		desc.flag = flags;
+	}
 }

+ 33 - 30
Source/SBansheeEngine/Source/BsScriptFixedJoint.cpp

@@ -1,31 +1,34 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptFixedJoint.h"
-#include "BsFixedJoint.h"
-
-namespace BansheeEngine
-{
-	ScriptFixedJoint::ScriptFixedJoint(MonoObject* instance, const SPtr<Joint>& joint)
-		:TScriptJoint(instance, joint)
-	{
-
-	}
-
-	void ScriptFixedJoint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptFixedJoint::internal_CreateInstance);
-	}
-
-	FixedJoint* ScriptFixedJoint::getFixedJoint() const
-	{
-		return static_cast<FixedJoint*>(mJoint.get());
-	}
-
-	void ScriptFixedJoint::internal_CreateInstance(MonoObject* instance)
-	{
-		SPtr<FixedJoint> joint = FixedJoint::create();
-		joint->_setOwner(PhysicsOwnerType::Script, instance);
-
-		ScriptFixedJoint* scriptJoint = new (bs_alloc<ScriptFixedJoint>()) ScriptFixedJoint(instance, joint);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptFixedJoint.h"
+#include "BsFixedJoint.h"
+
+namespace BansheeEngine
+{
+	ScriptFixedJoint::ScriptFixedJoint(MonoObject* instance, const SPtr<Joint>& joint)
+		:TScriptJoint(instance, joint)
+	{
+
+	}
+
+	void ScriptFixedJoint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptFixedJoint::internal_CreateInstance);
+	}
+
+	FixedJoint* ScriptFixedJoint::getFixedJoint() const
+	{
+		return static_cast<FixedJoint*>(mJoint.get());
+	}
+
+	void ScriptFixedJoint::internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData)
+	{
+		FIXED_JOINT_DESC desc;
+		commonData->toDesc(desc);
+
+		SPtr<FixedJoint> joint = FixedJoint::create(desc);
+		joint->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptFixedJoint* scriptJoint = new (bs_alloc<ScriptFixedJoint>()) ScriptFixedJoint(instance, joint);
+	}
 }

+ 1 - 1
Source/SBansheeEngine/Source/BsScriptFixedJoint.h

@@ -26,7 +26,7 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_CreateInstance(MonoObject* instance);
+		static void internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData);
 	};
 
 	/** @} */

+ 81 - 70
Source/SBansheeEngine/Source/BsScriptHingeJoint.cpp

@@ -1,71 +1,82 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptHingeJoint.h"
-#include "BsHingeJoint.h"
-#include "BsScriptJointCommon.h"
-
-namespace BansheeEngine
-{
-	ScriptHingeJoint::ScriptHingeJoint(MonoObject* instance, const SPtr<Joint>& joint)
-		:TScriptJoint(instance, joint)
-	{
-
-	}
-
-	void ScriptHingeJoint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptHingeJoint::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_GetAngle", &ScriptHingeJoint::internal_GetAngle);
-		metaData.scriptClass->addInternalCall("Internal_GetSpeed", &ScriptHingeJoint::internal_GetSpeed);
-		metaData.scriptClass->addInternalCall("Internal_SetLimit", &ScriptHingeJoint::internal_SetLimit);
-		metaData.scriptClass->addInternalCall("Internal_SetDrive", &ScriptHingeJoint::internal_SetDrive);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableLimit", &ScriptHingeJoint::internal_SetEnableLimit);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableDrive", &ScriptHingeJoint::internal_SetEnableDrive);
-	}
-
-	HingeJoint* ScriptHingeJoint::getHingeJoint() const
-	{
-		return static_cast<HingeJoint*>(mJoint.get());
-	}
-
-	void ScriptHingeJoint::internal_CreateInstance(MonoObject* instance)
-	{
-		SPtr<HingeJoint> joint = HingeJoint::create();
-		joint->_setOwner(PhysicsOwnerType::Script, instance);
-
-		ScriptHingeJoint* scriptJoint = new (bs_alloc<ScriptHingeJoint>()) ScriptHingeJoint(instance, joint);
-	}
-
-	float ScriptHingeJoint::internal_GetAngle(ScriptHingeJoint* thisPtr)
-	{
-		return thisPtr->getHingeJoint()->getAngle().valueRadians();
-	}
-
-	float ScriptHingeJoint::internal_GetSpeed(ScriptHingeJoint* thisPtr)
-	{
-		return thisPtr->getHingeJoint()->getSpeed();
-	}
-
-	void ScriptHingeJoint::internal_SetLimit(ScriptHingeJoint* thisPtr, MonoObject* limit)
-	{
-		LimitAngularRange nativeLimit = ScriptLimitAngularRange::convert(limit);
-
-		thisPtr->getHingeJoint()->setLimit(nativeLimit);
-	}
-
-	void ScriptHingeJoint::internal_SetDrive(ScriptHingeJoint* thisPtr, MonoObject* drive)
-	{
-		HingeJoint::Drive nativeDrive = ScriptHingeJointDrive::convert(drive);
-		thisPtr->getHingeJoint()->setDrive(nativeDrive);
-	}
-
-	void ScriptHingeJoint::internal_SetEnableLimit(ScriptHingeJoint* thisPtr, bool value)
-	{
-		thisPtr->getHingeJoint()->setFlag(HingeJoint::Flag::Limit, value);
-	}
-
-	void ScriptHingeJoint::internal_SetEnableDrive(ScriptHingeJoint* thisPtr, bool value)
-	{
-		thisPtr->getHingeJoint()->setFlag(HingeJoint::Flag::Drive, value);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptHingeJoint.h"
+#include "BsHingeJoint.h"
+#include "BsScriptJointCommon.h"
+
+namespace BansheeEngine
+{
+	ScriptHingeJoint::ScriptHingeJoint(MonoObject* instance, const SPtr<Joint>& joint)
+		:TScriptJoint(instance, joint)
+	{
+
+	}
+
+	void ScriptHingeJoint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptHingeJoint::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetAngle", &ScriptHingeJoint::internal_GetAngle);
+		metaData.scriptClass->addInternalCall("Internal_GetSpeed", &ScriptHingeJoint::internal_GetSpeed);
+		metaData.scriptClass->addInternalCall("Internal_SetLimit", &ScriptHingeJoint::internal_SetLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetDrive", &ScriptHingeJoint::internal_SetDrive);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableLimit", &ScriptHingeJoint::internal_SetEnableLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableDrive", &ScriptHingeJoint::internal_SetEnableDrive);
+	}
+
+	HingeJoint* ScriptHingeJoint::getHingeJoint() const
+	{
+		return static_cast<HingeJoint*>(mJoint.get());
+	}
+
+	void ScriptHingeJoint::internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptHingeJointData* data)
+	{
+		HINGE_JOINT_DESC desc;
+		commonData->toDesc(desc);
+		data->toDesc(desc);
+
+		SPtr<HingeJoint> joint = HingeJoint::create(desc);
+		joint->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptHingeJoint* scriptJoint = new (bs_alloc<ScriptHingeJoint>()) ScriptHingeJoint(instance, joint);
+	}
+
+	float ScriptHingeJoint::internal_GetAngle(ScriptHingeJoint* thisPtr)
+	{
+		return thisPtr->getHingeJoint()->getAngle().valueRadians();
+	}
+
+	float ScriptHingeJoint::internal_GetSpeed(ScriptHingeJoint* thisPtr)
+	{
+		return thisPtr->getHingeJoint()->getSpeed();
+	}
+
+	void ScriptHingeJoint::internal_SetLimit(ScriptHingeJoint* thisPtr, MonoObject* limit)
+	{
+		LimitAngularRange nativeLimit = ScriptLimitAngularRange::convert(limit);
+
+		thisPtr->getHingeJoint()->setLimit(nativeLimit);
+	}
+
+	void ScriptHingeJoint::internal_SetDrive(ScriptHingeJoint* thisPtr, MonoObject* drive)
+	{
+		HingeJoint::Drive nativeDrive = ScriptHingeJointDrive::convert(drive);
+		thisPtr->getHingeJoint()->setDrive(nativeDrive);
+	}
+
+	void ScriptHingeJoint::internal_SetEnableLimit(ScriptHingeJoint* thisPtr, bool value)
+	{
+		thisPtr->getHingeJoint()->setFlag(HingeJoint::Flag::Limit, value);
+	}
+
+	void ScriptHingeJoint::internal_SetEnableDrive(ScriptHingeJoint* thisPtr, bool value)
+	{
+		thisPtr->getHingeJoint()->setFlag(HingeJoint::Flag::Drive, value);
+	}
+
+	void ScriptHingeJointData::toDesc(HINGE_JOINT_DESC& desc) const
+	{
+		desc.limit = ScriptLimitAngularRange::convert(limit);
+		desc.drive = ScriptHingeJointDrive::convert(drive);
+		desc.flag = flags;
+	}
 }

+ 15 - 1
Source/SBansheeEngine/Source/BsScriptHingeJoint.h

@@ -5,6 +5,7 @@
 #include "BsScriptEnginePrerequisites.h"
 #include "BsScriptCollider.h"
 #include "BsScriptJoint.h"
+#include "BsHingeJoint.h"
 
 namespace BansheeEngine
 {
@@ -12,6 +13,8 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	struct ScriptHingeJointData;
+
 	/** Interop class between C++ & CLR for HingeJoint. */
 	class BS_SCR_BE_EXPORT ScriptHingeJoint : public TScriptJoint<ScriptHingeJoint>
 	{
@@ -26,7 +29,7 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_CreateInstance(MonoObject* instance);
+		static void internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptHingeJointData* data);
 		static float internal_GetAngle(ScriptHingeJoint* thisPtr);
 		static float internal_GetSpeed(ScriptHingeJoint* thisPtr);
 		static void internal_SetLimit(ScriptHingeJoint* thisPtr, MonoObject* limit);
@@ -35,5 +38,16 @@ namespace BansheeEngine
 		static void internal_SetEnableDrive(ScriptHingeJoint* thisPtr, bool value);
 	};
 
+	/** Used for passing common Joint initialization data between native and managed code. */
+	struct ScriptHingeJointData // Note: Must match C# struct ScriptHingeJointData
+	{
+		MonoObject* limit;
+		MonoObject* drive;
+		HingeJoint::Flag flags;
+
+		/** Converts this structure into a descriptor used for initializing a joint. */
+		void toDesc(HINGE_JOINT_DESC& desc) const;
+	};
+
 	/** @} */
 }

+ 115 - 91
Source/SBansheeEngine/Source/BsScriptJoint.cpp

@@ -1,92 +1,116 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptJoint.h"
-#include "BsMonoUtil.h"
-#include "BsMonoClass.h"
-#include "BsMonoMethod.h"
-#include "BsScriptRigidbody.h"
-
-namespace BansheeEngine
-{
-	ScriptJoint::OnJointBreakThunkDef ScriptJoint::onJointBreakThunk = nullptr;
-
-	ScriptJointBase::ScriptJointBase(MonoObject* instance)
-		:ScriptObjectBase(instance)
-	{ }
-
-	void ScriptJointBase::initialize(const SPtr<Joint>& joint)
-	{
-		mJoint = joint;
-
-		MonoObject* instance = getManagedInstance();
-		joint->onJointBreak.connect(std::bind(&ScriptJoint::onJointBreak, instance));
-	}
-
-	void ScriptJointBase::destroyJoint()
-	{
-		mJoint = nullptr;
-	}
-
-	ScriptJoint::ScriptJoint(MonoObject* instance)
-		:TScriptJoint(instance, nullptr)
-	{ }
-
-	void ScriptJoint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptJoint::internal_Destroy);
-		metaData.scriptClass->addInternalCall("Internal_SetBody", &ScriptJoint::internal_SetBody);
-		metaData.scriptClass->addInternalCall("Internal_SetPosition", &ScriptJoint::internal_SetPosition);
-		metaData.scriptClass->addInternalCall("Internal_SetRotation", &ScriptJoint::internal_SetRotation);
-		metaData.scriptClass->addInternalCall("Internal_SetBreakForce", &ScriptJoint::internal_SetBreakForce);
-		metaData.scriptClass->addInternalCall("Internal_SetBreakTorque", &ScriptJoint::internal_SetBreakTorque);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableCollision", &ScriptJoint::internal_SetEnableCollision);
-
-		onJointBreakThunk = (OnJointBreakThunkDef)metaData.scriptClass->getMethod("Internal_DoOnJointBreak")->getThunk();
-	}
-
-	void ScriptJoint::onJointBreak(MonoObject* instance)
-	{
-		MonoUtil::invokeThunk(onJointBreakThunk, instance);
-	}
-
-	void ScriptJoint::internal_Destroy(ScriptJointBase* thisPtr)
-	{
-		thisPtr->destroyJoint();
-	}
-
-	void ScriptJoint::internal_SetBody(ScriptJointBase* thisPtr, JointBody body, ScriptRigidbody* value)
-	{
-		Rigidbody* rigidbody = nullptr;
-		if (value != nullptr)
-			rigidbody = value->getRigidbody();
-
-		thisPtr->mJoint->setBody(body, rigidbody);
-	}
-
-	void ScriptJoint::internal_SetPosition(ScriptJointBase* thisPtr, JointBody body, Vector3* position)
-	{
-		Quaternion rotation = thisPtr->mJoint->getRotation(body);
-		thisPtr->mJoint->setTransform(body, *position, rotation);
-	}
-
-	void ScriptJoint::internal_SetRotation(ScriptJointBase* thisPtr, JointBody body, Quaternion* rotation)
-	{
-		Vector3 position = thisPtr->mJoint->getPosition(body);
-		thisPtr->mJoint->setTransform(body, position, *rotation);
-	}
-
-	void ScriptJoint::internal_SetBreakForce(ScriptJointBase* thisPtr, float force)
-	{
-		thisPtr->mJoint->setBreakForce(force);
-	}
-
-	void ScriptJoint::internal_SetBreakTorque(ScriptJointBase* thisPtr, float torque)
-	{
-		thisPtr->mJoint->setBreakTorque(torque);
-	}
-
-	void ScriptJoint::internal_SetEnableCollision(ScriptJointBase* thisPtr, bool value)
-	{
-		thisPtr->mJoint->setEnableCollision(value);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptJoint.h"
+#include "BsMonoUtil.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsScriptRigidbody.h"
+
+namespace BansheeEngine
+{
+	ScriptJoint::OnJointBreakThunkDef ScriptJoint::onJointBreakThunk = nullptr;
+
+	ScriptJointBase::ScriptJointBase(MonoObject* instance)
+		:ScriptObjectBase(instance)
+	{ }
+
+	void ScriptJointBase::initialize(const SPtr<Joint>& joint)
+	{
+		mJoint = joint;
+
+		MonoObject* instance = getManagedInstance();
+		joint->onJointBreak.connect(std::bind(&ScriptJoint::onJointBreak, instance));
+	}
+
+	void ScriptJointBase::destroyJoint()
+	{
+		mJoint = nullptr;
+	}
+
+	ScriptJoint::ScriptJoint(MonoObject* instance)
+		:TScriptJoint(instance, nullptr)
+	{ }
+
+	void ScriptJoint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptJoint::internal_Destroy);
+		metaData.scriptClass->addInternalCall("Internal_SetBody", &ScriptJoint::internal_SetBody);
+		metaData.scriptClass->addInternalCall("Internal_SetPosition", &ScriptJoint::internal_SetPosition);
+		metaData.scriptClass->addInternalCall("Internal_SetRotation", &ScriptJoint::internal_SetRotation);
+		metaData.scriptClass->addInternalCall("Internal_SetBreakForce", &ScriptJoint::internal_SetBreakForce);
+		metaData.scriptClass->addInternalCall("Internal_SetBreakTorque", &ScriptJoint::internal_SetBreakTorque);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableCollision", &ScriptJoint::internal_SetEnableCollision);
+
+		onJointBreakThunk = (OnJointBreakThunkDef)metaData.scriptClass->getMethod("Internal_DoOnJointBreak")->getThunk();
+	}
+
+	void ScriptJoint::onJointBreak(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(onJointBreakThunk, instance);
+	}
+
+	void ScriptJoint::internal_Destroy(ScriptJointBase* thisPtr)
+	{
+		thisPtr->destroyJoint();
+	}
+
+	void ScriptJoint::internal_SetBody(ScriptJointBase* thisPtr, JointBody body, ScriptRigidbody* value)
+	{
+		Rigidbody* rigidbody = nullptr;
+		if (value != nullptr)
+			rigidbody = value->getRigidbody();
+
+		thisPtr->mJoint->setBody(body, rigidbody);
+	}
+
+	void ScriptJoint::internal_SetPosition(ScriptJointBase* thisPtr, JointBody body, Vector3* position)
+	{
+		Quaternion rotation = thisPtr->mJoint->getRotation(body);
+		thisPtr->mJoint->setTransform(body, *position, rotation);
+	}
+
+	void ScriptJoint::internal_SetRotation(ScriptJointBase* thisPtr, JointBody body, Quaternion* rotation)
+	{
+		Vector3 position = thisPtr->mJoint->getPosition(body);
+		thisPtr->mJoint->setTransform(body, position, *rotation);
+	}
+
+	void ScriptJoint::internal_SetBreakForce(ScriptJointBase* thisPtr, float force)
+	{
+		thisPtr->mJoint->setBreakForce(force);
+	}
+
+	void ScriptJoint::internal_SetBreakTorque(ScriptJointBase* thisPtr, float torque)
+	{
+		thisPtr->mJoint->setBreakTorque(torque);
+	}
+
+	void ScriptJoint::internal_SetEnableCollision(ScriptJointBase* thisPtr, bool value)
+	{
+		thisPtr->mJoint->setEnableCollision(value);
+	}
+
+	void ScriptCommonJointData::toDesc(JOINT_DESC& desc) const
+	{
+		ScriptArray bodiesArray(bodies);
+		ScriptRigidbody* rigidbodyA = bodiesArray.get<ScriptRigidbody*>(0);
+		if (rigidbodyA != nullptr)
+			desc.bodies[0].body = rigidbodyA->getRigidbody();
+
+		ScriptRigidbody* rigidbodyB = bodiesArray.get<ScriptRigidbody*>(1);
+		if (rigidbodyB != nullptr)
+			desc.bodies[1].body = rigidbodyB->getRigidbody();
+
+		ScriptArray positionArray(positions);
+		desc.bodies[0].position = positionArray.get<Vector3>(0);
+		desc.bodies[1].position = positionArray.get<Vector3>(1);
+
+		ScriptArray rotationArray(rotations);
+		desc.bodies[0].rotation = rotationArray.get<Quaternion>(0);
+		desc.bodies[1].rotation = rotationArray.get<Quaternion>(1);
+
+		desc.breakForce = breakForce;
+		desc.breakTorque = breakTorque;
+		desc.enableCollision = enableCollision;
+	}
 }

+ 66 - 56
Source/SBansheeEngine/Source/BsScriptSliderJoint.cpp

@@ -1,57 +1,67 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptSliderJoint.h"
-#include "BsScriptJointCommon.h"
-#include "BsSliderJoint.h"
-
-namespace BansheeEngine
-{
-	ScriptSliderJoint::ScriptSliderJoint(MonoObject* instance, const SPtr<Joint>& joint)
-		:TScriptJoint(instance, joint)
-	{
-
-	}
-
-	void ScriptSliderJoint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptSliderJoint::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_GetPosition", &ScriptSliderJoint::internal_GetPosition);
-		metaData.scriptClass->addInternalCall("Internal_GetSpeed", &ScriptSliderJoint::internal_GetSpeed);
-		metaData.scriptClass->addInternalCall("Internal_SetLimit", &ScriptSliderJoint::internal_SetLimit);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableLimit", &ScriptSliderJoint::internal_SetEnableLimit);
-	}
-
-	SliderJoint* ScriptSliderJoint::getSliderJoint() const
-	{
-		return static_cast<SliderJoint*>(mJoint.get());
-	}
-
-	void ScriptSliderJoint::internal_CreateInstance(MonoObject* instance)
-	{
-		SPtr<SliderJoint> joint = SliderJoint::create();
-		joint->_setOwner(PhysicsOwnerType::Script, instance);
-
-		ScriptSliderJoint* scriptJoint = new (bs_alloc<ScriptSliderJoint>()) ScriptSliderJoint(instance, joint);
-	}
-
-	float ScriptSliderJoint::internal_GetPosition(ScriptSliderJoint* thisPtr)
-	{
-		return thisPtr->getSliderJoint()->getPosition();
-	}
-
-	float ScriptSliderJoint::internal_GetSpeed(ScriptSliderJoint* thisPtr)
-	{
-		return thisPtr->getSliderJoint()->getSpeed();
-	}
-
-	void ScriptSliderJoint::internal_SetLimit(ScriptSliderJoint* thisPtr, MonoObject* limit)
-	{
-		LimitLinearRange nativeLimit = ScriptLimitLinearRange::convert(limit);
-		thisPtr->getSliderJoint()->setLimit(nativeLimit);
-	}
-
-	void ScriptSliderJoint::internal_SetEnableLimit(ScriptSliderJoint* thisPtr, bool enable)
-	{
-		thisPtr->getSliderJoint()->setFlag(SliderJoint::Flag::Limit, enable);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptSliderJoint.h"
+#include "BsScriptJointCommon.h"
+#include "BsSliderJoint.h"
+
+namespace BansheeEngine
+{
+	ScriptSliderJoint::ScriptSliderJoint(MonoObject* instance, const SPtr<Joint>& joint)
+		:TScriptJoint(instance, joint)
+	{
+
+	}
+
+	void ScriptSliderJoint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptSliderJoint::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetPosition", &ScriptSliderJoint::internal_GetPosition);
+		metaData.scriptClass->addInternalCall("Internal_GetSpeed", &ScriptSliderJoint::internal_GetSpeed);
+		metaData.scriptClass->addInternalCall("Internal_SetLimit", &ScriptSliderJoint::internal_SetLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableLimit", &ScriptSliderJoint::internal_SetEnableLimit);
+	}
+
+	SliderJoint* ScriptSliderJoint::getSliderJoint() const
+	{
+		return static_cast<SliderJoint*>(mJoint.get());
+	}
+
+	void ScriptSliderJoint::internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptSliderJointData* data)
+	{
+		SLIDER_JOINT_DESC desc;
+		commonData->toDesc(desc);
+		data->toDesc(desc);
+
+		SPtr<SliderJoint> joint = SliderJoint::create(desc);
+		joint->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptSliderJoint* scriptJoint = new (bs_alloc<ScriptSliderJoint>()) ScriptSliderJoint(instance, joint);
+	}
+
+	float ScriptSliderJoint::internal_GetPosition(ScriptSliderJoint* thisPtr)
+	{
+		return thisPtr->getSliderJoint()->getPosition();
+	}
+
+	float ScriptSliderJoint::internal_GetSpeed(ScriptSliderJoint* thisPtr)
+	{
+		return thisPtr->getSliderJoint()->getSpeed();
+	}
+
+	void ScriptSliderJoint::internal_SetLimit(ScriptSliderJoint* thisPtr, MonoObject* limit)
+	{
+		LimitLinearRange nativeLimit = ScriptLimitLinearRange::convert(limit);
+		thisPtr->getSliderJoint()->setLimit(nativeLimit);
+	}
+
+	void ScriptSliderJoint::internal_SetEnableLimit(ScriptSliderJoint* thisPtr, bool enable)
+	{
+		thisPtr->getSliderJoint()->setFlag(SliderJoint::Flag::Limit, enable);
+	}
+
+	void ScriptSliderJointData::toDesc(SLIDER_JOINT_DESC& desc) const
+	{
+		desc.limit = ScriptLimitLinearRange::convert(limit);
+		desc.flag = enableLimit ? SliderJoint::Flag::Limit : (SliderJoint::Flag)0;
+	}
 }

+ 13 - 1
Source/SBansheeEngine/Source/BsScriptSliderJoint.h

@@ -12,6 +12,8 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	struct ScriptSliderJointData;
+
 	/** Interop class between C++ & CLR for SliderJoint. */
 	class BS_SCR_BE_EXPORT ScriptSliderJoint : public TScriptJoint<ScriptSliderJoint>
 	{
@@ -26,12 +28,22 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_CreateInstance(MonoObject* instance);
+		static void internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptSliderJointData* data);
 		static float internal_GetPosition(ScriptSliderJoint* thisPtr);
 		static float internal_GetSpeed(ScriptSliderJoint* thisPtr);
 		static void internal_SetLimit(ScriptSliderJoint* thisPtr, MonoObject* limit);
 		static void internal_SetEnableLimit(ScriptSliderJoint* thisPtr, bool enable);
 	};
 
+	/** Used for passing common Joint initialization data between native and managed code. */
+	struct ScriptSliderJointData // Note: Must match C# struct ScriptSliderJointData
+	{
+		MonoObject* limit;
+		bool enableLimit;
+
+		/** Converts this structure into a descriptor used for initializing a joint. */
+		void toDesc(SLIDER_JOINT_DESC& desc) const;
+	};
+
 	/** @} */
 }

+ 54 - 44
Source/SBansheeEngine/Source/BsScriptSphericalJoint.cpp

@@ -1,45 +1,55 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsScriptSphericalJoint.h"
-#include "BsScriptJointCommon.h"
-#include "BsSphericalJoint.h"
-
-namespace BansheeEngine
-{
-	ScriptSphericalJoint::ScriptSphericalJoint(MonoObject* instance, const SPtr<Joint>& joint)
-		:TScriptJoint(instance, joint)
-	{
-
-	}
-
-	void ScriptSphericalJoint::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptSphericalJoint::internal_CreateInstance);
-		metaData.scriptClass->addInternalCall("Internal_SetLimit", &ScriptSphericalJoint::internal_SetLimit);
-		metaData.scriptClass->addInternalCall("Internal_SetEnableLimit", &ScriptSphericalJoint::internal_SetEnableLimit);
-	}
-
-	SphericalJoint* ScriptSphericalJoint::getSphericalJoint() const
-	{
-		return static_cast<SphericalJoint*>(mJoint.get());
-	}
-
-	void ScriptSphericalJoint::internal_CreateInstance(MonoObject* instance)
-	{
-		SPtr<SphericalJoint> joint = SphericalJoint::create();
-		joint->_setOwner(PhysicsOwnerType::Script, instance);
-
-		ScriptSphericalJoint* scriptJoint = new (bs_alloc<ScriptSphericalJoint>()) ScriptSphericalJoint(instance, joint);
-	}
-
-	void ScriptSphericalJoint::internal_SetLimit(ScriptSphericalJoint* thisPtr, MonoObject* limit)
-	{
-		LimitConeRange nativeLimit = ScriptLimitConeRange::convert(limit);
-		thisPtr->getSphericalJoint()->setLimit(nativeLimit);
-	}
-
-	void ScriptSphericalJoint::internal_SetEnableLimit(ScriptSphericalJoint* thisPtr, bool enable)
-	{
-		thisPtr->getSphericalJoint()->setFlag(SphericalJoint::Flag::Limit, enable);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptSphericalJoint.h"
+#include "BsScriptJointCommon.h"
+#include "BsSphericalJoint.h"
+
+namespace BansheeEngine
+{
+	ScriptSphericalJoint::ScriptSphericalJoint(MonoObject* instance, const SPtr<Joint>& joint)
+		:TScriptJoint(instance, joint)
+	{
+
+	}
+
+	void ScriptSphericalJoint::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptSphericalJoint::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_SetLimit", &ScriptSphericalJoint::internal_SetLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetEnableLimit", &ScriptSphericalJoint::internal_SetEnableLimit);
+	}
+
+	SphericalJoint* ScriptSphericalJoint::getSphericalJoint() const
+	{
+		return static_cast<SphericalJoint*>(mJoint.get());
+	}
+
+	void ScriptSphericalJoint::internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptSphericalJointData* data)
+	{
+		SPHERICAL_JOINT_DESC desc;
+		commonData->toDesc(desc);
+		data->toDesc(desc);
+
+		SPtr<SphericalJoint> joint = SphericalJoint::create(desc);
+		joint->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptSphericalJoint* scriptJoint = new (bs_alloc<ScriptSphericalJoint>()) ScriptSphericalJoint(instance, joint);
+	}
+
+	void ScriptSphericalJoint::internal_SetLimit(ScriptSphericalJoint* thisPtr, MonoObject* limit)
+	{
+		LimitConeRange nativeLimit = ScriptLimitConeRange::convert(limit);
+		thisPtr->getSphericalJoint()->setLimit(nativeLimit);
+	}
+
+	void ScriptSphericalJoint::internal_SetEnableLimit(ScriptSphericalJoint* thisPtr, bool enable)
+	{
+		thisPtr->getSphericalJoint()->setFlag(SphericalJoint::Flag::Limit, enable);
+	}
+
+	void ScriptSphericalJointData::toDesc(SPHERICAL_JOINT_DESC& desc) const
+	{
+		desc.limit = ScriptLimitConeRange::convert(limit);
+		desc.flag = enableLimit ? SphericalJoint::Flag::Limit : (SphericalJoint::Flag)0;
+	}
 }

+ 13 - 1
Source/SBansheeEngine/Source/BsScriptSphericalJoint.h

@@ -12,6 +12,8 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	struct ScriptSphericalJointData;
+
 	/** Interop class between C++ & CLR for SphericalJoint. */
 	class BS_SCR_BE_EXPORT ScriptSphericalJoint : public TScriptJoint<ScriptSphericalJoint>
 	{
@@ -26,10 +28,20 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_CreateInstance(MonoObject* instance);
+		static void internal_CreateInstance(MonoObject* instance, ScriptCommonJointData* commonData, ScriptSphericalJointData* data);
 		static void internal_SetLimit(ScriptSphericalJoint* thisPtr, MonoObject* limit);
 		static void internal_SetEnableLimit(ScriptSphericalJoint* thisPtr, bool enable);
 	};
 
+	/** Used for passing common Joint initialization data between native and managed code. */
+	struct ScriptSphericalJointData // Note: Must match C# struct ScriptSphericalJointData
+	{
+		MonoObject* limit;
+		bool enableLimit;
+
+		/** Converts this structure into a descriptor used for initializing a joint. */
+		void toDesc(SPHERICAL_JOINT_DESC& desc) const;
+	};
+
 	/** @} */
 }