/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose */ // import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { Viewport } from 'mol-view/camera/util'; import { Camera } from 'mol-view/camera/base'; import Scene from './scene'; import { Context, createImageData } from './webgl/context'; import { Mat4, Vec3 } from 'mol-math/linear-algebra'; import { Renderable } from './renderable'; import { Color } from 'mol-util/color'; import { ValueCell } from 'mol-util'; import { RenderableValues, GlobalUniformValues } from './renderable/schema'; export interface RendererStats { programCount: number shaderCount: number bufferCount: number textureCount: number vaoCount: number } interface Renderer { render: (scene: Scene, pick: boolean) => void setViewport: (viewport: Viewport) => void setClearColor: (color: Color) => void getImageData: () => ImageData stats: RendererStats dispose: () => void } function getPixelRatio() { return (typeof window !== 'undefined') ? window.devicePixelRatio : 1 } export const DefaultRendererProps = { clearColor: 0x000000 as Color, viewport: Viewport.create(0, 0, 0, 0) } export type RendererProps = Partial namespace Renderer { export function create(ctx: Context, camera: Camera, props: RendererProps = {}): Renderer { const { gl } = ctx let { clearColor, viewport: _viewport } = { ...DefaultRendererProps, ...props } const model = Mat4.identity() const viewport = Viewport.clone(_viewport) const pixelRatio = getPixelRatio() // const lightPosition = Vec3.create(0, 0, -100) const lightColor = Vec3.create(1.0, 1.0, 1.0) const lightAmbient = Vec3.create(0.5, 0.5, 0.5) function setClearColor(color: Color) { const [ r, g, b ] = Color.toRgbNormalized(color) gl.clearColor(r, g, b, 1.0) } setClearColor(clearColor) const globalUniforms: GlobalUniformValues = { uModel: ValueCell.create(Mat4.clone(model)), uView: ValueCell.create(Mat4.clone(camera.view)), uProjection: ValueCell.create(Mat4.clone(camera.projection)), uPixelRatio: ValueCell.create(pixelRatio), uViewportHeight: ValueCell.create(viewport.height), uLightColor: ValueCell.create(Vec3.clone(lightColor)), uLightAmbient: ValueCell.create(Vec3.clone(lightAmbient)) } let currentProgramId = -1 const renderObject = (r: Renderable, pick: boolean) => { const program = pick ? r.pickProgram : r.drawProgram if (r.state.visible) { if (currentProgramId !== program.id) { program.use() program.setUniforms(globalUniforms) currentProgramId = program.id } if (r.values.dDoubleSided.ref.value) { gl.disable(gl.CULL_FACE) } else { gl.enable(gl.CULL_FACE) } if (r.values.dFlipSided.ref.value) { gl.frontFace(gl.CW) gl.cullFace(gl.FRONT) } else { gl.frontFace(gl.CCW) gl.cullFace(gl.BACK) } gl.depthMask(r.state.depthMask) if (pick) { r.pick() } else { r.draw() } } } const render = (scene: Scene, pick: boolean) => { ValueCell.update(globalUniforms.uView, camera.view) ValueCell.update(globalUniforms.uProjection, camera.projection) currentProgramId = -1 gl.depthMask(true) gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.disable(gl.BLEND) gl.enable(gl.DEPTH_TEST) scene.eachOpaque((r) => renderObject(r, pick)) gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.enable(gl.BLEND) scene.eachTransparent((r) => renderObject(r, pick)) gl.finish() } return { render, setClearColor, setViewport: (newViewport: Viewport) => { Viewport.copy(viewport, newViewport) gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height) ValueCell.update(globalUniforms.uViewportHeight, viewport.height) }, getImageData: () => { const { width, height } = viewport const buffer = new Uint8Array(width * height * 4) ctx.unbindFramebuffer() ctx.readPixels(0, 0, width, height, buffer) return createImageData(buffer, width, height) }, get stats(): RendererStats { return { programCount: ctx.programCache.count, shaderCount: ctx.shaderCache.count, bufferCount: ctx.bufferCount, textureCount: ctx.textureCount, vaoCount: ctx.vaoCount, } }, dispose: () => { // TODO } } } } export default Renderer