Skip to content

Commit

Permalink
Add palette example
Browse files Browse the repository at this point in the history
  • Loading branch information
raub committed Oct 16, 2023
1 parent 725b491 commit b66cd23
Show file tree
Hide file tree
Showing 30 changed files with 1,790 additions and 1,020 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"no-console": [0],
"node/no-unsupported-features/es-builtins": 0,
"node/no-unsupported-features/node-builtins": 0,
"node/no-unsupported-features/es-syntax": 0,
"func-names": [
"error",
"never",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This is a part of [Node3D](https://github.com/node-3d) project.
[![ESLint](https://github.com/node-3d/3d-core-raub/actions/workflows/eslint.yml/badge.svg)](https://github.com/node-3d/3d-core-raub/actions/workflows/eslint.yml)
[![Test](https://github.com/node-3d/3d-core-raub/actions/workflows/test.yml/badge.svg)](https://github.com/node-3d/3d-core-raub/actions/workflows/test.yml)

```
```console
npm i -s 3d-core-raub
```

Expand Down
232 changes: 232 additions & 0 deletions examples/palette/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@

const { read } = require('addon-tools-raub');
const glfw = require('glfw-raub');

const { init, addThreeHelpers } = require('../../index');
const { generatePalette } = require('./utils/palette');
const { debugShaders } = require('./utils/debug-shaders');
const { createPostMaterial } = require('./utils/create-post-material');
const { createColorQuads } = require('./utils/create-color-quads');
const { createRenderTarget } = require('./utils/create-render-target');
const { populateScene } = require('./utils/populate-scene');

const hueModes = [
'monochromatic', 'analagous', 'complementary', 'triadic', 'tetradic',
];

const { Window } = glfw;

(async () => {
const THREE = await import('three');
const { OrbitControls } = await import('three/examples/jsm/controls/OrbitControls.js');

const fragmentShader = await read(`${__dirname}/post.glsl`);

const {
doc, requestAnimationFrame, gl,
} = init({ isGles3: true, width: 1280, height: 720 });
addThreeHelpers(THREE, gl);

doc.setPointerCapture = () => {
doc.setInputMode(glfw.CURSOR, glfw.CURSOR_DISABLED);
};
doc.releasePointerCapture = () => {
doc.setInputMode(glfw.CURSOR, glfw.CURSOR_NORMAL);
};
doc.on('mousedown', (e) => { doc.emit('pointerdown', e); });
doc.on('mouseup', (e) => { doc.emit('pointerup', e); });
doc.on('mousemove', (e) => { doc.emit('pointermove', e); });

const cameraOrtho = new THREE.OrthographicCamera(
-doc.w / 2, doc.w / 2, doc.h / 2, -doc.h / 2, - 10, 10,
);
cameraOrtho.position.z = 5;

const cameraPerspective = new THREE.PerspectiveCamera(50, doc.w / doc.h, 1, 1000);
const controls = new OrbitControls(cameraPerspective, doc);

cameraPerspective.position.z = 9;
controls.update();

const scene = new THREE.Scene();
let mesh;
populateScene(scene, (m) => { mesh = m; });

const scenePost = new THREE.Scene();

let isSwap = false;
let modeGrayscale = 0;
let modeHue = 0;
let numColors = 9;

const rawPalette0 = generatePalette(hueModes[modeHue], numColors);
let palette = rawPalette0.map((c) => (new THREE.Color(...c)));

let materialPost = createPostMaterial(THREE, numColors, isSwap, modeGrayscale, palette, fragmentShader);

let rt = createRenderTarget(THREE, materialPost, doc.w, doc.h);

let quadPost = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), materialPost);
scenePost.add(quadPost);

const quadHelp = new THREE.Mesh(
new THREE.PlaneGeometry(256, 256),
new THREE.MeshBasicMaterial({
side: THREE.CullFaceFront,
depthTest: false,
depthWrite: false,
transparent: true,
map: new THREE.TextureLoader().load( __dirname + '/textures/help.png' ),
}),
);
quadHelp.position.set(doc.w / 2 - 128, -doc.h / 2 + 128, 1);
scenePost.add(quadHelp);

let colorQuads = createColorQuads(THREE, scenePost, palette, isSwap);

const setPalette = (newValue) => {
palette = newValue;
materialPost.uniforms.colors.value = palette;
if (palette.length !== colorQuads.length) {
if (colorQuads) {
colorQuads.forEach((q) => scenePost.remove(q));
}
colorQuads = createColorQuads(THREE, scenePost, palette, isSwap);
} else {
palette.forEach((color, i) => {
colorQuads[i].material.uniforms.color.value = color;
});
}
};

const setModeGrayscale = (newValue) => {
if (isSwap && !newValue) {
newValue = 1;
}
modeGrayscale = newValue;
materialPost.uniforms.modeGrayscale.value = modeGrayscale;
};

const setIsSwap = (newValue) => {
if (isSwap && !modeGrayscale) {
setModeGrayscale(1);
}
isSwap = newValue;
materialPost.uniforms.isSwap.value = isSwap;
palette.forEach((color, i) => {
colorQuads[i].visible = isSwap;
});
};

const randomizePalette = () => {
const rawPalette = generatePalette(hueModes[modeHue], numColors);
const colorPalette = rawPalette.map((c) => (new THREE.Color(...c)));
setPalette(colorPalette);
};

const setModeHue = (newValue) => {
modeHue = newValue;
randomizePalette();
};

const setNumColors = (newValue) => {
if (numColors === newValue) {
return;
}
numColors = newValue;

const rawPalette = generatePalette(hueModes[modeHue], numColors);
const colorPalette = rawPalette.map((c) => (new THREE.Color(...c)));
materialPost = createPostMaterial(
THREE, numColors, isSwap, modeGrayscale, colorPalette, fragmentShader,
);
materialPost.uniforms.t.value = rt.texture;

scenePost.remove(quadPost);
quadPost = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), materialPost);
scenePost.add(quadPost);

randomizePalette();
};

doc.on('keydown', (e) => {
if (e.keyCode === glfw.KEY_P) {
randomizePalette();
return;
}
if (e.keyCode === glfw.KEY_M) {
setModeHue((modeHue + 1) % hueModes.length);
return;
}
if (e.keyCode === glfw.KEY_S) {
setIsSwap(!isSwap);
return;
}
if (e.keyCode === glfw.KEY_G) {
setModeGrayscale((modeGrayscale + 1) % 4);
return;
}
if (e.keyCode === Window.extraCodes[glfw.KEY_EQUAL]) {
setNumColors(Math.min(16, numColors + 1));
return;
}
if (e.keyCode === Window.extraCodes[glfw.KEY_MINUS]) {
setNumColors(Math.max(2, numColors - 1));
return;
}
if (e.keyCode === glfw.KEY_H || e.keyCode === Window.extraCodes[glfw.KEY_F1]) {
quadHelp.visible = !quadHelp.visible;
return;
}
// console.log('kk', e.keyCode, glfw.KEY_EQUAL, Window.extraCodes[glfw.KEY_EQUAL]);
});

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(doc.devicePixelRatio);
renderer.setSize(doc.w, doc.h);
debugShaders(renderer);

doc.on('resize', () => {
cameraPerspective.aspect = doc.w / doc.h;
cameraPerspective.updateProjectionMatrix();
controls.update();

cameraOrtho.left = -doc.w / 2;
cameraOrtho.right = doc.w / 2;
cameraOrtho.top = doc.h / 2;
cameraOrtho.bottom = -doc.h / 2;
cameraOrtho.updateProjectionMatrix();

quadHelp.position.set(doc.w / 2 - 128, -doc.h / 2 + 128, 1);

renderer.setSize(doc.w, doc.h);
if (rt) {
rt.dispose();
rt = null;
}
rt = createRenderTarget(THREE, materialPost, doc.w, doc.h);
});

const render = () => {
const rtOld = renderer.getRenderTarget();
renderer.setRenderTarget(rt);
renderer.render(scene, cameraPerspective);
renderer.setRenderTarget(rtOld);

renderer.render(scenePost, cameraOrtho);
};

const animate = () => {
requestAnimationFrame(animate);

controls.update();

if (mesh) {
mesh.rotation.y = Date.now() * 0.00005;
}

render();
};

animate();
})();
Binary file added examples/palette/models/LittlestTokyo.glb
Binary file not shown.
44 changes: 44 additions & 0 deletions examples/palette/post.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
in vec2 tc;
out vec4 fragColor;

#ifndef NUM_COLORS
#define NUM_COLORS 8
#endif

#define IDX_LAST (NUM_COLORS - 1)

uniform bool isSwap;
uniform int modeGrayscale;
uniform sampler2D t;
uniform vec3 colors[NUM_COLORS];


int getIndex(float value) {
return clamp(int(value * float(IDX_LAST) + 0.5), 0, IDX_LAST);
}


vec3 paletteSwap(float gray, vec3 colors[NUM_COLORS]) {
int index = getIndex(gray);
return colors[index];
}

float toGrayscale(vec3 rgb) {
if (modeGrayscale == 1) {
return 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b; // Luminosity
}
if (modeGrayscale == 2) {
return 0.5 * (max(rgb.r, max(rgb.g, rgb.b)) + min(rgb.r, min(rgb.g, rgb.b))); // Lightness
}
return (rgb.r + rgb.g + rgb.b) * 0.3333333; // Average
}

void main() {
vec4 rgba = texture(t, tc);
float gray = toGrayscale(rgba.rgb);
vec3 finalColor = rgba.rgb;
if (modeGrayscale > 0) {
finalColor = vec3(gray);
}
fragColor = vec4(mix(finalColor, paletteSwap(gray, colors), bvec3(isSwap)), rgba.a);
}
Binary file added examples/palette/textures/help.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/palette/textures/help.xcf
Binary file not shown.
Loading

0 comments on commit b66cd23

Please sign in to comment.