Skip to content

Commit

Permalink
Merge branch 'main' into mouse-wheel-brush-size
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbethke committed Nov 8, 2024
2 parents 723a0e4 + f719d7a commit cd8413f
Show file tree
Hide file tree
Showing 16 changed files with 842 additions and 180 deletions.
332 changes: 175 additions & 157 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.13.0",
"@eslint/js": "^9.14.0",
"@playcanvas/eslint-config": "^2.0.6",
"@playcanvas/pcui": "^4.5.1",
"@playcanvas/pcui": "^4.5.2",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-image": "^3.0.3",
"@rollup/plugin-json": "^6.1.0",
Expand All @@ -37,24 +37,24 @@
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.1",
"@types/wicg-file-system-access": "^2023.10.5",
"@typescript-eslint/eslint-plugin": "^8.12.2",
"@typescript-eslint/parser": "^8.12.2",
"@typescript-eslint/eslint-plugin": "^8.13.0",
"@typescript-eslint/parser": "^8.13.0",
"autoprefixer": "^10.4.20",
"concurrently": "^9.0.1",
"concurrently": "^9.1.0",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"eslint": "^9.13.0",
"eslint": "^9.14.0",
"i18next": "^23.16.4",
"i18next-browser-languagedetector": "^8.0.0",
"jest": "^29.7.0",
"playcanvas": "^2.2.1",
"playcanvas": "^2.2.2",
"postcss": "^8.4.47",
"rollup": "^4.24.3",
"rollup": "^4.24.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.80.5",
"sass": "^1.80.6",
"serve": "^14.2.4",
"tslib": "^2.8.0",
"tslib": "^2.8.1",
"typescript": "^5.6.3"
}
}
47 changes: 46 additions & 1 deletion src/edit-ops.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Mat4, Vec3 } from 'playcanvas';
import { Color, Mat4 } from 'playcanvas';
import { Splat } from './splat';
import { State } from './splat-state';
import { Transform } from './transform';
Expand Down Expand Up @@ -318,6 +318,49 @@ class PlacePivotOp {
}
}

type ColorAdjustment = {
tintClr?: Color
brightness?: number,
blackPoint?: number,
whitePoint?: number,
transparency?: number
};

class SetSplatColorAdjustmentOp {
name: "setSplatColor";
splat: Splat;

newState: ColorAdjustment;
oldState: ColorAdjustment;

constructor(options: { splat: Splat, oldState: ColorAdjustment, newState: ColorAdjustment }) {
const { splat, oldState, newState } = options;
this.splat = splat;
this.oldState = oldState;
this.newState = newState;
}

do() {
const { splat } = this;
const { tintClr, brightness, blackPoint, whitePoint, transparency } = this.newState;
if (tintClr) splat.tintClr = tintClr;
if (brightness !== null) splat.brightness = brightness;
if (blackPoint !== null) splat.blackPoint = blackPoint;
if (whitePoint !== null) splat.whitePoint = whitePoint;
if (transparency !== null) splat.transparency = transparency;
}

undo() {
const { splat } = this;
const { tintClr, brightness, blackPoint, whitePoint, transparency } = this.oldState;
if (tintClr) splat.tintClr = tintClr;
if (brightness !== null) splat.brightness = brightness;
if (blackPoint !== null) splat.blackPoint = blackPoint;
if (whitePoint !== null) splat.whitePoint = whitePoint;
if (transparency !== null) splat.transparency = transparency;
}
}

class MultiOp {
name = "multiOp";
ops: EditOp[];
Expand Down Expand Up @@ -348,5 +391,7 @@ export {
EntityTransformOp,
SplatsTransformOp,
PlacePivotOp,
ColorAdjustment,
SetSplatColorAdjustmentOp,
MultiOp
};
7 changes: 7 additions & 0 deletions src/shaders/splat-shader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ uniform sampler2D transformPalette; // palette of transform matrices
uniform vec4 selectedClr;
uniform vec4 lockedClr;
uniform vec3 clrOffset;
uniform vec4 clrScale;
varying mediump vec3 texCoordIsLocked; // store locked flat in z
varying mediump vec4 color;
Expand Down Expand Up @@ -120,12 +123,16 @@ void main(void)
color.xyz = max(color.xyz + evalSH(viewDir), 0.0);
#endif
// apply locked/selected colors
if ((vertexState & 2u) != 0u) {
// locked
color *= lockedClr;
} else if ((vertexState & 1u) != 0u) {
// selected
color.xyz = mix(color.xyz, selectedClr.xyz * 0.8, selectedClr.a);
} else {
// apply tint/brightness
color = color * clrScale + vec4(clrOffset, 0.0);
}
#endif
Expand Down
63 changes: 57 additions & 6 deletions src/splat-serialize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Color,
GSplatData,
Mat3,
Mat4,
Expand Down Expand Up @@ -123,6 +124,31 @@ class SplatTransformCache {
}
}

// apply color adjustments
const applyColorTint = (target: { f_dc_0: number, f_dc_1: number, f_dc_2: number, opacity: number }, adjustment: { blackPoint: number, whitePoint: number, brightness: number, tintClr: Color, transparency: number }) => {
const { blackPoint, whitePoint, brightness, tintClr, transparency } = adjustment;

if (!tintClr.equals(Color.WHITE) || blackPoint !== 0 || whitePoint !== 1 || brightness !== 1) {
const SH_C0 = 0.28209479177387814;
const to = (value: number) => value * SH_C0 + 0.5;
const from = (value: number) => (value - 0.5) / SH_C0;

const offset = -blackPoint + brightness;
const scale = 1 / (whitePoint - blackPoint);

target.f_dc_0 = from(offset + to(target.f_dc_0) * tintClr.r * scale);
target.f_dc_1 = from(offset + to(target.f_dc_1) * tintClr.g * scale);
target.f_dc_2 = from(offset + to(target.f_dc_2) * tintClr.b * scale);
}

if (transparency !== 1) {
const invSig = (value: number) => (value <= 0) ? -400 : ((value >= 1) ? 400 : -Math.log(1 / value - 1));
const sig = (value: number) => 1 / (1 + Math.exp(-value));

target.opacity = invSig(sig(target.opacity) * transparency);
}
};

const v = new Vec3();
const q = new Quat();

Expand Down Expand Up @@ -174,7 +200,7 @@ const serializePly = async (splats: Splat[], write: WriteFunc) => {
const storage = propNames.map((name) => splatData.getProp(name));
const transformCache = new SplatTransformCache(splats[e]);

let shData;
let shData: any[];
let shCoeffs: number[];
if (hasSH) {
// get sh coefficient array
Expand Down Expand Up @@ -230,6 +256,19 @@ const serializePly = async (splats: Splat[], write: WriteFunc) => {
}
}

// apply color tints
applyColorTint(splat, splats[e]);

if (hasSH) {
const { blackPoint, whitePoint, tintClr } = splats[e];
const scale = 1 / (whitePoint - blackPoint);
for (let j = 0; j < 15; ++j) {
splat[`f_rest_${j}`] *= tintClr.r * scale;
splat[`f_rest_${j + 15}`] *= tintClr.g * scale;
splat[`f_rest_${j + 30}`] *= tintClr.b * scale;
}
}

// write
for (let j = 0; j < propNames.length; ++j) {
dataView.setFloat32(offset, splat[propNames[j]], true);
Expand Down Expand Up @@ -273,7 +312,8 @@ class SingleSplat {
rot_3 = 0;

read(splats: Splat[], index: CompressedIndex) {
const { splatData } = splats[index.splatIndex];
const splat = splats[index.splatIndex];
const { splatData } = splat;
const val = (prop: string) => splatData.getProp(prop)[index.i];
[this.x, this.y, this.z] = [val('x'), val('y'), val('z')];
[this.scale_0, this.scale_1, this.scale_2] = [val('scale_0'), val('scale_1'), val('scale_2')];
Expand Down Expand Up @@ -615,6 +655,9 @@ const serializePlyCompressed = async (splats: Splat[], write: WriteFunc) => {
const t = transformCaches[index.splatIndex];
singleSplat.transform(t.getMat(index.i), t.getRot(index.i), t.getScale(index.i));

// apply color
applyColorTint(singleSplat, splats[index.splatIndex]);

// set
chunk.set(j, singleSplat);
}
Expand Down Expand Up @@ -705,11 +748,19 @@ const serializeSplat = async (splats: Splat[], write: WriteFunc) => {
dataView.setFloat32(off + 16, Math.exp(scale_1[i]) * scale.x, true);
dataView.setFloat32(off + 20, Math.exp(scale_2[i]) * scale.x, true);

const clr = {
f_dc_0: f_dc_0[i],
f_dc_1: f_dc_1[i],
f_dc_2: f_dc_2[i],
opacity: opacity[i]
};
applyColorTint(clr, splat);

const SH_C0 = 0.28209479177387814;
dataView.setUint8(off + 24, clamp((0.5 + SH_C0 * f_dc_0[i]) * 255));
dataView.setUint8(off + 25, clamp((0.5 + SH_C0 * f_dc_1[i]) * 255));
dataView.setUint8(off + 26, clamp((0.5 + SH_C0 * f_dc_2[i]) * 255));
dataView.setUint8(off + 27, clamp((1 / (1 + Math.exp(-opacity[i]))) * 255));
dataView.setUint8(off + 24, clamp((0.5 + SH_C0 * clr.f_dc_0) * 255));
dataView.setUint8(off + 25, clamp((0.5 + SH_C0 * clr.f_dc_1) * 255));
dataView.setUint8(off + 26, clamp((0.5 + SH_C0 * clr.f_dc_2) * 255));
dataView.setUint8(off + 27, clamp((1 / (1 + Math.exp(-clr.opacity))) * 255));

q.set(rot_1[i], rot_2[i], rot_3[i], rot_0[i]).mul2(quat, q).normalize();
dataView.setUint8(off + 28, clamp(q.w * 128 + 128));
Expand Down
75 changes: 75 additions & 0 deletions src/splat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ class Splat extends Element {

selectionAlpha = 1;

_tintClr = new Color(1, 1, 1);
_brightness = 0;
_blackPoint = 0;
_whitePoint = 1;
_transparency = 1;

rebuildMaterial: (bands: number) => void;

constructor(asset: Asset) {
Expand Down Expand Up @@ -283,6 +289,8 @@ class Splat extends Element {
serializer.packa(this.entity.getWorldTransform().data);
serializer.pack(this.changedCounter);
serializer.pack(this.visible);
serializer.pack(this.tintClr.r, this.tintClr.g, this.tintClr.b);
serializer.pack(this.brightness, this.blackPoint, this.whitePoint, this.transparency);
}

onPreRender() {
Expand All @@ -307,6 +315,18 @@ class Splat extends Element {
material.setParameter('unselectedClr', [unselectedClr.r, unselectedClr.g, unselectedClr.b, unselectedClr.a]);
material.setParameter('lockedClr', [lockedClr.r, lockedClr.g, lockedClr.b, lockedClr.a]);

// combine black pointer, white point and brightness
const offset = -this.blackPoint + this.brightness;
const scale = 1 / (this.whitePoint - this.blackPoint);

material.setParameter('clrOffset', [offset, offset, offset]);
material.setParameter('clrScale', [
this.tintClr.r * scale,
this.tintClr.g * scale,
this.tintClr.b * scale,
this.transparency
]);

if (this.visible && selected) {
// render bounding box
if (events.invoke('camera.bound')) {
Expand Down Expand Up @@ -408,6 +428,61 @@ class Splat extends Element {
this.scene.events.fire('splat.visibility', this);
}
}

get tintClr() {
return this._tintClr;
}

set tintClr(value: Color) {
if (!this._tintClr.equals(value)) {
this._tintClr.set(value.r, value.g, value.b);
this.scene.events.fire('splat.tintClr', this);
}
}

get brightness() {
return this._brightness;
}

set brightness(value: number) {
if (value !== this._brightness) {
this._brightness = value;
this.scene.events.fire('splat.brightness', this);
}
}

get blackPoint() {
return this._blackPoint;
}

set blackPoint(value: number) {
if (value !== this._blackPoint) {
this._blackPoint = value;
this.scene.events.fire('splat.blackPoint', this);
}
}

get whitePoint() {
return this._whitePoint;
}

set whitePoint(value: number) {
if (value !== this._whitePoint) {
this._whitePoint = value;
this.scene.events.fire('splat.whitePoint', this);
}
}

get transparency() {
return this._transparency;
}

set transparency(value: number) {
if (value !== this._transparency) {
this._transparency = value;
this.scene.events.fire('splat.transparency', this);
}
}
}

export { Splat };
6 changes: 6 additions & 0 deletions src/ui/camera-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ class CameraPanel extends Container {
setVisible(false);
}
});

events.on('colorPanel.visible', (visible: boolean) => {
if (visible) {
setVisible(false);
}
});
}
}

Expand Down
Loading

0 comments on commit cd8413f

Please sign in to comment.