|
@@ -9,13 +9,15 @@ import { Camera } from '../mol-canvas3d/camera';
|
|
|
|
|
|
import Scene from './scene';
|
|
|
import { WebGLContext } from './webgl/context';
|
|
|
-import { Mat4, Vec3, Vec4, Vec2 } from '../mol-math/linear-algebra';
|
|
|
+import { Mat4, Vec3, Vec4, Vec2, Quat } from '../mol-math/linear-algebra';
|
|
|
import { Renderable } from './renderable';
|
|
|
import { Color } from '../mol-util/color';
|
|
|
-import { ValueCell } from '../mol-util';
|
|
|
+import { ValueCell, deepEqual } from '../mol-util';
|
|
|
import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema';
|
|
|
import { GraphicsRenderVariant } from './webgl/render-item';
|
|
|
import { ParamDefinition as PD } from '../mol-util/param-definition';
|
|
|
+import { Clipping } from '../mol-theme/clipping';
|
|
|
+import { stringToWords } from '../mol-util/string';
|
|
|
|
|
|
export interface RendererStats {
|
|
|
programCount: number
|
|
@@ -71,6 +73,19 @@ export const RendererParams = {
|
|
|
metallic: PD.Group({}),
|
|
|
plastic: PD.Group({}),
|
|
|
}, { label: 'Lighting', description: 'Style in which the 3D scene is rendered/lighted' }),
|
|
|
+
|
|
|
+ clip: PD.Group({
|
|
|
+ variant: PD.Select('instance', PD.arrayToOptions<Clipping.Variant>(['instance', 'pixel'])),
|
|
|
+ objects: PD.ObjectList({
|
|
|
+ type: PD.Select('plane', PD.objectToOptions(Clipping.Type, t => stringToWords(t))),
|
|
|
+ position: PD.Vec3(Vec3()),
|
|
|
+ rotation: PD.Group({
|
|
|
+ axis: PD.Vec3(Vec3.create(1, 0, 0)),
|
|
|
+ angle: PD.Numeric(0, { min: -180, max: 180, step: 0.1 }),
|
|
|
+ }, { isExpanded: true }),
|
|
|
+ scale: PD.Vec3(Vec3.create(1, 1, 1)),
|
|
|
+ }, o => stringToWords(o.type))
|
|
|
+ })
|
|
|
};
|
|
|
export type RendererProps = PD.Values<typeof RendererParams>
|
|
|
|
|
@@ -106,11 +121,44 @@ function getStyle(props: RendererProps['style']) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+type Clip = {
|
|
|
+ variant: Clipping.Variant
|
|
|
+ objects: {
|
|
|
+ count: number
|
|
|
+ type: number[]
|
|
|
+ position: number[]
|
|
|
+ rotation: number[]
|
|
|
+ scale: number[]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const tmpQuat = Quat();
|
|
|
+function getClip(props: RendererProps['clip'], clip?: Clip): Clip {
|
|
|
+ const { type, position, rotation, scale } = clip?.objects || {
|
|
|
+ type: (new Array(5)).fill(1),
|
|
|
+ position: (new Array(5 * 3)).fill(0),
|
|
|
+ rotation: (new Array(5 * 4)).fill(0),
|
|
|
+ scale: (new Array(5 * 3)).fill(1),
|
|
|
+ };
|
|
|
+ for (let i = 0, il = props.objects.length; i < il; ++i) {
|
|
|
+ const p = props.objects[i];
|
|
|
+ type[i] = Clipping.Type[p.type];
|
|
|
+ Vec3.toArray(p.position, position, i * 3);
|
|
|
+ Quat.toArray(Quat.setAxisAngle(tmpQuat, p.rotation.axis, p.rotation.angle), rotation, i * 4);
|
|
|
+ Vec3.toArray(p.scale, scale, i * 3);
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ variant: props.variant,
|
|
|
+ objects: { count: props.objects.length, type, position, rotation, scale }
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
namespace Renderer {
|
|
|
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
|
|
|
const { gl, state, stats } = ctx;
|
|
|
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
|
|
|
const style = getStyle(p.style);
|
|
|
+ const clip = getClip(p.clip);
|
|
|
|
|
|
const viewport = Viewport();
|
|
|
const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor);
|
|
@@ -151,6 +199,11 @@ namespace Renderer {
|
|
|
uFogColor: ValueCell.create(bgColor),
|
|
|
uTransparentBackground: ValueCell.create(0),
|
|
|
|
|
|
+ uClipObjectType: ValueCell.create(clip.objects.type),
|
|
|
+ uClipObjectPosition: ValueCell.create(clip.objects.position),
|
|
|
+ uClipObjectRotation: ValueCell.create(clip.objects.rotation),
|
|
|
+ uClipObjectScale: ValueCell.create(clip.objects.scale),
|
|
|
+
|
|
|
// the following are general 'material' uniforms
|
|
|
uLightIntensity: ValueCell.create(style.lightIntensity),
|
|
|
uAmbientIntensity: ValueCell.create(style.ambientIntensity),
|
|
@@ -177,6 +230,17 @@ namespace Renderer {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ let definesNeedUpdate = false;
|
|
|
+ if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
|
|
|
+ ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
|
|
|
+ definesNeedUpdate = true;
|
|
|
+ }
|
|
|
+ if (r.values.dClipVariant.ref.value !== clip.variant) {
|
|
|
+ ValueCell.update(r.values.dClipVariant, clip.variant);
|
|
|
+ definesNeedUpdate = true;
|
|
|
+ }
|
|
|
+ if (definesNeedUpdate) r.update();
|
|
|
+
|
|
|
const program = r.getProgram(variant);
|
|
|
if (state.currentProgramId !== program.id) {
|
|
|
// console.log('new program')
|
|
@@ -341,6 +405,15 @@ namespace Renderer {
|
|
|
ValueCell.updateIfChanged(globalUniforms.uRoughness, style.roughness);
|
|
|
ValueCell.updateIfChanged(globalUniforms.uReflectivity, style.reflectivity);
|
|
|
}
|
|
|
+
|
|
|
+ if (props.clip !== undefined && !deepEqual(props.clip, p.clip)) {
|
|
|
+ p.clip = props.clip;
|
|
|
+ Object.assign(clip, getClip(props.clip, clip));
|
|
|
+ ValueCell.update(globalUniforms.uClipObjectPosition, clip.objects.position);
|
|
|
+ ValueCell.update(globalUniforms.uClipObjectRotation, clip.objects.rotation);
|
|
|
+ ValueCell.update(globalUniforms.uClipObjectScale, clip.objects.scale);
|
|
|
+ ValueCell.update(globalUniforms.uClipObjectType, clip.objects.type);
|
|
|
+ }
|
|
|
},
|
|
|
setViewport: (x: number, y: number, width: number, height: number) => {
|
|
|
gl.viewport(x, y, width, height);
|