Skip to content

Commit

Permalink
brush UI and soft brushes
Browse files Browse the repository at this point in the history
  • Loading branch information
cassiebeckley committed Jan 14, 2021
1 parent 5d204cb commit 39c1fa1
Show file tree
Hide file tree
Showing 20 changed files with 541 additions and 212 deletions.
9 changes: 9 additions & 0 deletions src/brushEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import fragBrush2dShader from './shaders/brush/2d.shader/frag.glsl';

import vertBrush3dShader from './shaders/brush/3d.shader/vert.glsl';
import fragBrush3dShader from './shaders/brush/3d.shader/frag.glsl';
import Mesh from './mesh';

export default class BrushEngine {
gl: WebGLRenderingContext;

radius: number;
spacing: number;
soft: boolean;

slate: Slate;

segmentStart: vec2;
Expand Down Expand Up @@ -44,6 +47,7 @@ export default class BrushEngine {
const radius = diameter / 2;
this.radius = radius;
this.spacing = spacing;
this.soft = false;

this.windowManager = windowManager;
this.slate = windowManager.slate;
Expand Down Expand Up @@ -273,6 +277,8 @@ export default class BrushEngine {
modelViewMatrix
);

gl.uniform1i(this.brush2dShader.uniforms.uSoft, Number(this.soft));

{
const size = 2;
const type = gl.FLOAT; // 32 bit floats
Expand Down Expand Up @@ -360,6 +366,7 @@ export default class BrushEngine {

// TODO: cover seams properly
// TODO: maybe solve this by adding extra geometry at seams in a pre-process step when the mesh is loaded?
// TODO: or maybe they can be covered by drawing geometry as lines after triangles?

const gl = this.gl;
gl.disable(gl.CULL_FACE);
Expand Down Expand Up @@ -412,6 +419,8 @@ export default class BrushEngine {
this.slate.height
);

gl.uniform1i(this.brush3dShader.uniforms.uSoft, Number(this.soft));

{
const size = 3;
const type = gl.FLOAT; // 32 bit floats
Expand Down
50 changes: 48 additions & 2 deletions src/components/BackgroundSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
import * as React from 'react';
import { useState } from 'react';
import Widget from './Widget';
import useInertia from '../hooks/inertia';

import EnvironmentBall from '../widgets/environmentBall';
import { ROTATE_SENSITIVITY } from '../constants';

export default function BackgroundSettings({
rotation,
backgroundOffset,
setBackgroundOffset,
}) {
const [lastPosition, setLastPosition] = useState(0);
const [rotating, setRotating] = useState(false);

const stopRotating = (e: React.PointerEvent) => {
if (rotating) {
setRotating(false);
}
};

const handlePointerDown = (e: React.PointerEvent) => {
if (e.button === 0) {
setLastPosition(e.clientX);
setRotating(true);
}
};
const handlePointerUp = (e: React.PointerEvent) => {
stopRotating(e);
};
const handlePointerMove = (e: React.PointerEvent) => {
if (rotating) {
const deltaAngle = (lastPosition - e.clientX) * -ROTATE_SENSITIVITY;

setBackgroundOffset(backgroundOffset + deltaAngle);
setLastPosition(e.clientX);
}
};
const handlePointerLeave = (e: React.PointerEvent) => {
stopRotating(e);
};

export default function BackgroundSettings({ rotation, backgroundOffset }) {
return (
<Widget constructor={EnvironmentBall} widgetProps={{ rotation, backgroundOffset }} style={{width: '150px', height: '150px'}} zindex={1} />
<Widget
constructor={EnvironmentBall}
widgetProps={{ rotation, backgroundOffset }}
style={{ width: '150px', height: '150px' }}
zindex={1}
onPointerDown={handlePointerDown}
onPointerUp={handlePointerUp}
onPointerMove={handlePointerMove}
onPointerLeave={handlePointerLeave}
/>
);
}
94 changes: 0 additions & 94 deletions src/components/BrushMaterial.tsx

This file was deleted.

34 changes: 27 additions & 7 deletions src/components/MeshPaint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,22 @@ export default function MeshPaint({}) {
const handleRotateBackgroundStart = (x: number) => {
setRotatingBackground(true);
setLastBackgroundOffset(x);
}
};

const handleRotateBackgroundStop = () => {
setRotatingBackground(false);
}
};

const handleRotateBackgroundMove = (x: number) => {
const deltaAngle = (lastBackgroundOffset - x) * -ROTATE_SENSITIVITY;

setBackgroundOffset(backgroundOffset + deltaAngle);
setLastBackgroundOffset(x);

if (!preventContext) {
setPreventContext(true);
}
}
};

const handlePanStart = (panPosition: vec3) => {
setPan(true);
Expand Down Expand Up @@ -292,9 +292,29 @@ export default function MeshPaint({}) {
onPointerLeave={handlePointerLeave}
onContextMenu={handleContextMenu}
>
<div style={{position: 'absolute', right: '20px', bottom: '20px', top: '20px', width: '150px'}}>
<BackgroundSettings rotation={rotationMatrix} backgroundOffset={backgroundOffset} />
<div style={{position: 'absolute', bottom: '0px', width: '100%', height: '150px', backgroundColor: 'red'}}></div>
<div
style={{
position: 'absolute',
right: '20px',
bottom: '20px',
top: '20px',
width: '150px',
}}
>
<BackgroundSettings
rotation={rotationMatrix}
backgroundOffset={backgroundOffset}
setBackgroundOffset={setBackgroundOffset}
/>
<div
style={{
position: 'absolute',
bottom: '0px',
width: '100%',
height: '150px',
backgroundColor: 'red',
}}
></div>
</div>
</Widget>
</div>
Expand Down
18 changes: 17 additions & 1 deletion src/components/TexturePaint.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mat4, vec2, vec3 } from 'gl-matrix';
import * as React from 'react';
import { useContext, useRef, useState } from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
import { SCROLL_SCALE } from '../constants';
import { normalizeWheelEvent } from '../utils';
import TextureDisplay, { getModelMatrix } from '../widgets/textureDisplay';
Expand All @@ -16,6 +16,8 @@ export default function TexturePaint() {
const [view, setView] = useState(() => {
const viewMatrix = mat4.create();
mat4.identity(viewMatrix);
// start offscreen before div bounds can be calculated
mat4.translate(viewMatrix, viewMatrix, [-10000, -10000, 0]);
return viewMatrix;
});

Expand All @@ -29,6 +31,20 @@ export default function TexturePaint() {

const div = useRef(null);

useEffect(() => {
const bounds = div.current.getBoundingClientRect();

const adjusted = mat4.create();
mat4.identity(adjusted);
mat4.translate(adjusted, adjusted, [
bounds.width / 2 - windowManager.slate.width / 2,
bounds.height / 2 - windowManager.slate.height / 2,
0,
]);

setView(adjusted);
}, []);

const uiToImageCoordinates = (uiCoord: vec2) => {
const modelMatrix = getModelMatrix(
windowManager.slate.width,
Expand Down
4 changes: 1 addition & 3 deletions src/components/Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,7 @@ export default function Widget({
}
}

return (
<div {...props} ref={div} className={`widget ${className}`}></div>
);
return <div {...props} ref={div} className={`widget ${className}`}></div>;
}

export const WindowContext: React.Context<WindowManager> = createContext(null);
76 changes: 76 additions & 0 deletions src/components/menu/BrushMaterial.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { vec3 } from 'gl-matrix';
import * as React from 'react';
import { useContext, useState } from 'react';
import { HALF_COLOR } from '../../constants';
import ColorWheel from '../ColorWheel';
import TabPanel from './TabPanel';
import { WindowContext } from '../Widget';

export default function BrushMaterial({ visible, onClick }) {
const windowManager = useContext(WindowContext);

const [brushColor, setBrushColor] = useState(vec3.create());
const [roughness, setRoughness] = useState(0.5);
const [metallic, setMetallic] = useState(0);

const color = vec3.create();
vec3.mul(color, brushColor, [255, 255, 255]);
vec3.round(color, color);

// TODO: use http://danielstern.ca/range.css/ to style the range inputs

return (
<TabPanel
buttonClass="brush-color"
buttonStyle={{ backgroundColor: `rgb(${color})` }}
showPanel={visible}
onClick={onClick}
>
<div className="border-top" />
<ColorWheel
brushColor={brushColor}
setBrushColor={(c: vec3) => {
setBrushColor(c);
windowManager.slate.brushColor = c;
}}
/>
<div
style={{
backgroundColor: HALF_COLOR,
padding: '22px',
}}
>
<label>
Roughness
<input
type="range"
min="0"
max="1"
step="0.01"
value={roughness}
onChange={(e) => {
const r = e.target.valueAsNumber;
setRoughness(r);
windowManager.slate.brushRoughness = r;
}}
/>
</label>
<label>
Metallic
<input
type="range"
min="0"
max="1"
step="0.01"
value={metallic}
onChange={(e) => {
const m = e.target.valueAsNumber;
setMetallic(e.target.valueAsNumber);
windowManager.slate.brushMetallic = m;
}}
/>
</label>
</div>
</TabPanel>
);
}
Loading

0 comments on commit 39c1fa1

Please sign in to comment.