init
This commit is contained in:
93
sanctification-tcg/card-harness/src/cardGeometry.ts
Normal file
93
sanctification-tcg/card-harness/src/cardGeometry.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export const cardDimensions = {
|
||||
width: 0.063,
|
||||
height: 0.0882,
|
||||
thickness: 0.0004,
|
||||
cornerRadius: 0.0028,
|
||||
} as const
|
||||
|
||||
const sceneHeight = 4
|
||||
const sceneScale = sceneHeight / cardDimensions.height
|
||||
|
||||
export const cardSceneDimensions = {
|
||||
width: cardDimensions.width * sceneScale,
|
||||
height: cardDimensions.height * sceneScale,
|
||||
thickness: cardDimensions.thickness * sceneScale,
|
||||
cornerRadius: cardDimensions.cornerRadius * sceneScale,
|
||||
} as const
|
||||
|
||||
function createRoundedRectangle(width: number, height: number, radius: number) {
|
||||
const halfWidth = width / 2
|
||||
const halfHeight = height / 2
|
||||
const shape = new THREE.Shape()
|
||||
|
||||
shape.moveTo(-halfWidth + radius, -halfHeight)
|
||||
shape.lineTo(halfWidth - radius, -halfHeight)
|
||||
shape.quadraticCurveTo(halfWidth, -halfHeight, halfWidth, -halfHeight + radius)
|
||||
shape.lineTo(halfWidth, halfHeight - radius)
|
||||
shape.quadraticCurveTo(halfWidth, halfHeight, halfWidth - radius, halfHeight)
|
||||
shape.lineTo(-halfWidth + radius, halfHeight)
|
||||
shape.quadraticCurveTo(-halfWidth, halfHeight, -halfWidth, halfHeight - radius)
|
||||
shape.lineTo(-halfWidth, -halfHeight + radius)
|
||||
shape.quadraticCurveTo(-halfWidth, -halfHeight, -halfWidth + radius, -halfHeight)
|
||||
|
||||
return shape
|
||||
}
|
||||
|
||||
export function createCardGeometry(
|
||||
frontMaterial: THREE.Material,
|
||||
backMaterial: THREE.Material,
|
||||
edgeMaterial: THREE.Material,
|
||||
) {
|
||||
const { width, height, thickness, cornerRadius } = cardSceneDimensions
|
||||
const shape = createRoundedRectangle(width, height, cornerRadius)
|
||||
|
||||
const bodyGeometry = new THREE.ExtrudeGeometry(shape, {
|
||||
curveSegments: 16,
|
||||
depth: thickness,
|
||||
steps: 1,
|
||||
bevelEnabled: false,
|
||||
})
|
||||
bodyGeometry.translate(0, 0, -thickness / 2)
|
||||
// Extrusion caps would duplicate the dedicated faces and z-fight at Pack's camera distances.
|
||||
const sideWalls = bodyGeometry.groups.find((group) => group.materialIndex === 1)
|
||||
if (!sideWalls) throw new Error('Card extrusion is missing its side-wall geometry')
|
||||
bodyGeometry.setDrawRange(sideWalls.start, sideWalls.count)
|
||||
|
||||
const faceGeometry = new THREE.ShapeGeometry(shape, 16)
|
||||
const positions = faceGeometry.getAttribute('position')
|
||||
const uvs = faceGeometry.getAttribute('uv')
|
||||
for (let index = 0; index < positions.count; index += 1) {
|
||||
uvs.setXY(
|
||||
index,
|
||||
positions.getX(index) / width + 0.5,
|
||||
positions.getY(index) / height + 0.5,
|
||||
)
|
||||
}
|
||||
uvs.needsUpdate = true
|
||||
const faceOffset = thickness / 2
|
||||
|
||||
const body = new THREE.Mesh(bodyGeometry, edgeMaterial)
|
||||
body.name = 'CARD_EDGE'
|
||||
|
||||
const front = new THREE.Mesh(faceGeometry, frontMaterial)
|
||||
front.name = 'CARD_FRONT'
|
||||
front.position.z = faceOffset
|
||||
|
||||
const back = new THREE.Mesh(faceGeometry.clone(), backMaterial)
|
||||
back.name = 'CARD_BACK'
|
||||
back.position.z = -faceOffset
|
||||
back.rotation.y = Math.PI
|
||||
|
||||
const card = new THREE.Group()
|
||||
card.name = 'CARD_PROCEDURAL'
|
||||
card.add(body, front, back)
|
||||
|
||||
for (const mesh of [body, front, back]) {
|
||||
mesh.castShadow = true
|
||||
mesh.receiveShadow = true
|
||||
}
|
||||
|
||||
return card
|
||||
}
|
||||
Reference in New Issue
Block a user