three#BufferAttribute TypeScript Examples

The following examples show how to use three#BufferAttribute. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: NGraphics.ts    From FairyGUI-threejs with MIT License 6 votes vote down vote up
private writeAttribute(gm: BufferGeometry, name: string, arr: Array<number>, itemSize: number): void {
        let attr: BufferAttribute = <BufferAttribute>gm.attributes[name];
        if (!attr || !attr.isBufferAttribute || attr.array.length < arr.length) {
            attr = new BufferAttribute(new Float32Array(arr.length), itemSize);
            gm.setAttribute(name, attr);
        }

        attr.copyArray(arr);
        attr.needsUpdate = true;
    }
Example #2
Source File: NGraphics.ts    From FairyGUI-threejs with MIT License 6 votes vote down vote up
private writeIndexAttribute(gm: BufferGeometry, arr: Array<number>): void {
        let attr: BufferAttribute = <BufferAttribute>gm.index;
        if (!attr || !attr.isBufferAttribute || attr.array.length < arr.length) {
            attr = new BufferAttribute(new Uint16Array(arr.length), 1);
            gm.index = attr;
        }

        attr.copyArray(arr);
        attr.needsUpdate = true;
    }
Example #3
Source File: collision.ts    From spacesvr with MIT License 6 votes vote down vote up
useTrimeshCollision = (geometry: BufferGeometry) => {
  const indices = (geometry.index as BufferAttribute).array as number[];

  const isInterleaved =
    // @ts-ignore
    geometry.attributes.position.isInterleavedBufferAttribute;

  let vertices: number[] = [];
  if (isInterleaved) {
    const attr = geometry.attributes.position as InterleavedBufferAttribute;
    const data = attr.data;
    for (let i = attr.offset; i < data.array.length; i += data.stride) {
      for (let x = 0; x < attr.itemSize; x++) {
        vertices.push(data.array[i + x]);
      }
    }
  } else {
    vertices = (geometry.attributes.position as BufferAttribute)
      .array as number[];
  }

  const [hitbox] = useTrimesh(() => ({
    type: "Static",
    args: [vertices, indices],
  }));

  return hitbox;
}
Example #4
Source File: Geometry.ts    From trois with MIT License 5 votes vote down vote up
Geometry = defineComponent({
  emits: ['created'],
  props: {
    rotateX: Number,
    rotateY: Number,
    rotateZ: Number,
    attributes: { type: Array as PropType<Array<GeometryAttributeInterface>>, default: () => ([]) },
  },
  // inject for sub components
  inject: {
    mesh: MeshInjectionKey as symbol,
  },
  setup(): GeometrySetupInterface {
    return {}
  },
  created() {
    if (!this.mesh) {
      console.error('Missing parent Mesh')
      return
    }

    this.createGeometry()
    this.rotateGeometry()
    if (this.geometry) this.mesh.setGeometry(this.geometry)

    Object.keys(this.$props).forEach(prop => {
      // @ts-ignore
      watch(() => this[prop], this.refreshGeometry)
    })
  },
  unmounted() {
    this.geometry?.dispose()
  },
  methods: {
    createGeometry() {
      const bufferAttributes: Record<string, unknown> = {}
      const geometry = new BufferGeometry()
      this.attributes.forEach(attribute => {
        if (attribute.name && attribute.itemSize && attribute.array) {
          const bufferAttribute = bufferAttributes[attribute.name] = new BufferAttribute(attribute.array, attribute.itemSize, attribute.normalized)
          geometry.setAttribute(attribute.name, bufferAttribute)
        }
      })
      geometry.computeBoundingBox()
      geometry.userData.component = this
      this.geometry = geometry
      this.$emit('created', geometry)
    },
    rotateGeometry() {
      if (!this.geometry) return
      if (this.rotateX) this.geometry.rotateX(this.rotateX)
      if (this.rotateY) this.geometry.rotateY(this.rotateY)
      if (this.rotateZ) this.geometry.rotateZ(this.rotateZ)
    },
    refreshGeometry() {
      const oldGeo = this.geometry
      this.createGeometry()
      this.rotateGeometry()
      if (this.geometry && this.mesh) this.mesh.setGeometry(this.geometry)
      oldGeo?.dispose()
    },
  },
  render() { return [] },
})
Example #5
Source File: physics-provider.tsx    From use-ammojs with MIT License 4 votes vote down vote up
export function Physics({
  drawDebug,
  drawDebugMode = DEFAULT_DEBUG_MODE,
  gravity,
  epsilon,
  fixedTimeStep,
  maxSubSteps,
  solverIterations,
  simulationSpeed = 1,
  children,
}: PropsWithChildren<AmmoPhysicsProps>) {
  const [physicsState, setPhysicsState] = useState<PhysicsState>();

  const sharedBuffersRef = useRef<SharedBuffers>({} as any);

  // Functions that are executed while the main thread holds control over the shared data
  const threadSafeQueueRef = useRef<(() => void)[]>([]);

  const physicsPerformanceInfoRef = useRef<PhysicsPerformanceInfo>({
    substepCounter: 0,
    lastTickMs: 0,
  });

  useEffect(() => {
    const uuids: string[] = [];
    const object3Ds: Record<string, Object3D> = {};
    const uuidToIndex: Record<string, number> = {};
    const IndexToUuid: Record<number, string> = {};
    const bodyOptions: Record<string, BodyConfig> = {};

    const softBodies: Record<UUID, Mesh> = {};

    const ammoWorker: Worker = createAmmoWorker();

    const workerHelpers = WorkerHelpers(ammoWorker);

    const rigidBodyBuffer = allocateCompatibleBuffer(
      4 * BUFFER_CONFIG.HEADER_LENGTH + //header
        4 * BUFFER_CONFIG.BODY_DATA_SIZE * BUFFER_CONFIG.MAX_BODIES + //matrices
        4 * BUFFER_CONFIG.MAX_BODIES //velocities
    );
    const headerIntArray = new Int32Array(
      rigidBodyBuffer,
      0,
      BUFFER_CONFIG.HEADER_LENGTH
    );
    const headerFloatArray = new Float32Array(
      rigidBodyBuffer,
      0,
      BUFFER_CONFIG.HEADER_LENGTH
    );
    const objectMatricesIntArray = new Int32Array(
      rigidBodyBuffer,
      BUFFER_CONFIG.HEADER_LENGTH * 4,
      BUFFER_CONFIG.BODY_DATA_SIZE * BUFFER_CONFIG.MAX_BODIES
    );
    const objectMatricesFloatArray = new Float32Array(
      rigidBodyBuffer,
      BUFFER_CONFIG.HEADER_LENGTH * 4,
      BUFFER_CONFIG.BODY_DATA_SIZE * BUFFER_CONFIG.MAX_BODIES
    );

    objectMatricesIntArray[0] = BufferState.UNINITIALIZED;

    const debugBuffer = allocateCompatibleBuffer(4 + 2 * DefaultBufferSize * 4);
    const debugIndex = new Uint32Array(debugBuffer, 0, 4);
    const debugVertices = new Float32Array(debugBuffer, 4, DefaultBufferSize);
    const debugColors = new Float32Array(
      debugBuffer,
      4 + DefaultBufferSize,
      DefaultBufferSize
    );
    const debugGeometry = new BufferGeometry();
    debugGeometry.setAttribute(
      "position",
      new BufferAttribute(debugVertices, 3).setUsage(DynamicDrawUsage)
    );
    debugGeometry.setAttribute(
      "color",
      new BufferAttribute(debugColors, 3).setUsage(DynamicDrawUsage)
    );

    sharedBuffersRef.current = {
      rigidBodies: {
        headerIntArray,
        headerFloatArray,
        objectMatricesFloatArray,
        objectMatricesIntArray,
      },

      softBodies: [],

      debug: {
        indexIntArray: debugIndex,
        vertexFloatArray: debugVertices,
        colorFloatArray: debugColors,
      },
    };

    const worldConfig: WorldConfig = {
      debugDrawMode: ammoDebugOptionsToNumber(drawDebugMode),
      gravity: gravity && new Vector3(gravity[0], gravity[1], gravity[2]),
      epsilon,
      fixedTimeStep,
      maxSubSteps,
      solverIterations,
    };

    workerHelpers.initWorld(worldConfig, sharedBuffersRef.current);

    const workerInitPromise = new Promise<PhysicsState>((resolve) => {
      ammoWorker.onmessage = async (event) => {
        const type: ClientMessageType = event.data.type;

        switch (type) {
          case ClientMessageType.READY: {
            if (event.data.sharedBuffers) {
              sharedBuffersRef.current = event.data.sharedBuffers;
            }

            resolve({
              workerHelpers,
              sharedBuffersRef,
              debugGeometry,
              debugBuffer,
              bodyOptions,
              uuids,
              object3Ds,
              softBodies,
              uuidToIndex,
              debugIndex,
              addRigidBody,
              removeRigidBody,
              addSoftBody,
              removeSoftBody,
              rayTest,
            });
            return;
          }
          case ClientMessageType.RIGIDBODY_READY: {
            const uuid = event.data.uuid;
            uuids.push(uuid);
            uuidToIndex[uuid] = event.data.index;
            IndexToUuid[event.data.index] = uuid;
            return;
          }
          case ClientMessageType.SOFTBODY_READY: {
            threadSafeQueueRef.current.push(() => {
              sharedBuffersRef.current.softBodies.push(
                event.data.sharedSoftBodyBuffers
              );
            });
            return;
          }
          case ClientMessageType.TRANSFER_BUFFERS: {
            sharedBuffersRef.current = event.data.sharedBuffers;
            return;
          }
          case ClientMessageType.RAYCAST_RESPONSE: {
            workerHelpers.resolveAsyncRequest(event.data);
            return;
          }
        }
        throw new Error("unknown message type" + type);
      };
    });

    workerInitPromise.then(setPhysicsState);

    function addRigidBody(
      uuid,
      mesh,
      shape: ShapeDescriptor,
      options: BodyConfig = {}
    ) {
      bodyOptions[uuid] = options;
      object3Ds[uuid] = mesh;

      if (!mesh.userData.useAmmo) {
        mesh.userData.useAmmo = {};
      }

      mesh.userData.useAmmo.rigidBody = {
        uuid,
      };

      workerHelpers.addRigidBody(uuid, mesh, shape, options);
    }

    function removeRigidBody(uuid: string) {
      uuids.splice(uuids.indexOf(uuid), 1);
      delete IndexToUuid[uuidToIndex[uuid]];
      delete uuidToIndex[uuid];
      delete bodyOptions[uuid];
      delete object3Ds[uuid].userData.useAmmo.rigidBody;
      delete object3Ds[uuid];
      workerHelpers.removeRigidBody(uuid);
    }

    function addSoftBody(uuid: UUID, mesh: Mesh, options: SoftBodyConfig = {}) {
      if (!mesh.geometry) {
        console.error("useSoftBody received: ", mesh);
        throw new Error("useSoftBody is only supported on BufferGeometries");
      }

      let indexLength: number;
      let vertexLength: number;
      let normalLength: number;

      switch (options.type) {
        case SoftBodyType.TRIMESH:
          // console.log("before merge ", mesh.geometry.attributes.position.count);
          mesh.geometry.deleteAttribute("normal");
          mesh.geometry.deleteAttribute("uv");
          mesh.geometry = mergeVertices(mesh.geometry);
          mesh.geometry.computeVertexNormals();
          // console.log("after merge ", mesh.geometry.attributes.position.count);

          indexLength =
            mesh.geometry.index!.count * mesh.geometry.index!.itemSize;
          vertexLength =
            mesh.geometry.attributes.position.count *
            mesh.geometry.attributes.position.itemSize;
          normalLength =
            mesh.geometry.attributes.normal.count *
            mesh.geometry.attributes.normal.itemSize;

          break;
        case SoftBodyType.ROPE:
          indexLength = 0;
          vertexLength =
            mesh.geometry.attributes.instanceStart.count *
            mesh.geometry.attributes.instanceStart.itemSize;
          normalLength = 0;

          break;
        default:
          throw new Error("unknown soft body type " + options.type);
      }

      const buffer = allocateCompatibleBuffer(
        indexLength * 4 + vertexLength * 4 + normalLength * 4
      );

      const sharedSoftBodyBuffers: SharedSoftBodyBuffers = {
        uuid,
        indexIntArray: new (indexLength > 65535 ? Uint32Array : Uint16Array)(
          buffer,
          0,
          indexLength
        ),
        vertexFloatArray: new Float32Array(
          buffer,
          indexLength * 4,
          vertexLength
        ),
        normalFloatArray: new Float32Array(
          buffer,
          indexLength * 4 + vertexLength * 4,
          normalLength
        ),
      };

      // Bullet softbodies operate in world-space,
      // so the transform needs to be baked into the vertex data
      mesh.updateMatrixWorld(true);
      mesh.geometry.applyMatrix4(mesh.matrixWorld);

      mesh.position.set(0, 0, 0);
      mesh.quaternion.set(0, 0, 0, 1);
      mesh.scale.set(1, 1, 1);

      mesh.frustumCulled = false;

      if (options.type === SoftBodyType.TRIMESH) {
        sharedSoftBodyBuffers.vertexFloatArray.set(
          mesh.geometry.attributes.position.array
        );

        sharedSoftBodyBuffers.indexIntArray.set(mesh.geometry.index!.array);
        sharedSoftBodyBuffers.normalFloatArray.set(
          mesh.geometry.attributes.normal.array
        );
      } else {
        for (let i = 0; i < vertexLength; i++) {
          sharedSoftBodyBuffers.vertexFloatArray[
            i * 3
          ] = mesh.geometry.attributes.instanceStart.getX(i);
          sharedSoftBodyBuffers.vertexFloatArray[
            i * 3 + 1
          ] = mesh.geometry.attributes.instanceStart.getY(i);
          sharedSoftBodyBuffers.vertexFloatArray[
            i * 3 + 2
          ] = mesh.geometry.attributes.instanceStart.getZ(i);
        }
      }

      if (isSharedArrayBufferSupported) {
        if (options.type === SoftBodyType.TRIMESH) {
          mesh.geometry.setAttribute(
            "position",
            new BufferAttribute(
              sharedSoftBodyBuffers.vertexFloatArray,
              3
            ).setUsage(DynamicDrawUsage)
          );

          mesh.geometry.setAttribute(
            "normal",
            new BufferAttribute(
              sharedSoftBodyBuffers.normalFloatArray,
              3
            ).setUsage(DynamicDrawUsage)
          );
        }
      }

      softBodies[uuid] = mesh;

      workerHelpers.addSoftBody(uuid, sharedSoftBodyBuffers, options);
    }

    function removeSoftBody(uuid: string) {
      delete softBodies[uuid];
      workerHelpers.removeSoftBody(uuid);

      sharedBuffersRef.current.softBodies = sharedBuffersRef.current.softBodies.filter(
        (ssbb) => ssbb.uuid !== uuid
      );
    }

    async function rayTest(options: RaycastOptions): Promise<RaycastHit[]> {
      const { hits } = await workerHelpers.makeAsyncRequest({
        type: MessageType.RAYCAST_REQUEST,
        ...options,
      });

      return hits.map(
        (hit: RaycastHitMessage): RaycastHit => {
          return {
            object: object3Ds[hit.uuid] || softBodies[hit.uuid],

            hitPosition: new Vector3(
              hit.hitPosition.x,
              hit.hitPosition.y,
              hit.hitPosition.z
            ),

            normal: new Vector3(hit.normal.x, hit.normal.y, hit.normal.z),
          };
        }
      );
    }

    return () => {
      ammoWorker.terminate();
      setPhysicsState(undefined);
    };
  }, []);

  useEffect(() => {
    if (!isSharedArrayBufferSupported) {
      if (drawDebug) {
        console.warn("debug visuals require SharedArrayBuffer support");
      }
      return;
    }

    if (physicsState) {
      if (drawDebug) {
        workerHelpers.enableDebug(true, physicsState.debugBuffer);
      } else {
        workerHelpers.enableDebug(false, physicsState.debugBuffer);
      }
    }
  }, [drawDebug, physicsState]);

  useEffect(() => {
    if (physicsState?.workerHelpers) {
      workerHelpers.setSimulationSpeed(simulationSpeed);
    }
  }, [physicsState?.workerHelpers, simulationSpeed]);

  if (!physicsState) {
    return null;
  }

  const { workerHelpers, debugGeometry } = physicsState;

  return (
    <AmmoPhysicsContext.Provider
      value={{
        ...workerHelpers,

        // workerHelpers Overrides
        addRigidBody: physicsState.addRigidBody,
        removeRigidBody: physicsState.removeRigidBody,

        addSoftBody: physicsState.addSoftBody,
        removeSoftBody: physicsState.removeSoftBody,

        object3Ds: physicsState.object3Ds,

        rayTest: physicsState.rayTest,

        physicsPerformanceInfoRef,
      }}
    >
      <PhysicsUpdate
        {...{
          physicsState,
          sharedBuffersRef,
          threadSafeQueueRef,
          physicsPerformanceInfoRef,
        }}
      />
      {drawDebug && <PhysicsDebug geometry={debugGeometry} />}
      {children}
    </AmmoPhysicsContext.Provider>
  );
}
Example #6
Source File: physics-update.tsx    From use-ammojs with MIT License 4 votes vote down vote up
export function PhysicsUpdate({
  physicsState,
  sharedBuffersRef,
  threadSafeQueueRef,
  physicsPerformanceInfoRef,
}: PhysicsUpdateProps) {
  useFrame(() => {
    if (!physicsState) {
      return;
    }

    const {
      workerHelpers,
      debugGeometry,
      bodyOptions,
      uuids,
      object3Ds,
      uuidToIndex,
      debugIndex,
      softBodies,
    } = physicsState;

    const sharedBuffers = sharedBuffersRef.current;

    if (
      // Check if the worker is finished with the buffer
      (!isSharedArrayBufferSupported &&
        sharedBuffers.rigidBodies.objectMatricesFloatArray.byteLength !== 0) ||
      (isSharedArrayBufferSupported &&
        Atomics.load(sharedBuffers.rigidBodies.headerIntArray, 0) ===
          BufferState.READY)
    ) {
      const lastSubstep = physicsPerformanceInfoRef.current.substepCounter;

      physicsPerformanceInfoRef.current.lastTickMs =
        sharedBuffers.rigidBodies.headerFloatArray[1];
      physicsPerformanceInfoRef.current.substepCounter =
        sharedBuffers.rigidBodies.headerIntArray[2];

      while (threadSafeQueueRef.current.length) {
        const fn = threadSafeQueueRef.current.shift();
        fn!();
      }

      // Skip copy if the physics worker didnt update
      if (lastSubstep !== physicsPerformanceInfoRef.current.substepCounter) {
        for (let i = 0; i < uuids.length; i++) {
          const uuid = uuids[i];
          const type = bodyOptions[uuid].type
            ? bodyOptions[uuid].type
            : BodyType.DYNAMIC;
          const object3D = object3Ds[uuid];
          if (type === BodyType.DYNAMIC) {
            matrix.fromArray(
              sharedBuffers.rigidBodies.objectMatricesFloatArray,
              uuidToIndex[uuid] * BUFFER_CONFIG.BODY_DATA_SIZE
            );

            inverse.copy(object3D.parent!.matrixWorld).invert();
            transform.multiplyMatrices(inverse, matrix);
            transform.decompose(object3D.position, object3D.quaternion, scale);
          } else {
            // sharedBuffers.rigidBodies.objectMatricesFloatArray.set(
            //   object3D.matrixWorld.elements,
            //   uuidToIndex[uuid] * BUFFER_CONFIG.BODY_DATA_SIZE
            // );
          }

          // print velocities
          // console.log(
          //   uuid,
          //   objectMatricesFloatArray[indexes[uuid] * BUFFER_CONFIG.BODY_DATA_SIZE + 16],
          //   objectMatricesFloatArray[indexes[uuid] * BUFFER_CONFIG.BODY_DATA_SIZE + 17]
          // );

          // print coliisions
          // const collisions = [];
          // for (let j = 18; j < 26; j++) {
          //   const collidingIndex = objectMatricesIntArray[uuidToIndex[uuid] * BUFFER_CONFIG.BODY_DATA_SIZE + j];
          //   if (collidingIndex !== -1) {
          //     collisions.push(IndexToUuid[collidingIndex]);
          //   }
          // }
          // console.log(uuid, collisions);
        }

        for (const softBodyBuffers of sharedBuffersRef.current.softBodies) {
          const softBodyMesh = softBodies[softBodyBuffers.uuid];

          if (softBodyMesh) {
            if ((softBodyMesh.geometry as LineGeometry).isLineGeometry) {
              (softBodyMesh.geometry as LineGeometry).setPositions(
                softBodyBuffers.vertexFloatArray
              );
              softBodyMesh.geometry.attributes.instanceStart.needsUpdate = true;
              softBodyMesh.geometry.attributes.instanceEnd.needsUpdate = true;
            } else {
              if (!isSharedArrayBufferSupported) {
                (softBodyMesh.geometry.attributes
                  .position as BufferAttribute).copyArray(
                  softBodyBuffers.vertexFloatArray
                );
                (softBodyMesh.geometry.attributes
                  .normal as BufferAttribute).copyArray(
                  softBodyBuffers.normalFloatArray
                );
              }

              softBodyMesh.geometry.attributes.position.needsUpdate = true;
              if (softBodyMesh.geometry.attributes.normal) {
                softBodyMesh.geometry.attributes.normal.needsUpdate = true;
              }
            }
          }
        }
      }

      if (isSharedArrayBufferSupported) {
        Atomics.store(
          sharedBuffers.rigidBodies.headerIntArray,
          0,
          BufferState.CONSUMED
        );
      } else {
        workerHelpers.transferSharedBuffers(sharedBuffersRef.current);
      }
    }

    if (isSharedArrayBufferSupported) {
      /* DEBUG RENDERING */
      const index = Atomics.load(debugIndex, 0);
      if (!!index) {
        debugGeometry.attributes.position.needsUpdate = true;
        debugGeometry.attributes.color.needsUpdate = true;
        debugGeometry.setDrawRange(0, index);
      }
      Atomics.store(debugIndex, 0, 0);
    }
  });

  return null;
}