Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View Modes #3259

Merged
merged 71 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
fd86c98
Edge drawing render pass using both depth and normal data
AlexandruPopovici Aug 20, 2024
e5f3410
Merge branch 'main' into alex/WEB-1354
AlexandruPopovici Aug 22, 2024
6d40fa6
Edges are now mixed with the rest of the passes in the final pass
AlexandruPopovici Aug 23, 2024
da87e8a
Set MSAA for SS edges framebuffers to maximum number of samples
AlexandruPopovici Aug 23, 2024
cacbe66
New depth gradient extraction approach. Several improvements over the…
AlexandruPopovici Sep 2, 2024
1789a8d
Some changes to the normal gradient extraction. Using Robert's Cross …
AlexandruPopovici Sep 4, 2024
6dabdd8
Combined static AO with edges
AlexandruPopovici Sep 9, 2024
2237219
Implemented SMAA and static TAA. Both toggeable in realtime for bette…
AlexandruPopovici Sep 10, 2024
9376058
Added the normal pass and fixed some issues
AlexandruPopovici Sep 25, 2024
801c281
The pipeline is now capable of jitter individual passes if requested
AlexandruPopovici Sep 25, 2024
7869833
Added TAA pass. Technically it can be used to antialias anything. Rig…
AlexandruPopovici Sep 25, 2024
ef7c100
Added a TAA pipeline on the final image. Added DPR into pipeline resi…
AlexandruPopovici Oct 1, 2024
143568f
Edges pipeline
AlexandruPopovici Oct 1, 2024
1fe769b
Edges + TAA + AO + COLOR pipeline
AlexandruPopovici Oct 1, 2024
d3ba413
ShadedView mode
AlexandruPopovici Oct 3, 2024
084d4b0
Visibility is now properly reset when coming from a visibility condit…
AlexandruPopovici Oct 7, 2024
2fc5ccb
Added another version for 'shaded' view, inspired by blender's viewpo…
AlexandruPopovici Oct 7, 2024
c575c90
Added pen view together with a sample paper like texture for vissual …
AlexandruPopovici Oct 8, 2024
901fe7d
A form of techincal view
AlexandruPopovici Oct 9, 2024
cbf3f39
Arctic view. Separate viewport pass from matcap pass
AlexandruPopovici Oct 9, 2024
554e45c
Cleaned up the ao shader a bit. Formalized progressive ao pass options
AlexandruPopovici Oct 9, 2024
710d9d3
WIP on BasitMode
AlexandruPopovici Oct 10, 2024
b15add3
Added pipeline switching to the sandbox. Edge pass now has an optiona…
AlexandruPopovici Oct 10, 2024
fe45a5e
Implemented BasicMode using material overrides
AlexandruPopovici Oct 10, 2024
490008d
Merged with latest main
AlexandruPopovici Oct 14, 2024
b422fc3
Moar merging
AlexandruPopovici Oct 14, 2024
ab71daa
Fix merge issues
AlexandruPopovici Oct 14, 2024
cf8bae2
More merge misses
AlexandruPopovici Oct 14, 2024
08a93bf
Stating stream
AlexandruPopovici Oct 14, 2024
58c86f3
Fixed an issue that took hours to debug and it turned out to be a lit…
AlexandruPopovici Oct 14, 2024
19250e8
Formalized the progressive pipeline concept which removed a lot of du…
AlexandruPopovici Oct 14, 2024
eff00ce
Added pipeline reseting. Added pipeline passthrough to allow features…
AlexandruPopovici Oct 15, 2024
9d54ea2
Implemented the required stencil passes used for stencil selection as…
AlexandruPopovici Oct 15, 2024
1a4c7ad
Fixed stencil selection in pen view. Decided to only show outlines be…
AlexandruPopovici Oct 15, 2024
1ebdef0
Implemented proper pass clearing values and flags. Also made sure the…
AlexandruPopovici Oct 16, 2024
13a5c3c
Formalized options for all passes that had any. Tidied up some shader…
AlexandruPopovici Oct 17, 2024
ca47e21
Centralized render target creation and removed a lot of render target…
AlexandruPopovici Oct 18, 2024
4cb9251
Matcap and Viewport passes where merged into a single Viewport pass t…
AlexandruPopovici Oct 21, 2024
eb6aec8
Updated PassReader extension to work with the latest pipeline. NNowth…
AlexandruPopovici Oct 21, 2024
cf77b35
WIP on MRT
AlexandruPopovici Oct 22, 2024
dd5e9b0
Implemented DepthNormal MRT pass and used in a pipeline. Working fine…
AlexandruPopovici Oct 22, 2024
a936807
Full Support for WebGL1.0
AlexandruPopovici Oct 22, 2024
1cfcf77
Fix for viewport material not updating it's texture properly
AlexandruPopovici Oct 23, 2024
84e806f
Added an extension that enables hotkeys for view modes
AlexandruPopovici Oct 23, 2024
d67213c
Merged
AlexandruPopovici Oct 23, 2024
cee30f8
Fixed jittering for orthographic projections
AlexandruPopovici Oct 23, 2024
23fb460
Fixed the section tool integration issues with the new pipelines
AlexandruPopovici Oct 24, 2024
5ef7710
Added MRT versions of the pipelines that need it]
AlexandruPopovici Oct 24, 2024
9474e0d
Added a wrapper ViewModes extensions that defines the existing view m…
AlexandruPopovici Oct 24, 2024
30d90e4
Error fixes
AlexandruPopovici Oct 24, 2024
28b528a
Merged with main
AlexandruPopovici Oct 24, 2024
4ff4ad2
Another error
AlexandruPopovici Oct 24, 2024
2a7e0fb
Added shadowcatcher to arctic and shaded view since it was missing by…
AlexandruPopovici Oct 24, 2024
b47b679
Disabled pen view background texture
AlexandruPopovici Oct 24, 2024
c79fe9a
When reseting the pipeline the jitter index is also reset in order to…
AlexandruPopovici Oct 24, 2024
7a97fc2
Quick solution for integrating view modes with per-object operatins l…
AlexandruPopovici Oct 25, 2024
4c4e82a
View modes now have the ability of overriding the batch's default mat…
AlexandruPopovici Oct 25, 2024
3a1792d
Fiter applies now trigger a hard reset, which is required and fixes a…
AlexandruPopovici Oct 25, 2024
2312f50
Viewport pass can now render transparency. Arctic mode now renders tr…
AlexandruPopovici Oct 25, 2024
3469847
Forcing the multipel render targets we use for MRT to use a 32 bit de…
AlexandruPopovici Oct 25, 2024
2e14f57
Fixed an issue where incorrect visible ranges were being perpetuated …
AlexandruPopovici Oct 28, 2024
fa15408
Merged with main
AlexandruPopovici Oct 28, 2024
d44a8f8
PROPS are not rendered twice anymore and they've been moved to the ov…
AlexandruPopovici Oct 28, 2024
f8549ef
Merge branch 'main' into alex/g-pipeline
AlexandruPopovici Oct 28, 2024
b070ea4
Reorganized passes and pipelines. Deleted old passes
AlexandruPopovici Oct 28, 2024
f8615f1
Renamed all new passes. Only the interface and base pass types have a…
AlexandruPopovici Oct 28, 2024
dc35b82
Fixed some compiling errors
AlexandruPopovici Oct 29, 2024
337956c
Dont change viewmodes when inputs are being used
Mikehrn Oct 29, 2024
d3fcc0e
Merge branch 'alex/g-pipeline' of github.com:specklesystems/speckle-s…
Mikehrn Oct 29, 2024
7ff669c
Pipeline gets resized whenever it's set in the renderer. Some other s…
AlexandruPopovici Oct 29, 2024
8fd0984
Merge branch 'alex/g-pipeline' of github.com:specklesystems/speckle-s…
AlexandruPopovici Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/frontend-2/components/viewer/gendo/Panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ const timeOutWait = ref(false)

const enqueMagic = async () => {
isLoading.value = true
const screenshot = await viewerInstance.getExtension(PassReader).read()
const [depthData, width, height] = await viewerInstance
.getExtension(PassReader)
.read('DEPTH')
const screenshot = PassReader.toBase64(
PassReader.decodeDepth(depthData),
width,
height
)
void lodgeRequest(screenshot)

timeOutWait.value = true
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend-2/lib/viewer/composables/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
import { buildManualPromise } from '@speckle/ui-components'
import { PassReader } from '../extensions/PassReader'
import { ViewModesKeys } from '../extensions/ViewModesKeys'

export type LoadedModel = NonNullable<
Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>
Expand Down Expand Up @@ -337,6 +338,7 @@ function createViewerDataBuilder(params: { viewerDebug: boolean }) {
verbose: !!(import.meta.client && params.viewerDebug)
})
viewer.createExtension(PassReader)
viewer.createExtension(ViewModesKeys)
const initPromise = viewer.init()

return {
Expand Down
81 changes: 52 additions & 29 deletions packages/frontend-2/lib/viewer/extensions/PassReader.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import { type SpecklePass } from '@speckle/viewer'
import type { SpeckleRenderer } from '@speckle/viewer'
import { Extension } from '@speckle/viewer'
import type SpeckleRenderer from '@speckle/viewer/dist/modules/SpeckleRenderer'
import type { WebGLRenderTarget } from 'three'
import { Vector3, Vector4 } from 'three'

export class PassReader extends Extension {
private outputBuffer: Uint8ClampedArray = new Uint8ClampedArray()
private renderTarget: WebGLRenderTarget | undefined = undefined
private renderTarget: WebGLRenderTarget | null = null
private needsRead: boolean = false
private readbackExecutor: ((arg: string) => void) | null = null
private readbackExecutor:
| ((arg: [Uint8ClampedArray, number, number]) => void)
| null = null

public async read(): Promise<string> {
return new Promise<string>((resolve, reject) => {
public async read(passName: string): Promise<[Uint8ClampedArray, number, number]> {
return new Promise<[Uint8ClampedArray, number, number]>((resolve, reject) => {
const renderer: SpeckleRenderer = this.viewer.getRenderer()

const dephPass: SpecklePass = renderer.pipeline.composer
.passes[0] as unknown as SpecklePass
// o_0
const depthPass = renderer.pipeline.getPass(passName)[0]

if (!depthPass) {
reject(`Pipeline does not have a ${passName} pass`)
return
}

this.renderTarget = depthPass.outputTarget

this.renderTarget = dephPass.outputRenderTarget
if (!this.renderTarget) {
reject('Issue with depth pass render target')
reject('Pass does not have a render target assigned')
return
}

Expand All @@ -34,7 +39,7 @@ export class PassReader extends Extension {
}

public onRender(): void {
if (!this.needsRead || this.renderTarget === undefined) return
if (!this.needsRead || !this.renderTarget) return

this.viewer
.getRenderer()
Expand All @@ -46,6 +51,17 @@ export class PassReader extends Extension {
this.renderTarget.height,
this.outputBuffer
)

if (this.readbackExecutor)
this.readbackExecutor([
this.outputBuffer,
this.renderTarget.width,
this.renderTarget.height
])
this.needsRead = false
}

public static decodeDepth(buffer: Uint8ClampedArray): Uint8ClampedArray {
const UnpackDownscale = 255 / 256
const PackFactors = new Vector3(256 * 256 * 256, 256 * 256, 256)
const UnpackFactors = new Vector4(
Expand All @@ -56,31 +72,39 @@ export class PassReader extends Extension {
)

const v4 = new Vector4()
for (let k = 0; k < this.outputBuffer.length; k += 4) {
for (let k = 0; k < buffer.length; k += 4) {
v4.set(
this.outputBuffer[k] / 255,
this.outputBuffer[k + 1] / 255,
this.outputBuffer[k + 2] / 255,
this.outputBuffer[k + 3] / 255
buffer[k] / 255,
buffer[k + 1] / 255,
buffer[k + 2] / 255,
buffer[k + 3] / 255
)
const res = v4.dot(UnpackFactors)
this.outputBuffer[k] = res * 255
this.outputBuffer[k + 1] = res * 255
this.outputBuffer[k + 2] = res * 255
this.outputBuffer[k + 3] = 255
buffer[k] = res * 255
buffer[k + 1] = res * 255
buffer[k + 2] = res * 255
buffer[k + 3] = 255
}

return buffer
}

public static toBase64(
buffer: Uint8ClampedArray,
width: number,
height: number
): string {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (!ctx) return
canvas.width = this.renderTarget.width
canvas.height = this.renderTarget.height
if (!ctx) return ''
canvas.width = width
canvas.height = height

// create imageData object
const idata = ctx.createImageData(this.renderTarget.width, this.renderTarget.height)
const idata = ctx.createImageData(width, height)

// set our buffer as source
idata.data.set(this.outputBuffer)
idata.data.set(buffer)

// update canvas with new data
ctx.putImageData(idata, 0, 0)
Expand All @@ -89,10 +113,9 @@ export class PassReader extends Extension {
*/
ctx.globalCompositeOperation = 'copy'
ctx.scale(1, -1)
ctx.drawImage(canvas, 0, 0, this.renderTarget.width, -this.renderTarget.height)
ctx.drawImage(canvas, 0, 0, width, -height)
ctx.restore()

if (this.readbackExecutor) this.readbackExecutor(canvas.toDataURL())
this.needsRead = false
return canvas.toDataURL()
}
}
48 changes: 48 additions & 0 deletions packages/frontend-2/lib/viewer/extensions/ViewModesKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
Extension,
InputEvent,
ViewMode,
ViewModes,
type IViewer
} from '@speckle/viewer'

export class ViewModesKeys extends Extension {
public get inject() {
return [ViewModes]
}

constructor(viewer: IViewer, protected viewModes: ViewModes) {
super(viewer)
const renderer = viewer.getRenderer()

renderer.input.on(InputEvent.KeyUp, (arg: KeyboardEvent) => {
// Dont trigger on inputs, textareas or contenteditable elements
// We should handle this more gracefully but it works for now
if (
arg.target &&
((arg.target as HTMLElement).tagName.toLowerCase() === 'input' ||
(arg.target as HTMLElement).tagName.toLowerCase() === 'textarea' ||
(arg.target as HTMLElement).getAttribute('contenteditable') === 'true')
)
return

switch (arg.key) {
case '1':
viewModes.setViewMode(ViewMode.DEFAULT)
break
case '2':
viewModes.setViewMode(ViewMode.DEFAULT_EDGES)
break
case '3':
viewModes.setViewMode(ViewMode.SHADED)
break
case '4':
viewModes.setViewMode(ViewMode.PEN)
break
case '5':
viewModes.setViewMode(ViewMode.ARCTIC)
break
}
})
}
}
117 changes: 117 additions & 0 deletions packages/viewer-sandbox/src/Extensions/PassReader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import type { SpeckleRenderer } from '@speckle/viewer'
import { Extension } from '@speckle/viewer'
import type { WebGLRenderTarget } from 'three'
import { Vector3, Vector4 } from 'three'

export class PassReader extends Extension {
private outputBuffer: Uint8ClampedArray = new Uint8ClampedArray()
private renderTarget: WebGLRenderTarget | null = null
private needsRead: boolean = false
private readbackExecutor:
| ((arg: [Uint8ClampedArray, number, number]) => void)
| null = null

public async read(passName: string): Promise<[Uint8ClampedArray, number, number]> {
return new Promise<[Uint8ClampedArray, number, number]>((resolve, reject) => {
const renderer: SpeckleRenderer = this.viewer.getRenderer()

const depthPass = renderer.pipeline.getPass(passName)[0]

if (!depthPass) {
reject(`Pipeline does not have a ${passName} pass`)
return
}

this.renderTarget = depthPass.outputTarget

if (!this.renderTarget) {
reject('Pass does not have a render target assigned')
return
}

const bufferSize =
Math.floor(this.renderTarget.width) * Math.floor(this.renderTarget.height) * 4
if (this.outputBuffer.length !== bufferSize)
this.outputBuffer = new Uint8ClampedArray(bufferSize)
this.needsRead = true
this.readbackExecutor = resolve
})
}

public onRender(): void {
if (!this.needsRead || !this.renderTarget) return

this.viewer
.getRenderer()
.renderer.readRenderTargetPixels(
this.renderTarget,
0,
0,
this.renderTarget.width,
this.renderTarget.height,
this.outputBuffer
)

if (this.readbackExecutor)
this.readbackExecutor([
this.outputBuffer,
this.renderTarget.width,
this.renderTarget.height
])
this.needsRead = false
}

public static decodeDepth(buffer: Uint8ClampedArray): Uint8ClampedArray {
const UnpackDownscale = 255 / 256
const PackFactors = new Vector3(256 * 256 * 256, 256 * 256, 256)
const UnpackFactors = new Vector4(
UnpackDownscale / PackFactors.x,
UnpackDownscale / PackFactors.y,
UnpackDownscale / PackFactors.z,
1
)

const v4 = new Vector4()
for (let k = 0; k < buffer.length; k += 4) {
v4.set(
buffer[k] / 255,
buffer[k + 1] / 255,
buffer[k + 2] / 255,
buffer[k + 3] / 255
)
const res = v4.dot(UnpackFactors)
buffer[k] = res * 255
buffer[k + 1] = res * 255
buffer[k + 2] = res * 255
buffer[k + 3] = 255
}

return buffer
}

public static toBase64(buffer: Uint8ClampedArray, width: number, height: number) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (!ctx) return
canvas.width = width
canvas.height = height

// create imageData object
const idata = ctx.createImageData(width, height)

// set our buffer as source
idata.data.set(buffer)

// update canvas with new data
ctx.putImageData(idata, 0, 0)
ctx.save()
/** Flipping the image by drawing it on itself
*/
ctx.globalCompositeOperation = 'copy'
ctx.scale(1, -1)
ctx.drawImage(canvas, 0, 0, width, -height)
ctx.restore()

return canvas.toDataURL()
}
}
37 changes: 37 additions & 0 deletions packages/viewer-sandbox/src/Extensions/ViewModesKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
Extension,
InputEvent,
ViewMode,
ViewModes,
type IViewer
} from '@speckle/viewer'

export class ViewModesKeys extends Extension {
public get inject() {
return [ViewModes]
}

constructor(viewer: IViewer, protected viewModes: ViewModes) {
super(viewer)
const renderer = viewer.getRenderer()
renderer.input.on(InputEvent.KeyUp, (arg: KeyboardEvent) => {
switch (arg.key) {
case '1':
viewModes.setViewMode(ViewMode.DEFAULT)
break
case '2':
viewModes.setViewMode(ViewMode.DEFAULT_EDGES)
break
case '3':
viewModes.setViewMode(ViewMode.SHADED)
break
case '4':
viewModes.setViewMode(ViewMode.PEN)
break
case '5':
viewModes.setViewMode(ViewMode.ARCTIC)
break
}
})
}
}
Loading
Loading