mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
feat: integrate RotatingTetrahedronCanvas and update HeroCTA links for enhanced landing page experience
- Added RotatingTetrahedronCanvas component to display a dynamic 3D visual on the landing page. - Replaced outdated links in HeroCTA component to direct users to the updated TinyHumans resources.
This commit is contained in:
@@ -18,7 +18,7 @@ export default function HeroCTA() {
|
||||
Download 🔥
|
||||
</button>
|
||||
<Link
|
||||
href="https://x.com/openhumanxyz"
|
||||
href="https://x.com/tinyhumansai"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="rounded-lg border border-zinc-800 px-6 py-3 text-sm font-semibold text-white transition-colors hover:border-zinc-700"
|
||||
@@ -26,7 +26,7 @@ export default function HeroCTA() {
|
||||
Join the Community
|
||||
</Link>
|
||||
<Link
|
||||
href="https://openhuman.gitbook.io/docs"
|
||||
href="https://tinyhumans.gitbook.io/openhuman"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="rounded-lg border border-zinc-800 px-6 py-3 text-sm font-semibold text-white transition-colors hover:border-zinc-700"
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import * as THREE from 'three';
|
||||
|
||||
export default function RotatingTetrahedronCanvas() {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas,
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
powerPreference: 'high-performance',
|
||||
});
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 1.15;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
|
||||
camera.position.set(0, 0, 4.6);
|
||||
|
||||
// Subdivided tet + smooth normals ≈ Oblivion-style blunted / softened silhouette (not razor facets).
|
||||
const geometry = new THREE.TetrahedronGeometry(1.35, 2);
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
const fillMaterial = new THREE.MeshPhysicalMaterial({
|
||||
color: '#5b8fd9',
|
||||
emissive: '#0d1f35',
|
||||
emissiveIntensity: 0.45,
|
||||
metalness: 0.42,
|
||||
roughness: 0.18,
|
||||
clearcoat: 0.92,
|
||||
clearcoatRoughness: 0.12,
|
||||
transparent: true,
|
||||
opacity: 0.9,
|
||||
flatShading: false,
|
||||
});
|
||||
|
||||
const fillMesh = new THREE.Mesh(geometry, fillMaterial);
|
||||
scene.add(fillMesh);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight('#cbd5e1', 0.35);
|
||||
const keyLight = new THREE.PointLight('#7ab4ff', 1.35, 24);
|
||||
keyLight.position.set(2.8, 2.5, 4);
|
||||
const rimLight = new THREE.PointLight('#4a83dd', 0.85, 18);
|
||||
rimLight.position.set(-3.2, -1.2, 3.5);
|
||||
const fillLight = new THREE.DirectionalLight('#e8f0ff', 0.55);
|
||||
fillLight.position.set(-1.5, 2, 6);
|
||||
|
||||
scene.add(ambientLight);
|
||||
scene.add(keyLight);
|
||||
scene.add(rimLight);
|
||||
scene.add(fillLight);
|
||||
|
||||
let animationFrame = 0;
|
||||
|
||||
const resize = () => {
|
||||
const parent = canvas.parentElement;
|
||||
if (!parent) return;
|
||||
|
||||
const { width, height } = parent.getBoundingClientRect();
|
||||
if (!width || !height) return;
|
||||
|
||||
renderer.setSize(width, height, false);
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
const observer = new ResizeObserver(resize);
|
||||
if (canvas.parentElement) observer.observe(canvas.parentElement);
|
||||
resize();
|
||||
|
||||
const animate = () => {
|
||||
fillMesh.rotation.x += 0.006;
|
||||
fillMesh.rotation.y += 0.009;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
animationFrame = window.requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.cancelAnimationFrame(animationFrame);
|
||||
observer.disconnect();
|
||||
geometry.dispose();
|
||||
fillMaterial.dispose();
|
||||
renderer.dispose();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<canvas ref={canvasRef} className="h-full w-full block" aria-label="Rotating tetrahedron" />
|
||||
);
|
||||
}
|
||||
+4
-1
@@ -1,11 +1,14 @@
|
||||
import HeroCTA from './components/HeroCTA';
|
||||
import RotatingTetrahedronCanvas from './components/RotatingTetrahedronCanvas';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white relative">
|
||||
<main className="mx-auto max-w-7xl px-6 sm:px-8 sm:pt-32 relative z-10">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<img src="/android-chrome-192x192.png" alt="OpenHuman" className="w-[150px] h-[150px] mx-auto mb-4" />
|
||||
<div className="mx-auto mb-6 h-[180px] w-[180px] sm:h-[200px] sm:w-[200px]">
|
||||
<RotatingTetrahedronCanvas />
|
||||
</div>
|
||||
<h1 className="text-5xl font-bold tracking-tight sm:text-6xl lg:text-7xl">
|
||||
OpenHuman
|
||||
</h1>
|
||||
|
||||
Reference in New Issue
Block a user