@recast-navigation/three 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,7 @@ import * as RecastNavigationThree from '@recast-navigation/three';
26
26
 
27
27
  ### `threeToNavMesh`
28
28
 
29
- As a convenience, this package exports a function for converting all descendants within three.js `Object3D` to a `NavMesh`:
29
+ As a convenience, this package exports a function for converting an array of `Mesh` objects to a `NavMesh`:
30
30
 
31
31
  ```ts
32
32
  import * as THREE from 'three';
@@ -38,13 +38,21 @@ const scene = new THREE.Scene();
38
38
  /* add some objects to the scene */
39
39
  // ...
40
40
 
41
- const navMesh: NavMesh = threeToNavMesh(scene, { /* NavMesh config */ }});
41
+ const meshes: Mesh[] = [];
42
+
43
+ scene.traverse((child) => {
44
+ if (child instanceof Mesh) {
45
+ meshes.push(child);
46
+ }
47
+ });
48
+
49
+ const navMesh: NavMesh = threeToNavMesh(meshes, { /* NavMesh config */ }});
42
50
  ```
43
51
 
44
52
  You can also use `threeToNavMeshArgs` to get the arguments for `NavMesh.build`:
45
53
 
46
54
  ```ts
47
- const [positions, indices] = threeToNavMeshArgs(scene);
55
+ const [positions, indices] = threeToNavMeshArgs(meshes);
48
56
  ```
49
57
 
50
58
  ### `NavMeshHelper`
package/dist/index.es.js CHANGED
@@ -1,2 +1,2 @@
1
- import{MeshBasicMaterial as e,Group as t,Vector3 as s,CapsuleGeometry as o,Mesh as a,BufferGeometry as n,BufferAttribute as r,BoxGeometry as i,CylinderGeometry as h}from"three";import{NavMesh as c}from"@recast-navigation/core";class l{agents;agentMeshes=new Map;recastCrowd;crowdMaterial;constructor({crowd:s,crowdMaterial:o}){this.recastCrowd=s,this.crowdMaterial=o??new e({color:"blue",wireframe:!0}),this.agents=new t,this.updateAgents(),s.onPostUpdate.add((()=>{this.updateAgents()}))}updateAgents(){const e=this.recastCrowd.getAgents(),t=new Set(this.agentMeshes.keys());for(const o of e){t.delete(o);const e=this.recastCrowd.getAgentPosition(o),a=this.recastCrowd.getAgentVelocity(o),n=this.recastCrowd.getAgentParameters(o);let r=this.agentMeshes.get(o);void 0===r&&(r=this.createAgentMesh(n),this.agents.add(r),this.agentMeshes.set(o,r)),r.position.set(e.x,e.y+n.height/2,e.z),r.lookAt((new s).copy(r.position).add(a))}for(const e of t){const t=this.agentMeshes.get(e);t&&(this.agents.remove(t),this.agentMeshes.delete(e))}}createAgentMesh(e){const t=new o(e.radius,e.height-2*e.radius);return new a(t,this.crowdMaterial)}}class d{navMesh;obstacles;obstacleMeshes=new Map;navMeshMaterial;obstaclesMaterial;recastNavMesh;constructor({navMesh:s,navMeshMaterial:o,obstaclesMaterial:r}){this.recastNavMesh=s,this.navMeshMaterial=o||new e({color:"blue",wireframe:!0}),this.obstaclesMaterial=r||new e({color:"red",wireframe:!0}),this.navMesh=new a(new n,this.navMeshMaterial),this.obstacles=new t,this.updateNavMesh(),this.updateObstacles(),s.onNavMeshChange.add((()=>{this.updateNavMesh()})),s.onObstacleChange.add((()=>{this.updateObstacles()}))}updateNavMesh(){const{positions:e,indices:t}=this.recastNavMesh.getDebugNavMesh();for(let e=0;e<t.length;e+=3){const s=t[e];t[e]=t[e+2],t[e+2]=s}const s=new n;s.setAttribute("position",new r(new Float32Array(e),3)),s.setIndex(new r(new Uint16Array(t),1)),s.computeVertexNormals(),this.navMesh.geometry=s}updateObstacles(){const e=new Set(this.obstacleMeshes.keys());for(const[t,s]of this.recastNavMesh.obstacles){let o=this.obstacleMeshes.get(t);if(e.delete(t),!o){const{position:e}=s,o=new a(void 0,this.obstaclesMaterial);if(o.position.copy(e),"box"===s.type){const{extent:e,angle:t}=s;o.geometry=new i(2*e.x,2*e.y,2*e.z),o.rotation.y=t}else{if("cylinder"!==s.type)throw new Error(`Unknown obstacle type: ${s}`);{const{radius:e,height:t}=s;o.geometry=new h(e,e,t,16),o.position.y+=t/2}}this.obstacles.add(o),this.obstacleMeshes.set(t,o)}}for(const t of e){const e=this.obstacleMeshes.get(t);e&&(this.obstacles.remove(e),this.obstacleMeshes.delete(t))}}}const g=e=>{const t=[];let o,n,r;e.traverse((e=>{e instanceof a&&t.push(e)}));const i=[],h=[];let c=0;for(o=0;o<t.length;o++)if(t[o]){const e=t[o],a=e.geometry.getIndex()?.array;if(!a)continue;const l=e.geometry.getAttribute("position").array;if(!l)continue;for(n=0;n<a.length;n++)i.push(a[n]+c);const d=new s;for(r=0;r<l.length;r+=3)d.set(l[r],l[r+1],l[r+2]),e.localToWorld(d),h.push(d.x,d.y,d.z);c+=l.length/3}return[h,i]},M=(e,t={})=>{const[s,o]=g(e),a=new c;return a.build(s,o,t),a};export{l as CrowdHelper,d as NavMeshHelper,M as threeToNavMesh,g as threeToNavMeshArgs};
1
+ import{MeshBasicMaterial as e,Group as t,Vector3 as s,CapsuleGeometry as o,Mesh as a,BufferGeometry as n,BufferAttribute as r,BoxGeometry as i,CylinderGeometry as h}from"three";import{NavMesh as c}from"@recast-navigation/core";class l{agents;agentMeshes=new Map;recastCrowd;crowdMaterial;constructor({crowd:s,crowdMaterial:o}){this.recastCrowd=s,this.crowdMaterial=o??new e({color:"blue",wireframe:!0}),this.agents=new t,this.updateAgents(),s.onPostUpdate.add((()=>{this.updateAgents()}))}updateAgents(){const e=this.recastCrowd.getAgents(),t=new Set(this.agentMeshes.keys());for(const o of e){t.delete(o);const e=this.recastCrowd.getAgentPosition(o),a=this.recastCrowd.getAgentVelocity(o),n=this.recastCrowd.getAgentParameters(o);let r=this.agentMeshes.get(o);void 0===r&&(r=this.createAgentMesh(n),this.agents.add(r),this.agentMeshes.set(o,r)),r.position.set(e.x,e.y+n.height/2,e.z),r.lookAt((new s).copy(r.position).add(a))}for(const e of t){const t=this.agentMeshes.get(e);t&&(this.agents.remove(t),this.agentMeshes.delete(e))}}createAgentMesh(e){const t=new o(e.radius,e.height-2*e.radius);return new a(t,this.crowdMaterial)}}class d{navMesh;obstacles;obstacleMeshes=new Map;navMeshMaterial;obstaclesMaterial;recastNavMesh;constructor({navMesh:s,navMeshMaterial:o,obstaclesMaterial:r}){this.recastNavMesh=s,this.navMeshMaterial=o||new e({color:"blue",wireframe:!0}),this.obstaclesMaterial=r||new e({color:"red",wireframe:!0}),this.navMesh=new a(new n,this.navMeshMaterial),this.obstacles=new t,this.updateNavMesh(),this.updateObstacles(),s.onNavMeshChange.add((()=>{this.updateNavMesh()})),s.onObstacleChange.add((()=>{this.updateObstacles()}))}updateNavMesh(){const{positions:e,indices:t}=this.recastNavMesh.getDebugNavMesh();for(let e=0;e<t.length;e+=3){const s=t[e];t[e]=t[e+2],t[e+2]=s}const s=new n;s.setAttribute("position",new r(new Float32Array(e),3)),s.setIndex(new r(new Uint16Array(t),1)),s.computeVertexNormals(),this.navMesh.geometry=s}updateObstacles(){const e=new Set(this.obstacleMeshes.keys());for(const[t,s]of this.recastNavMesh.obstacles){let o=this.obstacleMeshes.get(t);if(e.delete(t),!o){const{position:e}=s,o=new a(void 0,this.obstaclesMaterial);if(o.position.copy(e),"box"===s.type){const{extent:e,angle:t}=s;o.geometry=new i(2*e.x,2*e.y,2*e.z),o.rotation.y=t}else{if("cylinder"!==s.type)throw new Error(`Unknown obstacle type: ${s}`);{const{radius:e,height:t}=s;o.geometry=new h(e,e,t,16),o.position.y+=t/2}}this.obstacles.add(o),this.obstacleMeshes.set(t,o)}}for(const t of e){const e=this.obstacleMeshes.get(t);e&&(this.obstacles.remove(e),this.obstacleMeshes.delete(t))}}}const g=e=>{let t,o,a;const n=[],r=[];let i=0;for(t=0;t<e.length;t++)if(e[t]){const h=e[t],c=h.geometry.getIndex()?.array;if(!c)continue;const l=h.geometry.getAttribute("position").array;if(!l)continue;for(o=0;o<c.length;o++)n.push(c[o]+i);const d=new s;for(a=0;a<l.length;a+=3)d.set(l[a],l[a+1],l[a+2]),h.localToWorld(d),r.push(d.x,d.y,d.z);i+=l.length/3}return[r,n]},M=(e,t={})=>{const[s,o]=g(e),a=new c;return a.build(s,o,t),a};export{l as CrowdHelper,d as NavMeshHelper,M as threeToNavMesh,g as threeToNavMeshArgs};
2
2
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/helpers/crowd-helper.ts","../src/helpers/nav-mesh-helper.ts","../src/utils/three-to-nav-mesh.ts"],"sourcesContent":["import { Crowd, CrowdAgentParams } from '@recast-navigation/core';\nimport {\n CapsuleGeometry,\n Group,\n Material,\n Mesh,\n MeshBasicMaterial,\n Vector3,\n} from 'three';\n\nexport type CrowdHelperParams = {\n crowd: Crowd;\n crowdMaterial?: Material;\n};\n\nexport class CrowdHelper {\n agents: Group;\n\n agentMeshes: Map<number, Mesh> = new Map();\n\n recastCrowd: Crowd;\n\n crowdMaterial: Material;\n\n constructor({ crowd, crowdMaterial }: CrowdHelperParams) {\n this.recastCrowd = crowd;\n \n this.crowdMaterial = crowdMaterial ?? new MeshBasicMaterial({ color: 'blue', wireframe: true });\n\n this.agents = new Group();\n\n this.updateAgents();\n\n crowd.onPostUpdate.add(() => {\n this.updateAgents()\n })\n }\n\n private updateAgents() {\n const agentsIndices = this.recastCrowd.getAgents();\n\n const unseen = new Set(this.agentMeshes.keys());\n\n for (const i of agentsIndices) {\n unseen.delete(i);\n\n const position = this.recastCrowd.getAgentPosition(i);\n const velocity = this.recastCrowd.getAgentVelocity(i);\n const agentParams = this.recastCrowd.getAgentParameters(i);\n\n let agentMesh = this.agentMeshes.get(i);\n\n if (agentMesh === undefined) {\n agentMesh = this.createAgentMesh(agentParams);\n \n this.agents.add(agentMesh);\n this.agentMeshes.set(i, agentMesh);\n }\n\n agentMesh.position.set(\n position.x,\n position.y + agentParams.height / 2,\n position.z\n );\n agentMesh.lookAt(\n new Vector3().copy(agentMesh.position).add(velocity as Vector3)\n ); \n }\n\n for (const agentId of unseen) {\n const agentMesh = this.agentMeshes.get(agentId);\n\n if (agentMesh) {\n this.agents.remove(agentMesh);\n this.agentMeshes.delete(agentId);\n }\n }\n }\n\n private createAgentMesh(agentParams: CrowdAgentParams): Mesh {\n const geometry = new CapsuleGeometry(\n agentParams.radius,\n agentParams.height - agentParams.radius * 2\n );\n\n return new Mesh(geometry, this.crowdMaterial);\n }\n}\n","import { NavMesh, ObstacleRef } from '@recast-navigation/core';\nimport {\n BoxGeometry,\n BufferAttribute,\n BufferGeometry,\n CylinderGeometry,\n Group,\n Material,\n Mesh,\n MeshBasicMaterial,\n Vector3,\n} from 'three';\n\nexport type NavMeshHelperParams = {\n navMesh: NavMesh;\n navMeshMaterial?: Material;\n obstaclesMaterial?: Material;\n};\n\nexport class NavMeshHelper {\n navMesh: Mesh;\n\n obstacles: Group;\n\n obstacleMeshes: Map<ObstacleRef, Mesh> = new Map();\n\n navMeshMaterial: Material;\n\n obstaclesMaterial: Material;\n\n recastNavMesh: NavMesh;\n\n constructor({\n navMesh,\n navMeshMaterial,\n obstaclesMaterial,\n }: NavMeshHelperParams) {\n this.recastNavMesh = navMesh;\n\n this.navMeshMaterial = navMeshMaterial\n ? navMeshMaterial\n : new MeshBasicMaterial({ color: 'blue', wireframe: true });\n this.obstaclesMaterial = obstaclesMaterial\n ? obstaclesMaterial\n : new MeshBasicMaterial({ color: 'red', wireframe: true });\n\n this.navMesh = new Mesh(new BufferGeometry(), this.navMeshMaterial);\n\n this.obstacles = new Group();\n\n this.updateNavMesh();\n this.updateObstacles();\n\n navMesh.onNavMeshChange.add(() => {\n this.updateNavMesh();\n });\n\n navMesh.onObstacleChange.add(() => {\n this.updateObstacles();\n });\n }\n\n private updateNavMesh() {\n const { positions, indices } = this.recastNavMesh.getDebugNavMesh();\n\n // Set the winding order of the affected faces to clockwise\n for (let i = 0; i < indices.length; i += 3) {\n const tmp = indices[i];\n indices[i] = indices[i + 2];\n indices[i + 2] = tmp;\n }\n\n const geometry = new BufferGeometry();\n\n geometry.setAttribute(\n 'position',\n new BufferAttribute(new Float32Array(positions), 3)\n );\n\n geometry.setIndex(new BufferAttribute(new Uint16Array(indices), 1));\n\n geometry.computeVertexNormals();\n\n this.navMesh.geometry = geometry;\n }\n\n private updateObstacles() { \n const unseen = new Set(this.obstacleMeshes.keys());\n\n for (const [ref, obstacle] of this.recastNavMesh.obstacles) {\n let obstacleMesh = this.obstacleMeshes.get(ref);\n\n unseen.delete(ref);\n\n if (!obstacleMesh) {\n const { position } = obstacle;\n\n const mesh = new Mesh(undefined, this.obstaclesMaterial);\n\n mesh.position.copy(position as Vector3);\n\n if (obstacle.type === 'box') {\n const { extent, angle } = obstacle;\n\n mesh.geometry = new BoxGeometry(\n extent.x * 2,\n extent.y * 2,\n extent.z * 2\n );\n\n mesh.rotation.y = angle;\n } else if (obstacle.type === 'cylinder') {\n const { radius, height } = obstacle;\n\n mesh.geometry = new CylinderGeometry(radius, radius, height, 16);\n\n mesh.position.y += height / 2;\n } else {\n throw new Error(`Unknown obstacle type: ${obstacle}`);\n }\n\n this.obstacles.add(mesh);\n this.obstacleMeshes.set(ref, mesh);\n }\n }\n\n for (const ref of unseen) {\n const obstacleMesh = this.obstacleMeshes.get(ref);\n\n if (obstacleMesh) {\n this.obstacles.remove(obstacleMesh);\n this.obstacleMeshes.delete(ref);\n }\n }\n }\n}\n","import { NavMesh, NavMeshConfig } from '@recast-navigation/core';\nimport { BufferAttribute, Mesh, Object3D, Vector3 } from 'three';\n\nexport const threeToNavMeshArgs = (\n object: Object3D\n): [positions: number[], indices: number[]] => {\n const meshes: Mesh[] = [];\n\n object.traverse((child) => {\n if (child instanceof Mesh) {\n meshes.push(child);\n }\n });\n\n let index: number;\n let tri: number;\n let pt: number;\n\n const indices: number[] = [];\n const positions: number[] = [];\n\n let offset = 0;\n for (index = 0; index < meshes.length; index++) {\n if (meshes[index]) {\n const mesh = meshes[index];\n\n const meshIndices = mesh.geometry.getIndex()?.array;\n if (!meshIndices) {\n // todo: to indexed?\n continue;\n }\n\n const meshPositions = (mesh.geometry.getAttribute(\n 'position'\n ) as BufferAttribute)!.array;\n if (!meshPositions) {\n continue;\n }\n\n for (tri = 0; tri < meshIndices.length; tri++) {\n indices.push(meshIndices[tri] + offset);\n }\n\n const position = new Vector3();\n for (pt = 0; pt < meshPositions.length; pt += 3) {\n position.set(\n meshPositions[pt],\n meshPositions[pt + 1],\n meshPositions[pt + 2]\n );\n mesh.localToWorld(position);\n\n positions.push(position.x, position.y, position.z);\n }\n\n offset += meshPositions.length / 3;\n }\n }\n\n return [positions, indices];\n};\n\nexport const threeToNavMesh = (\n object: Object3D,\n navMeshConfig: Partial<NavMeshConfig> = {}\n): NavMesh => {\n const [positions, indices] = threeToNavMeshArgs(object);\n\n const navMesh = new NavMesh();\n navMesh.build(positions, indices, navMeshConfig);\n\n return navMesh;\n};\n"],"names":["CrowdHelper","agents","agentMeshes","Map","recastCrowd","crowdMaterial","constructor","crowd","this","MeshBasicMaterial","color","wireframe","Group","updateAgents","onPostUpdate","add","agentsIndices","getAgents","unseen","Set","keys","i","delete","position","getAgentPosition","velocity","getAgentVelocity","agentParams","getAgentParameters","agentMesh","get","undefined","createAgentMesh","set","x","y","height","z","lookAt","Vector3","copy","agentId","remove","geometry","CapsuleGeometry","radius","Mesh","NavMeshHelper","navMesh","obstacles","obstacleMeshes","navMeshMaterial","obstaclesMaterial","recastNavMesh","BufferGeometry","updateNavMesh","updateObstacles","onNavMeshChange","onObstacleChange","positions","indices","getDebugNavMesh","length","tmp","setAttribute","BufferAttribute","Float32Array","setIndex","Uint16Array","computeVertexNormals","ref","obstacle","obstacleMesh","mesh","type","extent","angle","BoxGeometry","rotation","Error","CylinderGeometry","threeToNavMeshArgs","object","meshes","index","tri","pt","traverse","child","push","offset","meshIndices","getIndex","array","meshPositions","getAttribute","localToWorld","threeToNavMesh","navMeshConfig","NavMesh","build"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/helpers/crowd-helper.ts","../src/helpers/nav-mesh-helper.ts","../src/utils/three-to-nav-mesh.ts"],"sourcesContent":["import { Crowd, CrowdAgentParams } from '@recast-navigation/core';\nimport {\n CapsuleGeometry,\n Group,\n Material,\n Mesh,\n MeshBasicMaterial,\n Vector3,\n} from 'three';\n\nexport type CrowdHelperParams = {\n crowd: Crowd;\n crowdMaterial?: Material;\n};\n\nexport class CrowdHelper {\n agents: Group;\n\n agentMeshes: Map<number, Mesh> = new Map();\n\n recastCrowd: Crowd;\n\n crowdMaterial: Material;\n\n constructor({ crowd, crowdMaterial }: CrowdHelperParams) {\n this.recastCrowd = crowd;\n \n this.crowdMaterial = crowdMaterial ?? new MeshBasicMaterial({ color: 'blue', wireframe: true });\n\n this.agents = new Group();\n\n this.updateAgents();\n\n crowd.onPostUpdate.add(() => {\n this.updateAgents()\n })\n }\n\n private updateAgents() {\n const agentsIndices = this.recastCrowd.getAgents();\n\n const unseen = new Set(this.agentMeshes.keys());\n\n for (const i of agentsIndices) {\n unseen.delete(i);\n\n const position = this.recastCrowd.getAgentPosition(i);\n const velocity = this.recastCrowd.getAgentVelocity(i);\n const agentParams = this.recastCrowd.getAgentParameters(i);\n\n let agentMesh = this.agentMeshes.get(i);\n\n if (agentMesh === undefined) {\n agentMesh = this.createAgentMesh(agentParams);\n \n this.agents.add(agentMesh);\n this.agentMeshes.set(i, agentMesh);\n }\n\n agentMesh.position.set(\n position.x,\n position.y + agentParams.height / 2,\n position.z\n );\n agentMesh.lookAt(\n new Vector3().copy(agentMesh.position).add(velocity as Vector3)\n ); \n }\n\n for (const agentId of unseen) {\n const agentMesh = this.agentMeshes.get(agentId);\n\n if (agentMesh) {\n this.agents.remove(agentMesh);\n this.agentMeshes.delete(agentId);\n }\n }\n }\n\n private createAgentMesh(agentParams: CrowdAgentParams): Mesh {\n const geometry = new CapsuleGeometry(\n agentParams.radius,\n agentParams.height - agentParams.radius * 2\n );\n\n return new Mesh(geometry, this.crowdMaterial);\n }\n}\n","import { NavMesh, ObstacleRef } from '@recast-navigation/core';\nimport {\n BoxGeometry,\n BufferAttribute,\n BufferGeometry,\n CylinderGeometry,\n Group,\n Material,\n Mesh,\n MeshBasicMaterial,\n Vector3,\n} from 'three';\n\nexport type NavMeshHelperParams = {\n navMesh: NavMesh;\n navMeshMaterial?: Material;\n obstaclesMaterial?: Material;\n};\n\nexport class NavMeshHelper {\n navMesh: Mesh;\n\n obstacles: Group;\n\n obstacleMeshes: Map<ObstacleRef, Mesh> = new Map();\n\n navMeshMaterial: Material;\n\n obstaclesMaterial: Material;\n\n recastNavMesh: NavMesh;\n\n constructor({\n navMesh,\n navMeshMaterial,\n obstaclesMaterial,\n }: NavMeshHelperParams) {\n this.recastNavMesh = navMesh;\n\n this.navMeshMaterial = navMeshMaterial\n ? navMeshMaterial\n : new MeshBasicMaterial({ color: 'blue', wireframe: true });\n this.obstaclesMaterial = obstaclesMaterial\n ? obstaclesMaterial\n : new MeshBasicMaterial({ color: 'red', wireframe: true });\n\n this.navMesh = new Mesh(new BufferGeometry(), this.navMeshMaterial);\n\n this.obstacles = new Group();\n\n this.updateNavMesh();\n this.updateObstacles();\n\n navMesh.onNavMeshChange.add(() => {\n this.updateNavMesh();\n });\n\n navMesh.onObstacleChange.add(() => {\n this.updateObstacles();\n });\n }\n\n private updateNavMesh() {\n const { positions, indices } = this.recastNavMesh.getDebugNavMesh();\n\n // Set the winding order of the affected faces to clockwise\n for (let i = 0; i < indices.length; i += 3) {\n const tmp = indices[i];\n indices[i] = indices[i + 2];\n indices[i + 2] = tmp;\n }\n\n const geometry = new BufferGeometry();\n\n geometry.setAttribute(\n 'position',\n new BufferAttribute(new Float32Array(positions), 3)\n );\n\n geometry.setIndex(new BufferAttribute(new Uint16Array(indices), 1));\n\n geometry.computeVertexNormals();\n\n this.navMesh.geometry = geometry;\n }\n\n private updateObstacles() { \n const unseen = new Set(this.obstacleMeshes.keys());\n\n for (const [ref, obstacle] of this.recastNavMesh.obstacles) {\n let obstacleMesh = this.obstacleMeshes.get(ref);\n\n unseen.delete(ref);\n\n if (!obstacleMesh) {\n const { position } = obstacle;\n\n const mesh = new Mesh(undefined, this.obstaclesMaterial);\n\n mesh.position.copy(position as Vector3);\n\n if (obstacle.type === 'box') {\n const { extent, angle } = obstacle;\n\n mesh.geometry = new BoxGeometry(\n extent.x * 2,\n extent.y * 2,\n extent.z * 2\n );\n\n mesh.rotation.y = angle;\n } else if (obstacle.type === 'cylinder') {\n const { radius, height } = obstacle;\n\n mesh.geometry = new CylinderGeometry(radius, radius, height, 16);\n\n mesh.position.y += height / 2;\n } else {\n throw new Error(`Unknown obstacle type: ${obstacle}`);\n }\n\n this.obstacles.add(mesh);\n this.obstacleMeshes.set(ref, mesh);\n }\n }\n\n for (const ref of unseen) {\n const obstacleMesh = this.obstacleMeshes.get(ref);\n\n if (obstacleMesh) {\n this.obstacles.remove(obstacleMesh);\n this.obstacleMeshes.delete(ref);\n }\n }\n }\n}\n","import { NavMesh, NavMeshConfig } from '@recast-navigation/core';\nimport { BufferAttribute, Mesh, Vector3 } from 'three';\n\nexport const threeToNavMeshArgs = (\n meshes: Mesh[]\n): [positions: number[], indices: number[]] => {\n let index: number;\n let tri: number;\n let pt: number;\n\n const indices: number[] = [];\n const positions: number[] = [];\n\n let offset = 0;\n for (index = 0; index < meshes.length; index++) {\n if (meshes[index]) {\n const mesh = meshes[index];\n\n const meshIndices = mesh.geometry.getIndex()?.array;\n if (!meshIndices) {\n // todo: to indexed?\n continue;\n }\n\n const meshPositions = (mesh.geometry.getAttribute(\n 'position'\n ) as BufferAttribute)!.array;\n if (!meshPositions) {\n continue;\n }\n\n for (tri = 0; tri < meshIndices.length; tri++) {\n indices.push(meshIndices[tri] + offset);\n }\n\n const position = new Vector3();\n for (pt = 0; pt < meshPositions.length; pt += 3) {\n position.set(\n meshPositions[pt],\n meshPositions[pt + 1],\n meshPositions[pt + 2]\n );\n mesh.localToWorld(position);\n\n positions.push(position.x, position.y, position.z);\n }\n\n offset += meshPositions.length / 3;\n }\n }\n\n return [positions, indices];\n};\n\nexport const threeToNavMesh = (\n meshes: Mesh[],\n navMeshConfig: Partial<NavMeshConfig> = {}\n): NavMesh => {\n const [positions, indices] = threeToNavMeshArgs(meshes);\n\n const navMesh = new NavMesh();\n navMesh.build(positions, indices, navMeshConfig);\n\n return navMesh;\n};\n"],"names":["CrowdHelper","agents","agentMeshes","Map","recastCrowd","crowdMaterial","constructor","crowd","this","MeshBasicMaterial","color","wireframe","Group","updateAgents","onPostUpdate","add","agentsIndices","getAgents","unseen","Set","keys","i","delete","position","getAgentPosition","velocity","getAgentVelocity","agentParams","getAgentParameters","agentMesh","get","undefined","createAgentMesh","set","x","y","height","z","lookAt","Vector3","copy","agentId","remove","geometry","CapsuleGeometry","radius","Mesh","NavMeshHelper","navMesh","obstacles","obstacleMeshes","navMeshMaterial","obstaclesMaterial","recastNavMesh","BufferGeometry","updateNavMesh","updateObstacles","onNavMeshChange","onObstacleChange","positions","indices","getDebugNavMesh","length","tmp","setAttribute","BufferAttribute","Float32Array","setIndex","Uint16Array","computeVertexNormals","ref","obstacle","obstacleMesh","mesh","type","extent","angle","BoxGeometry","rotation","Error","CylinderGeometry","threeToNavMeshArgs","meshes","index","tri","pt","offset","meshIndices","getIndex","array","meshPositions","getAttribute","push","localToWorld","threeToNavMesh","navMeshConfig","NavMesh","build"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  import { NavMesh, NavMeshConfig } from '@recast-navigation/core';
2
- import { Object3D } from 'three';
3
- export declare const threeToNavMeshArgs: (object: Object3D) => [positions: number[], indices: number[]];
4
- export declare const threeToNavMesh: (object: Object3D, navMeshConfig?: Partial<NavMeshConfig>) => NavMesh;
2
+ import { Mesh } from 'three';
3
+ export declare const threeToNavMeshArgs: (meshes: Mesh[]) => [positions: number[], indices: number[]];
4
+ export declare const threeToNavMesh: (meshes: Mesh[], navMeshConfig?: Partial<NavMeshConfig>) => NavMesh;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "email": "isaac@isaacmason.com",
7
7
  "url": "https://isaacmason.com/"
8
8
  },
9
- "version": "0.0.1",
9
+ "version": "0.0.3",
10
10
  "license": "MIT",
11
11
  "homepage": "https://github.com/isaac-mason/recast-navigation-js",
12
12
  "bugs": {
@@ -24,7 +24,7 @@
24
24
  "build": "rollup --config rollup.config.js --bundleConfigAsCjs"
25
25
  },
26
26
  "dependencies": {
27
- "@recast-navigation/core": "0.0.1",
27
+ "@recast-navigation/core": "0.0.3",
28
28
  "three": "^0.150.1"
29
29
  },
30
30
  "peerDependencies": {