Not getting the result as expected

I want to implement this:

here is my code:

import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { RoundedBoxGeometry } from "three/examples/jsm/geometries/RoundedBoxGeometry.js";

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0f1117);

const axesHelper = new THREE.AxesHelper(10);

const HEIGHT_OF_THE_CARD = 8;
const WIDTH_OF_THE_CARD = 5;
const DISTANCE_BETWEEN_TWO_CARDS = 4;
const ANGLE = Math.asin(DISTANCE_BETWEEN_TWO_CARDS / (2 * HEIGHT_OF_THE_CARD)); // sin inverse (0.25) = 14.5 degree = 0.25 radian.

const geometry = new THREE.PlaneGeometry(WIDTH_OF_THE_CARD, HEIGHT_OF_THE_CARD);

const material = new THREE.MeshBasicMaterial({
  color: "red",
  wireframe: true,
});

const leftCard = new THREE.Mesh(geometry, material);
leftCard.rotation.x = ANGLE;
scene.add(leftCard);

const rightCard = new THREE.Mesh(geometry, material);
rightCard.rotation.x = -ANGLE;
rightCard.position.z = DISTANCE_BETWEEN_TWO_CARDS;

scene.add(rightCard);


const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);

let aspectRatio = window.innerWidth / window.innerHeight;

// camera
const camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 200);
camera.position.z = 15;

// canvas
const canvas = document.querySelector("canvas.threejs");

const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
  antialias: true,
});

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

// control
const control = new OrbitControls(camera, canvas);
control.enableDamping = true;

window.addEventListener("resize", () => {
  aspectRatio = window.innerWidth / window.innerHeight;
  camera.aspect = aspectRatio;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

const renderLoop = () => {
  control.update();
  renderer.render(scene, camera);
  window.requestAnimationFrame(renderLoop);
};
renderLoop();

But i am getting this result:

What’s the error in my code.

Are you accounting for the fact that the “pivot ponts” of the cards would be by default at their centres? Eg…

You could try the following…

const ANGLE = Math.asin(DISTANCE_BETWEEN_TWO_CARDS / HEIGHT_OF_THE_CARD);
2 Likes

if you would like to maintain the absolute distance between cards being 4 at the bottom you can keep the angle equation you used initially and make preliminary transformations to the card geometry prior to making the mesh rotations…

https://codepen.io/forerunrun/full/gbrVWaB

1 Like

also if you wanted to stack these cards as instances you could use a pattern similar to the following pen…

https://codepen.io/forerunrun/full/qEZeKrP

1 Like

I have also implemented this (https://codepen.io/Sujal-Agrawal-the-solid/pen/YPqmbZa), but it uses more memory. I will learn a lot from your code. Thank you!

1 Like

Yes it looks like you’re creating a new mesh for every card, InstancedMesh will give you a lot better performance, one thing to note is that there will always be 3x the amount of cards for the “level” or “row” number so they can be procedurally stacked quite easily eg level 15 will have 15 triads (left, right, bottom) or 45 cards in total, level 16 = 48 cards, level 22 = 66 cards etc…

Do you plan to add physics to the resulting stack?

Yes, I’m thinking of adding physics and some animations.
When we click on the card house, it should fall naturally.
I have no idea how to do it. I think physics can be handled using cannon.js and for animation i guess no external library is needed.
And I feel, it will be hard to implement, since each card need its own physics, animation and interaction with other cards.

I’ve tried to put together a Rapier Physics example from the previous demo (as Rapier is pretty lightweight), however there seems to be a fundamental floor in the instanced body implementation of three’s Rapier Physics as the cards’ rotations are not being preserved…

https://codepen.io/forerunrun/pen/dPXyGQM?editors=1010

@Mugen87 would you know the reason why the quaternion property of createBody within createInstancedBody is null in the RapierPhysics implementation? Is this an oversight or is there a technical reason an instances quaternion rotation can’t be derived to be used as the body’s rotation on initialization of physics?

@Mugen87 I managed to patch the Rapier Physics implementation to include quaternion calculations for instances by composing and decomposing a matrix from each instance, i did this in a separate repo but i can make an official PR if it helps…