Skip to content

Commit

Permalink
New grid, bounding boxes, perf improvements and more (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
slimbuck authored Jul 18, 2024
1 parent 2cc8b66 commit b7b6d76
Show file tree
Hide file tree
Showing 17 changed files with 349 additions and 86 deletions.
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "supersplat",
"version": "0.22.2",
"version": "0.23.0",
"author": "PlayCanvas<[email protected]>",
"homepage": "https://playcanvas.com/supersplat/editor",
"description": "3D Gaussian Splat Editor",
Expand Down Expand Up @@ -68,7 +68,7 @@
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
"jest": "^29.7.0",
"playcanvas": "^1.72.1",
"playcanvas": "^1.73.0",
"rollup": "^4.18.0",
"rollup-plugin-sass": "^1.13.0",
"rollup-plugin-visualizer": "^5.12.0",
Expand Down
4 changes: 3 additions & 1 deletion src/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ class Camera extends Element {
this.entity.camera.setShaderPass(`debug_${this.scene.config.camera.debug_render}`);
}

this.controller = new PointerController(this, this.scene.canvas);
const target = document.getElementById('canvas-container');

this.controller = new PointerController(this, target);

// apply scene config
const config = this.scene.config;
Expand Down
6 changes: 0 additions & 6 deletions src/controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,6 @@ class PointerController {
camera.pickFocalPoint(event.offsetX, event.offsetY);
};

const contextmenu = (event: globalThis.MouseEvent) => {
event.preventDefault();
};

// key state
const keys: any = {
ArrowUp: 0,
Expand Down Expand Up @@ -192,7 +188,6 @@ class PointerController {
target.addEventListener('pointermove', pointermove);
target.addEventListener('wheel', wheel);
target.addEventListener('dblclick', dblclick);
target.addEventListener('contextmenu', contextmenu);
document.addEventListener('keydown', keydown);
document.addEventListener('keyup', keyup);

Expand All @@ -202,7 +197,6 @@ class PointerController {
target.removeEventListener('pointermove', pointermove);
target.removeEventListener('wheel', wheel);
target.removeEventListener('dblclick', dblclick);
target.removeEventListener('contextmenu', contextmenu);
document.removeEventListener('keydown', keydown);
document.removeEventListener('keyup', keyup);
};
Expand Down
4 changes: 2 additions & 2 deletions src/file-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const writeToFile = async (stream: FileSystemWritableFileStream, data: ArrayBuff
};

// initialize file handler events
const initFileHandler = async (scene: Scene, events: Events, canvas: HTMLCanvasElement, remoteStorageDetails: RemoteStorageDetails) => {
const initFileHandler = async (scene: Scene, events: Events, dropTarget: HTMLElement, remoteStorageDetails: RemoteStorageDetails) => {

// create a file selector element as fallback when showOpenFilePicker isn't available
let fileSelector: HTMLInputElement;
Expand All @@ -109,7 +109,7 @@ const initFileHandler = async (scene: Scene, events: Events, canvas: HTMLCanvasE
}

// create the file drag & drop handler
CreateDropHandler(document.body, async (entries) => {
CreateDropHandler(dropTarget, async (entries) => {
const modelExtensions = ['.ply'];
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
Expand Down
209 changes: 209 additions & 0 deletions src/infinite-grid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {
CULLFACE_NONE,
FUNC_ALWAYS,
SEMANTIC_POSITION,
BlendState,
DepthState,
Mat4,
QuadRender,
Shader,
Vec3,
createShaderFromCode
} from 'playcanvas';
import { Element, ElementType } from './element';
import { Serializer } from './serializer';

const vsCode = /*glsl*/ `
uniform mat4 matrix_viewProjectionInverse;
attribute vec2 vertex_position;
varying vec3 worldFar;
void main(void) {
gl_Position = vec4(vertex_position, 0.0, 1.0);
vec4 v = matrix_viewProjectionInverse * vec4(vertex_position, 1.0, 1.0);
worldFar = v.xyz / v.w;
}
`;

const fsCode = /*glsl*/ `
uniform mat4 matrix_viewProjection;
uniform vec3 view_position;
uniform sampler2D blueNoiseTex32;
varying vec3 worldFar;
bool intersectPlane(inout float t, vec3 pos, vec3 dir, vec4 plane) {
float d = dot(dir, plane.xyz);
if (abs(d) < 1e-06) {
return false;
}
float n = -(dot(pos, plane.xyz) + plane.w) / d;
if (n < 0.0) {
return false;
}
t = n;
return true;
}
// https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8#1e7c
float pristineGrid( in vec2 uv, in vec2 ddx, in vec2 ddy, vec2 lineWidth)
{
vec2 uvDeriv = vec2(length(vec2(ddx.x, ddy.x)), length(vec2(ddx.y, ddy.y)));
bvec2 invertLine = bvec2(lineWidth.x > 0.5, lineWidth.y > 0.5);
vec2 targetWidth = vec2(
invertLine.x ? 1.0 - lineWidth.x : lineWidth.x,
invertLine.y ? 1.0 - lineWidth.y : lineWidth.y
);
vec2 drawWidth = clamp(targetWidth, uvDeriv, vec2(0.5));
vec2 lineAA = uvDeriv * 1.5;
vec2 gridUV = abs(fract(uv) * 2.0 - 1.0);
gridUV.x = invertLine.x ? gridUV.x : 1.0 - gridUV.x;
gridUV.y = invertLine.y ? gridUV.y : 1.0 - gridUV.y;
vec2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
grid2 *= clamp(targetWidth / drawWidth, 0.0, 1.0);
grid2 = mix(grid2, targetWidth, clamp(uvDeriv * 2.0 - 1.0, 0.0, 1.0));
grid2.x = invertLine.x ? 1.0 - grid2.x : grid2.x;
grid2.y = invertLine.y ? 1.0 - grid2.y : grid2.y;
return mix(grid2.x, 1.0, grid2.y);
}
float calcDepth(vec3 p) {
vec4 v = matrix_viewProjection * vec4(p, 1.0);
return (v.z / v.w) * 0.5 + 0.5;
}
void main(void) {
vec3 p = view_position;
vec3 v = normalize(worldFar - view_position);
// intersect ray with the world xz plane
float t;
if (!intersectPlane(t, p, v, vec4(0, 1, 0, 0))) {
discard;
}
// calculate grid intersection
vec3 pos = p + v * t;
// discard distant pixels
float dist = length(pos.xz - view_position.xz);
if (dist > 200.0) {
discard;
}
// evaluate the grid function
float grid = pristineGrid(pos.xz, dFdx(pos.xz), dFdy(pos.xz), vec2(1.0 / 50.0));
// smooth fade into distance
float a = grid * (1.0 - sin(dist / 200.0 * 3.14159 * 0.5));
// early discard semitrans pixels
if (a < 0.1) {
discard;
}
if (a < 0.9) {
// apply dithered discard for semitrans pixels
vec2 uv = fract(gl_FragCoord.xy / 32.0);
float noise = texture2DLodEXT(blueNoiseTex32, uv, 0.0).y;
if (a < noise) {
discard;
}
}
// calculate color
vec3 color;
vec3 apos = abs(pos);
if (apos.x < 0.05) {
if (apos.z < 0.05) {
color = vec3(1.0);
} else {
color = vec3(0.2, 0.2, 1.0);
}
} else if (apos.z < 0.05) {
color = vec3(1.0, 0.2, 0.2);
} else {
color = vec3(0.6);
}
gl_FragColor = vec4(color, 1.0);
gl_FragDepth = calcDepth(pos);
}
`;

const attributes = {
vertex_position: SEMANTIC_POSITION
};

class InfiniteGrid extends Element {
shader: Shader;
quadRender: QuadRender;
blendState = new BlendState(false);
depthState = new DepthState(FUNC_ALWAYS, true);

visible = true;

constructor() {
super(ElementType.debug);
}

add() {
const device = this.scene.app.graphicsDevice;

this.shader = createShaderFromCode(device, vsCode, fsCode, 'infinite-grid', attributes);
this.quadRender = new QuadRender(this.shader);

const viewPosition = device.scope.resolve('view_position');
const viewProjection = device.scope.resolve('matrix_viewProjection');
const viewProjectionInverse = device.scope.resolve('matrix_viewProjectionInverse');

this.scene.debugLayer.onPreRenderOpaque = () => {
if (this.visible) {
device.setBlendState(BlendState.ALPHABLEND);
device.setCullMode(CULLFACE_NONE);
device.setDepthState(DepthState.WRITEDEPTH);
device.setStencilState(null, null);

// update viewProjectionInverse matrix
const projectionMatrix = this.scene.camera.entity.camera.projectionMatrix;
const cameraMatrix = this.scene.camera.entity.getWorldTransform();

const mat = new Mat4();
mat.invert(projectionMatrix);
mat.mul2(cameraMatrix, mat);

const mat2 = new Mat4();
mat2.invert(mat);

const viewPos = new Vec3();
cameraMatrix.getTranslation(viewPos);

viewPosition.setValue([viewPos.x, viewPos.y, viewPos.z]);
viewProjection.setValue(mat2.data);
viewProjectionInverse.setValue(mat.data);

this.quadRender.render();
}
};
}

remove() {
this.scene.debugLayer.onPreRenderOpaque = null;
}

serialize(serializer: Serializer): void {
serializer.pack(this.visible);
}
}

export { InfiniteGrid };
6 changes: 3 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ const main = async () => {
toolManager.register('move', new MoveTool(events, editHistory, scene));
toolManager.register('rotate', new RotateTool(events, editHistory, scene));
toolManager.register('scale', new ScaleTool(events, editHistory, scene));
toolManager.register('rectSelection', new RectSelection(events, editorUI.toolsContainer.dom, editorUI.canvas));
toolManager.register('brushSelection', new BrushSelection(events, editorUI.toolsContainer.dom, editorUI.canvas));
toolManager.register('pickerSelection', new PickerSelection(events, editorUI.toolsContainer.dom, editorUI.canvas));
toolManager.register('rectSelection', new RectSelection(events, editorUI.toolsContainer.dom));
toolManager.register('brushSelection', new BrushSelection(events, editorUI.toolsContainer.dom));
toolManager.register('pickerSelection', new PickerSelection(events, editorUI.toolsContainer.dom));

window.scene = scene;

Expand Down
3 changes: 2 additions & 1 deletion src/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { Model } from './model';
import { Splat } from './splat';
import { Camera } from './camera';
import { CustomShadow as Shadow } from './custom-shadow';
import { Grid } from './grid';
// import { Grid } from './grid';
import { InfiniteGrid as Grid } from './infinite-grid';

class Scene {
events: Events;
Expand Down
Loading

0 comments on commit b7b6d76

Please sign in to comment.