diff --git a/src/mol-app/ui/transform/spacefill.tsx b/src/mol-app/ui/transform/spacefill.tsx index 0c102bc10480479bb35dfeb89eb1b94e71f223da..d613dbaa4a2c39bb4a4dd8a105462c726281ff8c 100644 --- a/src/mol-app/ui/transform/spacefill.tsx +++ b/src/mol-app/ui/transform/spacefill.tsx @@ -30,6 +30,8 @@ export type ColorThemeInfo = keyof typeof ColorThemeInfo interface SpacefillState { doubleSided: boolean + flipSided: boolean + flatShaded: boolean detail: number colorTheme: ColorTheme colorValue: Color @@ -41,6 +43,8 @@ interface SpacefillState { export class Spacefill extends View<Controller<any>, SpacefillState, { transform: SpacefillUpdate, entity: SpacefillEntity, ctx: StateContext }> { state = { doubleSided: true, + flipSided: false, + flatShaded: false, detail: 2, colorTheme: { name: 'element-symbol' } as ColorTheme, colorValue: 0x000000, @@ -160,6 +164,33 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform /> </div> </div> + <div className='molstar-control-row molstar-options-group'> + <div> + <Toggle + value={this.state.doubleSided} + label='Double sided' + onChange={value => this.update({ doubleSided: value })} + /> + </div> + </div> + <div className='molstar-control-row molstar-options-group'> + <div> + <Toggle + value={this.state.flipSided} + label='Flip sided' + onChange={value => this.update({ flipSided: value })} + /> + </div> + </div> + <div className='molstar-control-row molstar-options-group'> + <div> + <Toggle + value={this.state.flatShaded} + label='Flat shaded' + onChange={value => this.update({ flatShaded: value })} + /> + </div> + </div> <div className='molstar-control-row molstar-options-group'> <div> <Slider diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index 9d283761bc08e09fd5cac33f0dfe1ea3101df368..11f21138d1a0604fc873c5a7cbfd6cef097d6746 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -71,6 +71,7 @@ export function StructureRepresentation<P extends StructureProps>(reprCtor: () = const { repr, group } = groupRepr const state = { message: 'Updating structure unit representations...', current: i, max: il }; if (!await repr.update(props).runAsChild(ctx, state)) { + console.log('update failed, need to rebuild') await repr.create(group, props).runAsChild(ctx, state) } renderObjects.push(...repr.renderObjects) diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index 71e8b31e256e6de13f6468f2b6ce9e747d68c9f9..de7352294e8b09aea3eac47495f085874f30e06a 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -25,16 +25,18 @@ import { Mesh } from '../../shape/mesh'; export const DefaultSpacefillProps = { ...DefaultStructureProps, + flipSided: false, + flatShaded: false, detail: 0, } export type SpacefillProps = Partial<typeof DefaultSpacefillProps> -function createSpacefillMesh(unit: Unit, detail: number) { +function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) { return Task.create('Sphere mesh', async ctx => { const { elements } = unit; const elementCount = elements.length; const vertexCount = elementCount * icosahedronVertexCount(detail) - const meshBuilder = MeshBuilder.create(vertexCount) + const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh) let radius: Element.Property<number> if (Unit.isAtomic(unit)) { @@ -68,7 +70,9 @@ function createSpacefillMesh(unit: Unit, detail: number) { } } - return meshBuilder.getMesh() + const _mesh = meshBuilder.getMesh() + console.log(_mesh) + return _mesh }) } @@ -78,6 +82,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { let currentProps: typeof DefaultSpacefillProps let mesh: Mesh let currentGroup: Unit.SymmetryGroup + let vertexMap: VertexMap return { renderObjects, @@ -92,8 +97,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh') // console.log(mesh) - - const vertexMap = VertexMap.fromMesh(mesh) + vertexMap = VertexMap.fromMesh(mesh) await ctx.update('Computing spacefill transforms'); const transforms = createTransforms(group) @@ -120,8 +124,8 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { instanceCount: ValueCell.create(instanceCount), dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)), - dFlatShaded: ValueCell.create(false), - dFlipSided: ValueCell.create(false), + dFlatShaded: ValueCell.create(defaults(props.flatShaded, false)), + dFlipSided: ValueCell.create(defaults(props.flipSided, false)), } const state: RenderableState = { depthMask: defaults(props.depthMask, true), @@ -137,22 +141,39 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { return Task.create('Spacefill.update', async ctx => { if (!spheres) return false - // if (newProps.detail !== currentProps.detail) return false - if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) return false + + let updateColor = false if (newProps.detail !== currentProps.detail) { - await createSpacefillMesh(currentGroup.units[0], newProps.detail).runAsChild(ctx, 'Computing spacefill mesh') - const vertexMap = VertexMap.fromMesh(mesh) + mesh = await createSpacefillMesh(currentGroup.units[0], newProps.detail, mesh).runAsChild(ctx, 'Computing spacefill mesh') + ValueCell.update(spheres.values.drawCount, mesh.triangleCount * 3) + // TODO update in-place + vertexMap = VertexMap.fromMesh(mesh) + updateColor = true + } - await ctx.update('Computing spacefill transforms'); - createTransforms(currentGroup) + if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) { + updateColor = true + } + if (updateColor) { await ctx.update('Computing spacefill colors'); - createColors(currentGroup, vertexMap, newProps.colorTheme) + createColors(currentGroup, vertexMap, newProps.colorTheme, spheres.values) } - ValueCell.update(spheres.values.uAlpha, newProps.alpha) - ValueCell.update(spheres.values.dDoubleSided, newProps.doubleSided) + // TODO handle in a generic way + if (spheres.values.uAlpha.ref.value !== newProps.alpha) { + ValueCell.update(spheres.values.uAlpha, newProps.alpha) + } + if (spheres.values.dDoubleSided.ref.value !== newProps.doubleSided) { + ValueCell.update(spheres.values.dDoubleSided, newProps.doubleSided) + } + if (spheres.values.dFlipSided.ref.value !== newProps.flipSided) { + ValueCell.update(spheres.values.dFlipSided, newProps.flipSided) + } + if (spheres.values.dFlatShaded.ref.value !== newProps.flatShaded) { + ValueCell.update(spheres.values.dFlatShaded, newProps.flatShaded) + } spheres.state.visible = newProps.visible spheres.state.depthMask = newProps.depthMask diff --git a/src/mol-geo/shape/mesh-builder.ts b/src/mol-geo/shape/mesh-builder.ts index d628ce4aa94690f86daac3dd1fccd30887c8f83f..b34f2b5c9fe025c52c72e1851ca2347e5da9eb1c 100644 --- a/src/mol-geo/shape/mesh-builder.ts +++ b/src/mol-geo/shape/mesh-builder.ts @@ -92,19 +92,23 @@ export namespace MeshBuilder { }, getMesh: () => { ChunkedArray.add(offsets, vertices.elementCount) - const mesh = { + const vb = ChunkedArray.compact(vertices, true) as Float32Array + const ib = ChunkedArray.compact(indices, true) as Uint32Array + const nb = ChunkedArray.compact(normals, true) as Float32Array + const idb = ChunkedArray.compact(ids, true) as Float32Array + const ob = ChunkedArray.compact(offsets, true) as Uint32Array + return { vertexCount: vertices.elementCount, triangleCount: indices.elementCount, offsetCount: offsets.elementCount, - vertexBuffer: ValueCell.create(ChunkedArray.compact(vertices, true) as Float32Array), - indexBuffer: ValueCell.create(ChunkedArray.compact(indices, true) as Uint32Array), - normalBuffer: ValueCell.create(ChunkedArray.compact(normals, true) as Float32Array), - idBuffer: ValueCell.create(ChunkedArray.compact(ids, true) as Float32Array), - offsetBuffer: ValueCell.create(ChunkedArray.compact(offsets, true) as Uint32Array), + vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb), + indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib), + normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb), + idBuffer: mesh ? ValueCell.update(mesh.idBuffer, idb) : ValueCell.create(idb), + offsetBuffer: mesh ? ValueCell.update(mesh.offsetBuffer, ob) : ValueCell.create(ob), normalsComputed: true, offsetsComputed: true, } - return mesh } } } diff --git a/src/mol-geo/util/color-data.ts b/src/mol-geo/util/color-data.ts index f36dfe7305cdd9b1a5692aeb8710da760c3664c3..09dfc525f08bc14a226198cc67f285cf7dcb01f1 100644 --- a/src/mol-geo/util/color-data.ts +++ b/src/mol-geo/util/color-data.ts @@ -28,7 +28,9 @@ export interface UniformColorProps { export function createUniformColor(props: UniformColorProps, colorData?: ColorData): ColorData { if (colorData) { ValueCell.update(colorData.uColor, Color.toRgbNormalized(props.value) as Vec3) - ValueCell.update(colorData.dColorType, 'uniform') + if (colorData.dColorType.ref.value !== 'uniform') { + ValueCell.update(colorData.dColorType, 'uniform') + } return colorData } else { return { @@ -60,8 +62,11 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col } } if (colorData) { + console.log('update colordata attribute') ValueCell.update(colorData.aColor, colors) - ValueCell.update(colorData.dColorType, 'attribute') + if (colorData.dColorType.ref.value !== 'attribute') { + ValueCell.update(colorData.dColorType, 'attribute') + } return colorData } else { return { @@ -76,9 +81,12 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col export function createTextureColor(colors: TextureImage, type: ColorType, colorData?: ColorData): ColorData { if (colorData) { + console.log('update colordata texture') ValueCell.update(colorData.tColor, colors) ValueCell.update(colorData.uColorTexSize, Vec2.create(colors.width, colors.height)) - ValueCell.update(colorData.dColorType, type) + if (colorData.dColorType.ref.value !== type) { + ValueCell.update(colorData.dColorType, type) + } return colorData } else { return { @@ -103,7 +111,7 @@ export function createInstanceColor(props: InstanceColorProps, colorData?: Color for (let i = 0; i < instanceCount; i++) { Color.toArray(colorFn(i), colors.array, i * 3) } - return createTextureColor(colors, 'instance') + return createTextureColor(colors, 'instance', colorData) } export interface ElementColorProps { @@ -119,7 +127,7 @@ export function createElementColor(props: ElementColorProps, colorData?: ColorDa for (let i = 0, il = elementCount; i < il; ++i) { Color.toArray(colorFn(i), colors.array, i * 3) } - return createTextureColor(colors, 'element') + return createTextureColor(colors, 'element', colorData) } export interface ElementInstanceColorProps { @@ -141,7 +149,7 @@ export function createElementInstanceColor(props: ElementInstanceColorProps, col colorOffset += 3 } } - return createTextureColor(colors, 'elementInstance') + return createTextureColor(colors, 'elementInstance', colorData) } /** Create color attribute or texture, depending on the vertexMap */ diff --git a/src/mol-gl/webgl/buffer.ts b/src/mol-gl/webgl/buffer.ts index 7a82d95a748ee612d98bf85da807875b894505ed..bde6066f1cd4aa9ca214ed43e8f926a5495f3361 100644 --- a/src/mol-gl/webgl/buffer.ts +++ b/src/mol-gl/webgl/buffer.ts @@ -7,6 +7,9 @@ import { Context } from './context' import { ValueCell } from 'mol-util'; import { RenderableSchema } from '../renderable/schema'; +import { idFactory } from 'mol-util/id-factory'; + +const getNextBufferId = idFactory() export type UsageHint = 'static' | 'dynamic' | 'stream' export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32' @@ -78,6 +81,8 @@ export function getBufferType(ctx: Context, bufferType: BufferType) { } export interface Buffer { + readonly id: number + readonly _buffer: WebGLBuffer readonly _usageHint: number readonly _bufferType: number @@ -117,6 +122,8 @@ export function createBuffer(ctx: Context, array: ArrayType, itemSize: BufferIte ctx.bufferCount += 1 return { + id: getNextBufferId(), + _buffer, _usageHint, _bufferType, diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 1be4125555511dcc81e19b9f4c1644b0eda89ad4..f2556466ea961655417d50e117728fd4e19c640c 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/src/mol-gl/webgl/render-item.ts @@ -5,12 +5,15 @@ */ import { UniformValues } from './uniform'; -import { AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsBuffer } from './buffer'; +import { AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsBuffer, createAttributeBuffer, ArrayKind, AttributeBuffers } from './buffer'; import { TextureValues, createTextures } from './texture'; import { Context } from './context'; import { ShaderCode, addShaderDefines, DefineValues } from '../shader-code'; import { Program } from './program'; -import { RenderableSchema, RenderableValues } from '../renderable/schema'; +import { RenderableSchema, RenderableValues, AttributeSpec } from '../renderable/schema'; +import { idFactory } from 'mol-util/id-factory'; + +const getNextRenderItemId = idFactory() export type DrawMode = 'points' | 'lines' | 'line-strip' | 'line-loop' | 'triangles' | 'triangle-strip' | 'triangle-fan' @@ -41,7 +44,39 @@ function splitValues(schema: RenderableSchema, values: RenderableValues) { return { attributeValues, defineValues, textureValues, uniformValues } } +type Versions<T extends RenderableValues> = { [k in keyof T]: number } +function getValueVersions<T extends RenderableValues>(values: T) { + const versions: Versions<any> = {} + Object.keys(values).forEach(k => { + versions[k] = values[k].ref.version + }) + return versions as Versions<T> +} + +function createVertexArray(ctx: Context, program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) { + const { oesVertexArrayObject } = ctx.extensions + let vertexArray: WebGLVertexArrayObjectOES | undefined = undefined + if (oesVertexArrayObject) { + vertexArray = oesVertexArrayObject.createVertexArrayOES() + oesVertexArrayObject.bindVertexArrayOES(vertexArray) + program.bindAttributes(attributeBuffers) + if (elementsBuffer) elementsBuffer.bind() + ctx.vaoCount += 1 + oesVertexArrayObject.bindVertexArrayOES(null!) + } + return vertexArray +} + +function deleteVertexArray(ctx: Context, vertexArray?: WebGLVertexArrayObjectOES) { + const { oesVertexArrayObject } = ctx.extensions + if (oesVertexArrayObject && vertexArray) { + oesVertexArrayObject.deleteVertexArrayOES(vertexArray) + ctx.vaoCount -= 1 + } +} + export interface RenderItem { + readonly id: number readonly programId: number readonly program: Program @@ -51,40 +86,30 @@ export interface RenderItem { } export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues): RenderItem { + const id = getNextRenderItemId() const { programCache } = ctx const { angleInstancedArrays, oesVertexArrayObject } = ctx.extensions const { attributeValues, defineValues, textureValues, uniformValues } = splitValues(schema, values) + const versions = getValueVersions(values) const glDrawMode = getDrawMode(ctx, drawMode) - const programRef = programCache.get(ctx, { + let programRef = programCache.get(ctx, { shaderCode: addShaderDefines(defineValues, shaderCode), schema }) - const program = programRef.value + let program = programRef.value const textures = createTextures(ctx, schema, textureValues) const attributeBuffers = createAttributeBuffers(ctx, schema, attributeValues) + let elementsBuffer: ElementsBuffer | undefined const elements = values.elements - - let vertexArray: WebGLVertexArrayObjectOES - if (oesVertexArrayObject) { - vertexArray = oesVertexArrayObject.createVertexArrayOES() - oesVertexArrayObject.bindVertexArrayOES(vertexArray) - program.bindAttributes(attributeBuffers) - ctx.vaoCount += 1 - } - - let elementsBuffer: ElementsBuffer if (elements && elements.ref.value) { elementsBuffer = createElementsBuffer(ctx, elements.ref.value) } - // needs to come after elements buffer creation to include it in the vao - if (oesVertexArrayObject) { - oesVertexArrayObject.bindVertexArrayOES(null!) - } + let vertexArray: WebGLVertexArrayObjectOES | undefined = createVertexArray(ctx, program, attributeBuffers, elementsBuffer) let drawCount = values.drawCount.ref let instanceCount = values.instanceCount.ref @@ -92,16 +117,17 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S let destroyed = false return { - programId: program.id, - program, + id, + get programId () { return program.id }, + get program () { return program }, draw: () => { program.setUniforms(uniformValues) - if (oesVertexArrayObject) { + if (oesVertexArrayObject && vertexArray) { oesVertexArrayObject.bindVertexArrayOES(vertexArray) } else { program.bindAttributes(attributeBuffers) - elementsBuffer.bind() + if (elementsBuffer) elementsBuffer.bind() } program.bindTextures(textures) if (elementsBuffer) { @@ -111,6 +137,31 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S } }, update: () => { + let defineChange = false + Object.keys(defineValues).forEach(k => { + const value = defineValues[k] + if (value.ref.version === versions[k]) { + // console.log('define version unchanged', k) + } else { + console.log('define version changed', k) + defineChange = true + versions[k] = value.ref.version + } + }) + + if (defineChange) { + console.log('some defines changed, need to rebuild program') + programRef.free() + // programCache.clear() + // console.log('programCache.count', programCache.count) + programRef = programCache.get(ctx, { + shaderCode: addShaderDefines(defineValues, shaderCode), + schema + }) + program = programRef.value + } + + console.log('RenderItem.update', id, values) if (values.drawCount.ref.version !== drawCount.version) { console.log('drawCount version changed') drawCount = values.drawCount.ref @@ -120,29 +171,64 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S instanceCount = values.instanceCount.ref } - // Object.keys(attributeValues).forEach(k => { - // const value = attributeValues[k] - // if (value === undefined) return - // const buffer = attributeBuffers[k] - // if (buffer.length >= value.length) { - // attributeBuffers[k].updateData(value) - // } else { + let bufferChange = false + + Object.keys(attributeValues).forEach(k => { + const value = attributeValues[k] + if (value.ref.version === versions[k]) { + // console.log('attribute version unchanged', k) + return + } + const buffer = attributeBuffers[k] + if (buffer.length >= value.ref.value.length) { + console.log('attribute array large enough to update', k) + attributeBuffers[k].updateData(value.ref.value) + } else { + console.log('attribute array to small, need to create new attribute', k) + attributeBuffers[k].destroy() + const spec = schema[k] as AttributeSpec<ArrayKind> + attributeBuffers[k] = createAttributeBuffer(ctx, value.ref.value, spec.itemSize, spec.divisor) + bufferChange = true + } + versions[k] = value.ref.version + }) + + if (elementsBuffer && values.elements.ref.version !== versions.elements) { + if (elementsBuffer.length >= values.elements.ref.value.length) { + console.log('elements array large enough to update') + elementsBuffer.updateData(values.elements.ref.value) + } else { + console.log('elements array to small, need to create new elements') + elementsBuffer.destroy() + elementsBuffer = createElementsBuffer(ctx, values.elements.ref.value) + bufferChange = true + } + versions.elements = values.elements.ref.version + } + + if (defineChange || bufferChange) { + console.log('program/defines or buffers changed, rebuild vao') + deleteVertexArray(ctx, vertexArray) + vertexArray = createVertexArray(ctx, program, attributeBuffers, elementsBuffer) + } - // } - // }) + Object.keys(textureValues).forEach(k => { + const value = textureValues[k] + if (value.ref.version === versions[k]) { + // console.log('texture version unchanged', k) + return + } + console.log('texture version changed, uploading image', k) + textures[k].load(value.ref.value) + }) }, destroy: () => { if (destroyed) return programRef.free() Object.keys(textures).forEach(k => textures[k].destroy()) Object.keys(attributeBuffers).forEach(k => attributeBuffers[k].destroy()) - if (elements) { - elementsBuffer.destroy() - } - if (oesVertexArrayObject) { - oesVertexArrayObject.deleteVertexArrayOES(vertexArray) - ctx.vaoCount -= 1 - } + if (elementsBuffer) elementsBuffer.destroy() + deleteVertexArray(ctx, vertexArray) destroyed = true } } diff --git a/src/mol-gl/webgl/shader.ts b/src/mol-gl/webgl/shader.ts index 08bd601d7d46a86286c6443a10786c9283e37bb2..9181afec526382e04b030a61ad83920851eb082e 100644 --- a/src/mol-gl/webgl/shader.ts +++ b/src/mol-gl/webgl/shader.ts @@ -6,6 +6,9 @@ import { createReferenceCache, ReferenceCache } from 'mol-util/reference-cache'; import { Context } from './context'; +import { idFactory } from 'mol-util/id-factory'; + +const getNextShaderId = idFactory() function addLineNumbers(source: string) { const lines = source.split('\n') @@ -18,6 +21,7 @@ function addLineNumbers(source: string) { export type ShaderType = 'vert' | 'frag' export interface ShaderProps { type: ShaderType, source: string } export interface Shader { + readonly id: number attach: (program: WebGLProgram) => void destroy: () => void } @@ -40,6 +44,7 @@ function createShader(ctx: Context, props: ShaderProps): Shader { } return { + id: getNextShaderId(), attach: (program: WebGLProgram) => { gl.attachShader(program, shader) }, diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts index 41c4d5ab41f10c63526cae2f19405bf292ae7b1a..0aa527bcd8309db255d381f40622f1a5ef2cbef9 100644 --- a/src/mol-gl/webgl/texture.ts +++ b/src/mol-gl/webgl/texture.ts @@ -12,7 +12,7 @@ import { idFactory } from 'mol-util/id-factory'; const getNextTextureId = idFactory() export interface Texture { - id: number + readonly id: number load: (image: TextureImage) => void bind: (id: TextureId) => void unbind: (id: TextureId) => void @@ -25,6 +25,7 @@ export type TextureValues = { [k: string]: ValueCell<TextureImage> } export type Textures = { [k: string]: Texture } export function createTexture(ctx: Context): Texture { + const id = getNextTextureId() const { gl } = ctx const texture = gl.createTexture() if (texture === null) { @@ -41,7 +42,7 @@ export function createTexture(ctx: Context): Texture { ctx.textureCount += 1 return { - id: getNextTextureId(), + id, load: (image: TextureImage) => { const { array, width, height } = image gl.bindTexture(_textureType, texture) diff --git a/src/mol-view/state/transform.ts b/src/mol-view/state/transform.ts index 79fdafef8be9855c39ae9141fb6f550b791fb829..f7e16db18dab957787eea9d881c8a39df7d32983 100644 --- a/src/mol-view/state/transform.ts +++ b/src/mol-view/state/transform.ts @@ -116,7 +116,7 @@ export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill const spacefillRepr = spacefillEntity.value await spacefillRepr.update(props).run(ctx.log) ctx.viewer.add(spacefillRepr) - ctx.viewer.update() + // ctx.viewer.update() ctx.viewer.requestDraw() console.log('stats', ctx.viewer.stats) return NullEntity