Home Reference Source Repository

js/SceneKit/SCNRenderer.js

  1. 'use strict'
  2.  
  3. import CGPoint from '../CoreGraphics/CGPoint'
  4. import CGRect from '../CoreGraphics/CGRect'
  5. import CGSize from '../CoreGraphics/CGSize'
  6. import NSObject from '../ObjectiveC/NSObject'
  7. //import SCNSceneRenderer from './SCNSceneRenderer'
  8. //import SCNTechniqueSupport from './SCNTechniqueSupport'
  9. //import SCNScene from './SCNScene'
  10. //import SCNAntialiasingMode from './SCNAntialiasingMode'
  11. import SCNMaterial from './SCNMaterial'
  12. import SCNMaterialProperty from './SCNMaterialProperty'
  13. import SCNMatrix4 from './SCNMatrix4'
  14. import SCNMatrix4MakeTranslation from './SCNMatrix4MakeTranslation'
  15. import SCNNode from './SCNNode'
  16. import SCNProgram from './SCNProgram'
  17. import SCNPhysicsWorld from './SCNPhysicsWorld'
  18. import SCNCamera from './SCNCamera'
  19. import SCNLight from './SCNLight'
  20. import SCNVector3 from './SCNVector3'
  21. import SCNVector4 from './SCNVector4'
  22. import SCNGeometryPrimitiveType from './SCNGeometryPrimitiveType'
  23. import SCNGeometrySource from './SCNGeometrySource'
  24. import SCNHitTestOption from './SCNHitTestOption'
  25. import SCNHitTestResult from './SCNHitTestResult'
  26. import SKColor from '../SpriteKit/SKColor'
  27.  
  28. import SKSpriteNode from '../SpriteKit/SKSpriteNode'
  29. import SKTexture from '../SpriteKit/SKTexture'
  30.  
  31. import _SCNDefaultVertexShader from './_SCNDefaultVertexShader'
  32. import _SCNDefaultFragmentShader from './_SCNDefaultFragmentShader'
  33. import _SCNDefaultPBRFragmentShader from './_SCNDefaultPBRFragmentShader'
  34. import _SCNDefaultShadowVertexShader from './_SCNDefaultShadowVertexShader'
  35. import _SCNDefaultShadowFragmentShader from './_SCNDefaultShadowFragmentShader'
  36. import _SCNDefaultParticleVertexShader from './_SCNDefaultParticleVertexShader'
  37. import _SCNDefaultParticleFragmentShader from './_SCNDefaultParticleFragmentShader'
  38. import _SCNDefaultHitTestVertexShader from './_SCNDefaultHitTestVertexShader'
  39. import _SCNDefaultHitTestFragmentShader from './_SCNDefaultHitTestFragmentShader'
  40.  
  41. import _InstanceOf from '../util/_InstanceOf'
  42.  
  43. const _cameraLoc = 0
  44. const _materialLoc = 1
  45. const _lightLoc = 2
  46. const _scnLightsLoc = 3
  47. const _fogLoc = 4
  48.  
  49. const _shadowTextureBaseIndex = 8
  50.  
  51. const _fsDirectionalShadow = `
  52. //float shadow = convDepth(texture(u_shadowTexture__I__, v_directionalShadowTexcoord[__I__].xy / v_directionalShadowTexcoord[__I__].w));
  53. //if(v_directionalShadowDepth[__I__].z / v_directionalShadowDepth[__I__].w - 0.0001 > shadow){
  54. // _output.color.rgb += material.diffuse.rgb * light.directionalShadow[__I__].shadowColor.rgb;
  55. //}else{
  56. // // diffuse
  57. // vec3 lightVec = normalize(v_light[numLights]);
  58. // float diffuse = clamp(dot(lightVec, _surface.normal), 0.0f, 1.0f);
  59. // _output.color.rgb += light.directionalShadow[__I__].color.rgb * material.diffuse.rgb * diffuse;
  60.  
  61. // // specular
  62. // if(diffuse > 0.0f){
  63. // vec3 halfVec = normalize(lightVec + _surface.view);
  64. // float specular = pow(dot(halfVec, _surface.normal), material.shininess);
  65. // _output.color.rgb += specularColor.rgb * specular;
  66. // }
  67. //}
  68.  
  69. {
  70. float shadow = 0.0;
  71. for(int i=0; i<4; i++){
  72. float d = convDepth(texture(u_shadowTexture__I__, (v_directionalShadowTexcoord[__I__].xy + poissonDisk[i]/700.0) / v_directionalShadowTexcoord[__I__].w));
  73. if(v_directionalShadowDepth[__I__].z / v_directionalShadowDepth[__I__].w - 0.0001 > d){
  74. shadow += 0.25;
  75. }
  76. }
  77. //vec3 shadowColor = material.diffuse.rgb * light.directionalShadow[__I__].shadowColor.rgb;
  78. vec3 shadowColor = light.directionalShadow[__I__].shadowColor.rgb;
  79. // diffuse
  80. vec3 lightVec = normalize(v_light[numLights]);
  81. float diffuse = clamp(dot(lightVec, _surface.normal), 0.0f, 1.0f);
  82. vec3 lightDiffuse = light.directionalShadow[__I__].color.rgb * diffuse;
  83. _lightingContribution.diffuse += shadowColor * shadow + lightDiffuse * (1.0 - shadow);
  84.  
  85. // specular
  86. if(diffuse > 0.0f){
  87. vec3 halfVec = normalize(lightVec + _surface.view);
  88. float specular = pow(dot(halfVec, _surface.normal), _surface.shininess);
  89. // TODO: use intensity
  90. _lightingContribution.specular += vec3(specular);
  91. }
  92. //_output.color.rgb += shadowColor * shadow + lightColor * (1.0 - shadow);
  93. }
  94.  
  95. numLights += 1;
  96. `
  97.  
  98. const _defaultCameraDistance = 15
  99.  
  100.  
  101.  
  102. /**
  103. * A renderer for displaying SceneKit scene in an an existing Metal workflow or OpenGL context.
  104. * @access public
  105. * @extends {NSObject}
  106. * @implements {SCNSceneRenderer}
  107. * @implements {SCNTechniqueSupport}
  108. * @see https://developer.apple.com/documentation/scenekit/scnrenderer
  109. */
  110. export default class SCNRenderer extends NSObject {
  111. // Creating a Renderer
  112.  
  113. /**
  114. * Creates a renderer with the specified Metal device.
  115. * @access public
  116. * @constructor
  117. * @param {?MTLDevice} device - A Metal device.
  118. * @param {?Map<AnyHashable, Object>} [options = null] - An optional dictionary for future extensions.
  119. * @desc Use this initializer to create a SceneKit renderer that draws into the rendering targets your app already uses to draw other content. For the device parameter, pass the MTLDevice object your app uses for drawing. Then, to tell SceneKit to render your content, call the SCNRenderer method, providing a command buffer and render pass descriptor for SceneKit to use in its rendering.
  120. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1518404-init
  121. */
  122. constructor(device, options = null) {
  123. super()
  124.  
  125. // Specifying a Scene
  126.  
  127. /**
  128. * The scene to be rendered.
  129. * @type {?SCNScene}
  130. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1518400-scene
  131. */
  132. this.scene = null
  133.  
  134. // Managing Animation Timing
  135.  
  136. this._nextFrameTime = 0
  137.  
  138. /**
  139. * context to draw frame
  140. * @type {WebGLRenderingContext}
  141. */
  142. this._context = null
  143.  
  144. /**
  145. *
  146. * @access private
  147. * @type {SKColor}
  148. */
  149. this._backgroundColor = null
  150.  
  151. //////////////////////
  152. // SCNSceneRenderer //
  153. //////////////////////
  154.  
  155. // Managing Scene Display
  156.  
  157. /**
  158. * Required. The node from which the scene’s contents are viewed for rendering.
  159. * @access private
  160. * @type {?SCNNode}
  161. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523982-pointofview
  162. */
  163. this._pointOfView = null
  164.  
  165. /**
  166. * Required. A Boolean value that determines whether SceneKit automatically adds lights to a scene.
  167. * @type {boolean}
  168. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523812-autoenablesdefaultlighting
  169. */
  170. this.autoenablesDefaultLighting = false
  171.  
  172. /**
  173. * Required. A Boolean value that determines whether SceneKit applies jittering to reduce aliasing artifacts.
  174. * @type {boolean}
  175. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1524026-isjitteringenabled
  176. */
  177. this.isJitteringEnabled = false
  178.  
  179. /**
  180. * Required. A Boolean value that determines whether SceneKit displays rendering performance statistics in an accessory view.
  181. * @type {boolean}
  182. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522763-showsstatistics
  183. */
  184. this.showsStatistics = false
  185.  
  186. /**
  187. * Required. Options for drawing overlay content in a scene that can aid debugging.
  188. * @type {SCNDebugOptions}
  189. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523281-debugoptions
  190. */
  191. this.debugOptions = null
  192.  
  193. this._renderingAPI = null
  194.  
  195. // Managing Scene Animation Timing
  196.  
  197. /**
  198. * Required. The current scene time.
  199. * @type {number}
  200. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522680-scenetime
  201. */
  202. this.sceneTime = 0
  203.  
  204. /**
  205. * current time in seconds
  206. * @access private
  207. * @type {number}
  208. */
  209. this._time = 0
  210.  
  211. /**
  212. * Required. A Boolean value that determines whether the scene is playing.
  213. * @type {boolean}
  214. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523401-isplaying
  215. */
  216. this.isPlaying = false
  217.  
  218. /**
  219. * Required. A Boolean value that determines whether SceneKit restarts the scene time after all animations in the scene have played.
  220. * @type {boolean}
  221. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522878-loops
  222. */
  223. this.loops = false
  224.  
  225.  
  226. // Participating in the Scene Rendering Process
  227.  
  228. /**
  229. * Required. A delegate object that receives messages about SceneKit’s rendering process.
  230. * @type {?SCNSceneRendererDelegate}
  231. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522671-delegate
  232. */
  233. this.delegate = null
  234.  
  235.  
  236. // Customizing Scene Rendering with Metal
  237.  
  238. this._currentRenderCommandEncoder = null
  239. this._device = null
  240. this._commandQueue = null
  241. this._colorPixelFormat = null
  242. this._depthPixelFormat = null
  243. this._stencilPixelFormat = null
  244.  
  245. // Rendering Sprite Kit Content over a Scene
  246.  
  247. /**
  248. * Required. A Sprite Kit scene to be rendered on top of the SceneKit content.
  249. * @type {?SKScene}
  250. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1524051-overlayskscene
  251. */
  252. this.overlaySKScene = null
  253.  
  254.  
  255. // Working With Positional Audio
  256.  
  257. /**
  258. * Required. The node representing the listener’s position in the scene for use with positional audio effects.
  259. * @type {?SCNNode}
  260. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523747-audiolistener
  261. */
  262. //this.audioListener = null
  263. //this._audioEnvironmentNode = null
  264. //this._audioEngine = null
  265.  
  266. // Instance Properties
  267.  
  268. /**
  269. * Required.
  270. * @type {number}
  271. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522854-currenttime
  272. */
  273. this.currentTime = 0
  274.  
  275. /**
  276. * @access private
  277. * @type {SCNProgram}
  278. */
  279. this.__defaultProgram = null
  280.  
  281. /**
  282. * @access private
  283. * @type {SCNProgram}
  284. */
  285. this.__defaultPBRProgram = null
  286.  
  287. /**
  288. * @access private
  289. * @type {SCNProgram}
  290. */
  291. this.__defaultParticleProgram = null
  292.  
  293. /**
  294. * @access private
  295. * @type {SCNProgram}
  296. */
  297. this.__defaultHitTestProgram = null
  298.  
  299. /**
  300. * @access private
  301. * @type {SCNProgram}
  302. */
  303. this.__defaultShadowProgram = null
  304.  
  305.  
  306. this._location = new Map()
  307.  
  308. this._defaultCameraPosNode = new SCNNode()
  309. this._defaultCameraRotNode = new SCNNode()
  310. this._defaultCameraNode = new SCNNode()
  311. this._defaultCameraNode.name = 'kSCNFreeViewCameraName'
  312.  
  313. const camera = new SCNCamera()
  314. camera.name = 'kSCNFreeViewCameraNameCamera'
  315. this._defaultCameraNode.camera = camera
  316. this._defaultCameraNode.position = new SCNVector3(0, 0, _defaultCameraDistance)
  317. this._defaultCameraNode._presentation = this._defaultCameraNode.copy()
  318.  
  319. this._defaultCameraPosNode.addChildNode(this._defaultCameraRotNode)
  320. this._defaultCameraPosNode._presentation = this._defaultCameraPosNode.copy()
  321. this._defaultCameraRotNode.addChildNode(this._defaultCameraNode)
  322. this._defaultCameraRotNode._presentation = this._defaultCameraRotNode.copy()
  323.  
  324. this._defaultLightNode = new SCNNode()
  325. const light = new SCNLight()
  326. light.color = SKColor.white
  327. light.type = SCNLight.LightType.omni
  328. light.position = new SCNVector3(0, 10, 10)
  329. this._defaultLightNode.light = light
  330. this._defaultLightNode._presentation = this._defaultLightNode.copy()
  331.  
  332. /**
  333. * @access private
  334. * @type {CGRect}
  335. */
  336. this._viewRect = new CGRect(new CGPoint(0, 0), new CGSize(0, 0))
  337.  
  338. /**
  339. * The background color of the view.
  340. * @type {SKColor}
  341. */
  342. this._backgroundColor = SKColor.white
  343.  
  344. /**
  345. * @access private
  346. * @type {WebGLTexture}
  347. */
  348. this.__dummyTexture = null
  349.  
  350. /**
  351. * @access private
  352. * @type {Object}
  353. */
  354. this._lightNodes = {}
  355.  
  356. /**
  357. * @access private
  358. * @type {Object}
  359. */
  360. this._numLights = {}
  361.  
  362. /**
  363. * @access private
  364. * @type {WebGLBuffer}
  365. */
  366. this._cameraBuffer = null
  367.  
  368. /**
  369. * @access private
  370. * @type {WebGLBuffer}
  371. */
  372. this._lightBuffer = null
  373.  
  374. /**
  375. * @access private
  376. * @type {WebGLBuffer}
  377. */
  378. this._scnLightsBuffer = null
  379.  
  380. /**
  381. * @access private
  382. * @type {WebGLBuffer}
  383. */
  384. this._fogBuffer = null
  385.  
  386. ////////////////////////////
  387. // Hit Test
  388. ////////////////////////////
  389.  
  390. /**
  391. * @access private
  392. * @type {WebGLFramebuffer}
  393. */
  394. this._hitFrameBuffer = null
  395.  
  396. /**
  397. * @access private
  398. * @type {WebGLRenderbuffer}
  399. */
  400. this._hitDepthBuffer = null
  401.  
  402. /**
  403. * @access private
  404. * @type {WebGLTexture}
  405. */
  406. this._hitObjectIDTexture = null
  407.  
  408. /**
  409. * @access private
  410. * @type {WebGLTexture}
  411. */
  412. this._hitFaceIDTexture = null
  413.  
  414. /**
  415. * @access private
  416. * @type {WebGLTexture}
  417. */
  418. this._hitPositionTexture = null
  419.  
  420. /**
  421. * @access private
  422. * @type {WebGLTexture}
  423. */
  424. this._hitNormalTexture = null
  425.  
  426. /**
  427. * @access private
  428. * @type {SCNProgram}
  429. */
  430. this._currentProgram = null
  431. }
  432.  
  433. // Managing Animation Timing
  434.  
  435. /**
  436. * The timestamp for the next frame to be rendered.
  437. * @type {number}
  438. * @desc If the renderer’s scene has any attached actions or animations, use this property to determine how long your app should wait before telling the renderer to draw another frame. If this property’s value matches that of the renderer’s currentTime property, the scene contains a continuous animation—schedule your next render at whatever time best maintains your app’s performance. If the value is infinite, the scene has no running actions or animations.
  439. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1518410-nextframetime
  440. */
  441. get nextFrameTime() {
  442. return this._nextFrameTime
  443. }
  444.  
  445. // Rendering a Scene Using Metal
  446.  
  447. /**
  448. * Renders the scene’s contents at the specified system time in the specified Metal command buffer.
  449. * @access public
  450. * @param {number} time - The timestamp, in seconds, at which to render the scene.
  451. * @param {CGRect} viewport - The pixel dimensions in which to render.
  452. * @param {MTLCommandBuffer} commandBuffer - The Metal command buffer in which SceneKit should schedule rendering commands.
  453. * @param {MTLRenderPassDescriptor} renderPassDescriptor - The Metal render pass descriptor describing the rendering target.
  454. * @returns {void}
  455. * @desc This method can be used only with an SCNRenderer object created with the SCNRenderer initializer. Call this method to tell SceneKit to draw the renderer’s scene into the render target described by the renderPassDescriptor parameter, by encoding render commands into the commandBuffer parameter.When you call this method, SceneKit updates its hierarchy of presentation nodes based on the specified timestamp, and then draws the scene using the specified Metal objects. NoteBy default, the playback timing of actions and animations in a scene is based on the system time, not the scene time. Before using this method to control the playback of animations, set the usesSceneTimeBase property of each animation to true, or specify the playUsingSceneTimeBase option when loading a scene file that contains animations.
  456. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1518401-render
  457. */
  458. renderAtTimePassDescriptor(time, viewport, commandBuffer, renderPassDescriptor) {
  459. }
  460.  
  461. // Rendering a Scene Using OpenGL
  462.  
  463. /**
  464. * Renders the scene’s contents in the renderer’s OpenGL context.
  465. * @deprecated
  466. * @access public
  467. * @returns {void}
  468. * @desc This method can be used only with an SCNRenderer object created with the SCNRenderer initializer. Call this method to tell SceneKit to draw the renderer’s scene into the OpenGL context you created the renderer with.When you call this method, SceneKit updates its hierarchy of presentation nodes based on the current system time, and then draws the scene.
  469. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1518403-render
  470. */
  471. render() {
  472. if(this.context === null){
  473. console.error('SCNRenderer.render(): context is null')
  474. return
  475. }
  476. const gl = this.context
  477.  
  478. if(this.scene === null){
  479. if(this.overlaySKScene){
  480. const sk = this.overlaySKScene
  481. gl.clearColor(sk.backgroundColor.red, sk.backgroundColor.green, sk.backgroundColor.blue, sk.backgroundColor.alpha)
  482. gl.clear(gl.COLOR_BUFFER_BIT)
  483. this._renderOverlaySKScene()
  484. }
  485. return
  486. }
  487.  
  488. this._lightNodes = this._createLightNodeArray() // createLightNodeArray must be called before getting program
  489.  
  490. const p = this._defaultProgram
  491. const glProgram = p._getGLProgramForContext(gl)
  492.  
  493. gl.clearColor(this._backgroundColor.red, this._backgroundColor.green, this._backgroundColor.blue, this._backgroundColor.alpha)
  494. gl.clearDepth(1.0)
  495. gl.clearStencil(0)
  496. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
  497.  
  498. //gl.useProgram(glProgram)
  499. this._useProgram(p)
  500.  
  501. gl.depthFunc(gl.LEQUAL)
  502. gl.depthMask(true)
  503. gl.enable(gl.BLEND)
  504. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  505.  
  506. //////////////////////////
  507. // Camera
  508. //////////////////////////
  509. if(this._cameraBuffer === null){
  510. this._initializeCameraBuffer(glProgram)
  511. }
  512. const cameraData = []
  513. const cameraNode = this._getCameraNode()
  514. cameraNode._updateWorldTransform()
  515. const cameraPNode = cameraNode.presentation || cameraNode
  516. const camera = cameraPNode.camera
  517. camera._updateProjectionTransform(this._viewRect)
  518.  
  519. cameraData.push(...cameraPNode.worldTransform.getTranslation().floatArray(), 0)
  520. cameraData.push(...cameraPNode.viewTransform.floatArray())
  521. cameraData.push(...cameraPNode.inverseViewTransform.floatArray())
  522. cameraData.push(...cameraPNode.viewProjectionTransform.floatArray())
  523. gl.bindBuffer(gl.UNIFORM_BUFFER, this._cameraBuffer)
  524. gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(cameraData), gl.DYNAMIC_DRAW)
  525. gl.bindBuffer(gl.UNIFORM_BUFFER, null)
  526. //console.log('cameraNode.worldPosition: ' + cameraPNode.worldTransform.getTranslation().float32Array())
  527. //console.log('viewTransform: ' + cameraPNode.viewTransform.float32Array())
  528. //console.log('projectionTransform: ' + cameraNode.camera.projectionTransform.float32Array())
  529. //console.log('viewProjectionTransform: ' + cameraNode.viewProjectionTransform.float32Array())
  530.  
  531. //////////////////////////
  532. // Fog
  533. //////////////////////////
  534. if(this._fogBuffer === null){
  535. this._initializeFogBuffer(glProgram)
  536. }
  537. const fogData = []
  538. if(this.scene.fogColor !== null && this.scene.fogEndDistance !== 0){
  539. fogData.push(
  540. ...this.scene.fogColor.floatArray(),
  541. this.scene.fogStartDistance,
  542. this.scene.fogEndDistance,
  543. this.scene.fogDensityExponent,
  544. 0
  545. )
  546. }else{
  547. fogData.push(0, 0, 0, 0, camera.zFar * 2, camera.zFar * 2 + 1, 1, 0)
  548. }
  549. gl.bindBuffer(gl.UNIFORM_BUFFER, this._fogBuffer)
  550. gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(fogData), gl.DYNAMIC_DRAW)
  551. gl.bindBuffer(gl.UNIFORM_BUFFER, null)
  552.  
  553. //////////////////////////
  554. // Lights
  555. //////////////////////////
  556. if(this._lightBuffer === null){
  557. this._initializeLightBuffer(glProgram)
  558. }
  559. const lights = this._lightNodes
  560. const lightData = []
  561. lights.ambient.forEach((node) => {
  562. lightData.push(...node.presentation.light.color.float32Array())
  563. })
  564. lights.directional.forEach((node) => {
  565. const direction = (new SCNVector3(0, 0, -1)).rotateWithQuaternion(node.presentation._worldOrientation)
  566. lightData.push(
  567. ...node.presentation.light.color.float32Array(),
  568. ...direction.float32Array(), 0
  569. )
  570. })
  571. lights.directionalShadow.forEach((node) => {
  572. const direction = (new SCNVector3(0, 0, -1)).rotateWithQuaternion(node.presentation._worldOrientation)
  573. node.presentation.light._updateProjectionTransform()
  574. lightData.push(
  575. ...node.presentation.light.color.float32Array(),
  576. ...direction.float32Array(), 0,
  577. ...node.presentation.light.shadowColor.float32Array(),
  578. ...node.presentation.lightViewProjectionTransform.float32Array(),
  579. ...node.presentation.shadowProjectionTransform.float32Array()
  580. )
  581. })
  582. lights.omni.forEach((node) => {
  583. lightData.push(
  584. ...node.presentation.light.color.float32Array(),
  585. ...node.presentation._worldTranslation.float32Array(), 0
  586. )
  587. })
  588. lights.probe.forEach((node) => {
  589. lightData.push(...node.presentation.light.color.float32Array())
  590. })
  591. lights.spot.forEach((node) => {
  592. lightData.push(...node.presentation.light.color.float32Array())
  593. })
  594.  
  595. gl.bindBuffer(gl.UNIFORM_BUFFER, this._lightBuffer)
  596. gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(lightData), gl.DYNAMIC_DRAW)
  597. gl.bindBuffer(gl.UNIFORM_BUFFER, null)
  598.  
  599. // FIXME: set params for each light
  600. const scnLightsData = []
  601. if(lights.directionalShadow.length > 0){
  602. const l = lights.directionalShadow[0].presentation
  603. const direction = (new SCNVector3(0, 0, -1)).rotateWithQuaternion(l._worldOrientation)
  604. scnLightsData.push(...direction.float32Array(), 0)
  605. scnLightsData.push(...l.shadowProjectionTransform.float32Array())
  606. }else{
  607. // direction
  608. scnLightsData.push(0, 0, 0, 0)
  609. // identity matrix
  610. scnLightsData.push(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
  611. }
  612. gl.bindBuffer(gl.UNIFORM_BUFFER, this._scnLightsBuffer)
  613. gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(scnLightsData), gl.DYNAMIC_DRAW)
  614. gl.bindBuffer(gl.UNIFORM_BUFFER, null)
  615.  
  616. //////////////////////////
  617. // Background (SkyBox)
  618. //////////////////////////
  619. if(this.scene.background._contents !== null){
  620. const skyBox = this.scene._skyBox
  621. skyBox.position = cameraPNode._worldTranslation
  622. const scale = camera.zFar * 1.154
  623. skyBox.scale = new SCNVector3(scale, scale, scale)
  624. skyBox._updateWorldTransform()
  625.  
  626. // disable fog
  627. const disabledFogData = fogData.slice(0)
  628. disabledFogData[4] = camera.zFar * 2.0 // startDistance
  629. disabledFogData[5] = camera.zFar * 2.1 // endDistance
  630. disabledFogData[6] = 1.0 // densityExponent
  631. gl.bindBuffer(gl.UNIFORM_BUFFER, this._fogBuffer)
  632. gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(disabledFogData), gl.DYNAMIC_DRAW)
  633. gl.bindBuffer(gl.UNIFORM_BUFFER, null)
  634.  
  635. this._renderNode(skyBox)
  636.  
  637. // enable fog
  638. gl.bindBuffer(gl.UNIFORM_BUFFER, this._fogBuffer)
  639. gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(fogData), gl.DYNAMIC_DRAW)
  640. gl.bindBuffer(gl.UNIFORM_BUFFER, null)
  641. }
  642.  
  643. //////////////////////////
  644. // Shadow
  645. //////////////////////////
  646. //gl.useProgram(this._defaultShadowProgram._glProgram)
  647. this._useProgram(this._defaultShadowProgram)
  648. gl.enable(gl.DEPTH_TEST)
  649. gl.depthMask(true)
  650. gl.depthFunc(gl.LEQUAL)
  651. gl.clearDepth(1.0)
  652. gl.clearColor(1.0, 1.0, 1.0, 1.0)
  653. gl.disable(gl.BLEND)
  654. const shadowRenderingArray = this._createShadowNodeArray()
  655. for(const key of Object.keys(lights)){
  656. for(const lightNode of lights[key]){
  657. this._renderNodesShadowOfLight(shadowRenderingArray, lightNode)
  658. }
  659. }
  660. this._setViewPort() // reset viewport size
  661. this._useProgram(p)
  662. for(let i=0; i<lights.directionalShadow.length; i++){
  663. const node = lights.directionalShadow[i]
  664. const symbol = `TEXTURE${i+_shadowTextureBaseIndex}`
  665. gl.activeTexture(gl[symbol])
  666. gl.bindTexture(gl.TEXTURE_2D, node.presentation.light._shadowDepthTexture)
  667. }
  668. gl.enable(gl.BLEND)
  669.  
  670. //////////////////////////
  671. // Nodes
  672. //////////////////////////
  673. const renderingArray = this._createRenderingNodeArray()
  674. renderingArray.forEach((node) => {
  675. this._renderNode(node)
  676. })
  677.  
  678. const particleProgram = this._defaultParticleProgram._glProgram
  679. //gl.useProgram(particleProgram)
  680. this._useProgram(this._defaultParticleProgram)
  681. gl.depthMask(false)
  682. gl.enable(gl.BLEND)
  683. gl.blendFunc(gl.SRC_ALPHA, gl.ONE)
  684. gl.uniformMatrix4fv(gl.getUniformLocation(particleProgram, 'viewTransform'), false, cameraPNode.viewTransform.float32Array())
  685. gl.uniformMatrix4fv(gl.getUniformLocation(particleProgram, 'projectionTransform'), false, cameraPNode.projectionTransform.float32Array())
  686.  
  687. //////////////////////////
  688. // Particles
  689. //////////////////////////
  690. if(this.scene._particleSystems !== null){
  691. for(const system of this.scene._particleSystems){
  692. this._renderParticleSystem(system)
  693. }
  694. }
  695. const particleArray = this._createParticleNodeArray()
  696. particleArray.forEach((node) => {
  697. this._renderParticle(node)
  698. })
  699.  
  700. //////////////////////////
  701. // 2D Overlay
  702. //////////////////////////
  703. this._renderOverlaySKScene()
  704.  
  705. // DEBUG: show shadow map
  706. //this._showShadowMapOfLight(lights.directionalShadow[0])
  707.  
  708. gl.flush()
  709. }
  710.  
  711. _renderOverlaySKScene() {
  712. if(this.overlaySKScene === null){
  713. return
  714. }
  715. const gl = this.context
  716. gl.disable(gl.CULL_FACE)
  717.  
  718. gl.enable(gl.DEPTH_TEST)
  719. gl.depthFunc(gl.GEQUAL)
  720.  
  721. gl.enable(gl.BLEND)
  722. gl.depthMask(true)
  723. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  724.  
  725. gl.clearStencil(0)
  726. gl.clearDepth(-1)
  727. gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
  728.  
  729. const skNodes = this._createSKNodeArray()
  730. for(const node of skNodes){
  731. this._renderSKNode(node)
  732. }
  733. }
  734.  
  735. /**
  736. * @access private
  737. * @returns {SCNNode} -
  738. */
  739. _getCameraNode() {
  740. let cameraNode = this._pointOfView
  741. if(cameraNode === null){
  742. cameraNode = this._searchCameraNode()
  743. this._pointOfView = cameraNode
  744. if(cameraNode === null){
  745. cameraNode = this._defaultCameraNode
  746. }
  747. }
  748. if(cameraNode === this._defaultCameraNode){
  749. this._defaultCameraPosNode._updateWorldTransform()
  750. }
  751. return cameraNode
  752. }
  753.  
  754. /**
  755. *
  756. * @access private
  757. * @returns {SCNNode[]} -
  758. */
  759. _createShadowNodeArray() {
  760. const arr = [this.scene._rootNode]
  761. const targetNodes = []
  762. while(arr.length > 0){
  763. const node = arr.shift()
  764. if(node.presentation !== null
  765. && node.presentation.geometry !== null
  766. && node.presentation.castsShadow
  767. && node.presentation._worldOpacity > 0
  768. && !node.presentation.isHidden){
  769. targetNodes.push(node)
  770. }
  771. arr.push(...node.childNodes)
  772. }
  773.  
  774. return targetNodes
  775. }
  776.  
  777. /**
  778. *
  779. * @access private
  780. * @returns {SCNNode[]} -
  781. */
  782. _createRenderingNodeArray() {
  783. const arr = [this.scene._rootNode]
  784. const targetNodes = []
  785. while(arr.length > 0){
  786. const node = arr.shift()
  787. if(node.presentation !== null && node.presentation.geometry !== null){
  788. targetNodes.push(node)
  789. }
  790. arr.push(...node.childNodes)
  791. }
  792. targetNodes.sort((a, b) => {
  793. return (a.presentation.renderingOrder - b.presentation.renderingOrder) + (b.presentation._worldOpacity - a.presentation._worldOpacity) * 0.5
  794. })
  795.  
  796. return targetNodes
  797. }
  798.  
  799. /**
  800. *
  801. * @access private
  802. * @returns {SCNNode[]} -
  803. */
  804. _createParticleNodeArray() {
  805. const arr = [this.scene._rootNode]
  806. const targetNodes = []
  807. while(arr.length > 0){
  808. const node = arr.shift()
  809. if(node.presentation !== null && node.presentation.particleSystems !== null){
  810. targetNodes.push(node)
  811. }
  812. arr.push(...node.childNodes)
  813. }
  814. targetNodes.sort((a, b) => { return (a.renderingOrder - b.renderingOrder) + (b.opacity - a.opacity) * 0.5 })
  815.  
  816. return targetNodes
  817. }
  818.  
  819. /**
  820. *
  821. * @access private
  822. * @returns {SCNNode[]} -
  823. */
  824. _createLightNodeArray() {
  825. const targetNodes = {
  826. ies: [],
  827. ambient: [],
  828. directional: [],
  829. omni: [],
  830. probe: [],
  831. spot: [],
  832. directionalShadow: []
  833. }
  834.  
  835. const arr = [this.scene.rootNode]
  836. let numLights = 0
  837. while(arr.length > 0){
  838. const node = arr.shift()
  839. if(node.presentation !== null && node.presentation.light !== null){
  840. if(node.presentation.light.type === 'directional' && node.presentation.light.castsShadow){
  841. targetNodes.directionalShadow.push(node)
  842. }else{
  843. targetNodes[node.presentation.light.type].push(node)
  844. }
  845. if(node.presentation.light.type !== SCNLight.LightType.ambient){
  846. numLights += 1
  847. }
  848. }
  849. arr.push(...node.childNodes)
  850. }
  851. if(this.autoenablesDefaultLighting && numLights === 0){
  852. targetNodes[this._defaultLightNode.light.type].push(this._defaultLightNode)
  853. }
  854.  
  855. return targetNodes
  856. }
  857.  
  858. /**
  859. *
  860. * @access private
  861. * @returns {SCNNode[]} -
  862. */
  863. _createRenderingPhysicsNodeArray() {
  864. const arr = [this.scene._rootNode]
  865. const targetNodes = []
  866. while(arr.length > 0){
  867. const node = arr.shift()
  868. if(node.presentation !== null
  869. && node.presentation.physicsBody !== null
  870. && node.presentation.physicsBody.physicsShape !== null){
  871. targetNodes.push(node)
  872. }
  873. arr.push(...node.childNodes)
  874. }
  875. targetNodes.sort((a, b) => { return a.renderingOrder - b.renderingOrder })
  876.  
  877. return targetNodes
  878. }
  879.  
  880. /**
  881. *
  882. * @access private
  883. * @param {SCNNode[]} nodes -
  884. * @param {SCNNode} lightNode -
  885. * @returns {void}
  886. */
  887. _renderNodesShadowOfLight(nodes, lightNode) {
  888. const lp = lightNode.presentation
  889. const light = lp.light
  890. if(!lp.castsShadow){
  891. return
  892. }
  893. this._setViewPort(light._shadowMapWidth, light._shadowMapHeight)
  894. const gl = this.context
  895. const glProgram = this._defaultShadowProgram._getGLProgramForContext(gl)
  896. gl.bindFramebuffer(gl.FRAMEBUFFER, light._getDepthBufferForContext(gl))
  897. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  898.  
  899. gl.uniformMatrix4fv(gl.getUniformLocation(glProgram, 'viewProjectionTransform'), false, lp.lightViewProjectionTransform.float32Array())
  900.  
  901. for(const node of nodes){
  902. const geometry = node.presentation.geometry
  903. const geometryCount = geometry.geometryElements.length
  904. if(geometryCount === 0){
  905. // nothing to draw...
  906. continue
  907. }
  908.  
  909. if(geometry._shadowVAO === null){
  910. this._initializeShadowVAO(node, glProgram)
  911. }
  912.  
  913. if(node.morpher !== null){
  914. //this._updateVAO(node)
  915. }
  916.  
  917. if(node.presentation.skinner !== null){
  918. if(node.presentation.skinner._useGPU){
  919. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), node.presentation.skinner.numSkinningJoints)
  920. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation.skinner.float32Array())
  921. }else{
  922. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  923. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), SCNMatrix4MakeTranslation(0, 0, 0).float32Array3x4f())
  924. }
  925. }else{
  926. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  927. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation._worldTransform.float32Array3x4f())
  928. }
  929.  
  930. for(let i=0; i<geometryCount; i++){
  931. const vao = geometry._shadowVAO[i]
  932. const element = geometry.geometryElements[i]
  933.  
  934. gl.bindVertexArray(vao)
  935. // FIXME: use bufferData instead of bindBufferBase
  936.  
  937. let shape = null
  938. switch(element.primitiveType){
  939. case SCNGeometryPrimitiveType.triangles:
  940. shape = gl.TRIANGLES
  941. break
  942. case SCNGeometryPrimitiveType.triangleStrip:
  943. shape = gl.TRIANGLE_STRIP
  944. break
  945. case SCNGeometryPrimitiveType.line:
  946. shape = gl.LINES
  947. break
  948. case SCNGeometryPrimitiveType.point:
  949. shape = gl.POINTS
  950. break
  951. case SCNGeometryPrimitiveType.polygon:
  952. shape = gl.TRIANGLE_FAN
  953. break
  954. default:
  955. throw new Error(`unsupported primitiveType: ${element.primitiveType}`)
  956. }
  957.  
  958. let size = null
  959. switch(element.bytesPerIndex){
  960. case 1:
  961. size = gl.UNSIGNED_BYTE
  962. break
  963. case 2:
  964. size = gl.UNSIGNED_SHORT
  965. break
  966. case 4:
  967. size = gl.UNSIGNED_INT
  968. break
  969. default:
  970. throw new Error(`unsupported index size: ${element.bytesPerIndex}`)
  971. }
  972.  
  973. gl.drawElements(shape, element._glData.length, size, 0)
  974. }
  975. }
  976. gl.bindFramebuffer(gl.FRAMEBUFFER, null)
  977. }
  978.  
  979. /**
  980. *
  981. * @access private
  982. * @param {SCNNode} node -
  983. * @returns {void}
  984. */
  985. _renderNode(node) {
  986. if(node.presentation.isHidden || node.presentation._worldOpacity <= 0){
  987. return
  988. }
  989. const gl = this.context
  990. const geometry = node.presentation.geometry
  991. const geometryCount = geometry.geometryElements.length
  992. if(geometryCount === 0){
  993. // nothing to draw...
  994. return
  995. }
  996. const scnProgram = this._getProgramForGeometry(geometry)
  997. const glProgram = scnProgram._getGLProgramForContext(gl)
  998.  
  999. this._switchProgram(scnProgram)
  1000.  
  1001. if(geometry._vertexArrayObjects === null){
  1002. this._initializeVAO(node, glProgram)
  1003. this._initializeUBO(node, glProgram) // FIXME: program should have UBO, not node.
  1004. }
  1005.  
  1006. if(node.morpher !== null || (node.skinner && !node.skinner._useGPU)){
  1007. this._updateVAO(node)
  1008. }
  1009.  
  1010. gl.uniformMatrix4fv(gl.getUniformLocation(glProgram, 'modelTransform'), false, node._worldTransform.float32Array())
  1011.  
  1012. if(node.presentation.skinner !== null){
  1013. if(node.presentation.skinner._useGPU){
  1014. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), node.presentation.skinner.numSkinningJoints)
  1015. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation.skinner.float32Array())
  1016. }else{
  1017. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  1018. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), SCNMatrix4MakeTranslation(0, 0, 0).float32Array3x4f())
  1019. }
  1020. }else{
  1021. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  1022. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation._worldTransform.float32Array3x4f())
  1023. }
  1024.  
  1025. for(let i=0; i<geometryCount; i++){
  1026. const materialCount = geometry.materials.length
  1027. const material = geometry.materials[i % materialCount]
  1028. let p = glProgram
  1029. if(material && (material.program || material.lightingModel === SCNMaterial.LightingModel.physicallyBased)){
  1030. const _scnProgram = material.program ? material.program : this._defaultPBRProgram
  1031. this._switchProgram(_scnProgram)
  1032.  
  1033. // TODO: refactoring
  1034. p = _scnProgram._getGLProgramForContext(gl)
  1035. if(node.presentation.skinner !== null){
  1036. if(node.presentation.skinner._useGPU){
  1037. gl.uniform1i(gl.getUniformLocation(p, 'numSkinningJoints'), node.presentation.skinner.numSkinningJoints)
  1038. gl.uniform4fv(gl.getUniformLocation(p, 'skinningJoints'), node.presentation.skinner.float32Array())
  1039. }else{
  1040. gl.uniform1i(gl.getUniformLocation(p, 'numSkinningJoints'), 0)
  1041. gl.uniform4fv(gl.getUniformLocation(p, 'skinningJoints'), SCNMatrix4MakeTranslation(0, 0, 0).float32Array3x4f())
  1042. }
  1043. }else{
  1044. gl.uniform1i(gl.getUniformLocation(p, 'numSkinningJoints'), 0)
  1045. gl.uniform4fv(gl.getUniformLocation(p, 'skinningJoints'), node.presentation._worldTransform.float32Array3x4f())
  1046. }
  1047. const materialIndex = gl.getUniformBlockIndex(p, 'materialUniform')
  1048. gl.uniformBlockBinding(p, materialIndex, _materialLoc)
  1049. gl.bindBufferBase(gl.UNIFORM_BUFFER, _materialLoc, geometry._materialBuffer)
  1050.  
  1051. material._callBindingHandlerForNodeProgramContextRenderer(node, p, gl, this)
  1052. }else{
  1053. this._switchProgram(scnProgram)
  1054.  
  1055. geometry._callBindingHandlerForNodeProgramContextRenderer(node, glProgram, gl, this)
  1056. }
  1057. const vao = geometry._vertexArrayObjects[i]
  1058. const element = geometry.geometryElements[i]
  1059.  
  1060. gl.bindVertexArray(vao)
  1061. // FIXME: use bufferData instead of bindBufferBase
  1062. gl.bindBufferBase(gl.UNIFORM_BUFFER, _materialLoc, geometry._materialBuffer)
  1063.  
  1064. geometry._bufferMaterialData(gl, p, i, node.presentation._worldOpacity)
  1065.  
  1066. let shape = null
  1067. switch(element.primitiveType){
  1068. case SCNGeometryPrimitiveType.triangles:
  1069. shape = gl.TRIANGLES
  1070. break
  1071. case SCNGeometryPrimitiveType.triangleStrip:
  1072. shape = gl.TRIANGLE_STRIP
  1073. break
  1074. case SCNGeometryPrimitiveType.line:
  1075. shape = gl.LINES
  1076. break
  1077. case SCNGeometryPrimitiveType.point:
  1078. shape = gl.POINTS
  1079. break
  1080. case SCNGeometryPrimitiveType.polygon:
  1081. shape = gl.TRIANGLE_FAN
  1082. break
  1083. default:
  1084. throw new Error(`unsupported primitiveType: ${element.primitiveType}`)
  1085. }
  1086.  
  1087. let size = null
  1088. switch(element.bytesPerIndex){
  1089. case 1:
  1090. size = gl.UNSIGNED_BYTE
  1091. break
  1092. case 2:
  1093. size = gl.UNSIGNED_SHORT
  1094. break
  1095. case 4:
  1096. size = gl.UNSIGNED_INT
  1097. break
  1098. default:
  1099. throw new Error(`unsupported index size: ${element.bytesPerIndex}`)
  1100. }
  1101.  
  1102. gl.drawElements(shape, element._glData.length, size, 0)
  1103. }
  1104. }
  1105.  
  1106. /**
  1107. *
  1108. * @access private
  1109. * @param {SCNNode} node -
  1110. * @returns {void}
  1111. */
  1112. _renderParticle(node) {
  1113. if(node.presentation.isHidden){
  1114. return
  1115. }
  1116.  
  1117. //const systems = node.presentation.particleSystems
  1118. const systems = node.particleSystems
  1119. systems.forEach((system) => {
  1120. this._renderParticleSystem(system, node)
  1121. })
  1122. }
  1123.  
  1124. /**
  1125. *
  1126. * @access private
  1127. * @param {SCNParticleSystem} system -
  1128. * @param {?SCNNode} [node = null] -
  1129. * @returns {void}
  1130. */
  1131. _renderParticleSystem(system, node = null) {
  1132. //this.currentTime
  1133. const gl = this.context
  1134. //let program = this._defaultParticleProgram._glProgram
  1135. //if(system._program !== null){
  1136. // program = system._program._glProgram
  1137. //}
  1138. let p = this._defaultParticleProgram
  1139. if(system._program !== null){
  1140. p = system._program
  1141. }
  1142. const glProgram = p._getGLProgramForContext(gl)
  1143. this._useProgram(p)
  1144. //this._switchProgram(p)
  1145. gl.disable(gl.CULL_FACE)
  1146.  
  1147. if(system._vertexBuffer === null){
  1148. system._initializeVAO(gl, glProgram)
  1149. }
  1150. gl.bindVertexArray(system._vertexArray)
  1151.  
  1152. system._bufferMaterialData(gl, glProgram)
  1153. if(node){
  1154. gl.uniformMatrix4fv(gl.getUniformLocation(glProgram, 'modelTransform'), false, node._worldTransform.float32Array())
  1155. }else{
  1156. let m = SCNMatrix4MakeTranslation(0, 0, 0)
  1157. gl.uniformMatrix4fv(gl.getUniformLocation(glProgram, 'modelTransform'), false, m.float32Array())
  1158. }
  1159.  
  1160. gl.drawElements(gl.TRIANGLES, system._particles.length * 6, system._glIndexSize, 0)
  1161. }
  1162.  
  1163. /**
  1164. *
  1165. * @access private
  1166. * @param {SCNNode} node -
  1167. * @param {number} objectID -
  1168. * @param {Map} options -
  1169. * @returns {void}
  1170. */
  1171. _renderNodeForHitTest(node, objectID, options) {
  1172. const gl = this.context
  1173. const geometry = node.presentation.geometry
  1174. const glProgram = this._defaultHitTestProgram._getGLProgramForContext(gl)
  1175.  
  1176. const geometryCount = geometry.geometryElements.length
  1177. if(geometryCount === 0){
  1178. // nothing to draw...
  1179. return
  1180. }
  1181. if(geometry._vertexArrayObjects === null){
  1182. // geometry is not ready
  1183. return
  1184. }
  1185. if(geometry._hitTestVAO === null){
  1186. this._initializeHitTestVAO(node, glProgram)
  1187. }
  1188.  
  1189. gl.uniform1i(gl.getUniformLocation(glProgram, 'objectID'), objectID)
  1190.  
  1191. if(node.presentation.skinner !== null && node.presentation.skinner._useGPU){
  1192. if(node.presentation.skinner._useGPU){
  1193. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), node.presentation.skinner.numSkinningJoints)
  1194. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation.skinner.float32Array())
  1195. }else{
  1196. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  1197. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), SCNMatrix4MakeTranslation(0, 0, 0).float32Array3x4f())
  1198. }
  1199. }else{
  1200. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  1201. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation._worldTransform.float32Array3x4f())
  1202. }
  1203.  
  1204. for(let i=0; i<geometryCount; i++){
  1205. const vao = geometry._hitTestVAO[i]
  1206. const element = geometry.geometryElements[i]
  1207.  
  1208. gl.bindVertexArray(vao)
  1209. gl.uniform1i(gl.getUniformLocation(glProgram, 'geometryID'), i)
  1210.  
  1211. let shape = null
  1212. switch(element.primitiveType){
  1213. case SCNGeometryPrimitiveType.triangles:
  1214. shape = gl.TRIANGLES
  1215. break
  1216. case SCNGeometryPrimitiveType.triangleStrip:
  1217. shape = gl.TRIANGLE_STRIP
  1218. break
  1219. case SCNGeometryPrimitiveType.line:
  1220. shape = gl.LINES
  1221. break
  1222. case SCNGeometryPrimitiveType.point:
  1223. shape = gl.POINTS
  1224. break
  1225. case SCNGeometryPrimitiveType.polygon:
  1226. shape = gl.TRIANGLE_FAN
  1227. break
  1228. default:
  1229. throw new Error(`unsupported primitiveType: ${element.primitiveType}`)
  1230. }
  1231.  
  1232. let size = null
  1233. switch(element.bytesPerIndex){
  1234. case 1:
  1235. size = gl.UNSIGNED_BYTE
  1236. break
  1237. case 2:
  1238. size = gl.UNSIGNED_SHORT
  1239. break
  1240. case 4:
  1241. size = gl.UNSIGNED_INT
  1242. break
  1243. default:
  1244. throw new Error(`unsupported index size: ${element.bytesPerIndex}`)
  1245. }
  1246.  
  1247. //console.log(`hitTest drawElements: length: ${element._glData.length}`)
  1248. gl.drawElements(shape, element._glData.length, size, 0)
  1249. }
  1250. }
  1251.  
  1252. /**
  1253. *
  1254. * @access private
  1255. * @param {SCNNode} node -
  1256. * @param {number} objectID -
  1257. * @param {Map} options -
  1258. * @returns {void}
  1259. */
  1260. _renderPhysicsNodeForHitTest(node, objectID, options) {
  1261. const gl = this.context
  1262. const p = node.presentation
  1263. const body = p.physicsBody
  1264. const geometry = body.physicsShape._sourceGeometry
  1265. const geometryCount = geometry.geometryElements.length
  1266. if(geometryCount === 0){
  1267. // nothing to draw...
  1268. return
  1269. }
  1270. const glProgram = this._defaultHitTestProgram._getGLProgramForContext(gl)
  1271.  
  1272. if(geometry._vertexBuffer === null){
  1273. // should I copy the geometry?
  1274. geometry._createVertexBuffer(gl, node, false, geometry)
  1275. }
  1276. if(geometry._hitTestVAO === null){
  1277. this._initializeHitTestVAO(node, glProgram, true)
  1278. }
  1279.  
  1280. gl.uniform1i(gl.getUniformLocation(glProgram, 'objectID'), objectID)
  1281.  
  1282. if(node.presentation.skinner !== null && node.presentation.skinner._useGPU){
  1283. if(node.presentation.skinner._useGPU){
  1284. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), node.presentation.skinner.numSkinningJoints)
  1285. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation.skinner.float32Array())
  1286. }else{
  1287. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  1288. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), SCNMatrix4MakeTranslation(0, 0, 0).float32Array3x4f())
  1289. }
  1290. }else{
  1291. gl.uniform1i(gl.getUniformLocation(glProgram, 'numSkinningJoints'), 0)
  1292. gl.uniform4fv(gl.getUniformLocation(glProgram, 'skinningJoints'), node.presentation._worldTransform.float32Array3x4f())
  1293. }
  1294.  
  1295. for(let i=0; i<geometryCount; i++){
  1296. const vao = geometry._hitTestVAO[i]
  1297. const element = geometry.geometryElements[i]
  1298.  
  1299. gl.bindVertexArray(vao)
  1300. gl.uniform1i(gl.getUniformLocation(glProgram, 'geometryID'), i)
  1301.  
  1302. let shape = null
  1303. switch(element.primitiveType){
  1304. case SCNGeometryPrimitiveType.triangles:
  1305. shape = gl.TRIANGLES
  1306. break
  1307. case SCNGeometryPrimitiveType.triangleStrip:
  1308. shape = gl.TRIANGLE_STRIP
  1309. break
  1310. case SCNGeometryPrimitiveType.line:
  1311. shape = gl.LINES
  1312. break
  1313. case SCNGeometryPrimitiveType.point:
  1314. shape = gl.POINTS
  1315. break
  1316. case SCNGeometryPrimitiveType.polygon:
  1317. shape = gl.TRIANGLE_FAN
  1318. break
  1319. default:
  1320. throw new Error(`unsupported primitiveType: ${element.primitiveType}`)
  1321. }
  1322.  
  1323. let size = null
  1324. switch(element.bytesPerIndex){
  1325. case 1:
  1326. size = gl.UNSIGNED_BYTE
  1327. break
  1328. case 2:
  1329. size = gl.UNSIGNED_SHORT
  1330. break
  1331. case 4:
  1332. size = gl.UNSIGNED_INT
  1333. break
  1334. default:
  1335. throw new Error(`unsupported index size: ${element.bytesPerIndex}`)
  1336. }
  1337.  
  1338. gl.drawElements(shape, element._glData.length, size, 0)
  1339. }
  1340. }
  1341.  
  1342. /**
  1343. *
  1344. * @access private
  1345. * @returns {SKNode[]} -
  1346. */
  1347. _createSKNodeArray() {
  1348. if(this.overlaySKScene === null){
  1349. return []
  1350. }
  1351.  
  1352. const arr = [this.overlaySKScene]
  1353. const targetNodes = []
  1354. while(arr.length > 0){
  1355. const node = arr.shift()
  1356. targetNodes.push(node)
  1357. arr.push(...node.children)
  1358. }
  1359. //targetNodes.sort((a, b) => { return a.renderingOrder - b.renderingOrder })
  1360.  
  1361. return targetNodes
  1362. }
  1363.  
  1364. /**
  1365. *
  1366. * @access private
  1367. * @param {SKNode} node -
  1368. * @returns {void}
  1369. */
  1370. _renderSKNode(node) {
  1371. node._render(this.context, this._viewRect)
  1372. }
  1373.  
  1374. /**
  1375. * Renders the scene’s contents at the specified system time in the renderer’s OpenGL context.
  1376. * @access public
  1377. * @param {number} time - The timestamp, in seconds, at which to render the scene.
  1378. * @returns {void}
  1379. * @desc This method can be used only with an SCNRenderer object created with the SCNRenderer initializer. Call this method to tell SceneKit to draw the renderer’s scene into the OpenGL context you created the renderer with.When you call this method, SceneKit updates its hierarchy of presentation nodes based on the specified timestamp, and then draws the scene.NoteBy default, the playback timing of actions and animations in a scene is based on the system time, not the scene time. Before using this method to control the playback of animations, set the usesSceneTimeBase property of each animation to true, or specify the playUsingSceneTimeBase option when loading a scene file that contains animations.
  1380. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1518402-render
  1381. */
  1382. renderAtTime(time) {
  1383. }
  1384.  
  1385. // Capturing a Snapshot
  1386.  
  1387. /**
  1388. * Creates an image by drawing the renderer’s content at the specified system time.
  1389. * @access public
  1390. * @param {number} time - The timestamp, in seconds, at which to render the scene.
  1391. * @param {CGSize} size - The size, in pixels, of the image to create.
  1392. * @param {SCNAntialiasingMode} antialiasingMode - The antialiasing mode to use for the image output.
  1393. * @returns {Image} -
  1394. * @desc When you call this method, SceneKit updates its hierarchy of presentation nodes based on the specified timestamp, and then draws the scene into a new image object of the specified size.
  1395. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/1641767-snapshot
  1396. */
  1397. snapshotAtTimeWith(time, size, antialiasingMode) {
  1398. return null
  1399. }
  1400.  
  1401. // Instance Methods
  1402.  
  1403. /**
  1404. *
  1405. * @access public
  1406. * @param {SCNNode[]} lightProbes -
  1407. * @param {number} time -
  1408. * @returns {void}
  1409. * @see https://developer.apple.com/documentation/scenekit/scnrenderer/2097153-updateprobes
  1410. */
  1411. updateProbesAtTime(lightProbes, time) {
  1412. }
  1413.  
  1414. //////////////////////
  1415. // SCNSceneRenderer //
  1416. //////////////////////
  1417.  
  1418. // Presenting a Scene
  1419.  
  1420. /**
  1421. * Required. Displays the specified scene with an animated transition.
  1422. * @access public
  1423. * @param {SCNScene} scene - The new scene to be displayed.
  1424. * @param {SKTransition} transition - An object that specifies the duration and style of the animated transition.
  1425. * @param {?SCNNode} pointOfView - The node to use as the pointOfView property when displaying the new scene.
  1426. * @param {?function(): void} [completionHandler = null] - A block that SceneKit calls after the transition animation has completed.This block takes no parameters and has no return value.
  1427. * @returns {void}
  1428. * @desc Use this method to change the scene displayed in a SceneKit view (or other renderer) with an animated transition. For details on transition styles, see SKTransition.
  1429. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523028-present
  1430. */
  1431. presentWithIncomingPointOfView(scene, transition, pointOfView, completionHandler = null) {
  1432. }
  1433.  
  1434. // Managing Scene Display
  1435.  
  1436. /**
  1437. * Required. The node from which the scene’s contents are viewed for rendering.
  1438. * @type {?SCNNode}
  1439. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523982-pointofview
  1440. */
  1441. get pointOfView() {
  1442. return this._getCameraNode()
  1443. }
  1444.  
  1445. /**
  1446. * Required. The node from which the scene’s contents are viewed for rendering.
  1447. * @type {?SCNNode}
  1448. * @param {?SCNNode} newValue -
  1449. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523982-pointofview
  1450. */
  1451. set pointOfView(newValue) {
  1452. this._pointOfView = newValue
  1453. }
  1454.  
  1455. /**
  1456. * Required. The graphics technology SceneKit uses to render the scene.
  1457. * @type {SCNRenderingAPI}
  1458. * @desc You choose a graphics technology when initializing a scene renderer:When initializing a SCNView object, use the init(frame:options:) initializer and the preferredRenderingAPI key. Alternatively, create a view in Interface Builder and use the Rendering API control in the inspector. During initialization, the view will attempt to use the preferred API, but will fall back to a different API if the preferred one is not supported on the current hardware.To create a SCNRenderer object that renders into your own OpenGL contect, use the init(context:options:) initializer. To create a renderer for use in your own Metal workflow, use the init(device:options:) initializer.The rendering technology used by a SCNLayer object is determined by Core Animation.After initializing a renderer, this property reflects the rendering technology in use.
  1459. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522616-renderingapi
  1460. */
  1461. get renderingAPI() {
  1462. return this._renderingAPI
  1463. }
  1464.  
  1465. // Preloading Renderer Resources
  1466.  
  1467. /**
  1468. * Required. Prepares a SceneKit object for rendering.
  1469. * @access public
  1470. * @param {Object} object - An SCNScene, SCNNode, SCNGeometry, or SCNMaterial instance.
  1471. * @param {?function(): boolean} [block = null] - A block that SceneKit calls periodically while preparing the object. The block takes no parameters.Your block should return false to tell SceneKit to continue preparing the object, or true to cancel preparation.Pass nil for this parameter if you do not need an opportunity to cancel preparing the object.
  1472. * @returns {boolean} -
  1473. * @desc By default, SceneKit lazily loads resources onto the GPU for rendering. This approach uses memory and GPU bandwidth efficiently, but can lead to stutters in an otherwise smooth frame rate when you add large amounts of new content to an animated scene. To avoid such issues, use this method to prepare content for drawing before adding it to the scene. You can call this method on a secondary thread to prepare content asynchronously. SceneKit prepares all content associated with the object parameter you provide. If you provide an SCNMaterial object, SceneKit loads any texture images assigned to its material properties. If you provide an SCNGeometry object, SceneKit loads all materials attached to the geometry, as well as its vertex data. If you provide an SCNNode or SCNScene object, SceneKit loads all geometries and materials associated with the node and all its child nodes, or with the entire node hierarchy of the scene.You can use the block parameter to cancel preparation if content is no longer needed. For example, in a game you might use this method to preload areas of the game world the player is soon to enter, but if the player character dies before entering those areas, you can return true from the block to cancel preloading.You can observe the progress of this operation with the Progress class. For details, see Progress.
  1474. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522798-prepare
  1475. */
  1476. prepareShouldAbortBlock(object, block = null) {
  1477. return false
  1478. }
  1479.  
  1480. /**
  1481. * Required. Prepares the specified SceneKit objects for rendering, using a background thread.
  1482. * @access public
  1483. * @param {Object[]} objects - An array of containing one or more SCNScene, SCNNode, SCNGeometry, or SCNMaterial instances.
  1484. * @param {?function(arg1: boolean): void} [completionHandler = null] - A block that SceneKit calls when object preparation fails or completes.The block takes the following parameter:successtrue if all content was successfully prepared for rendering; otherwise, false.
  1485. * @returns {void}
  1486. * @desc By default, SceneKit lazily loads resources onto the GPU for rendering. This approach uses memory and GPU bandwidth efficiently, but can lead to stutters in an otherwise smooth frame rate when you add large amounts of new content to an animated scene. To avoid such issues, use this method to prepare content for drawing before adding it to the scene. SceneKit uses a secondary thread to prepare content asynchronously.SceneKit prepares all content associated with the objects you provide. If you provide an SCNMaterial object, SceneKit loads any texture images assigned to its material properties. If you provide an SCNGeometry object, SceneKit loads all materials attached to the geometry, as well as its vertex data. If you provide an SCNNode or SCNScene object, SceneKit loads all geometries and materials associated with the node and all its child nodes, or with the entire node hierarchy of the scene.You can observe the progress of this operation with the Progress class. For details, see Progress.
  1487. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523375-prepare
  1488. */
  1489. prepare(objects, completionHandler = null) {
  1490. }
  1491.  
  1492. // Working With Projected Scene Contents
  1493.  
  1494. /**
  1495. * Required. Searches the renderer’s scene for objects corresponding to a point in the rendered image.
  1496. * @access public
  1497. * @param {CGPoint} point -
  1498. * @param {?Map<SCNHitTestOption, Object>} [options = null] - A dictionary of options affecting the search. See Hit Testing Options Keys for acceptable values.
  1499. * @returns {SCNHitTestResult[]} -
  1500. * @desc A 2D point in the rendered screen coordinate space can refer to any point along a line segment in the 3D scene coordinate space. Hit-testing is the process of finding elements of a scene located along this line segment. For example, you can use this method to find the geometry corresponding to a click event in a SceneKit view.
  1501. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522929-hittest
  1502. */
  1503. hitTest(point, options = null) {
  1504. if(this.scene === null){
  1505. return []
  1506. }
  1507. let _options = new Map()
  1508. if(options instanceof Map){
  1509. _options = options
  1510. }else if(Array.isArray(options)){
  1511. _options = new Map(options)
  1512. }
  1513.  
  1514. const cameraNode = this._getCameraNode()
  1515. cameraNode.camera._updateProjectionTransform(this._viewRect)
  1516. const from = new SCNVector3(point.x, point.y, 0)
  1517. const to = new SCNVector3(point.x, point.y, 1.0)
  1518.  
  1519. const useGPU = false
  1520. if(!useGPU){
  1521. return this._hitTestByCPU(cameraNode.viewProjectionTransform, from, to, _options)
  1522. }
  1523. return this._hitTestByGPU(cameraNode.viewProjectionTransform, from, to, _options)
  1524. }
  1525.  
  1526. _initializeHitFrameBuffer() {
  1527. const gl = this.context
  1528. const width = this._viewRect.size.width
  1529. const height = this._viewRect.size.height
  1530. this._hitFrameBuffer = gl.createFramebuffer()
  1531. this._hitDepthBuffer = gl.createRenderbuffer()
  1532. gl.bindFramebuffer(gl.FRAMEBUFFER, this._hitFrameBuffer)
  1533. gl.bindRenderbuffer(gl.RENDERBUFFER, this._hitDepthBuffer)
  1534. gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height)
  1535.  
  1536. this._hitObjectIDTexture = gl.createTexture()
  1537. gl.bindTexture(gl.TEXTURE_2D, this._hitObjectIDTexture)
  1538. // texImage2D(target, level, internalformat, width, height, border, format, type, source)
  1539. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
  1540.  
  1541. this._hitFaceIDTexture = gl.createTexture()
  1542. gl.bindTexture(gl.TEXTURE_2D, this._hitFaceIDTexture)
  1543. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
  1544.  
  1545. this._hitPositionTexture = gl.createTexture()
  1546. gl.bindTexture(gl.TEXTURE_2D, this._hitPositionTexture)
  1547. //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, null)
  1548. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
  1549.  
  1550. this._hitNormalTexture = gl.createTexture()
  1551. gl.bindTexture(gl.TEXTURE_2D, this._hitNormalTexture)
  1552. //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, null)
  1553. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
  1554.  
  1555. //gl.framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer)
  1556. gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this._hitDepthBuffer)
  1557. //gl.framebufferTexture2D(target, attachment, textarget, texture, level)
  1558. gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._hitObjectIDTexture, 0)
  1559. gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this._hitFaceIDTexture, 0)
  1560. gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, this._hitPositionTexture, 0)
  1561. gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT3, gl.TEXTURE_2D, this._hitNormalTexture, 0)
  1562. gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3])
  1563.  
  1564. gl.bindRenderbuffer(gl.RENDERBUFFER, null)
  1565. gl.bindFramebuffer(gl.FRAMEBUFFER, null)
  1566. }
  1567.  
  1568. /**
  1569. * @access private
  1570. * @param {SCNMatrix4} viewProjectionMatrix -
  1571. * @param {SCNVector3} from -
  1572. * @param {SCNVector3} to -
  1573. * @param {Object} options -
  1574. * @returns {SCNHitTestResult[]} -
  1575. */
  1576. _hitTestByCPU(viewProjectionMatrix, from, to, options) {
  1577. const result = []
  1578.  
  1579. const invVp = viewProjectionMatrix.invert()
  1580. const rayFrom = from.transform(invVp)
  1581. const rayTo = to.transform(invVp)
  1582. //console.log(`rayFrom: ${rayFrom.float32Array()}`)
  1583. //console.log(`rayTo : ${rayTo.float32Array()}`)
  1584.  
  1585. //const rayVec = rayTo.sub(rayFrom)
  1586. const renderingArray = this._createRenderingNodeArray()
  1587. //console.log(`renderingArray.length: ${renderingArray.length}`)
  1588.  
  1589. let categoryBitMask = options.get(SCNHitTestOption.categoryBitMask)
  1590. if(typeof categoryBitMask === 'undefined'){
  1591. categoryBitMask = -1
  1592. }
  1593.  
  1594. for(const node of renderingArray){
  1595. if(node.categoryBitMask & categoryBitMask){
  1596. //result.push(...this._nodeHitTestByCPU(node, rayFrom, rayVec))
  1597. const hits = SCNPhysicsWorld._hitTestWithSegmentNode(rayFrom, rayTo, node)
  1598. if(hits.length > 0){
  1599. // convert from the child's coordinate to this node's coordinate
  1600. for(const h of hits){
  1601. h._node = node
  1602. h._worldCoordinates = node.convertPositionTo(h._localCoordinates, null)
  1603. h._worldNormal = node.convertPositionTo(h._localNormal, null)
  1604. }
  1605. result.push(...hits)
  1606. }
  1607. }
  1608. }
  1609.  
  1610. return result
  1611. }
  1612.  
  1613. /**
  1614. * @access private
  1615. * @param {SCNMatrix4} viewProjectionTransform -
  1616. * @param {SCNVector3} from -
  1617. * @param {SCNVector3} to -
  1618. * @param {Map} options -
  1619. * @returns {SCNHitTestResult[]} -
  1620. */
  1621. _hitTestByGPU(viewProjectionTransform, from, to, options) {
  1622. const result = []
  1623. const gl = this._context
  1624.  
  1625. if(this._hitFrameBuffer === null){
  1626. this._initializeHitFrameBuffer()
  1627. }
  1628. const prg = this._defaultHitTestProgram
  1629. const hitTestProgram = prg._glProgram
  1630. this._useProgram(prg)
  1631. //gl.useProgram(hitTestProgram)
  1632. gl.bindFramebuffer(gl.FRAMEBUFFER, this._hitFrameBuffer)
  1633.  
  1634. gl.depthMask(true)
  1635. gl.depthFunc(gl.LEQUAL)
  1636. gl.enable(gl.SCISSOR_TEST)
  1637. gl.disable(gl.BLEND)
  1638. gl.clearColor(0, 0, 0, 0)
  1639. gl.clearDepth(1.0)
  1640. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  1641.  
  1642. const x = (from.x + 1.0) * 0.5 * this._viewRect.size.width
  1643. const y = (from.y + 1.0) * 0.5 * this._viewRect.size.height
  1644. let sx = x - 1
  1645. let sy = y - 1
  1646. if(sx < 0){
  1647. sx = 0
  1648. }else if(sx + 3 > this._viewRect.size.width){
  1649. sx = this._viewRect.size.width - 3
  1650. }
  1651. if(sy < 0){
  1652. sy = 0
  1653. }else if(sy + 3 > this._viewRect.size.height){
  1654. sy = this._viewRect.size.width - 3
  1655. }
  1656.  
  1657. gl.scissor(sx, sy, 3, 3)
  1658. gl.uniformMatrix4fv(gl.getUniformLocation(hitTestProgram, 'viewProjectionTransform'), false, viewProjectionTransform.float32Array())
  1659. let backFaceCulling = options.get(SCNHitTestOption.backFaceCulling)
  1660. if(typeof backFaceCulling === 'undefined'){
  1661. backFaceCulling = true
  1662. }
  1663. if(backFaceCulling){
  1664. gl.enable(gl.CULL_FACE)
  1665. gl.cullFace(gl.BACK)
  1666. }else{
  1667. gl.disable(gl.CULL_FACE)
  1668. }
  1669.  
  1670. let categoryBitMask = options.get(SCNHitTestOption.categoryBitMask)
  1671. if(typeof categoryBitMask === 'undefined'){
  1672. categoryBitMask = -1
  1673. }
  1674. let ignoreHiddenNodes = options.get(SCNHitTestOption.ignoreHiddenNodes)
  1675. if(typeof ignoreHiddenNodes === 'undefined'){
  1676. ignoreHiddenNodes = true
  1677. }
  1678.  
  1679. const renderingArray = this._createRenderingNodeArray()
  1680. const len = renderingArray.length
  1681. for(let i=0; i<len; i++){
  1682. const node = renderingArray[i]
  1683. if((node.categoryBitMask & categoryBitMask) === 0){
  1684. continue
  1685. }
  1686. if(ignoreHiddenNodes && node.isHidden){
  1687. continue
  1688. }
  1689. this._renderNodeForHitTest(node, i + 100, options)
  1690. }
  1691.  
  1692. const objectIDBuf = new Uint8Array(4)
  1693. gl.readBuffer(gl.COLOR_ATTACHMENT0)
  1694. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, objectIDBuf, 0)
  1695. const objectID = objectIDBuf[0] * 256 + objectIDBuf[1]
  1696. const geometryIndex = objectIDBuf[2] * 256 + objectIDBuf[3]
  1697.  
  1698. const faceIDBuf = new Uint8Array(4)
  1699. gl.readBuffer(gl.COLOR_ATTACHMENT1)
  1700. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, faceIDBuf, 0)
  1701. const faceIndex = faceIDBuf[0] * 16777216 + faceIDBuf[1] * 65536 + faceIDBuf[2] * 256 + faceIDBuf[3]
  1702.  
  1703. const positionBuf = new Uint8Array(4)
  1704. gl.readBuffer(gl.COLOR_ATTACHMENT2)
  1705. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, positionBuf, 0)
  1706. //const screenPos = new SCNVector3(positionBuf[0] / 127.5 - 1.0, positionBuf[1] / 127.5 - 1.0, positionBuf[2] / 127.5 - 1.0)
  1707. //const position = screenPos.transform(viewProjectionTransform.invert())
  1708. const p = (((positionBuf[3] / 255.0 + positionBuf[2]) / 255.0 + positionBuf[1] / 255.0) + positionBuf[0]) / 255.0
  1709. const position = from.lerp(to, p)
  1710.  
  1711. const normalBuf = new Uint8Array(4)
  1712. gl.readBuffer(gl.COLOR_ATTACHMENT3)
  1713. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, normalBuf, 0)
  1714. const normal = new SCNVector3(normalBuf[0] / 127.5 - 1.0, normalBuf[1] / 127.5 - 1.0, normalBuf[2] / 127.5 - 1.0)
  1715.  
  1716. //console.log('***** Hit Result *****')
  1717. //console.log(`objectID: ${objectID}`)
  1718. //console.log(`geometryIndex: ${geometryIndex}`)
  1719. //console.log(`faceIndex: ${faceIndex}`)
  1720. //console.log(`position: ${position.floatArray()}`)
  1721. //console.log(`normal: ${normal.floatArray()}`)
  1722. //console.log('**********************')
  1723.  
  1724. if(objectID >= 100){
  1725. const r = new SCNHitTestResult()
  1726. const node = renderingArray[objectID - 100]
  1727. const worldInv = node.presentation._worldTransform.invert()
  1728. r._node = node
  1729. r._geometryIndex = geometryIndex
  1730. r._faceIndex = faceIndex
  1731. r._worldCoordinates = position
  1732. r._worldNormal = normal
  1733. r._modelTransform = node.presentation._worldTransform
  1734. r._localCoordinates = position.transform(worldInv)
  1735. r._localNormal = normal.transform(worldInv)
  1736.  
  1737. result.push(r)
  1738. }
  1739.  
  1740. gl.disable(gl.SCISSOR_TEST)
  1741. gl.bindFramebuffer(gl.FRAMEBUFFER, null)
  1742.  
  1743. return result
  1744. }
  1745.  
  1746. /**
  1747. * @access private
  1748. * @param {SCNVector3} from -
  1749. * @param {SCNVector3} to -
  1750. * @param {Map} options -
  1751. * @param {Object} _options -
  1752. * @returns {SCNHitTestResult[]} -
  1753. */
  1754. _physicsHitTestByGPU(from, to, options, _options) {
  1755. const result = []
  1756. const gl = this._context
  1757.  
  1758. const viewProjectionTransform = this._createViewProjectionTransformForRay(from, to)
  1759. const _from = from.transform(viewProjectionTransform)
  1760. const _to = to.transform(viewProjectionTransform)
  1761.  
  1762. if(this._hitFrameBuffer === null){
  1763. this._initializeHitFrameBuffer()
  1764. }
  1765. const prg = this._defaultHitTestProgram
  1766. const hitTestProgram = prg._glProgram
  1767. //gl.useProgram(hitTestProgram)
  1768. this._useProgram(prg)
  1769. gl.bindFramebuffer(gl.FRAMEBUFFER, this._hitFrameBuffer)
  1770.  
  1771. gl.depthMask(true)
  1772. gl.depthFunc(gl.LEQUAL)
  1773. gl.enable(gl.SCISSOR_TEST)
  1774. gl.disable(gl.BLEND)
  1775. gl.clearColor(0, 0, 0, 0)
  1776. gl.clearDepth(1.0)
  1777. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  1778.  
  1779. // screen position
  1780. const x = (_from.x + 1.0) * 0.5 * this._viewRect.size.width
  1781. const y = (_from.y + 1.0) * 0.5 * this._viewRect.size.height
  1782. // left top of the scissor area
  1783. const areaSize = 3
  1784. let sx = x - 1
  1785. let sy = y - 1
  1786. if(sx < 0){
  1787. sx = 0
  1788. }else if(sx + areaSize > this._viewRect.size.width){
  1789. sx = this._viewRect.size.width - areaSize
  1790. }
  1791. if(sy < 0){
  1792. sy = 0
  1793. }else if(sy + areaSize > this._viewRect.size.height){
  1794. sy = this._viewRect.size.width - areaSize
  1795. }
  1796.  
  1797. gl.scissor(sx, sy, areaSize, areaSize)
  1798. gl.uniformMatrix4fv(gl.getUniformLocation(hitTestProgram, 'viewProjectionTransform'), false, viewProjectionTransform.float32Array())
  1799. let backFaceCulling = options.get(SCNPhysicsWorld.TestOption.backfaceCulling)
  1800. if(typeof backFaceCulling === 'undefined'){
  1801. backFaceCulling = true
  1802. }
  1803. if(backFaceCulling){
  1804. gl.enable(gl.CULL_FACE)
  1805. gl.cullFace(gl.BACK)
  1806. }else{
  1807. gl.disable(gl.CULL_FACE)
  1808. }
  1809.  
  1810. let collisionBitMask = options.get(SCNPhysicsWorld.TestOption.collisionBitMask)
  1811. if(typeof collisionBitMask === 'undefined'){
  1812. collisionBitMask = -1
  1813. }
  1814.  
  1815. let searchMode = options.get(SCNPhysicsWorld.TestOption.searchMode)
  1816. if(typeof searchMode === 'undefined'){
  1817. searchMode = SCNPhysicsWorld.TestSearchMode.closest
  1818. }
  1819.  
  1820. let renderingArray = null
  1821. if(_options && _options.targets){
  1822. renderingArray = _options.targets
  1823. collisionBitMask = -1
  1824. }else{
  1825. renderingArray = this._createRenderingPhysicsNodeArray()
  1826. }
  1827.  
  1828. const len = renderingArray.length
  1829. for(let i=0; i<len; i++){
  1830. const node = renderingArray[i]
  1831. const body = node.physicsBody
  1832. if((body.categoryBitMask & collisionBitMask) === 0){
  1833. continue
  1834. }
  1835. this._renderPhysicsNodeForHitTest(node, i + 100, options)
  1836. }
  1837.  
  1838. const objectIDBuf = new Uint8Array(4)
  1839. gl.readBuffer(gl.COLOR_ATTACHMENT0)
  1840. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, objectIDBuf, 0)
  1841. const objectID = objectIDBuf[0] * 256 + objectIDBuf[1]
  1842. const geometryIndex = objectIDBuf[2] * 256 + objectIDBuf[3]
  1843.  
  1844. const faceIDBuf = new Uint8Array(4)
  1845. gl.readBuffer(gl.COLOR_ATTACHMENT1)
  1846. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, faceIDBuf, 0)
  1847. const faceIndex = faceIDBuf[0] * 16777216 + faceIDBuf[1] * 65536 + faceIDBuf[2] * 256 + faceIDBuf[3]
  1848.  
  1849. const positionBuf = new Uint8Array(4)
  1850. gl.readBuffer(gl.COLOR_ATTACHMENT2)
  1851. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, positionBuf, 0)
  1852. //const screenPos = new SCNVector3(positionBuf[0] / 127.5 - 1.0, positionBuf[1] / 127.5 - 1.0, positionBuf[2] / 127.5 - 1.0)
  1853. //const position = screenPos.transform(viewProjectionTransform.invert())
  1854. const p = (((positionBuf[3] / 255.0 + positionBuf[2]) / 255.0 + positionBuf[1] / 255.0) + positionBuf[0]) / 255.0
  1855. const screenPos = _from.lerp(_to, p)
  1856. const position = screenPos.transform(viewProjectionTransform.invert())
  1857.  
  1858. const normalBuf = new Uint8Array(4)
  1859. gl.readBuffer(gl.COLOR_ATTACHMENT3)
  1860. gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, normalBuf, 0)
  1861. const normal = new SCNVector3(normalBuf[0] / 127.5 - 1.0, normalBuf[1] / 127.5 - 1.0, normalBuf[2] / 127.5 - 1.0)
  1862.  
  1863. //console.log('***** Hit Result *****')
  1864. //console.log(`objectID: ${objectID}`)
  1865. //console.log(`geometryIndex: ${geometryIndex}`)
  1866. //console.log(`faceIndex: ${faceIndex}`)
  1867. //console.log(`from: ${from.floatArray()}`)
  1868. //console.log(`to: ${to.floatArray()}`)
  1869. //console.log(`positionBuf: ${positionBuf[0]}, ${positionBuf[1]}, ${positionBuf[2]}`)
  1870. //console.log(`sPos: ${screenPos.floatArray()}`)
  1871. //console.log(`position: ${position.floatArray()}`)
  1872. //console.log(`normal: ${normal.floatArray()}`)
  1873. //console.log('**********************')
  1874.  
  1875. if(objectID >= 100){
  1876. const r = new SCNHitTestResult()
  1877. const node = renderingArray[objectID - 100]
  1878. const worldInv = node.presentation._worldTransform.invert()
  1879. r._node = node
  1880. r._geometryIndex = geometryIndex
  1881. r._faceIndex = faceIndex
  1882. r._worldCoordinates = position
  1883. r._worldNormal = normal
  1884. r._modelTransform = node.presentation._worldTransform
  1885. r._localCoordinates = position.transform(worldInv)
  1886. r._localNormal = normal.transform(worldInv)
  1887.  
  1888. result.push(r)
  1889. }
  1890.  
  1891. gl.disable(gl.SCISSOR_TEST)
  1892. gl.bindFramebuffer(gl.FRAMEBUFFER, null)
  1893.  
  1894. return result
  1895. }
  1896.  
  1897. /**
  1898. *
  1899. * @access private
  1900. * @param {SCNVector3} from -
  1901. * @param {SCNVector3} to -
  1902. * @returns {SCNMatrix4} -
  1903. */
  1904. _createViewProjectionTransformForRay(from, to) {
  1905. const vec = to.sub(from)
  1906. const len = vec.length()
  1907. const zNear = 1
  1908. const zFar = zNear + len
  1909. const proj = new SCNMatrix4()
  1910. proj.m11 = 1
  1911. proj.m22 = 1
  1912. proj.m33 = -(zFar + zNear) / len
  1913. proj.m34 = -1
  1914. proj.m43 = -2 * zFar * zNear / len
  1915. // TODO: use an orthographic projection
  1916. //proj.m33 = -2 / len
  1917. //proj.m43 = -(zFar + zNear) / len
  1918. //proj.m44 = 1
  1919.  
  1920. const view = new SCNMatrix4()
  1921. const up = new SCNVector3(0, 1, 0)
  1922. if(vec.x === 0 && vec.z === 0){
  1923. up.y = 0
  1924. up.z = 1
  1925. }
  1926. const f = vec.normalize()
  1927. const s = f.cross(up).normalize()
  1928. const u = s.cross(f).normalize()
  1929. view.m11 = s.x
  1930. view.m21 = s.y
  1931. view.m31 = s.z
  1932. view.m12 = u.x
  1933. view.m22 = u.y
  1934. view.m32 = u.z
  1935. view.m13 = -f.x
  1936. view.m23 = -f.y
  1937. view.m33 = -f.z
  1938. view.m44 = 1
  1939. const eye = from.sub(f.mul(zNear))
  1940. const t = eye.transform(view)
  1941. view.m41 = -t.x
  1942. view.m42 = -t.y
  1943. view.m43 = -t.z
  1944.  
  1945. return view.mult(proj)
  1946. }
  1947.  
  1948. /**
  1949. * Required. Returns a Boolean value indicating whether a node might be visible from a specified point of view.
  1950. * @access public
  1951. * @param {SCNNode} node - The node whose visibility is to be tested.
  1952. * @param {SCNNode} pointOfView - A node defining a point of view, as used by the pointOfView property.
  1953. * @returns {boolean} -
  1954. * @desc Any node containing a camera or spotlight may serve as a point of view (see the pointOfView property for details). Such a node defines a viewing frustum—a portion of the scene’s coordinate space, shaped like a truncated pyramid, that encloses all points visible from that point of view.Use this method to test whether a node lies within the viewing frustum defined by another node (which may or may not be the scene renderer’s current pointOfView node). For example, in a game scene containing multiple camera nodes, you could use this method to determine which camera is currently best for viewing a moving player character.Note that this method does not perform occlusion testing. That is, it returns true if the tested node lies within the specified viewing frustum regardless of whether that node’s contents are obscured by other geometry.
  1955. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522647-isnode
  1956. */
  1957. isNodeInsideFrustumOf(node, pointOfView) {
  1958. return false
  1959. }
  1960.  
  1961. /**
  1962. * Required. Returns all nodes that might be visible from a specified point of view.
  1963. * @access public
  1964. * @param {SCNNode} pointOfView - A node defining a point of view, as used by the pointOfView property.
  1965. * @returns {SCNNode[]} -
  1966. * @desc Any node containing a camera or spotlight may serve as a point of view (see the pointOfView property for details). Such a node defines a viewing frustum—a portion of the scene’s coordinate space, shaped like a truncated pyramid, that encloses all points visible from that point of view.Use this method find all nodes whose content lies within the viewing frustum defined by another node (which may or may not be the scene renderer’s current pointOfView node).Note that this method does not perform occlusion testing. That is, the returned array includes any node that lies within the specified viewing frustum regardless of whether that node’s contents are obscured by other geometry.
  1967. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522942-nodesinsidefrustum
  1968. */
  1969. nodesInsideFrustumOf(pointOfView) {
  1970. return null
  1971. }
  1972.  
  1973. /**
  1974. * Required. Projects a point from the 3D world coordinate system of the scene to the 2D pixel coordinate system of the renderer.
  1975. * @access public
  1976. * @param {SCNVector3} point - A point in the world coordinate system of the renderer’s scene.
  1977. * @returns {SCNVector3} -
  1978. * @desc The z-coordinate of the returned point describes the depth of the projected point relative to the near and far clipping planes of the renderer’s viewing frustum (defined by its pointOfView node). Projecting a point on the near clipping plane returns a point whose z-coordinate is 0.0; projecting a point on the far clipping plane returns a point whose z-coordinate is 1.0.
  1979. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1524089-projectpoint
  1980. */
  1981. projectPoint(point) {
  1982. return null
  1983. }
  1984.  
  1985. /**
  1986. * Required. Unprojects a point from the 2D pixel coordinate system of the renderer to the 3D world coordinate system of the scene.
  1987. * @access public
  1988. * @param {SCNVector3} point - A point in the screen-space (view, layer, or GPU viewport) coordinate system of the scene renderer.
  1989. * @returns {SCNVector3} -
  1990. * @desc The z-coordinate of the point parameter describes the depth at which to unproject the point relative to the near and far clipping planes of the renderer’s viewing frustum (defined by its pointOfView node). Unprojecting a point whose z-coordinate is 0.0 returns a point on the near clipping plane; unprojecting a point whose z-coordinate is 1.0 returns a point on the far clipping plane.A 2D point in the rendered screen coordinate space can refer to any point along a line segment in the 3D scene coordinate space. To test for scene contents along this line—for example, to find the geometry corresponding to the location of a click event in a view—use the hitTest(_:options:) method.
  1991. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522631-unprojectpoint
  1992. */
  1993. unprojectPoint(point) {
  1994. return null
  1995. }
  1996.  
  1997. // Customizing Scene Rendering with Metal
  1998. /**
  1999. * Required. The Metal render command encoder in use for the current SceneKit rendering pass.
  2000. * @type {?MTLRenderCommandEncoder}
  2001. * @desc Use this render command encoder to encode additional rendering commands before or after SceneKit draws its own content.This property is valid only during the SceneKit rendering loop—that is, within one of the methods defined in the SCNSceneRendererDelegate protocol. Accessing this property at any other time returns nil.
  2002. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522609-currentrendercommandencoder
  2003. */
  2004. get currentRenderCommandEncoder() {
  2005. return this._currentRenderCommandEncoder
  2006. }
  2007.  
  2008. /**
  2009. * Required. The Metal device this renderer uses for rendering.
  2010. * @type {?MTLDevice}
  2011. * @desc Use this property to create or look up other Metal resources that use the same device as your SceneKit renderer.NoteThis property is valid only for scene renderers whose renderingAPI value is metal. You create a SceneKit view that renders using Metal with the preferredRenderingAPI initialization option or in Interface Builder, or an SCNRenderer that uses Metal with the init(device:options:) method. For OpenGL-based scene renderers, this property’s value is always nil.
  2012. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523935-device
  2013. */
  2014. get device() {
  2015. return this._device
  2016. }
  2017.  
  2018. /**
  2019. * Required. The Metal command queue this renderer uses for rendering.
  2020. * @type {?MTLCommandQueue}
  2021. * @desc Use this property to schedule additional command buffers for the Metal device to execute as part of the render cycle. For example, you can use a compute command encoder to modify the vertex data in a Metal buffer for use by a SCNGeometrySource object.NoteThis property is valid only for scene renderers whose renderingAPI value is metal. You create a SceneKit view that renders using Metal with the preferredRenderingAPI initialization option or in Interface Builder, or an SCNRenderer that uses Metal with the init(device:options:) method. For OpenGL-based scene renderers, this property’s value is always nil.
  2022. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523974-commandqueue
  2023. */
  2024. get commandQueue() {
  2025. return this._commandQueue
  2026. }
  2027.  
  2028. /**
  2029. * Required. The Metal pixel format for the renderer’s color output.
  2030. * @type {MTLPixelFormat}
  2031. * @desc Use this property, along with the depthPixelFormat and stencilPixelFormat properties, if you perform custom drawing with Metal (see the SCNSceneRendererDelegate and SCNNodeRendererDelegate classes) and need to create a new MTLRenderPipelineState object to change the GPU state as part of your rendering.NoteThis property is valid only for scene renderers whose renderingAPI value is metal. You create a SceneKit view that renders using Metal with the preferredRenderingAPI initialization option or in Interface Builder, or an SCNRenderer that uses Metal with the init(device:options:) method. For OpenGL-based scene renderers, this property’s value is always nil.
  2032. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523701-colorpixelformat
  2033. */
  2034. get colorPixelFormat() {
  2035. return this._colorPixelFormat
  2036. }
  2037.  
  2038. /**
  2039. * Required. The Metal pixel format for the renderer’s depth buffer.
  2040. * @type {MTLPixelFormat}
  2041. * @desc Use this property, along with the colorPixelFormat and stencilPixelFormat properties, if you perform custom drawing with Metal (see the SCNSceneRendererDelegate and SCNNodeRendererDelegate classes) and need to create a new MTLRenderPipelineState object to change the GPU state as part of your rendering.NoteThis property is valid only for scene renderers whose renderingAPI value is metal. You create a SceneKit view that renders using Metal with the preferredRenderingAPI initialization option or in Interface Builder, or an SCNRenderer that uses Metal with the init(device:options:) method. For OpenGL-based scene renderers, this property’s value is always nil.
  2042. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523780-depthpixelformat
  2043. */
  2044. get depthPixelFormat() {
  2045. return this._depthPixelFormat
  2046. }
  2047.  
  2048. /**
  2049. * Required. The Metal pixel format for the renderer’s stencil buffer.
  2050. * @type {MTLPixelFormat}
  2051. * @desc Use this property, along with the depthPixelFormat and colorPixelFormat properties, if you perform custom drawing with Metal (see the SCNSceneRendererDelegate and SCNNodeRendererDelegate classes) and need to create a new MTLRenderPipelineState object to change the GPU state as part of your rendering.NoteThis property is valid only for scene renderers whose renderingAPI value is metal. You create a SceneKit view that renders using Metal with the preferredRenderingAPI initialization option or in Interface Builder, or an SCNRenderer that uses Metal with the init(device:options:) method. For OpenGL-based scene renderers, this property’s value is always nil.
  2052. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523315-stencilpixelformat
  2053. */
  2054. get stencilPixelFormat() {
  2055. return this._stencilPixelFormat
  2056. }
  2057.  
  2058. // Customizing Scene Rendering with OpenGL
  2059.  
  2060. /**
  2061. * Required. The OpenGL rendering context that SceneKit uses for rendering the scene.
  2062. * @type {?Object}
  2063. * @desc In macOS, the value of this property is a Core OpenGL cglContextObj object.In iOS, the value of this property is an EAGLContext object.
  2064. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522840-context
  2065. */
  2066. get context() {
  2067. return this._context
  2068. }
  2069.  
  2070. _setContext(context) {
  2071. this._context = context
  2072. this._createDummyTexture()
  2073. }
  2074.  
  2075. // Working With Positional Audio
  2076.  
  2077. /**
  2078. * Required. The 3D audio mixing node SceneKit uses for positional audio effects.
  2079. * @type {AVAudioEnvironmentNode}
  2080. * @desc SceneKit uses this audio node to spatialize sounds from SCNAudioPlayer objects attached to nodes in the scene. You can use this object in conjunction with the audioEngine property to rearrange the audio graph to add other, non-spatialized audio sources or mix in audio processing effects.
  2081. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523582-audioenvironmentnode
  2082. */
  2083. get audioEnvironmentNode() {
  2084. return this._audioEnvironmentNode
  2085. }
  2086.  
  2087. /**
  2088. * Required. The audio engine SceneKit uses for playing scene sounds.
  2089. * @type {AVAudioEngine}
  2090. * @desc SceneKit uses this audio engine to play sounds from SCNAudioPlayer objects attached to nodes in the scene. You can use this object directly to add other sound sources not related to scene contents, or to add other sound processing nodes or mixing nodes to the audio engine. To identify the node SceneKit uses for spatializing scene sounds when connecting other nodes, use the audioEnvironmentNode property.
  2091. * @see https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522686-audioengine
  2092. */
  2093. get audioEngine() {
  2094. return this._audioEngine
  2095. }
  2096.  
  2097. /**
  2098. * @access private
  2099. * @param {SCNGeometry} geometry -
  2100. * @returns {SCNProgram} -
  2101. */
  2102. _getProgramForGeometry(geometry) {
  2103. if(geometry.program !== null && geometry.program._programCompiled){
  2104. return geometry.program
  2105. }
  2106.  
  2107. if(geometry.program || geometry.shaderModifiers !== null || geometry._shadableHelper !== null){
  2108. this._compileProgramForObject(geometry)
  2109. }
  2110. for(const material of geometry.materials){
  2111. if(material.program || material.shaderModifiers !== null || material._shadableHelper !== null){
  2112. this._compileProgramForObject(material)
  2113. }
  2114. }
  2115.  
  2116. if(geometry.program){
  2117. return geometry.program
  2118. }
  2119. return this._defaultProgram
  2120. }
  2121.  
  2122. /**
  2123. * @access private
  2124. * @param {SCNShadable} obj -
  2125. * @returns {void}
  2126. */
  2127. _compileProgramForObject(obj) {
  2128. const gl = this.context
  2129. let p = obj.program
  2130. if(!p){
  2131. p = new SCNProgram()
  2132. obj.program = p
  2133. }else if(p._programCompiled){
  2134. return p
  2135. }
  2136. p._parentObject = obj
  2137.  
  2138. const glProgram = p._getGLProgramForContext(gl)
  2139.  
  2140. const vsText = this._vertexShaderForObject(obj)
  2141. const fsText = this._fragmentShaderForObject(obj)
  2142.  
  2143. // initialize vertex shader
  2144. const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  2145. gl.shaderSource(vertexShader, vsText)
  2146. gl.compileShader(vertexShader)
  2147. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
  2148. const info = gl.getShaderInfoLog(vertexShader)
  2149. throw new Error(`vertex shader compile error: ${info}`)
  2150. }
  2151. p._glVertexShader = vertexShader
  2152.  
  2153. // initialize fragment shader
  2154. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
  2155. gl.shaderSource(fragmentShader, fsText)
  2156. gl.compileShader(fragmentShader)
  2157. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
  2158. const info = gl.getShaderInfoLog(fragmentShader)
  2159. throw new Error(`fragment shader compile error: ${info}`)
  2160. }
  2161. p._glFragmentShader = fragmentShader
  2162.  
  2163. gl.attachShader(glProgram, vertexShader)
  2164. gl.attachShader(glProgram, fragmentShader)
  2165.  
  2166. // link program object
  2167. gl.linkProgram(glProgram)
  2168. if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)){
  2169. const info = gl.getProgramInfoLog(glProgram)
  2170. throw new Error(`program link error: ${info}`)
  2171. }
  2172.  
  2173. p._programCompiled = true
  2174.  
  2175. return p
  2176. }
  2177.  
  2178. /**
  2179. * @access private
  2180. * @param {SCNProgram} program -
  2181. * @returns {void}
  2182. */
  2183. _useProgram(program) {
  2184. if(this._currentProgram === program){
  2185. return
  2186. }
  2187. const gl = this.context
  2188. const glProgram = program._getGLProgramForContext(gl)
  2189. gl.useProgram(glProgram)
  2190. program._setDummyTextureForContext(gl)
  2191. this._currentProgram = program
  2192. }
  2193.  
  2194. /**
  2195. * @access private
  2196. * @param {SCNProgram} program -
  2197. * @returns {void}
  2198. */
  2199. _switchProgram(program) {
  2200. if(this._currentProgram === program){
  2201. return
  2202. }
  2203.  
  2204. const gl = this.context
  2205. const glProgram = program._getGLProgramForContext(gl)
  2206. gl.useProgram(glProgram)
  2207.  
  2208. // set dummy textures
  2209. program._setDummyTextureForContext(gl)
  2210.  
  2211. // set shadow textures
  2212. const lights = this._lightNodes
  2213. for(let i=0; i<lights.directionalShadow.length; i++){
  2214. const node = lights.directionalShadow[i]
  2215. const symbol = `TEXTURE${i+8}`
  2216. gl.activeTexture(gl[symbol])
  2217. gl.bindTexture(gl.TEXTURE_2D, node.presentation.light._shadowDepthTexture)
  2218. }
  2219.  
  2220. // bind buffers
  2221. const cameraIndex = gl.getUniformBlockIndex(glProgram, 'cameraUniform')
  2222. gl.uniformBlockBinding(glProgram, cameraIndex, _cameraLoc)
  2223. gl.bindBufferBase(gl.UNIFORM_BUFFER, _cameraLoc, this._cameraBuffer)
  2224. const fogIndex = gl.getUniformBlockIndex(glProgram, 'fogUniform')
  2225. gl.uniformBlockBinding(glProgram, fogIndex, _fogLoc)
  2226. gl.bindBufferBase(gl.UNIFORM_BUFFER, _fogLoc, this._fogBuffer)
  2227. const lightIndex = gl.getUniformBlockIndex(glProgram, 'lightUniform')
  2228. gl.uniformBlockBinding(glProgram, lightIndex, _lightLoc)
  2229. gl.bindBufferBase(gl.UNIFORM_BUFFER, _lightLoc, this._lightBuffer)
  2230. const scnLightsIndex = gl.getUniformBlockIndex(glProgram, 'SCNLightsUniform')
  2231. gl.uniformBlockBinding(glProgram, scnLightsIndex, _scnLightsLoc)
  2232. gl.bindBufferBase(gl.UNIFORM_BUFFER, _scnLightsLoc, this._scnLightsBuffer)
  2233.  
  2234. // set uniform variables
  2235. const uniformTime = gl.getUniformLocation(glProgram, 'u_time')
  2236. if(uniformTime){
  2237. // this._time might be too large.
  2238. const time = this._time % 100000.0
  2239. gl.uniform1f(uniformTime, time)
  2240. }
  2241.  
  2242. const obj = program._parentObject
  2243. if(obj){
  2244. // bind custom uniforms
  2245. let textureNo = 16 // TEXTURE0-15 is reserved for the default renderer (0-11: material, 12-15: shadow)
  2246. for(const key of Object.keys(obj._valuesForUndefinedKeys)){
  2247. const loc = gl.getUniformLocation(glProgram, key)
  2248. if(loc !== null){
  2249. const val = obj._valuesForUndefinedKeys[key]
  2250. if(typeof val === 'number'){
  2251. gl.uniform1f(loc, val)
  2252. }else if(_InstanceOf(val, SCNVector3)){
  2253. gl.uniform3fv(loc, val.float32Array())
  2254. }else if(_InstanceOf(val, SCNVector4) || _InstanceOf(val, SKColor)){
  2255. gl.uniform4fv(loc, val.float32Array())
  2256. }else if(_InstanceOf(val, SCNMaterialProperty)){
  2257. // TODO: refactoring: SCNGeometry has the same function
  2258. if(val._contents instanceof Image){
  2259. //val._contents = this._createTexture(gl, val._contents)
  2260. const image = val._contents
  2261. const texture = gl.createTexture()
  2262. const canvas = document.createElement('canvas')
  2263. canvas.width = image.naturalWidth
  2264. canvas.height = image.naturalHeight
  2265. canvas.getContext('2d').drawImage(image, 0, 0)
  2266. gl.bindTexture(gl.TEXTURE_2D, texture)
  2267. // texImage2D(target, level, internalformat, width, height, border, format, type, source)
  2268. // Safari complains that 'source' is not ArrayBufferView type, but WebGL2 should accept HTMLCanvasElement.
  2269. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, image.width, image.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
  2270. gl.generateMipmap(gl.TEXTURE_2D)
  2271. val._contents = texture
  2272. }
  2273. if(val._contents instanceof WebGLTexture){
  2274. gl.uniform1i(loc, textureNo)
  2275. gl.activeTexture(gl[`TEXTURE${textureNo}`])
  2276. // TODO: check texture type
  2277. gl.bindTexture(gl.TEXTURE_2D, val._contents)
  2278. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, val._magnificationFilterFor(gl))
  2279. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, val._minificationFilterFor(gl))
  2280. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, val._wrapSFor(gl))
  2281. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, val._wrapTFor(gl))
  2282. }
  2283. textureNo += 1
  2284. }
  2285. // TODO: implement for other types
  2286. }
  2287. }
  2288. }
  2289.  
  2290. this._currentProgram = program
  2291. }
  2292.  
  2293. /**
  2294. * @access private
  2295. * @type {SCNProgram}
  2296. */
  2297. get _defaultProgram() {
  2298. const numLightsChanged = this._numLightsChanged()
  2299. if(this.__defaultProgram !== null && !numLightsChanged){
  2300. return this.__defaultProgram
  2301. }
  2302.  
  2303. const gl = this.context
  2304. if(this.__defaultProgram === null){
  2305. this.__defaultProgram = new SCNProgram()
  2306. }
  2307. const p = this.__defaultProgram
  2308. const glProgram = p._getGLProgramForContext(gl)
  2309. const vsText = this._defaultVertexShader
  2310. const fsText = this._defaultFragmentShader
  2311.  
  2312. // initialize vertex shader
  2313. const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  2314. gl.shaderSource(vertexShader, vsText)
  2315. gl.compileShader(vertexShader)
  2316. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
  2317. const info = gl.getShaderInfoLog(vertexShader)
  2318. throw new Error(`vertex shader compile error: ${info}`)
  2319. }
  2320. this.__defaultProgram._glVertexShader = vertexShader
  2321.  
  2322. // initialize fragment shader
  2323. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
  2324. gl.shaderSource(fragmentShader, fsText)
  2325. gl.compileShader(fragmentShader)
  2326. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
  2327. const info = gl.getShaderInfoLog(fragmentShader)
  2328. throw new Error(`fragment shader compile error: ${info}`)
  2329. }
  2330. this.__defaultProgram._glFragmentShader = fragmentShader
  2331.  
  2332. gl.attachShader(glProgram, vertexShader)
  2333. gl.attachShader(glProgram, fragmentShader)
  2334.  
  2335.  
  2336. // link program object
  2337. gl.linkProgram(glProgram)
  2338. if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)){
  2339. const info = gl.getProgramInfoLog(glProgram)
  2340. throw new Error(`program link error: ${info}`)
  2341. }
  2342.  
  2343. this._switchProgram(p)
  2344.  
  2345. gl.enable(gl.DEPTH_TEST)
  2346. gl.depthFunc(gl.LEQUAL)
  2347. gl.enable(gl.BLEND)
  2348. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  2349. gl.enable(gl.CULL_FACE)
  2350. gl.cullFace(gl.BACK)
  2351.  
  2352. // set default textures to prevent warnings
  2353. //this._setDummyTextureAsDefault(p)
  2354. return this.__defaultProgram
  2355. }
  2356.  
  2357. /**
  2358. * @access private
  2359. * @type {SCNProgram}
  2360. */
  2361. get _defaultPBRProgram() {
  2362. const numLightsChanged = this._numLightsChanged()
  2363. if(this.__defaultPBRProgram !== null && !numLightsChanged){
  2364. return this.__defaultPBRProgram
  2365. }
  2366.  
  2367. const gl = this.context
  2368. if(this.__defaultPBRProgram === null){
  2369. this.__defaultPBRProgram = new SCNProgram()
  2370. }
  2371. const p = this.__defaultPBRProgram
  2372. const glProgram = p._getGLProgramForContext(gl)
  2373. const vsText = this._defaultVertexShader
  2374. const fsText = this._defaultPBRFragmentShader
  2375.  
  2376. // initialize vertex shader
  2377. const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  2378. gl.shaderSource(vertexShader, vsText)
  2379. gl.compileShader(vertexShader)
  2380. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
  2381. const info = gl.getShaderInfoLog(vertexShader)
  2382. throw new Error(`vertex shader compile error: ${info}`)
  2383. }
  2384. this.__defaultPBRProgram._glVertexShader = vertexShader
  2385.  
  2386. // initialize fragment shader
  2387. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
  2388. gl.shaderSource(fragmentShader, fsText)
  2389. gl.compileShader(fragmentShader)
  2390. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
  2391. const info = gl.getShaderInfoLog(fragmentShader)
  2392. throw new Error(`fragment shader compile error: ${info}`)
  2393. }
  2394. this.__defaultPBRProgram._glFragmentShader = fragmentShader
  2395.  
  2396. gl.attachShader(glProgram, vertexShader)
  2397. gl.attachShader(glProgram, fragmentShader)
  2398.  
  2399.  
  2400. // link program object
  2401. gl.linkProgram(glProgram)
  2402. if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)){
  2403. const info = gl.getProgramInfoLog(glProgram)
  2404. throw new Error(`program link error: ${info}`)
  2405. }
  2406.  
  2407. this._switchProgram(p)
  2408.  
  2409. gl.enable(gl.DEPTH_TEST)
  2410. gl.depthFunc(gl.LEQUAL)
  2411. gl.enable(gl.BLEND)
  2412. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  2413. gl.enable(gl.CULL_FACE)
  2414. gl.cullFace(gl.BACK)
  2415.  
  2416. // set default textures to prevent warnings
  2417. //this._setDummyTextureAsDefault(p)
  2418. return this.__defaultPBRProgram
  2419. }
  2420.  
  2421. /**
  2422. * @access private
  2423. * @param {SCNGeometry} geometry -
  2424. * @returns {SCNProgram} -
  2425. */
  2426. _programForGeometry(geometry) {
  2427. }
  2428.  
  2429. /**
  2430. * @access private
  2431. * @returns {string} -
  2432. */
  2433. get _defaultVertexShader() {
  2434. return this._replaceTexts(_SCNDefaultVertexShader)
  2435. }
  2436.  
  2437. /**
  2438. * @access private
  2439. * @param {SCNShadable} obj -
  2440. * @returns {string} -
  2441. */
  2442. _vertexShaderForObject(obj) {
  2443. let txt = obj.program.vertexShader
  2444. if(!txt){
  2445. txt = _SCNDefaultVertexShader
  2446. }
  2447.  
  2448. return this._replaceTexts(txt, obj)
  2449. }
  2450.  
  2451. /**
  2452. * @access private
  2453. * @returns {string} -
  2454. */
  2455. get _defaultFragmentShader() {
  2456. return this._replaceTexts(_SCNDefaultFragmentShader)
  2457. }
  2458.  
  2459. /**
  2460. * @access private
  2461. * @returns {string} -
  2462. */
  2463. get _defaultPBRFragmentShader() {
  2464. return this._replaceTexts(_SCNDefaultPBRFragmentShader)
  2465. }
  2466.  
  2467. /**
  2468. * @access private
  2469. * @param {SCNShadable} obj -
  2470. * @returns {string} -
  2471. */
  2472. _fragmentShaderForObject(obj) {
  2473. let txt = obj.program.fragmentShader
  2474. if(!txt){
  2475. txt = _SCNDefaultFragmentShader
  2476. }
  2477.  
  2478. return this._replaceTexts(txt, obj)
  2479. }
  2480.  
  2481. /**
  2482. * @access private
  2483. * @param {string} text -
  2484. * @param {?SCNShadable} [shadable = null] -
  2485. * @returns {string} -
  2486. */
  2487. _replaceTexts(text, shadable = null) {
  2488. const vars = new Map()
  2489. const numAmbient = this._numLights[SCNLight.LightType.ambient]
  2490. const numDirectional = this._numLights[SCNLight.LightType.directional]
  2491. const numDirectionalShadow = this._numLights.directionalShadow
  2492. const numOmni = this._numLights[SCNLight.LightType.omni]
  2493. const numSpot = this._numLights[SCNLight.LightType.spot]
  2494. const numIES = this._numLights[SCNLight.LightType.IES]
  2495. const numProbe = this._numLights[SCNLight.LightType.probe]
  2496. const shadableHelper = shadable ? shadable._shadableHelper : null
  2497. const customProperties = shadable ? shadable._valuesForUndefinedKeys : {}
  2498. const shaderModifiers = shadable ? shadable.shaderModifiers : null
  2499.  
  2500. vars.set('__NUM_AMBIENT_LIGHTS__', numAmbient)
  2501. vars.set('__NUM_DIRECTIONAL_LIGHTS__', numDirectional)
  2502. vars.set('__NUM_DIRECTIONAL_SHADOW_LIGHTS__', numDirectionalShadow)
  2503. vars.set('__NUM_OMNI_LIGHTS__', numOmni)
  2504. vars.set('__NUM_SPOT_LIGHTS__', numSpot)
  2505. vars.set('__NUM_IES_LIGHTS__', numIES)
  2506. vars.set('__NUM_PROBE_LIGHTS__', numProbe)
  2507.  
  2508. vars.set('__USE_SHADER_MODIFIER_GEOMETRY__', 0)
  2509. vars.set('__SHADER_MODIFIER_GEOMETRY__', '')
  2510. vars.set('__USE_SHADER_MODIFIER_SURFACE__', 0)
  2511. vars.set('__SHADER_MODIFIER_SURFACE__', '')
  2512. vars.set('__USE_SHADER_MODIFIER_FRAGMENT__', 0)
  2513. vars.set('__SHADER_MODIFIER_FRAGMENT__', '')
  2514.  
  2515. let customUniform = ''
  2516. let customSurface = ''
  2517. let customTexcoord = ''
  2518. if(shaderModifiers){
  2519. for(const key of Object.keys(shaderModifiers)){
  2520. const mod = shaderModifiers[key]
  2521. const _texts = mod.split(/^\s*#pragma\s+body\s*$/m)
  2522. if(_texts.length === 1){
  2523. // nothing to do
  2524. }else if(_texts.length === 2){
  2525. customUniform += _texts[0].replace(/^\s*#pragma\s+.*$/mg, '')
  2526. }else{
  2527. throw new Error('found multiple "#pragma body" in the shaderModifier')
  2528. }
  2529. }
  2530. }else{
  2531. for(const key of Object.keys(customProperties)){
  2532. const val = customProperties[key]
  2533. if(typeof val === 'number'){
  2534. customUniform += `uniform float ${key};`
  2535. }else if(_InstanceOf(val, SCNMaterialProperty)){
  2536. customUniform += `uniform sampler2D ${key};`
  2537. customTexcoord += `_surface.${key}Texcoord = v_texcoord${val.mappingChannel};`
  2538. customSurface += `vec2 ${key}Texcoord;`
  2539. }else{
  2540. // TODO: implement for other types
  2541. throw new Error(`custom property for ${key} is not implemented`)
  2542. }
  2543. }
  2544. }
  2545.  
  2546. vars.set('__USER_CUSTOM_UNIFORM__', customUniform)
  2547. vars.set('__USER_CUSTOM_SURFACE__', customSurface)
  2548. vars.set('__USER_CUSTOM_TEXCOORD__', customTexcoord)
  2549.  
  2550. let modifiers = null
  2551. if(shaderModifiers){
  2552. modifiers = shaderModifiers
  2553. }else if(shadableHelper){
  2554. modifiers = shadableHelper._shaderModifiers
  2555. }
  2556.  
  2557. if(modifiers){
  2558. if(modifiers.SCNShaderModifierEntryPointGeometry){
  2559. const _text = this._processShaderText(modifiers.SCNShaderModifierEntryPointGeometry)
  2560. vars.set('__USE_SHADER_MODIFIER_GEOMETRY__', 1)
  2561. vars.set('__SHADER_MODIFIER_GEOMETRY__', _text)
  2562. }
  2563. if(modifiers.SCNShaderModifierEntryPointSurface){
  2564. const _text = this._processShaderText(modifiers.SCNShaderModifierEntryPointSurface)
  2565. vars.set('__USE_SHADER_MODIFIER_SURFACE__', 1)
  2566. vars.set('__SHADER_MODIFIER_SURFACE__', _text)
  2567. }
  2568. if(modifiers.SCNShaderModifierEntryPointFragment){
  2569. const _text = this._processShaderText(modifiers.SCNShaderModifierEntryPointFragment)
  2570. vars.set('__USE_SHADER_MODIFIER_FRAGMENT__', 1)
  2571. vars.set('__SHADER_MODIFIER_FRAGMENT__', _text)
  2572. }
  2573. }
  2574.  
  2575. let vsLighting = ''
  2576. let fsLighting = ''
  2577. if(numDirectionalShadow > 0){
  2578. for(let i=0; i<numDirectionalShadow; i++){
  2579. const fsDSText = _fsDirectionalShadow.replace(new RegExp('__I__', 'g'), i)
  2580. fsLighting += fsDSText
  2581. }
  2582. }
  2583. vars.set('__FS_LIGHTING__', fsLighting)
  2584.  
  2585. let result = text
  2586. vars.forEach((value, key) => {
  2587. const rex = new RegExp(key, 'g')
  2588. result = result.replace(rex, value)
  2589. })
  2590.  
  2591. return result
  2592. }
  2593.  
  2594. _processShaderText(text) {
  2595. let _text = text
  2596.  
  2597. const _texts = text.split(/^\s*#pragma\s+body\s*$/m)
  2598. if(_texts.length == 2){
  2599. _text = _texts[1].replace(/^\s*#pragma\s+.*$/mg, '')
  2600. }
  2601.  
  2602. _text = _text.replace(/texture2D/g, 'texture')
  2603. _text = _text.replace(/float2/g, 'vec2')
  2604. _text = _text.replace(/float3/g, 'vec3')
  2605. _text = _text.replace(/float4/g, 'vec4')
  2606. _text = _text.replace(/scn_frame\.time/g, 'u_time')
  2607. //_text = _text.replace(/#pragma alpha/g, '')
  2608. _text = _text.replace(/half /g, 'float ') // FIXME: check semicolon before half
  2609.  
  2610. _text = _text.replace(/u_modelTransform/g, 'modelTransform') // TODO: use u_modelTransform
  2611. _text = _text.replace(/u_viewTransform/g, 'camera.viewTransform') // TODO: use u_viewTransform
  2612. _text = _text.replace(/u_inverseViewTransform/g, 'camera.inverseViewTransform') // TODO: use u_inverseViewTransform
  2613. _text = _text.replace(/u_viewProjectionTransform/g, 'caemra.viewProjectionTransform') // TODO: use u_viewTransform
  2614. _text = _text.replace(/\s*uniform[^;]*;/g, '')
  2615.  
  2616. // workaround for Badger...
  2617. _text = _text.replace('uvs.x *= 2', 'uvs.x *= 2.0')
  2618. _text = _text.replace('tn * 2 - 1', 'tn * 2.0 - vec3(1)')
  2619. _text = _text.replace('tn2 * 2 - 1', 'tn2 * 2.0 - vec3(1)')
  2620.  
  2621. // workaround for Fox2...
  2622. _text = _text.replace('pow(_surface.ambientOcclusion,3)', 'pow(_surface.ambientOcclusion,3.0)')
  2623. _text = _text.replace('pow(AO,5)', 'pow(AO,5.0)')
  2624. _text = _text.replace('pow(1.-fresnelBasis , 6)', 'pow(1.-fresnelBasis , 6.0)')
  2625. _text = _text.replace('pow(1.-fresnelBasis , 4)', 'pow(1.-fresnelBasis , 4.0)')
  2626. _text = _text.replace('vec3(1,0.4,0.0) * 1;', 'vec3(1,0.4,0.0);')
  2627. _text = _text.replace('vec3(0.6,0.3,0.2) * 1;', 'vec3(0.6,0.3,0.2);')
  2628. _text = _text.replace('vec4 WorldPos', 'vec3 WorldPos')
  2629. _text = _text.replace('mult * 5;', 'mult * 5.0;')
  2630. _text = _text.replace('mask * (1 - feather) + feather / 2', 'mask * (1.0 - feather) + feather / 2.0')
  2631. _text = _text.replace(
  2632. 'vec4 pos = modelTransform * _geometry.position;',
  2633. 'vec4 pos = modelTransform * vec4(_geometry.position, 1);'
  2634. )
  2635. _text = _text.replace('cos((u_time * 0.5 + pos.x) * 2)', 'cos((u_time * 0.5 + pos.x) * 2.0)')
  2636. _text = _text.replace('(WorldPos.x * 10)', '(WorldPos.x * 10.0)')
  2637. _text = _text.replace('(WorldPos.z + WorldPos.x) * 3)', '(WorldPos.z + WorldPos.x) * 3.0)')
  2638. _text = _text.replace('pow(flowmap, 1.0/2.2)', 'pow(flowmap, vec2(1.0/2.2))')
  2639. _text = _text.replace(/\(flowmap\/2\)/g, '(flowmap/2.0)')
  2640.  
  2641. return _text
  2642. }
  2643.  
  2644. _initializeVAO(node, glProgram) {
  2645. const gl = this.context
  2646. const geometry = node.presentation.geometry
  2647. const baseGeometry = node.geometry
  2648.  
  2649. // prepare vertex array data
  2650. const vertexBuffer = geometry._createVertexBuffer(gl, node)
  2651. // TODO: retain attribute locations
  2652. const positionLoc = gl.getAttribLocation(glProgram, 'position')
  2653. const normalLoc = gl.getAttribLocation(glProgram, 'normal')
  2654. const tangentLoc = gl.getAttribLocation(glProgram, 'tangent')
  2655. const colorLoc = gl.getAttribLocation(glProgram, 'color')
  2656. const texcoord0Loc = gl.getAttribLocation(glProgram, 'texcoord0')
  2657. const texcoord1Loc = gl.getAttribLocation(glProgram, 'texcoord1')
  2658. const boneIndicesLoc = gl.getAttribLocation(glProgram, 'boneIndices')
  2659. const boneWeightsLoc = gl.getAttribLocation(glProgram, 'boneWeights')
  2660.  
  2661. geometry._vertexArrayObjects = []
  2662. const elementCount = node.presentation.geometry.geometryElements.length
  2663. for(let i=0; i<elementCount; i++){
  2664. const element = node.presentation.geometry.geometryElements[i]
  2665. const material = node.presentation.geometry.materials[i]
  2666. const vao = gl.createVertexArray()
  2667. gl.bindVertexArray(vao)
  2668.  
  2669. // initialize vertex buffer
  2670. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
  2671.  
  2672. gl.bindAttribLocation(glProgram, positionLoc, 'position')
  2673. gl.bindAttribLocation(glProgram, normalLoc, 'normal')
  2674. gl.bindAttribLocation(glProgram, tangentLoc, 'tangent')
  2675. gl.bindAttribLocation(glProgram, colorLoc, 'color')
  2676. gl.bindAttribLocation(glProgram, texcoord0Loc, 'texcoord0')
  2677. gl.bindAttribLocation(glProgram, texcoord1Loc, 'texcoord1')
  2678. gl.bindAttribLocation(glProgram, boneIndicesLoc, 'boneIndices')
  2679. gl.bindAttribLocation(glProgram, boneWeightsLoc, 'boneWeights')
  2680. // vertexAttribPointer(ulong idx, long size, ulong type, bool norm, long stride, ulong offset)
  2681.  
  2682. // position
  2683. const posSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.vertex)[0]
  2684. if(posSrc){
  2685. gl.enableVertexAttribArray(positionLoc)
  2686. gl.vertexAttribPointer(positionLoc, posSrc.componentsPerVector, gl.FLOAT, false, posSrc.dataStride, posSrc.dataOffset)
  2687. }else{
  2688. gl.disableVertexAttribArray(positionLoc)
  2689. }
  2690.  
  2691. // normal
  2692. const nrmSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.normal)[0]
  2693. if(nrmSrc){
  2694. gl.enableVertexAttribArray(normalLoc)
  2695. gl.vertexAttribPointer(normalLoc, nrmSrc.componentsPerVector, gl.FLOAT, false, nrmSrc.dataStride, nrmSrc.dataOffset)
  2696. }else{
  2697. gl.disableVertexAttribArray(normalLoc)
  2698. }
  2699.  
  2700. // tangent
  2701. const tanSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.tangent)[0]
  2702. if(tanSrc){
  2703. gl.enableVertexAttribArray(tangentLoc)
  2704. gl.vertexAttribPointer(tangentLoc, tanSrc.componentsPerVector, gl.FLOAT, false, tanSrc.dataStride, tanSrc.dataOffset)
  2705. }else{
  2706. gl.disableVertexAttribArray(tangentLoc)
  2707. }
  2708.  
  2709. // color
  2710. const colorSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.color)[0]
  2711. if(colorSrc){
  2712. gl.enableVertexAttribArray(colorLoc)
  2713. gl.vertexAttribPointer(colorLoc, colorSrc.componentsPerVector, gl.FLOAT, false, colorSrc.dataStride, colorSrc.dataOffset)
  2714. }else{
  2715. gl.disableVertexAttribArray(colorLoc)
  2716. }
  2717.  
  2718. // texcoord0
  2719. const tex0Src = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.texcoord)[0]
  2720. if(tex0Src){
  2721. //console.log(`texSrc: ${texcoordLoc}, ${texSrc.componentsPerVector}, ${texSrc.dataStride}, ${texSrc.dataOffset}`)
  2722. gl.enableVertexAttribArray(texcoord0Loc)
  2723. gl.vertexAttribPointer(texcoord0Loc, tex0Src.componentsPerVector, gl.FLOAT, false, tex0Src.dataStride, tex0Src.dataOffset)
  2724. }else{
  2725. gl.disableVertexAttribArray(texcoord0Loc)
  2726. }
  2727.  
  2728. // texcoord1
  2729. const tex1Src = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.texcoord)[1]
  2730. if(tex1Src){
  2731. gl.enableVertexAttribArray(texcoord1Loc)
  2732. gl.vertexAttribPointer(texcoord1Loc, tex1Src.componentsPerVector, gl.FLOAT, false, tex1Src.dataStride, tex1Src.dataOffset)
  2733. }else{
  2734. gl.disableVertexAttribArray(texcoord1Loc)
  2735. }
  2736.  
  2737. // boneIndices
  2738. const indSrc = (node.skinner && node.skinner._useGPU) ? node.skinner._boneIndices : null
  2739. if(indSrc){
  2740. //console.log(`indSrc: ${boneIndicesLoc}, ${indSrc.componentsPerVector}, ${indSrc.dataStride}, ${indSrc.dataOffset}`)
  2741. gl.enableVertexAttribArray(boneIndicesLoc)
  2742. gl.vertexAttribPointer(boneIndicesLoc, indSrc.componentsPerVector, gl.FLOAT, false, indSrc.dataStride, indSrc.dataOffset)
  2743. }else{
  2744. gl.disableVertexAttribArray(boneIndicesLoc)
  2745. }
  2746.  
  2747. // boneWeights
  2748. const wgtSrc = (node.skinner && node.skinner._useGPU) ? node.skinner._boneWeights : null
  2749. if(wgtSrc){
  2750. //console.log(`wgtSrc: ${boneWeightsLoc}, ${wgtSrc.componentsPerVector}, ${wgtSrc.dataStride}, ${wgtSrc.dataOffset}`)
  2751. gl.enableVertexAttribArray(boneWeightsLoc)
  2752. gl.vertexAttribPointer(boneWeightsLoc, wgtSrc.componentsPerVector, gl.FLOAT, false, wgtSrc.dataStride, wgtSrc.dataOffset)
  2753. }else{
  2754. gl.disableVertexAttribArray(boneWeightsLoc)
  2755. }
  2756.  
  2757. // FIXME: use setting
  2758. gl.disable(gl.CULL_FACE)
  2759.  
  2760. // initialize index buffer
  2761. // FIXME: check geometrySource semantic
  2762. const indexBuffer = element._createBuffer(gl)
  2763. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
  2764. geometry._vertexArrayObjects.push(vao)
  2765. }
  2766. }
  2767.  
  2768. _initializeShadowVAO(node, glProgram) {
  2769. const gl = this.context
  2770. const geometry = node.presentation.geometry
  2771. const baseGeometry = node.geometry
  2772.  
  2773. // prepare vertex array data
  2774. const vertexBuffer = geometry._createVertexBuffer(gl, node)
  2775. // TODO: retain attribute locations
  2776. const positionLoc = gl.getAttribLocation(glProgram, 'position')
  2777. const boneIndicesLoc = gl.getAttribLocation(glProgram, 'boneIndices')
  2778. const boneWeightsLoc = gl.getAttribLocation(glProgram, 'boneWeights')
  2779.  
  2780. geometry._shadowVAO = []
  2781. const elementCount = node.presentation.geometry.geometryElements.length
  2782. for(let i=0; i<elementCount; i++){
  2783. const element = node.presentation.geometry.geometryElements[i]
  2784. const material = node.presentation.geometry.materials[i]
  2785. const shadowVAO = gl.createVertexArray()
  2786. gl.bindVertexArray(shadowVAO)
  2787.  
  2788. // initialize vertex buffer
  2789. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
  2790.  
  2791. gl.bindAttribLocation(glProgram, positionLoc, 'position')
  2792. gl.bindAttribLocation(glProgram, boneIndicesLoc, 'boneIndices')
  2793. gl.bindAttribLocation(glProgram, boneWeightsLoc, 'boneWeights')
  2794. // vertexAttribPointer(ulong idx, long size, ulong type, bool norm, long stride, ulong offset)
  2795.  
  2796. // position
  2797. const posSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.vertex)[0]
  2798. if(posSrc){
  2799. gl.enableVertexAttribArray(positionLoc)
  2800. gl.vertexAttribPointer(positionLoc, posSrc.componentsPerVector, gl.FLOAT, false, posSrc.dataStride, posSrc.dataOffset)
  2801. }else{
  2802. gl.disableVertexAttribArray(positionLoc)
  2803. }
  2804.  
  2805. // boneIndices
  2806. const indSrc = (node.skinner && node.skinner._useGPU) ? node.skinner._boneIndices : null
  2807. if(indSrc){
  2808. gl.enableVertexAttribArray(boneIndicesLoc)
  2809. gl.vertexAttribPointer(boneIndicesLoc, indSrc.componentsPerVector, gl.FLOAT, false, indSrc.dataStride, indSrc.dataOffset)
  2810. }else{
  2811. gl.disableVertexAttribArray(boneIndicesLoc)
  2812. }
  2813.  
  2814. // boneWeights
  2815. const wgtSrc = (node.skinner && node.skinner._useGPU) ? node.skinner._boneWeights : null
  2816. if(wgtSrc){
  2817. gl.enableVertexAttribArray(boneWeightsLoc)
  2818. gl.vertexAttribPointer(boneWeightsLoc, wgtSrc.componentsPerVector, gl.FLOAT, false, wgtSrc.dataStride, wgtSrc.dataOffset)
  2819. }else{
  2820. gl.disableVertexAttribArray(boneWeightsLoc)
  2821. }
  2822.  
  2823. // FIXME: use setting
  2824. gl.disable(gl.CULL_FACE)
  2825.  
  2826. // initialize index buffer
  2827. // FIXME: check geometrySource semantic
  2828. const indexBuffer = element._createBuffer(gl)
  2829. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
  2830. geometry._shadowVAO.push(shadowVAO)
  2831. }
  2832. }
  2833.  
  2834. _initializeHitTestVAO(node, glProgram, physics = false) {
  2835. const gl = this.context
  2836. const geometry = physics ? node.physicsBody.physicsShape._sourceGeometry : node.presentation.geometry
  2837. const baseGeometry = physics ? geometry : node.geometry
  2838.  
  2839. // TODO: retain attribute locations
  2840. const positionLoc = gl.getAttribLocation(glProgram, 'position')
  2841. const normalLoc = gl.getAttribLocation(glProgram, 'normal')
  2842. const boneIndicesLoc = gl.getAttribLocation(glProgram, 'boneIndices')
  2843. const boneWeightsLoc = gl.getAttribLocation(glProgram, 'boneWeights')
  2844.  
  2845. geometry._hitTestVAO = []
  2846. const elementCount = geometry.geometryElements.length
  2847. for(let i=0; i<elementCount; i++){
  2848. const element = geometry.geometryElements[i]
  2849. const vao = gl.createVertexArray()
  2850. gl.bindVertexArray(vao)
  2851.  
  2852. gl.bindBuffer(gl.ARRAY_BUFFER, geometry._vertexBuffer)
  2853.  
  2854. gl.bindAttribLocation(glProgram, positionLoc, 'position')
  2855. gl.bindAttribLocation(glProgram, normalLoc, 'normal')
  2856. gl.bindAttribLocation(glProgram, boneIndicesLoc, 'boneIndices')
  2857. gl.bindAttribLocation(glProgram, boneWeightsLoc, 'boneWeights')
  2858. // vertexAttribPointer(ulong idx, long size, ulong type, bool norm, long stride, ulong offset)
  2859.  
  2860. // position
  2861. const posSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.vertex)[0]
  2862. if(posSrc){
  2863. gl.enableVertexAttribArray(positionLoc)
  2864. gl.vertexAttribPointer(positionLoc, posSrc.componentsPerVector, gl.FLOAT, false, posSrc.dataStride, posSrc.dataOffset)
  2865. }else{
  2866. gl.disableVertexAttribArray(positionLoc)
  2867. }
  2868.  
  2869. // normal
  2870. const nrmSrc = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.normal)[0]
  2871. if(nrmSrc){
  2872. gl.enableVertexAttribArray(normalLoc)
  2873. gl.vertexAttribPointer(normalLoc, nrmSrc.componentsPerVector, gl.FLOAT, false, nrmSrc.dataStride, nrmSrc.dataOffset)
  2874. }else{
  2875. gl.disableVertexAttribArray(normalLoc)
  2876. }
  2877.  
  2878. // boneIndices
  2879. const indSrc = node.skinner ? node.skinner._boneIndices : null
  2880. if(indSrc){
  2881. gl.enableVertexAttribArray(boneIndicesLoc)
  2882. gl.vertexAttribPointer(boneIndicesLoc, indSrc.componentsPerVector, gl.FLOAT, false, indSrc.dataStride, indSrc.dataOffset)
  2883. }else{
  2884. gl.disableVertexAttribArray(boneIndicesLoc)
  2885. }
  2886.  
  2887. // boneWeights
  2888. const wgtSrc = node.skinner ? node.skinner._boneWeights : null
  2889. if(wgtSrc){
  2890. gl.enableVertexAttribArray(boneWeightsLoc)
  2891. gl.vertexAttribPointer(boneWeightsLoc, wgtSrc.componentsPerVector, gl.FLOAT, false, wgtSrc.dataStride, wgtSrc.dataOffset)
  2892. }else{
  2893. gl.disableVertexAttribArray(boneWeightsLoc)
  2894. }
  2895.  
  2896. // initialize index buffer
  2897. // FIXME: check geometrySource semantic
  2898. const indexBuffer = element._createBuffer(gl)
  2899. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, element._buffer)
  2900. geometry._hitTestVAO.push(vao)
  2901. }
  2902. }
  2903.  
  2904. _initializeCameraBuffer(glProgram) {
  2905. const gl = this.context
  2906. const cameraIndex = gl.getUniformBlockIndex(glProgram, 'cameraUniform')
  2907.  
  2908. this._cameraBuffer = gl.createBuffer()
  2909. gl.uniformBlockBinding(glProgram, cameraIndex, _cameraLoc)
  2910. gl.bindBufferBase(gl.UNIFORM_BUFFER, _cameraLoc, this._cameraBuffer)
  2911.  
  2912. }
  2913.  
  2914. _initializeFogBuffer(glProgram) {
  2915. const gl = this.context
  2916. const fogIndex = gl.getUniformBlockIndex(glProgram, 'fogUniform')
  2917.  
  2918. this._fogBuffer = gl.createBuffer()
  2919. gl.uniformBlockBinding(glProgram, fogIndex, _fogLoc)
  2920. gl.bindBufferBase(gl.UNIFORM_BUFFER, _fogLoc, this._fogBuffer)
  2921. }
  2922.  
  2923. _initializeLightBuffer(glProgram) {
  2924. const gl = this.context
  2925. // TODO: replace lightUniform to SCNLightsUniform
  2926. const lightIndex = gl.getUniformBlockIndex(glProgram, 'lightUniform')
  2927. this._lightBuffer = gl.createBuffer()
  2928. gl.uniformBlockBinding(glProgram, lightIndex, _lightLoc)
  2929. gl.bindBufferBase(gl.UNIFORM_BUFFER, _lightLoc, this._lightBuffer)
  2930.  
  2931. const scnLightsIndex = gl.getUniformBlockIndex(glProgram, 'SCNLightsUniform')
  2932. this._scnLightsBuffer = gl.createBuffer()
  2933. gl.uniformBlockBinding(glProgram, scnLightsIndex, _scnLightsLoc)
  2934. gl.bindBufferBase(gl.UNIFORM_BUFFER, _scnLightsLoc, this._scnLightsBuffer)
  2935.  
  2936. for(let i=0; i<this._lightNodes.directionalShadow.length; i++){
  2937. const node = this._lightNodes.directionalShadow[i]
  2938. const symbol = `TEXTURE${i+_shadowTextureBaseIndex}`
  2939. const name = `u_shadowTexture${i}`
  2940.  
  2941. gl.uniform1i(gl.getUniformLocation(glProgram, name), i+_shadowTextureBaseIndex)
  2942. gl.activeTexture(gl[symbol])
  2943. gl.bindTexture(gl.TEXTURE_2D, node.presentation.light._shadowDepthTexture)
  2944. }
  2945. }
  2946.  
  2947. _initializeUBO(node, glProgram) {
  2948. const gl = this.context
  2949. const geometry = node.presentation.geometry
  2950.  
  2951. const materialIndex = gl.getUniformBlockIndex(glProgram, 'materialUniform')
  2952. geometry._materialBuffer = gl.createBuffer()
  2953. gl.uniformBlockBinding(glProgram, materialIndex, _materialLoc)
  2954. gl.bindBufferBase(gl.UNIFORM_BUFFER, _materialLoc, geometry._materialBuffer)
  2955. }
  2956.  
  2957. _updateVAO(node) {
  2958. const gl = this.context
  2959. const geometry = node.presentation.geometry
  2960. const baseGeometry = node.geometry
  2961.  
  2962. geometry._updateVertexBuffer(gl, baseGeometry)
  2963. }
  2964.  
  2965. get _dummyTexture() {
  2966. return this.__dummyTexture
  2967. }
  2968.  
  2969. _createDummyTexture() {
  2970. const gl = this.context
  2971.  
  2972. const canvas = document.createElement('canvas')
  2973. canvas.width = 1
  2974. canvas.height = 1
  2975. const context = canvas.getContext('2d')
  2976. context.fillStyle = 'rgba(255, 255, 255, 1.0)'
  2977. context.fillRect(0, 0, 1, 1)
  2978.  
  2979. this.__dummyTexture = gl.createTexture()
  2980.  
  2981. gl.bindTexture(gl.TEXTURE_2D, this.__dummyTexture)
  2982. // texImage2D(target, level, internalformat, width, height, border, format, type, source)
  2983. // Safari complains that 'source' is not ArrayBufferView type, but WebGL2 should accept HTMLCanvasElement.
  2984. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
  2985. gl.bindTexture(gl.TEXTURE_2D, null)
  2986. }
  2987.  
  2988. /**
  2989. * @access private
  2990. * @param {SCNProgram} program -
  2991. * @returns {void}
  2992. */
  2993. //_setDummyTextureAsDefault(program) {
  2994. // const gl = this.context
  2995. // const p = program
  2996.  
  2997. // const texNames = [
  2998. // gl.TEXTURE0,
  2999. // gl.TEXTURE1,
  3000. // gl.TEXTURE2,
  3001. // gl.TEXTURE3,
  3002. // gl.TEXTURE4,
  3003. // gl.TEXTURE5,
  3004. // gl.TEXTURE6,
  3005. // gl.TEXTURE7
  3006. // ]
  3007. // const texSymbols = [
  3008. // 'u_emissionTexture',
  3009. // 'u_ambientTexture',
  3010. // 'u_diffuseTexture',
  3011. // 'u_specularTexture',
  3012. // 'u_reflectiveTexture',
  3013. // 'u_transparentTexture',
  3014. // 'u_multiplyTexture',
  3015. // 'u_normalTexture'
  3016. // ]
  3017. // for(let i=0; i<texNames.length; i++){
  3018. // const texName = texNames[i]
  3019. // const symbol = texSymbols[i]
  3020. // gl.uniform1i(gl.getUniformLocation(p._glProgram, symbol), i)
  3021. // gl.activeTexture(texName)
  3022. // gl.bindTexture(gl.TEXTURE_2D, this.__dummyTexture)
  3023. // }
  3024. //}
  3025.  
  3026. _switchToDefaultCamera() {
  3027. if(this._pointOfView === null){
  3028. this._defaultCameraPosNode.position = new SCNVector3(0, 0, 0)
  3029. this._defaultCameraRotNode.rotation = new SCNVector4(0, 0, 0, 0)
  3030. this._defaultCameraNode.position = new SCNVector3(0, 0, _defaultCameraDistance)
  3031. }else if(this._pointOfView !== this._defaultCameraNode){
  3032. const rot = this.pointOfView.presentation._worldRotation
  3033. const rotMat = SCNMatrix4.matrixWithRotation(rot)
  3034. const pos = this.pointOfView.presentation._worldTranslation
  3035.  
  3036. this._defaultCameraPosNode.position = (new SCNVector3(0, 0, -_defaultCameraDistance)).rotate(rotMat).add(pos)
  3037. this._defaultCameraRotNode.rotation = rot
  3038. this._defaultCameraNode.position = new SCNVector3(0, 0, _defaultCameraDistance)
  3039. //console.log(`pov defined: pov.pos: ${this._pointOfView._worldTranslation.float32Array()}`)
  3040. //console.log(`pov defined: node.pos: ${this._defaultCameraNode._worldTranslation.float32Array()}`)
  3041. }
  3042. this._pointOfView = this._defaultCameraNode
  3043. }
  3044.  
  3045. _setDefaultCameraOrientation(orientation) {
  3046. this._defaultCameraRotNode.orientation = orientation
  3047. }
  3048.  
  3049. _searchCameraNode() {
  3050. const nodes = [this.scene._rootNode]
  3051. let node = nodes.shift()
  3052. while(node){
  3053. if(node.camera !== null){
  3054. return node
  3055. }
  3056. nodes.push(...node._childNodes)
  3057. node = nodes.shift()
  3058. }
  3059. return null
  3060. }
  3061.  
  3062. /**
  3063. * @access private
  3064. * @returns {SCNVector3} -
  3065. */
  3066. _getCameraPosition() {
  3067. if(this._pointOfView === this._defaultCameraNode){
  3068. return this._defaultCameraPosNode.position
  3069. }else if(this._pointOfView === null){
  3070. return new SCNVector3(0, 0, 0)
  3071. }
  3072. const rot = this._getCameraOrientation()
  3073. const rotMat = SCNMatrix4.matrixWithRotation(rot)
  3074. const pos = this._pointOfView.presentation._worldTranslation
  3075. return pos.add((new SCNVector3(0, 0, -_defaultCameraDistance)).rotate(rotMat))
  3076. }
  3077.  
  3078. /**
  3079. * @access private
  3080. * @returns {SCNVector4} -
  3081. */
  3082. _getCameraOrientation() {
  3083. if(this._pointOfView === this._defaultCameraNode){
  3084. return this._defaultCameraRotNode.presentation.orientation
  3085. }else if(this._pointOfView === null){
  3086. return new SCNVector4(0, 0, 0, 0)
  3087. }
  3088. return this._pointOfView.presentation._worldOrientation
  3089. }
  3090.  
  3091. /**
  3092. * @access private
  3093. * @returns {number} -
  3094. */
  3095. _getCameraDistance() {
  3096. if(this._pointOfView === this._defaultCameraNode){
  3097. return this._defaultCameraNode.presentation.position.z
  3098. }
  3099. return _defaultCameraDistance
  3100. }
  3101.  
  3102. /**
  3103. * @access private
  3104. * @returns {boolean} - true if the number of lights is changed.
  3105. */
  3106. _numLightsChanged() {
  3107. let changed = false
  3108. const types = [...Object.values(SCNLight.LightType), 'directionalShadow']
  3109. for(const type of types){
  3110. const num = this._lightNodes[type].length
  3111. if(num !== this._numLights[type]){
  3112. changed = true
  3113. this._numLights[type] = num
  3114. }
  3115. }
  3116. return changed
  3117. }
  3118.  
  3119. /**
  3120. * @access private
  3121. * @param {SCNNode} node -
  3122. * @param {SCNVector3} rayPoint -
  3123. * @param {SCNVector3} rayVec -
  3124. * @returns {SCNHitTestResult[]} -
  3125. */
  3126. _nodeHitTestByCPU(node, rayPoint, rayVec) {
  3127. const result = []
  3128. const geometry = node.presentation.geometry
  3129. const geometryCount = geometry.geometryElements.length
  3130. if(geometryCount === 0){
  3131. // nothing to draw...
  3132. return result
  3133. }
  3134. const invRay = rayVec.mul(-1)
  3135.  
  3136. //console.log(`rayPoint: ${rayPoint.float32Array()}`)
  3137. //console.log(`rayVec: ${rayVec.float32Array()}`)
  3138.  
  3139. //if(node.morpher !== null){
  3140. // this._updateVAO(node)
  3141. //}
  3142.  
  3143. // TODO: test the bounding box/sphere first for performance
  3144.  
  3145. const source = geometry.getGeometrySourcesForSemantic(SCNGeometrySource.Semantic.vertex)[0]
  3146. const sourceLen = source.vectorCount
  3147. const sourceData = []
  3148. const modelTransform = node.presentation._worldTransform
  3149. const skinningJoints = []
  3150. if(node.presentation.skinner){
  3151. const skinner = node.presentation.skinner
  3152. const numBones = skinner._bones.length
  3153. for(let i=0; i<numBones; i++){
  3154. const bone = skinner._bones[i]
  3155. const mat = skinner._boneInverseBindTransforms[i].mult(bone._presentation._worldTransform)
  3156. skinningJoints.push(mat)
  3157. }
  3158. for(let i=0; i<sourceLen; i++){
  3159. const weights = skinner._boneWeights._vectorAt(i)
  3160. const indices = skinner._boneIndices._vectorAt(i)
  3161. const mat = new SCNMatrix4()
  3162. for(let j=0; j<skinner.numSkinningJoints; j++){
  3163. mat.add(skinningJoints[indices[j]].mul(weights[j]))
  3164. }
  3165. sourceData.push(source._scnVectorAt(i).transform(mat))
  3166. }
  3167. }else{
  3168. for(let i=0; i<sourceLen; i++){
  3169. sourceData.push(source._scnVectorAt(i).transform(modelTransform))
  3170. }
  3171. }
  3172.  
  3173. for(let i=0; i<geometryCount; i++){
  3174. //console.log(`geometry element ${i}`)
  3175. const element = geometry.geometryElements[i]
  3176. switch(element.primitiveType){
  3177. case SCNGeometryPrimitiveType.line:
  3178. console.warn('hitTest for line is not implemented')
  3179. continue
  3180. case SCNGeometryPrimitiveType.point:
  3181. console.warn('hitTest for point is not implemented')
  3182. continue
  3183. }
  3184.  
  3185. const elementData = element._glData
  3186. const len = element.primitiveCount
  3187. //console.log(`primitiveCount: ${len}`)
  3188. // TODO: check cull settings
  3189. for(let pi=0; pi<len; pi++){
  3190. const indices = element._indexAt(pi)
  3191.  
  3192. const v0 = sourceData[indices[0]]
  3193. const v1 = sourceData[indices[1]]
  3194. const v2 = sourceData[indices[2]]
  3195.  
  3196. const e1 = v1.sub(v0)
  3197. const e2 = v2.sub(v0)
  3198.  
  3199. let denom = this._det(e1, e2, invRay)
  3200. if(denom <= 0){
  3201. continue
  3202. }
  3203. denom = 1.0 / denom
  3204.  
  3205. const d = rayPoint.sub(v0)
  3206. const u = this._det(d, e2, invRay) * denom
  3207. if(u < 0 || u > 1){
  3208. continue
  3209. }
  3210.  
  3211. const v = this._det(e1, d, invRay) * denom
  3212. if(v < 0 || v > 1){
  3213. continue
  3214. }
  3215.  
  3216. const t = this._det(e1, e2, d) * denom
  3217. if(t < 0){
  3218. continue
  3219. }
  3220.  
  3221. // Hit!
  3222. //console.log(`Hit! ${i}: ${pi}`)
  3223. const hitPoint = rayPoint.add(rayVec.mul(t))
  3224. const invModel = modelTransform.invert()
  3225.  
  3226. const res = new SCNHitTestResult()
  3227. res._node = node
  3228. res._geometryIndex = i
  3229. res._faceIndex = pi
  3230. res._worldCoordinates = hitPoint
  3231. res._localCoordinates = hitPoint.transform(invModel)
  3232. const nom = e1.cross(e2)
  3233. res._worldNormal = nom.normalize()
  3234. res._localNormal = nom.transform(invModel)
  3235. res._modelTransform = modelTransform
  3236. res._boneNode = null // it should be array... what should I put here?
  3237. result.push(res)
  3238. }
  3239. }
  3240.  
  3241. return result
  3242. }
  3243.  
  3244. /**
  3245. * @access private
  3246. * @type {SCNProgram}
  3247. */
  3248. get _defaultParticleProgram() {
  3249. if(this.__defaultParticleProgram !== null){
  3250. return this.__defaultParticleProgram
  3251. }
  3252. const gl = this.context
  3253. if(this.__defaultParticleProgram === null){
  3254. this.__defaultParticleProgram = new SCNProgram()
  3255. }
  3256. const p = this.__defaultParticleProgram
  3257. const glProgram = p._getGLProgramForContext(gl)
  3258. const vsText = _SCNDefaultParticleVertexShader
  3259. const fsText = _SCNDefaultParticleFragmentShader
  3260.  
  3261. // initialize vertex shader
  3262. const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  3263. gl.shaderSource(vertexShader, vsText)
  3264. gl.compileShader(vertexShader)
  3265. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
  3266. const info = gl.getShaderInfoLog(vertexShader)
  3267. throw new Error(`particle vertex shader compile error: ${info}`)
  3268. }
  3269.  
  3270. // initialize fragment shader
  3271. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
  3272. gl.shaderSource(fragmentShader, fsText)
  3273. gl.compileShader(fragmentShader)
  3274. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
  3275. const info = gl.getShaderInfoLog(fragmentShader)
  3276. throw new Error(`particle fragment shader compile error: ${info}`)
  3277. }
  3278.  
  3279. gl.attachShader(glProgram, vertexShader)
  3280. gl.attachShader(glProgram, fragmentShader)
  3281.  
  3282. // link program object
  3283. gl.linkProgram(glProgram)
  3284. if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)){
  3285. const info = gl.getProgramInfoLog(glProgram)
  3286. throw new Error(`program link error: ${info}`)
  3287. }
  3288.  
  3289. //gl.useProgram(glProgram)
  3290. this._useProgram(p)
  3291. //gl.clearColor(1, 1, 1, 1)
  3292. //gl.clearDepth(1.0)
  3293. //gl.clearStencil(0)
  3294.  
  3295. gl.enable(gl.DEPTH_TEST)
  3296. gl.depthFunc(gl.LEQUAL)
  3297. gl.enable(gl.BLEND)
  3298. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  3299. gl.enable(gl.CULL_FACE)
  3300. gl.cullFace(gl.BACK)
  3301.  
  3302. // set default textures to prevent warnings
  3303. this._setDummyParticleTextureAsDefault()
  3304. return this.__defaultParticleProgram
  3305. }
  3306.  
  3307. /**
  3308. * @access private
  3309. * @type {SCNProgram}
  3310. */
  3311. get _defaultShadowProgram() {
  3312. if(this.__defaultShadowProgram !== null){
  3313. return this.__defaultShadowProgram
  3314. }
  3315. const gl = this.context
  3316. if(this.__defaultShadowProgram === null){
  3317. this.__defaultShadowProgram = new SCNProgram()
  3318. }
  3319. const p = this.__defaultShadowProgram
  3320. const glProgram = p._getGLProgramForContext(gl)
  3321. const vsText = _SCNDefaultShadowVertexShader
  3322. const fsText = _SCNDefaultShadowFragmentShader
  3323.  
  3324. // initialize vertex shader
  3325. const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  3326. gl.shaderSource(vertexShader, vsText)
  3327. gl.compileShader(vertexShader)
  3328. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
  3329. const info = gl.getShaderInfoLog(vertexShader)
  3330. throw new Error(`particle vertex shader compile error: ${info}`)
  3331. }
  3332.  
  3333. // initialize fragment shader
  3334. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
  3335. gl.shaderSource(fragmentShader, fsText)
  3336. gl.compileShader(fragmentShader)
  3337. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
  3338. const info = gl.getShaderInfoLog(fragmentShader)
  3339. throw new Error(`particle fragment shader compile error: ${info}`)
  3340. }
  3341.  
  3342. gl.attachShader(glProgram, vertexShader)
  3343. gl.attachShader(glProgram, fragmentShader)
  3344.  
  3345. // link program object
  3346. gl.linkProgram(glProgram)
  3347. if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)){
  3348. const info = gl.getProgramInfoLog(glProgram)
  3349. throw new Error(`program link error: ${info}`)
  3350. }
  3351.  
  3352. //gl.useProgram(glProgram)
  3353. this._useProgram(p)
  3354. //gl.clearColor(1, 1, 1, 1)
  3355. //gl.clearDepth(1.0)
  3356. //gl.clearStencil(0)
  3357.  
  3358. gl.enable(gl.DEPTH_TEST)
  3359. gl.depthFunc(gl.LEQUAL)
  3360. //gl.enable(gl.BLEND)
  3361. gl.disable(gl.BLEND)
  3362. //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  3363. gl.enable(gl.CULL_FACE)
  3364. gl.cullFace(gl.BACK)
  3365.  
  3366. return this.__defaultShadowProgram
  3367. }
  3368.  
  3369. _setDummyParticleTextureAsDefault() {
  3370. const gl = this.context
  3371. const p = this._defaultParticleProgram
  3372. const glProgram = p._getGLProgramForContext(gl)
  3373.  
  3374. const texNames = [
  3375. gl.TEXTURE0
  3376. //gl.TEXTURE1
  3377. ]
  3378. const texSymbols = [
  3379. 'particleTexture'
  3380. //'colorTexture'
  3381. ]
  3382. for(let i=0; i<texNames.length; i++){
  3383. const texName = texNames[i]
  3384. const symbol = texSymbols[i]
  3385. gl.uniform1i(gl.getUniformLocation(glProgram, symbol), i)
  3386. gl.activeTexture(texName)
  3387. gl.bindTexture(gl.TEXTURE_2D, this.__dummyTexture)
  3388. }
  3389. }
  3390.  
  3391. /**
  3392. * @access private
  3393. * @type {SCNProgram}
  3394. */
  3395. get _defaultHitTestProgram() {
  3396. if(this.__defaultHitTestProgram !== null){
  3397. return this.__defaultHitTestProgram
  3398. }
  3399. const gl = this.context
  3400. if(this.__defaultHitTestProgram === null){
  3401. this.__defaultHitTestProgram = new SCNProgram()
  3402. }
  3403. const p = this.__defaultHitTestProgram
  3404. const glProgram = p._getGLProgramForContext(gl)
  3405. const vsText = _SCNDefaultHitTestVertexShader
  3406. const fsText = _SCNDefaultHitTestFragmentShader
  3407.  
  3408. // initialize vertex shader
  3409. const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  3410. gl.shaderSource(vertexShader, vsText)
  3411. gl.compileShader(vertexShader)
  3412. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
  3413. const info = gl.getShaderInfoLog(vertexShader)
  3414. throw new Error(`hitTest vertex shader compile error: ${info}`)
  3415. }
  3416.  
  3417. // initialize fragment shader
  3418. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
  3419. gl.shaderSource(fragmentShader, fsText)
  3420. gl.compileShader(fragmentShader)
  3421. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
  3422. const info = gl.getShaderInfoLog(fragmentShader)
  3423. throw new Error(`hitTest fragment shader compile error: ${info}`)
  3424. }
  3425.  
  3426. gl.attachShader(glProgram, vertexShader)
  3427. gl.attachShader(glProgram, fragmentShader)
  3428.  
  3429. // link program object
  3430. gl.linkProgram(glProgram)
  3431. if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)){
  3432. const info = gl.getProgramInfoLog(glProgram)
  3433. throw new Error(`program link error: ${info}`)
  3434. }
  3435.  
  3436. //gl.useProgram(glProgram)
  3437. this._useProgram(p)
  3438. //gl.clearColor(1, 1, 1, 1)
  3439. //gl.clearDepth(1.0)
  3440. //gl.clearStencil(0)
  3441.  
  3442. gl.enable(gl.DEPTH_TEST)
  3443. gl.depthFunc(gl.LEQUAL)
  3444. gl.enable(gl.BLEND)
  3445. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  3446. gl.enable(gl.CULL_FACE)
  3447. gl.cullFace(gl.BACK)
  3448.  
  3449. //this._setDummyHitTestTextureAsDefault()
  3450. return this.__defaultHitTestProgram
  3451. }
  3452.  
  3453. // for debug
  3454. _showShadowMapOfLight(lightNode) {
  3455. const gl = this.context
  3456. const p = lightNode.presentation
  3457. const light = p.light
  3458. if(!this.__debugShadowMapSprite){
  3459. const node = new SKSpriteNode()
  3460. node.size = new CGSize(100, 100)
  3461. node.anchorPoint = new CGPoint(0.0, 0.0)
  3462. const texture = new SKTexture()
  3463. texture._glTexture = light._shadowDepthTexture
  3464. texture._image = {
  3465. naturalWidth: 100,
  3466. naturalHeight: 100
  3467. }
  3468. node._texture = texture
  3469. node.__presentation = node.copy()
  3470. node.__presentation._isPresentationInstance = true
  3471. node.position = new CGPoint(100, 100)
  3472. node._updateWorldTransform()
  3473.  
  3474. this.__debugShadowMapSprite = node
  3475. }
  3476. gl.clearDepth(-1)
  3477. gl.clearStencil(0)
  3478. gl.depthMask(true)
  3479. gl.enable(gl.DEPTH_TEST)
  3480. gl.disable(gl.CULL_FACE)
  3481. gl.depthFunc(gl.GEQUAL)
  3482. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  3483. gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
  3484.  
  3485. this._renderSKNode(this.__debugShadowMapSprite)
  3486. }
  3487.  
  3488. _setViewPort(width = null, height = null) {
  3489. let w = width
  3490. let h = height
  3491. if(w === null || h === null){
  3492. w = this._viewRect.size.width
  3493. h = this._viewRect.size.height
  3494. }
  3495. this.context.viewport(0, 0, w, h)
  3496. }
  3497.  
  3498. /**
  3499. * calculate a determinant of 3x3 matrix from 3 vectors.
  3500. * @access private
  3501. * @param {SCNVector3} v1 -
  3502. * @param {SCNVector3} v2 -
  3503. * @param {SCNVector3} v3 -
  3504. * @returns {number} -
  3505. */
  3506. _det(v1, v2, v3) {
  3507. return (
  3508. v1.x * v2.y * v3.z
  3509. + v1.y * v2.z * v3.x
  3510. + v1.z * v2.x * v3.y
  3511. - v1.x * v2.z * v3.y
  3512. - v1.y * v2.x * v3.z
  3513. - v1.z * v2.y * v3.x
  3514. )
  3515. }
  3516. }
  3517.