123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
- import { WebGLContext } from '../../mol-gl/webgl/context';
- import Scene from '../../mol-gl/scene';
- import { Camera } from '../camera';
- import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
- import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
- import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
- import { GraphicsRenderObject } from '../../mol-gl/render-object';
- import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
- import { ColorNames } from '../../mol-util/color/names';
- import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
- import { Viewport } from '../camera/util';
- import { Sphere3D } from '../../mol-math/geometry';
- import { ParamDefinition as PD } from '../../mol-util/param-definition';
- import produce from 'immer';
- import { Shape } from '../../mol-model/shape';
- // TODO add scale line/grid
- const AxesParams = {
- ...Mesh.Params,
- alpha: { ...Mesh.Params.alpha, defaultValue: 0.33 },
- ignoreLight: { ...Mesh.Params.ignoreLight, defaultValue: true },
- colorX: PD.Color(ColorNames.red, { isEssential: true }),
- colorY: PD.Color(ColorNames.green, { isEssential: true }),
- colorZ: PD.Color(ColorNames.blue, { isEssential: true }),
- scale: PD.Numeric(0.33, { min: 0.1, max: 2, step: 0.1 }, { isEssential: true }),
- };
- type AxesParams = typeof AxesParams
- type AxesProps = PD.Values<AxesParams>
- export const CameraHelperParams = {
- axes: PD.MappedStatic('on', {
- on: PD.Group(AxesParams),
- off: PD.Group({})
- }, { cycle: true, description: 'Show camera orientation axes' }),
- };
- export type CameraHelperParams = typeof CameraHelperParams
- export type CameraHelperProps = PD.Values<CameraHelperParams>
- export class CameraHelper {
- scene: Scene
- camera: Camera
- props: CameraHelperProps = {
- axes: { name: 'off', params: {} }
- }
- private renderObject: GraphicsRenderObject | undefined
- constructor(private webgl: WebGLContext, props: Partial<CameraHelperProps> = {}) {
- this.scene = Scene.create(webgl);
- this.camera = new Camera();
- Vec3.set(this.camera.up, 0, 1, 0);
- Vec3.set(this.camera.target, 0, 0, 0);
- this.setProps(props);
- }
- setProps(props: Partial<CameraHelperProps>) {
- this.props = produce(this.props, p => {
- if (props.axes !== undefined) {
- p.axes.name = props.axes.name;
- if (props.axes.name === 'on') {
- this.scene.clear();
- const params = { ...props.axes.params, scale: props.axes.params.scale * this.webgl.pixelRatio };
- this.renderObject = createAxesRenderObject(params);
- this.scene.add(this.renderObject);
- this.scene.commit();
- Vec3.set(this.camera.position, 0, 0, params.scale * 200);
- Mat4.lookAt(this.camera.view, this.camera.position, this.camera.target, this.camera.up);
- p.axes.params = { ...props.axes.params };
- }
- }
- });
- }
- get isEnabled() {
- return this.props.axes.name === 'on';
- }
- update(camera: Camera) {
- if (!this.renderObject) return;
- updateCamera(this.camera, camera.viewport);
- Mat4.extractRotation(this.scene.view, camera.view);
- const r = this.renderObject.values.boundingSphere.ref.value.radius;
- Mat4.setTranslation(this.scene.view, Vec3.create(
- -camera.viewport.width / 2 + r,
- -camera.viewport.height / 2 + r,
- 0
- ));
- }
- }
- function updateCamera(camera: Camera, viewport: Viewport) {
- const { near, far } = camera;
- const fullLeft = -(viewport.width - viewport.x) / 2;
- const fullRight = (viewport.width - viewport.x) / 2;
- const fullTop = (viewport.height - viewport.y) / 2;
- const fullBottom = -(viewport.height - viewport.y) / 2;
- const dx = (fullRight - fullLeft) / 2;
- const dy = (fullTop - fullBottom) / 2;
- const cx = (fullRight + fullLeft) / 2;
- const cy = (fullTop + fullBottom) / 2;
- const left = cx - dx;
- const right = cx + dx;
- const top = cy + dy;
- const bottom = cy - dy;
- Mat4.ortho(camera.projection, left, right, top, bottom, near, far);
- }
- function createAxesMesh(scale: number, mesh?: Mesh) {
- const state = MeshBuilder.createState(512, 256, mesh);
- const radius = 0.05 * scale;
- const x = Vec3.scale(Vec3(), Vec3.unitX, scale);
- const y = Vec3.scale(Vec3(), Vec3.unitY, scale);
- const z = Vec3.scale(Vec3(), Vec3.unitZ, scale);
- const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
- state.currentGroup = 0;
- addSphere(state, Vec3.origin, radius, 2);
- state.currentGroup = 1;
- addSphere(state, x, radius, 2);
- addCylinder(state, Vec3.origin, x, 1, cylinderProps);
- state.currentGroup = 2;
- addSphere(state, y, radius, 2);
- addCylinder(state, Vec3.origin, y, 1, cylinderProps);
- state.currentGroup = 3;
- addSphere(state, z, radius, 2);
- addCylinder(state, Vec3.origin, z, 1, cylinderProps);
- return MeshBuilder.getMesh(state);
- }
- function getAxesShape(props: AxesProps, shape?: Shape<Mesh>) {
- const scale = 100 * props.scale;
- const mesh = createAxesMesh(scale, shape?.geometry);
- mesh.setBoundingSphere(Sphere3D.create(Vec3.create(scale / 2, scale / 2, scale / 2), scale + scale / 4));
- const getColor = (groupId: number) => {
- switch (groupId) {
- case 1: return props.colorX;
- case 2: return props.colorY;
- case 3: return props.colorZ;
- default: return ColorNames.grey;
- }
- };
- return Shape.create('axes', {}, mesh, getColor, () => 1, () => '');
- }
- function createAxesRenderObject(props: AxesProps) {
- const shape = getAxesShape(props);
- return Shape.createRenderObject(shape, props);
- }
|