Home Reference Source Repository

js/SceneKit/SCNPhysicsShape.js

  1. 'use strict'
  2.  
  3. import NSObject from '../ObjectiveC/NSObject'
  4. import SCNBox from './SCNBox'
  5. import SCNCapsule from './SCNCapsule'
  6. import SCNGeometry from './SCNGeometry'
  7. import SCNNode from './SCNNode'
  8. import SCNSphere from './SCNSphere'
  9. import SCNVector3 from './SCNVector3'
  10. import _InstanceOf from '../util/_InstanceOf'
  11.  
  12. const _Option = {
  13. collisionMargin: 'SCNPhysicsShapeCollisionMarginKey',
  14. keepAsCompound: 'SCNPhysicsShapeKeepAsCompoundKey',
  15. scale: 'SCNPhysicsShapeScaleKey',
  16. type: 'SCNPhysicsShapeTypeKey'
  17. }
  18.  
  19. const _ShapeType = {
  20. boundingBox: 'boundingBox',
  21. concavePolyhedron: 'concaveMesh',
  22. convexHull: 'convexHull'
  23. }
  24.  
  25.  
  26. /**
  27. * An abstraction of a physics body’s solid volume for use in tuning or optimizing collision detection.
  28. * @access public
  29. * @extends {NSObject}
  30. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape
  31. */
  32. export default class SCNPhysicsShape extends NSObject {
  33. static get _propTypes() {
  34. return {
  35. $constructor: (propNames, propValues) => {
  36. return new SCNPhysicsShape(propValues.referenceObject, propValues.options)
  37. },
  38. options: ['NSArray', null],
  39. referenceObject: ['NSObject', null]
  40. }
  41. }
  42.  
  43. // Creating Physics Shapes
  44.  
  45. /**
  46. * Creates a physics shape based on a geometry object.
  47. * @access public
  48. * @constructor
  49. * @param {SCNGeometry} geometry - A geometry object.
  50. * @param {?Map<SCNPhysicsShape.Option, Object>} [options = null] - A dictionary of options affecting the level of detail of the physics shape, or nil to use default options. For applicable keys and their possible values, see Shape Creation Options Keys.
  51. * @desc If you create a physics shape using one of the basic geometry classes (SCNBox, SCNSphere, SCNPyramid, SCNCone, SCNCylinder, or SCNCapsule), SceneKit uses an idealized form of that geometry for the physics shape instead of using the geometry’s vertex data to simulate collisions. For example, if you create a physics shape from an SCNSphere object, SceneKit simulates collisions for any object that passes within the sphere’s radius. Because the idealized forms of simple geometries are computationally much simpler than the vertex data needed for displaying them, using basic geometries for physics shapes (or compound shapes created from basic geometries with the init(shapes:transforms:) method) often provides the best balance between simulation accuracy and performance. To use the newly created physics shape, create a physics body with the the init(type:shape:) method, or assign the shape to the physicsShape property of an existing body.
  52. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape/1508897-init
  53. */
  54. constructor(geometry, options = null) {
  55. super()
  56.  
  57. let _options = options
  58. if(Array.isArray(options)){
  59. _options = {}
  60. for(const arr of options){
  61. _options[arr[0]] = arr[1]
  62. }
  63. }
  64.  
  65. /**
  66. * @type {SCNGeometry}
  67. */
  68. this._sourceGeometry = null
  69.  
  70. /**
  71. * @type {Object}
  72. */
  73. this._options = _options
  74.  
  75. /**
  76. * @type {SCNMatrix4}
  77. */
  78. this._transforms = null
  79.  
  80. /**
  81. * @type {SCNGeometry}
  82. */
  83. this._shape = null
  84.  
  85. /**
  86. * @type {SCNVector3}
  87. */
  88. this._center = new SCNVector3(0, 0, 0)
  89.  
  90. // Getting Information About a Shape
  91. this._sourceObject = null
  92. this._setSourceObject(geometry)
  93. this._createShape()
  94. }
  95.  
  96. _setSourceObject(obj) {
  97. this._sourceObject = obj
  98. if(_InstanceOf(this._sourceObject, SCNGeometry)){
  99. this._sourceGeometry = this._sourceObject
  100. }else if(_InstanceOf(this._sourceObject, SCNNode) && this._sourceObject.geometry){
  101. // TODO: get geometries recursively
  102. this._sourceGeometry = this._sourceObject.geometry
  103. }else{
  104. //throw new Error(`can't use it for source object: ${geometry.className}`)
  105. }
  106. if(!this._sourceGeometry){
  107. //throw new Error('source geometry is null')
  108. }
  109. }
  110.  
  111. _createShape() {
  112. if(!this._sourceGeometry){
  113. //throw new Error('SCNPhysicsShape: must have a geometry')
  114. return
  115. }
  116.  
  117. //if(this._options && this._options.get(_Option.type) === _ShapeType.boundingBox){
  118. if(this._options && this._options[_Option.type] === _ShapeType.boundingBox){
  119. this._createShapeAsBoundingBox()
  120. }else if(_InstanceOf(this._sourceGeometry, SCNCapsule)){
  121. // FIXME: do not convert to SCNBox
  122. this._createShapeAsBoundingBox()
  123. }else if(_InstanceOf(this._sourceGeometry, SCNBox)){
  124. this._createShapeAsBox()
  125. }else if(_InstanceOf(this._sourceGeometry, SCNSphere)){
  126. this._createShapeAsSphere()
  127. //}else if(this._options && this._options.get(_Option.type) === _ShapeType.convecHull){
  128. }else if(this._options && this._options[_Option.type] === _ShapeType.concavePolyhedron){
  129. // give up making a simple shape
  130. this._shape = this._sourceGeometry
  131. }else{
  132. this._createShapeAsSphere()
  133. }
  134. }
  135.  
  136. _createShapeAsBoundingBox() {
  137. const boundingBox = this._sourceGeometry._updateBoundingBox()
  138. const width = boundingBox.max.x - boundingBox.min.x
  139. const height = boundingBox.max.y - boundingBox.min.y
  140. const length = boundingBox.max.z - boundingBox.min.z
  141. const chamferRadius = 0
  142. const box = new SCNBox(width, height, length, chamferRadius)
  143. this._shape = box
  144. this._center = new SCNVector3(
  145. boundingBox.min.x + width * 0.5,
  146. boundingBox.min.y + height * 0.5,
  147. boundingBox.min.z + length * 0.5
  148. )
  149. }
  150.  
  151. _createShapeAsBox() {
  152. // TODO: copy the geometry
  153. this._shape = this._sourceGeometry
  154. this._center = new SCNVector3(0, 0, 0)
  155. }
  156.  
  157. _createShapeAsSphere() {
  158. if(_InstanceOf(this._sourceGeometry, SCNSphere)){
  159. // TODO: copy the geometry
  160. this._shape = this._sourceGeometry
  161. this._center = new SCNVector3(0, 0, 0)
  162. return
  163. }
  164. const boundingSphere = this._sourceGeometry.getBoundingSphere()
  165. const sphere = new SCNSphere(boundingSphere.radius)
  166. this._shape = sphere
  167. this._center = boundingSphere.center
  168. }
  169.  
  170. // Getting Information About a Shape
  171.  
  172. /**
  173. * The object that was used to create the shape.
  174. * @type {Object}
  175. * @desc This property, along with the transforms and options properties, provides the information that was used to create the shape. You can use this information, for example, to draw editing or debugging UI in your scene.If the shape was created with the init(geometry:options:) method, the source object is an SCNGeometry object, and the options property contains the options affecting the shape’s construction from that geometry.If the shape was created with the init(node:options:) method, the source object is an SCNNode object, and the options property contains the options affecting the shape’s construction from that node.If the shape was created with the init(shapes:transforms:) method, the source object is an array of SCNPhysicsShape objects and the transforms property describes how those shapes combine to form a compound shape.
  176. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape/1508888-sourceobject
  177. */
  178. get sourceObject() {
  179. return this._sourceObject
  180. }
  181.  
  182. /**
  183. * The options dictionary that was used to create the shape.
  184. * @type {?Map<SCNPhysicsShape.Option, Object>}
  185. * @desc You provide this dictionary in the init(geometry:options:) or init(node:options:) method. Use this dictionary along with the sourceObject property to recover the information that was used to create the shape. If the shape was created with the init(shapes:transforms:) method, this property’s value is nil.
  186. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape/1508904-options
  187. */
  188. get options() {
  189. return this._options
  190. }
  191.  
  192. get _type() {
  193. if(!this._options){
  194. return null
  195. }
  196. return this._options[_Option.type]
  197. }
  198.  
  199. /**
  200. * The array of transforms that was used to create a compound shape.
  201. * @type {?NSValue[]}
  202. * @desc You provide this array of NSValue objects, each containing an SCNMatrix4 value, in the init(shapes:transforms:) method to create a compound shape. Use this array along with the sourceObject property to recover the information that was used to create the shape. If the shape was created with the init(geometry:options:) or init(node:options:) method, this property's value is nil.
  203. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape/1508898-transforms
  204. */
  205. get transforms() {
  206. return this._transforms
  207. }
  208.  
  209. // Structures
  210.  
  211. /**
  212. * @type {Object} Option
  213. * @property {string} collisionMargin
  214. * @property {string} keepAsCompound An option for selecting whether to create a group of independent shapes or combine them into a single shape.
  215. * @property {string} scale An option for selecting the scale factor of the shape relative to the local coordinate space of the node containing it.
  216. * @property {string} type An option for selecting the level of detail at which to create shapes from geometry.
  217. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape.option
  218. */
  219. static get Option() {
  220. return _Option
  221. }
  222.  
  223. /**
  224. * @type {Object} ShapeType
  225. * @property {string} boundingBox The physics shape is the smallest box containing the geometry.
  226. * @property {string} concavePolyhedron The physics shape is a concave polyhedron closely following the surface of the geometry.
  227. * @property {string} convexHull The physics shape is a convex polyhedron roughly enclosing the geometry.
  228. * @see https://developer.apple.com/documentation/scenekit/scnphysicsshape.shapetype
  229. */
  230. static get ShapeType() {
  231. return _ShapeType
  232. }
  233.  
  234. /**
  235. * @access private
  236. * @returns {Ammo.btCollisionShape} -
  237. * @desc call Ammo.destroy(shape) after using it.
  238. */
  239. _createBtCollisionShape() {
  240. if(this._sourceObject === null){
  241. throw new Error('_sourceObject is null')
  242. }
  243. return this._sourceObject._createBtCollisionShape()
  244. }
  245. }