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

New grid, bounding boxes, perf improvements and more #134

Merged
merged 2 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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<support@playcanvas.com>",
"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';
slimbuck marked this conversation as resolved.
Show resolved Hide resolved
import { InfiniteGrid as Grid } from './infinite-grid';

class Scene {
events: Events;
Expand Down
Loading
Loading