Skip to content

Instantly share code, notes, and snippets.

@pallada-92
Last active July 23, 2023 19:41
Show Gist options
  • Save pallada-92/b60f25d5a6aeddf7269d941bc35b6793 to your computer and use it in GitHub Desktop.
Save pallada-92/b60f25d5a6aeddf7269d941bc35b6793 to your computer and use it in GitHub Desktop.
Simple 3d engine implementation in https://github.com/calculang/calculang
/*
Copyright (c) 2023 Yaroslav Sergienko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
export const screen_width = () => 50;
export const screen_height = () => 50;
export const xq = () => xq_in;
export const yq = () => yq_in;
export const row = () => yq() / screen_height();
export const col = () => xq() / screen_width();
export const nx = () => 0.04 + col() * 0.56 + row() * 0.15;
export const ny = () => 0.85 + row() * -0.52;
export const nz = () => 0.74 + col() * -0.27 + row() * 0.30;
export const n = () => Math.sqrt(nx() ** 2 + ny() ** 2 + nz() ** 2) * 1.6;
export const v = () => {
const n_ = n();
const kx_ = nx() / n_;
const ky_ = ny() / n_;
const kz_ = nz() / n_;
let res = 0;
for (let i=0; i<100; i++) {
const x = -0.61 + res * kx_;
const y = -0.90 + res * ky_;
const z = -1.24 + res * kz_;
const sdf = Math.max(
Math.abs(x) - 0.3,
Math.abs(y) - 0.3,
Math.abs(z) - 0.3,
- (1 + x * x + y * y + z * z) / 2 + 0.57
);
res += sdf;
}
return res;
}
export const brightness = () => Number(((v() - 1.75) / 1.70).toFixed(2));
export const brightness_2 = () => Math.min(Math.max(brightness(), 0), 1);
export const luminosity_quantized_char = () => {
const s = " .,-~:;=!*#$@";
return s.split('')[Math.floor((1 - brightness_2()) * s.length)];
}
/*
Copyright (c) 2023 Yaroslav Sergienko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
export const sdf_shape = () => sdf_shape_in;
export const screen_width = () => 50;
export const screen_height = () => 50;
export const xq = () => xq_in;
export const yq = () => yq_in;
export const row = () => 1 - yq() / screen_height();
export const col = () => xq() / screen_width();
export const alpha = () => alpha_in;
export const beta = () => beta_in;
export const fov = () => fov_in;
export const dist = () => dist_in;
export const rows = () => screen_width();
export const cols = () => screen_height();
export const cosAlpha = () => Math.cos(alpha() * Math.PI / 180);
export const sinAlpha = () => Math.sin(alpha() * Math.PI / 180);
export const cosBeta = () => Math.cos(beta() * Math.PI / 180);
export const sinBeta = () => Math.sin(beta() * Math.PI / 180);
export const camX = () => dist() * cosAlpha() * cosBeta();
export const camY = () => dist() * sinBeta();
export const camZ = () => dist() * sinAlpha() * cosBeta();
export const pixelSize = () => Math.tan(fov() * Math.PI / 180 / 2) / ((rows() - 1) / 2);
export const ux = () => -pixelSize() * sinAlpha();
export const uz = () => +pixelSize() * cosAlpha();
export const vx = () => +pixelSize() * cosAlpha() * sinBeta();
export const vy = () => -pixelSize() * cosBeta();
export const vz = () => +pixelSize() * sinAlpha() * sinBeta();
export const x0 = () => -cosAlpha() * cosBeta();
export const y0 = () => -sinBeta();
export const z0 = () => -sinAlpha() * cosBeta();
export const matX0 = () => x0() - cols() / 2 * ux() - rows() / 2 * vx() + 0.02;
export const matX1 = () => ux() * rows();
export const matX2 = () => vx() * cols();
export const matY0 = () => y0() - rows() / 2 * vy() + 0.05;
export const matY1 = () => 0;
export const matY2 = () => vy() * cols();
export const matZ0 = () => z0() - cols() / 2 * uz() - rows() / 2 * vz();
export const matZ1 = () => uz() * rows();
export const matZ2 = () => vz() * cols();
export const nx = () => matX0() + col() * matX1() + row() * matX2();
export const ny = () => matY0() + row() * matY2();
export const nz = () => matZ0() + col() * matZ1() + row() * matZ2();
export const n = () => Math.sqrt(nx() ** 2 + ny() ** 2 + nz() ** 2) * 1.6;
export const v = () => {
const camX_ = camX();
const camY_ = camY();
const camZ_ = camZ();
const nx_ = nx();
const ny_ = ny();
const nz_ = nz();
const n_ = n();
const sdf_shape_ = sdf_shape();
let res = 0;
for (let i = 0; i < 100; i++) {
const x = camX_ + res * nx_ / n_;
const y = camY_ + res * ny_ / n_;
const z = camZ_ + res * nz_ / n_;
let sdf;
if (sdf_shape_ === 'sphere') {
sdf = Math.sqrt(x ** 2 + y ** 2 + z ** 2) - 0.5;
} else if (sdf_shape_ === 'cube') {
sdf = Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) - 0.3;
} else if (sdf_shape_ === 'cube_with_holes') {
sdf = Math.max(Math.abs(x) - 0.3, Math.abs(y) - 0.3, Math.abs(z) - 0.3, - (1 + x * x + y * y + z * z) / 2 + 0.57);
} else if (sdf_shape_ === 'cube_and_torus') {
sdf = Math.min(
Math.max(
Math.abs(x) - 0.3,
Math.abs(y) - 0.3,
Math.abs(z) - 0.3,
-Math.sqrt(x ** 2 + y ** 2 + z ** 2) + 0.375
),
Math.sqrt((Math.sqrt((x - 0.25) ** 2 + (z - 0.25) **2) - 0.25) ** 2 + (y - 0.0) ** 2) - 0.05,
);
} else if (sdf_shape_ === 'teapot') {
sdf = Math.min(
Math.sqrt((Math.sqrt(x ** 2 + z ** 2) - 0.3) ** 2 + (y - 0.18) ** 2) - 0.02,
Math.sqrt(x ** 2 + y ** 2 * 2.5 + z ** 2) - 0.4,
Math.max(
x + y - 0.15 - 0.05 - 0.5,
-y + 0.19 - 0.1,
Math.sqrt((Math.sqrt((x - 0.55) ** 2 + (y - 0.09) ** 2) - 0.1) ** 2 + (z - 0.1) ** 2) - 0.04,
),
Math.max(
-(-y + 0.19 - 0.1),
Math.sqrt((Math.sqrt((x - 0.35) ** 2 + (y - 0.09) ** 2) - 0.1) ** 2 + (z - 0.1) ** 2) - 0.04,
),
Math.sqrt(x ** 2 + (y - 0.27) ** 2 + z ** 2) - 0.05
);
}
res += sdf;
}
return res;
}
export const brightness = () => Number(((v() - 1.75) / 1.70).toFixed(2));
export const brightness_2 = () => Math.min(Math.max(brightness(), 0), 1);
export const luminosity_quantized_char = () => {
const s = " .,-~:;=!*#$@";
return s.split('')[Math.floor((1 - brightness_2()) * s.length)];
}
/*
uis =({
alpha_in: Inputs.range([-180,180], {value: 69, step:1, label: "alpha_in"}),
beta_in: Inputs.range([-180, 180], {value: 38, step:1, label: "beta_in"}),
dist_in: Inputs.range([1, 3], {value: 1.88, step:0.01, label: "dist_in"}),
fov_in: Inputs.range([1,180], {value: 34, step:1, label: "fov_in"}),
sdf_shape_in: Inputs.select([
'sphere',
'cube',
'cube_with_holes',
'cube_and_torus',
'teapot',
], {value: 'cube_and_torus', label: "sdf_shape"})
})
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment