diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index 9fa66b0352c12dc84f0480a7ca8e6d1d16c03443..1e284a0a12b47738f2d32ca9dd550e07f9fbdbcd 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -40,7 +40,7 @@ export type ColorTheme = keyof typeof ColorTheme export default class State { viewer: Viewer - pdbId = '' + pdbId = '1crn' // pdbId = '5ire' emdId = '8116' // pdbId = '6G1K' @@ -77,6 +77,7 @@ export default class State { getSpacefillProps (): SpacefillProps { const colorThemeName = this.colorTheme.getValue() return { + doubleSided: true, detail: this.sphereDetail.getValue(), colorTheme: colorThemeName === 'uniform' ? { name: colorThemeName, value: this.colorValue.getValue() } : @@ -161,7 +162,10 @@ export default class State { this.surfaceRepr = VolumeRepresentation(Surface) await Run(this.surfaceRepr.create(v.volume, { isoValue: VolumeIsoValue.relative(v.volume.dataStats, 3.0), - alpha: 1.0 + alpha: 0.5, + flatShaded: false, + flipSided: true, + doubleSided: true }), log, 500) viewer.add(this.surfaceRepr) diff --git a/src/mol-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index 5b567b3690edc44332e491ef1cc7fe16942146fd..71f317e39c3a31e83cce6d0f63a3f25201406e29 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/src/mol-geo/representation/structure/point.ts @@ -20,7 +20,8 @@ import { deepEqual } from 'mol-util'; export const DefaultPointProps = { colorTheme: { name: 'instance-index' } as ColorTheme, sizeTheme: { name: 'vdw' } as SizeTheme, - alpha: 1 + alpha: 1, + visible: true } export type PointProps = Partial<typeof DefaultPointProps> @@ -60,7 +61,7 @@ export default function Point(): UnitsRepresentation<PointProps> { _units = units _elementGroup = elementGroup - const { colorTheme, sizeTheme, alpha } = curProps + const { colorTheme, sizeTheme, alpha, visible } = curProps const elementCount = OrderedSet.size(elementGroup.elements) const unitCount = units.length @@ -86,6 +87,7 @@ export default function Point(): UnitsRepresentation<PointProps> { points = createPointRenderObject({ objectId: 0, alpha, + visible, position: ValueCell.create(vertices), id: ValueCell.create(fillSerial(new Float32Array(elementCount))), diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index a4b2c485182b94c3697c1ea67d06efaaa969f457..689f2bfc9bfe0bfac81346a6294d5ae2330e2620 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -23,7 +23,9 @@ import { icosahedronVertexCount } from '../../primitive/icosahedron'; export const DefaultSpacefillProps = { detail: 0, colorTheme: { name: 'instance-index' } as ColorTheme, - alpha: 1 + alpha: 1, + visible: true, + doubleSided: false } export type SpacefillProps = Partial<typeof DefaultSpacefillProps> @@ -79,7 +81,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { return Task.create('Spacefill.create', async ctx => { renderObjects.length = 0 // clear - const { detail, colorTheme, alpha } = { ...DefaultSpacefillProps, ...props } + const { detail, colorTheme, alpha, visible, doubleSided } = { ...DefaultSpacefillProps, ...props } await ctx.update('Computing spacefill mesh'); const mesh = await ctx.runChild(createSpacefillMesh(units[0], elementGroup, detail)) @@ -96,6 +98,8 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { spheres = createMeshRenderObject({ objectId: 0, alpha, + visible, + doubleSided, position: mesh.vertexBuffer, normal: mesh.normalBuffer as ValueCell<Float32Array>, diff --git a/src/mol-geo/representation/volume/surface.ts b/src/mol-geo/representation/volume/surface.ts index 2961c611c0971cec31c839c8169240a7677c75b5..a2de1a61d10cef902ee848ee9efb6ddab6572ead 100644 --- a/src/mol-geo/representation/volume/surface.ts +++ b/src/mol-geo/representation/volume/surface.ts @@ -36,6 +36,7 @@ export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValu export const DefaultSurfaceProps = { isoValue: VolumeIsoValue.relative({ min: 0, max: 0, mean: 0, sigma: 0 }, 0), alpha: 0.5, + visible: true, flatShaded: true, flipSided: true, doubleSided: true @@ -53,13 +54,17 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> { return Task.create('Point.create', async ctx => { renderObjects.length = 0 // clear curProps = { ...DefaultSurfaceProps, ...props } - const { alpha, flatShaded, flipSided, doubleSided } = curProps + const { alpha, visible, flatShaded, flipSided, doubleSided } = curProps const mesh = await ctx.runChild(computeVolumeSurface(volume, curProps.isoValue)) + if (!flatShaded) { + Mesh.computeNormalsImmediate(mesh) + } surface = createMeshRenderObject({ objectId: 0, alpha, + visible, position: mesh.vertexBuffer, normal: mesh.normalBuffer, diff --git a/src/mol-geo/shape/mesh.ts b/src/mol-geo/shape/mesh.ts index fcd4ac207f5e8dfb879c3ecacdb4ef33d4525bd8..fcbb9c0f9f4bdf91acde9c734236d77e670a3222 100644 --- a/src/mol-geo/shape/mesh.ts +++ b/src/mol-geo/shape/mesh.ts @@ -8,7 +8,7 @@ import { Task } from 'mol-task' import { ValueCell } from 'mol-util' import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { Sphere3D } from 'mol-math/geometry' -import { transformPositionArray } from '../util'; +import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../util'; export interface Mesh { /** Number of vertices in the mesh */ @@ -86,7 +86,12 @@ export namespace Mesh { export function transformRangeImmediate(mesh: Mesh, t: Mat4, offset: number, count: number) { transformPositionArray(t, mesh.vertexBuffer.ref.value, offset, count) - // transformDirectionArray(n, mesh.normalBuffer.ref.value, offset, count) // TODO + // TODO normals transformation does not work for an unknown reason, ASR + // if (mesh.normalBuffer.ref.value) { + // const n = getNormalMatrix(Mat3.zero(), t) + // transformDirectionArray(n, mesh.normalBuffer.ref.value, offset, count) + // mesh.normalsComputed = true; + // } mesh.normalsComputed = false; // mesh.boundingSphere = void 0; } diff --git a/src/mol-geo/util.ts b/src/mol-geo/util.ts index 0f417021677d1341368c68c8359b31c70873fe4d..ad2156c06c38cccc9ec3ae6b5b87745276785738 100644 --- a/src/mol-geo/util.ts +++ b/src/mol-geo/util.ts @@ -19,21 +19,28 @@ export function normalizeVec3Array<T extends Helpers.NumberArray> (a: T) { } } -const tmpV = Vec3.zero() +export function getNormalMatrix(out: Mat3, t: Mat4) { + Mat3.fromMat4(out, t) + Mat3.invert(out, out) + Mat3.transpose(out, out) + return out +} + +const tmpV3 = Vec3.zero() export function transformPositionArray (t: Mat4, array: Helpers.NumberArray, offset: number, count: number) { for (let i = 0, il = count * 3; i < il; i += 3) { - Vec3.fromArray(tmpV, array, offset + i) - Vec3.transformMat4(tmpV, tmpV, t) - Vec3.toArray(tmpV, array, offset + i) + Vec3.fromArray(tmpV3, array, offset + i) + Vec3.transformMat4(tmpV3, tmpV3, t) + Vec3.toArray(tmpV3, array, offset + i) } } export function transformDirectionArray (n: Mat3, array: Helpers.NumberArray, offset: number, count: number) { for (let i = 0, il = count * 3; i < il; i += 3) { - Vec3.fromArray(tmpV, array, offset + i) - Vec3.transformMat3(tmpV, tmpV, n) - Vec3.toArray(tmpV, array, offset + i) + Vec3.fromArray(tmpV3, array, offset + i) + Vec3.transformMat3(tmpV3, tmpV3, n) + Vec3.toArray(tmpV3, array, offset + i) } } diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index ff80f1218f50efd3be9b7b0de4c3f63c9e87152a..fc669697aacf4f56b162b01040528908c06e5507 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -51,6 +51,7 @@ function createPoints() { return createPointRenderObject({ objectId: 0, alpha: 1.0, + visible: true, position, id, diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index fc4228d4b288483e1ffda04a2ce12310c9f229a1..d889f89c64a18487b75f82524ed125813dfec47e 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -8,6 +8,16 @@ import PointRenderable from './renderable/point' import MeshRenderable from './renderable/mesh' import { Program } from './webgl/program'; +export type BaseProps = { + objectId: number + alpha: number + visible: boolean + + flatShaded?: boolean + doubleSided?: boolean + flipSided?: boolean +} + export interface Renderable<T> { draw: () => void name: string diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index 91d3e432921ae39e5083be49cec733dac261beaa..71ddafacecf8bb7affb5f2eba98debb8d004a066 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -7,7 +7,7 @@ import { ValueCell } from 'mol-util/value-cell' import { ColorData } from 'mol-geo/util/color-data'; -import { Renderable } from '../renderable' +import { Renderable, BaseProps } from '../renderable' import { getBaseDefs, getBaseValues, getBaseDefines } from './util' import { MeshShaderCode, addShaderDefines } from '../shader-code' import { Context } from '../webgl/context'; @@ -17,9 +17,6 @@ type Mesh = 'mesh' namespace Mesh { export type Props = { - objectId: number - alpha: number - position: ValueCell<Float32Array> normal: ValueCell<Float32Array | undefined> id: ValueCell<Float32Array> @@ -32,11 +29,7 @@ namespace Mesh { instanceCount: number elementCount: number positionCount: number - - flatShaded?: boolean - doubleSided?: boolean - flipSided?: boolean - } + } & BaseProps export function create(ctx: Context, props: Props): Renderable<Props> { const defines = getBaseDefines(props) diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts index f0d4b78e0c605432bef6083095c2a251c4d4052f..83b206dde577e0aad08a15f3b735334bfd9e7b4a 100644 --- a/src/mol-gl/renderable/point.ts +++ b/src/mol-gl/renderable/point.ts @@ -6,7 +6,7 @@ import { ValueCell } from 'mol-util/value-cell' -import { Renderable } from '../renderable' +import { Renderable, BaseProps } from '../renderable' import { getBaseValues, getBaseDefs, getBaseDefines } from './util' import { PointShaderCode, addShaderDefines } from '../shader-code' import { ColorData } from 'mol-geo/util/color-data'; @@ -18,9 +18,6 @@ type Point = 'point' namespace Point { export type Props = { - objectId: number - alpha: number - position: ValueCell<Float32Array> id: ValueCell<Float32Array> @@ -33,7 +30,7 @@ namespace Point { positionCount: number, usePointSizeAttenuation?: boolean - } + } & BaseProps export function create<T = Props>(ctx: Context, props: Props): Renderable<Props> { const defines = getBaseDefines(props) diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index aea1cc8386a8f994b9f69f2beec6e3c8c6fd4b5b..5aa260f1248e25744c7a3d32da4624bb3e20da4c 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -69,8 +69,22 @@ namespace Renderer { let currentProgramId = -1 const drawObject = (r: Renderable<any>, o: RenderObject) => { - if (o.visible) { + if (o.props.visible) { if (currentProgramId !== r.program.id) { + if (o.props.doubleSided) { + gl.disable(gl.CULL_FACE) + } else { + gl.enable(gl.CULL_FACE) + } + + if (o.props.flipSided) { + gl.frontFace(gl.CW) + gl.cullFace(gl.FRONT) + } else { + gl.frontFace(gl.CCW) + gl.cullFace(gl.BACK) + } + r.program.use() r.program.setUniforms({ model, diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 28789540a70daf65c75a0b7e14bbc44561637c07..5e7c91763c4f73f327bb51f8b35da1442da72c23 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -16,16 +16,16 @@ function getNextId() { export type RenderData = { [k: string]: ValueCell<Helpers.TypedArray> } -export interface BaseRenderObject { id: number, type: string, props: {}, visible: boolean, transparent: boolean } +export interface BaseRenderObject { id: number, type: string, props: {} } export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', props: MeshRenderable.Props } export interface PointRenderObject extends BaseRenderObject { type: 'point', props: PointRenderable.Props } export type RenderObject = MeshRenderObject | PointRenderObject export function createMeshRenderObject(props: MeshRenderable.Props): MeshRenderObject { - return { id: getNextId(), type: 'mesh', props, visible: true, transparent: props.alpha < 1 } + return { id: getNextId(), type: 'mesh', props } } export function createPointRenderObject(props: PointRenderable.Props): PointRenderObject { - return { id: getNextId(), type: 'point', props, visible: true, transparent: props.alpha < 1 } + return { id: getNextId(), type: 'point', props } } export function createRenderable(ctx: Context, o: RenderObject) { @@ -73,12 +73,12 @@ namespace Scene { }, eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => { renderableMap.forEach((r, o) => { - if (!o.transparent) callbackFn(r, o) + if (o.props.alpha === 1) callbackFn(r, o) }) }, eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => { renderableMap.forEach((r, o) => { - if (o.transparent) callbackFn(r, o) + if (o.props.alpha < 1) callbackFn(r, o) }) }, get count() { diff --git a/src/mol-gl/shader/mesh.frag b/src/mol-gl/shader/mesh.frag index 513593c2c0c9e2d40334e2247ca86cd393a50925..9b4fb18cfbb799bfd4a0a9c794c041bb8a5824b4 100644 --- a/src/mol-gl/shader/mesh.frag +++ b/src/mol-gl/shader/mesh.frag @@ -48,10 +48,7 @@ void main() { #ifdef FLAT_SHADED vec3 fdx = dFdx(vViewPosition); vec3 fdy = dFdy(vViewPosition); - vec3 N = normalize(cross(fdx, fdy)); - #ifdef FLIP_SIDED - N = -N; - #endif + vec3 N = -normalize(cross(fdx, fdy)); #else vec3 N = -normalize(vNormal); #ifdef DOUBLE_SIDED diff --git a/src/mol-gl/shader/mesh.vert b/src/mol-gl/shader/mesh.vert index 4a8a196ac5324ca0bd41a114549868bd01066ff6..27ac8270222f783511e8b4da379a839f59df1352 100644 --- a/src/mol-gl/shader/mesh.vert +++ b/src/mol-gl/shader/mesh.vert @@ -39,8 +39,8 @@ void main(){ #ifndef FLAT_SHADED mat3 normalMatrix = transpose(inverse(mat3(modelView))); - vec3 transformedNormal = normalize(normalMatrix * normal); - #ifdef FLIP_SIDED + vec3 transformedNormal = normalize(normalMatrix * normalize(normal)); + #if defined(FLIP_SIDED) && !defined(DOUBLE_SIDED) // TODO checking DOUBLE_SIDED should not be required, ASR transformedNormal = -transformedNormal; #endif vNormal = transformedNormal; diff --git a/src/mol-math/linear-algebra/3d/mat3.ts b/src/mol-math/linear-algebra/3d/mat3.ts index bb2d47c5efbd2925ad82157164ccf199c4f54465..cd577776b23b29e6abbd21049ae009ce42ce3a50 100644 --- a/src/mol-math/linear-algebra/3d/mat3.ts +++ b/src/mol-math/linear-algebra/3d/mat3.ts @@ -179,6 +179,19 @@ namespace Mat3 { out[8] = (a11 * a00 - a01 * a10) * det; return out; } + + export function determinant(a: Mat3) { + const a00 = a[0], a01 = a[1], a02 = a[2]; + const a10 = a[3], a11 = a[4], a12 = a[5]; + const a20 = a[6], a21 = a[7], a22 = a[8]; + + const b01 = a22 * a11 - a12 * a21; + const b11 = -a22 * a10 + a12 * a20; + const b21 = a21 * a10 - a11 * a20; + + // Calculate the determinant + return a00 * b01 + a01 * b11 + a02 * b21; + } } export default Mat3 \ No newline at end of file diff --git a/src/mol-math/linear-algebra/3d/vec4.ts b/src/mol-math/linear-algebra/3d/vec4.ts index f481cf99d7224a0356b14eb827934f4b635dbb2f..54e2cda220efba0d36d0eaed13867cd9f820c55c 100644 --- a/src/mol-math/linear-algebra/3d/vec4.ts +++ b/src/mol-math/linear-algebra/3d/vec4.ts @@ -62,6 +62,20 @@ namespace Vec4 { return a } + export function toVec3Array(a: Vec4, out: Helpers.NumberArray, offset: number) { + out[offset + 0] = a[0]; + out[offset + 1] = a[1]; + out[offset + 2] = a[2]; + } + + export function fromVec3Array(a: Vec4, array: Helpers.NumberArray, offset: number) { + a[0] = array[offset + 0] + a[1] = array[offset + 1] + a[2] = array[offset + 2] + a[3] = 0 + return a + } + export function copy(out: Vec4, a: Vec4) { out[0] = a[0]; out[1] = a[1]; diff --git a/src/mol-view/viewer.ts b/src/mol-view/viewer.ts index 54a908c04b77f1c801dd93b806f6eccc0d56e0c9..4e8d9c26230f39a4407a14e79c137811f6bb9cb2 100644 --- a/src/mol-view/viewer.ts +++ b/src/mol-view/viewer.ts @@ -106,11 +106,11 @@ namespace Viewer { return { hide: (repr: Representation<any>) => { const renderObjectSet = reprMap.get(repr) - if (renderObjectSet) renderObjectSet.forEach(o => o.visible = false) + if (renderObjectSet) renderObjectSet.forEach(o => o.props.visible = false) }, show: (repr: Representation<any>) => { const renderObjectSet = reprMap.get(repr) - if (renderObjectSet) renderObjectSet.forEach(o => o.visible = true) + if (renderObjectSet) renderObjectSet.forEach(o => o.props.visible = true) }, add: (repr: Representation<any>) => {