Files
engine/examples/input-pointerRaycast.ts
2024-07-04 18:10:43 +08:00

194 lines
6.3 KiB
TypeScript

/**
* @title input-pointerRaycast
* @category input
* @thumbnail https://mdn.alipayobjects.com/merchant_appfe/afts/img/A*unTBRblIIkUAAAAAAAAAAAAADiR2AQ/original
*/
import {
BlinnPhongMaterial,
BoxColliderShape,
Camera,
Color,
DirectLight,
Entity,
HitResult,
Layer,
MeshRenderer,
PrimitiveMesh,
Ray,
Script,
StaticCollider,
UnlitMaterial,
Vector3,
WebGLEngine,
} from "@galacean/engine";
import { LitePhysics } from "@galacean/engine-physics-lite";
import { WireframeManager } from "@galacean/engine-toolkit";
// Create engine
WebGLEngine.create({ canvas: "canvas", physics: new LitePhysics() }).then(
(engine) => {
engine.canvas.resizeByClientSize();
engine.canvas._webCanvas.style.touchAction = "none";
const scene = engine.sceneManager.activeScene;
const rootEntity = scene.createRootEntity("root");
// add light
const lightEntity = rootEntity.createChild("light");
lightEntity.addComponent(DirectLight);
lightEntity.transform.setRotation(-45, 0, 0);
// init main camera
const mainCameraEntity = rootEntity.createChild("camera");
const mainCamera = mainCameraEntity.addComponent(Camera);
mainCameraEntity.transform.setPosition(0, 0, 20);
mainCameraEntity.transform.lookAt(new Vector3(0, 0, 0));
mainCamera.cullingMask = Layer.Layer0 | Layer.Layer1;
mainCamera.fieldOfView = 35;
mainCamera.nearClipPlane = 1;
mainCamera.farClipPlane = 30;
// add wire frame
rootEntity.addComponent(MeshRenderer);
rootEntity.addComponent(WireframeManager).addCameraWireframe(mainCamera);
// init side camera
const sideCameraEntity = rootEntity.createChild("sideCamera");
const sideCamera = sideCameraEntity.addComponent(Camera);
sideCamera.priority = 1;
sideCamera.viewport.set(0, 0.6, 0.4, 0.4);
sideCameraEntity.transform.setPosition(-45, 0, 0);
sideCameraEntity.transform.rotation.set(0, -90, 0);
sideCamera.cullingMask = Layer.Layer0 | Layer.Layer2;
sideCameraEntity.addComponent(
class extends Script {
onBeginRender(camera: Camera): void {
scene.background.solidColor.set(0, 0, 0, 1);
}
onEndRender(camera: Camera): void {
scene.background.solidColor.set(0.25, 0.25, 0.25, 1.0);
}
}
);
for (let i = 0; i < 30; i++) {
createRandomBox(rootEntity);
}
rootEntity.addComponent(
class extends Script {
private _entities: Entity[] = [];
private _tempRay: Ray = new Ray();
private _tempVec3: Vector3 = new Vector3();
private _hitResult: HitResult = new HitResult();
onUpdate(deltaTime: number): void {
const { pointers } = engine.inputManager;
const {
_tempRay: tempRay,
_entities: entities,
_hitResult: hitResult,
} = this;
for (let i = 0, n = entities.length; i < n; i++) {
entities[i].isActive = false;
}
for (let i = 0, n = pointers.length; i < n; i++) {
mainCamera.screenPointToRay(pointers[i].position, tempRay);
if (scene.physics.raycast(tempRay, 100, hitResult)) {
(
hitResult.entity.getComponent(BoxScript) as BoxScript
).hitFrameCount = engine.time.frameCount;
}
this._adjustByRay(this._getOrCreateRayEntity(i), tempRay);
}
}
private _getOrCreateRayEntity(index: number) {
const { _entities: entities } = this;
let entity = entities[index];
if (!entity) {
entity = entities[index] = this.entity.createChild(`ray${index}`);
const ray = entity.createChild();
ray.layer = Layer.Layer2;
const rayRenderer = ray.addComponent(MeshRenderer);
rayRenderer.mesh = PrimitiveMesh.createCylinder(
engine,
0.05,
0.05,
40
);
ray.transform.position = new Vector3(0, 0, -20);
ray.transform.rotation = new Vector3(-90, 0, 0);
const material = new UnlitMaterial(engine);
material.baseColor = new Color(0, 1, 0);
rayRenderer.setMaterial(material);
const ball = entity.createChild();
ball.layer = Layer.Layer1;
const ballRenderer = ball.addComponent(MeshRenderer);
ballRenderer.mesh = PrimitiveMesh.createSphere(engine, 0.008);
ballRenderer.setMaterial(material);
} else {
entity.isActive = true;
}
return entities[index];
}
private _adjustByRay(rayEntity: Entity, ray: Ray) {
const { _tempVec3: tempVec3 } = this;
const { origin, direction } = ray;
Vector3.scale(direction, 0.5, tempVec3);
Vector3.add(origin, tempVec3, rayEntity.transform.position);
Vector3.add(origin, direction, tempVec3);
rayEntity.transform.lookAt(tempVec3);
}
}
);
// Run engine
engine.run();
}
);
function createRandomBox(root: Entity) {
const { engine } = root;
const boxEntity = root.createChild("box");
boxEntity.transform.setPosition(
5 - Math.random() * 10,
5 - Math.random() * 10,
8 - Math.random() * 16
);
const renderer = boxEntity.addComponent(MeshRenderer);
const width = Math.random() * 1.5 + 0.5;
const height = Math.random() * 1.5 + 0.5;
const depth = Math.random() * 1.5 + 0.5;
renderer.mesh = PrimitiveMesh.createCuboid(engine, width, height, depth);
const material = new BlinnPhongMaterial(engine);
material.baseColor = new Color(1, 1, 1);
renderer.setMaterial(material);
const collider = boxEntity.addComponent(StaticCollider);
const shape = new BoxColliderShape();
shape.size.set(width, height, depth);
collider.addShape(shape);
boxEntity.addComponent(BoxScript);
}
class BoxScript extends Script {
hitFrameCount: number = 0;
private _material: BlinnPhongMaterial;
onStart(): void {
this._material = (
this.entity.getComponent(MeshRenderer) as MeshRenderer
).getMaterial() as BlinnPhongMaterial;
}
onLateUpdate(deltaTime: number): void {
if (this.engine.time.frameCount === this.hitFrameCount) {
this._material.baseColor = new Color(1, 0, 0);
} else {
this._material.baseColor = new Color(1, 1, 1);
}
}
}