Skip to content
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
10 changes: 7 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ jobs:
permissions:
contents: write
id-token: write
pages: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand All @@ -27,9 +28,6 @@ jobs:
- run: npx playwright install --with-deps chromium
- run: npm test
- run: npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: examples/dist
- run: |
for p in c3r c3r-three; do
v=$(node -p "require('./packages/$p/package.json').version")
Expand All @@ -38,6 +36,12 @@ jobs:
- run: gh release create "$GITHUB_REF_NAME" packages/c3r/dist/*.js packages/c3r-three/dist/*.js --generate-notes
env:
GH_TOKEN: ${{ github.token }}
- id: pages
uses: actions/configure-pages@v5
- run: node examples/thumbnails.mjs "${{ steps.pages.outputs.base_url }}"
- uses: actions/upload-pages-artifact@v3
with:
path: examples/dist

deploy:
needs: build
Expand Down
2 changes: 1 addition & 1 deletion examples/demos.eta
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="<%= it.viewport ?? "width=device-width,initial-scale=1" %>">
<meta http-equiv="Content-Security-Policy" content="script-src 'none'">
<title><%= it.title %></title>
<link rel="icon" href="data:,">
<link rel="icon" href="../../favicon.svg">
<link rel="stylesheet" href="style.css">
<style><%~ it.css %></style>
</head>
Expand Down
1 change: 1 addition & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CSS 3D examples</title>
<link rel="icon" href="/favicon.svg" />
<style>
@import "@picocss/pico/css/pico.classless.min.css";

Expand Down
2 changes: 2 additions & 0 deletions examples/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/standalone/billboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Billboards</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/canvas.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Drawable canvas cube</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/cube.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Spinning box</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/filters.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Filter effects</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/fire.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Fire</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/point_light.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Point light</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/shading.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Shading</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/standalone/specular.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Specular map</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/fps.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - FPS</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/geometry_cube.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Cube geometry</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/geometry_earth.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Earth</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/interactive_cubes_tween.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Interactive cubes</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/loader_obj.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - OBJ loader</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/periodictable.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Periodic table</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
49 changes: 41 additions & 8 deletions examples/threejs/physics_rope.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Physics rope</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down Expand Up @@ -65,6 +66,7 @@
import { C3RRenderer } from "c3r-three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import Stats from "three/addons/libs/stats.module.js";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";

// - Global variables -

Expand All @@ -88,6 +90,7 @@
let ropeSoftBody;
const ropeSegments = [];
const transformAux1 = new Ammo.btTransform();
const ropeAux0 = new THREE.Vector3();
const ropeAux1 = new THREE.Vector3();

let armMovement = 0;
Expand Down Expand Up @@ -136,13 +139,31 @@

const light = new THREE.DirectionalLight(0xffffff, 3);
light.position.set(-10, 10, 5);
light.castShadow = true;
scene.add(light);

document.body.appendChild(renderer.domElement);

stats = new Stats();
document.body.appendChild(stats.dom);

const params = { shading: true, shadows: false, soft: false };
const gui = new GUI();
const apply = () => {
renderer.shading = params.shading;
renderer.shadows =
params.shadows && (params.soft ? { blur: 0.1 } : true);
shadows.enable(params.shading);
soft.enable(params.shading && params.shadows);
};
gui.add(params, "shading").onChange(apply);
const shadows = gui.add(params, "shadows").onChange(apply);
const soft = gui
.add(params, "soft")
.name("soft shadows")
.onChange(apply);
apply();

window.addEventListener("resize", onWindowResize);
}

Expand Down Expand Up @@ -207,6 +228,8 @@
quat.set(0, 0, 0, 1);
createRigidBody(ball, ballShape, ballMass, pos, quat);
ball.userData.physicsBody.setFriction(0.5);
// Held by the rope, which doesn't wake it
ball.userData.physicsBody.setActivationState(4);

// Wall
const brickMass = 0.5;
Expand Down Expand Up @@ -271,6 +294,8 @@
const ropeMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
for (let i = 0; i < ropeNumSegments; i++) {
const segment = new THREE.Mesh(ropeGeometry, ropeMaterial);
segment.castShadow = true;
segment.receiveShadow = true;
scene.add(segment);
ropeSegments.push(segment);
}
Expand Down Expand Up @@ -332,6 +357,8 @@
quat,
baseMaterial,
);
// Its motor doesn't wake it
arm.userData.physicsBody.setActivationState(4);

// Glue the rope extremes to the ball and the arm
const influence = 1;
Expand Down Expand Up @@ -416,15 +443,12 @@
const body = new Ammo.btRigidBody(rbInfo);

threeObject.userData.physicsBody = body;
threeObject.castShadow = true;
threeObject.receiveShadow = true;

scene.add(threeObject);

if (mass > 0) {
rigidBodies.push(threeObject);

// Disable deactivation
body.setActivationState(4);
}
if (mass > 0) rigidBodies.push(threeObject);

physicsWorld.addRigidBody(body);
}
Expand Down Expand Up @@ -497,10 +521,19 @@
for (let i = 0; i < ropeSegments.length; i++) {
const a = nodes.at(i).get_m_x();
const b = nodes.at(i + 1).get_m_x();
ropeAux0.set(a.x(), a.y(), a.z());
ropeAux1.set(b.x(), b.y(), b.z());

const segment = ropeSegments[i];
segment.position.set(a.x(), a.y(), a.z());
// A jitter too small to see would still recast shadows
const end = (segment.userData.end ??= new THREE.Vector3(Infinity));
if (
segment.position.distanceTo(ropeAux0) < 1e-3 &&
end.distanceTo(ropeAux1) < 1e-3
)
continue;
end.copy(ropeAux1);
segment.position.copy(ropeAux0);
segment.scale.z = segment.position.distanceTo(ropeAux1);
segment.lookAt(ropeAux1);
}
Expand All @@ -516,7 +549,7 @@
const q = transformAux1.getRotation();
const next = [p.x(), p.y(), p.z(), q.x(), q.y(), q.z(), q.w()];
const current = [...objThree.position, ...objThree.quaternion];
// Bodies never sleep here, and a jitter too small to see still restyles
// The ball and arm never sleep, and a jitter too small to see still restyles
if (current.some((v, k) => Math.abs(v - next[k]) > 1e-3)) {
objThree.position.fromArray(next);
objThree.quaternion.fromArray(next, 3);
Expand Down
1 change: 1 addition & 0 deletions examples/threejs/sprites.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>c3r - Sprites</title>
<link rel="icon" href="/favicon.svg" />
<style>
body {
margin: 0;
Expand Down
80 changes: 80 additions & 0 deletions examples/thumbnails.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Screenshots each built example and adds link preview tags, after `npm run build`:
// node examples/thumbnails.mjs https://k-adam.github.io/CSS3D-Renderer
import { readFileSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { chromium } from "playwright";
import { preview } from "vite";

// Absolute, as crawlers don't resolve relative URLs. The slash keeps the last path segment
const site = new URL(process.argv[2].replace(/\/?$/, "/"));
const DIST = resolve(import.meta.dirname, "dist");
// 1.91:1, the ratio every major platform crops previews to
const [WIDTH, HEIGHT] = [1200, 630];
const DESCRIPTION =
"Textured 3D scenes from plain DOM elements and CSS transforms.";

const read = (page) => readFileSync(join(DIST, page), "utf8");
const pages = [...read("index.html").matchAll(/href="([^":]+\.html)"/g)].map(
(m) => m[1],
);
const imageOf = (page) =>
`thumbnails/${page.replaceAll("/", "-")}`.replace(/\.html$/, ".jpg");

const server = await preview({
configFile: false,
root: import.meta.dirname,
logLevel: "error",
});
const browser = await chromium.launch();
try {
const tab = await browser.newPage({
viewport: { width: WIDTH, height: HEIGHT },
});
for (const page of pages) {
await tab.goto(new URL(page, server.resolvedUrls.local[0]).href, {
waitUntil: "networkidle",
});
// Lets entry animations and tweens move away from their first frame
await tab.waitForTimeout(1000);
// Under 300 KB, which WhatsApp needs
await tab.screenshot({
path: join(DIST, imageOf(page)),
type: "jpeg",
quality: 80,
});
}
} finally {
await browser.close();
await server.close();
}

const tag = (attr, key, value) =>
`<meta ${attr}="${key}" content="${value.replace(/"/g, "&quot;")}">`;
// A screenshot of the link list would show nothing 3D
for (const [page, image] of [
...pages.map((p) => [p, imageOf(p)]),
["index.html", imageOf("demos/moon/index.html")],
]) {
const html = read(page);
const title = /<title>([^<]*)<\/title>/.exec(html)[1].trim();
const tags = [
tag("property", "og:type", "website"),
tag("property", "og:site_name", "CSS 3D Renderer"),
tag("property", "og:title", title),
tag("property", "og:description", DESCRIPTION),
tag(
"property",
"og:url",
new URL(page.replace(/index\.html$/, ""), site).href,
),
tag("property", "og:image", new URL(image, site).href),
tag("property", "og:image:width", `${WIDTH}`),
tag("property", "og:image:height", `${HEIGHT}`),
tag("property", "og:image:alt", `Screenshot of ${title}`),
tag("name", "twitter:card", "summary_large_image"),
];
writeFileSync(
join(DIST, page),
html.replace("</head>", `${tags.join("\n")}\n</head>`),
);
}
Loading
Loading