mirror of
https://github.com/robonen/canvas-3d.git
synced 2026-03-20 02:44:40 +00:00
feat(engine): integration rendering and inputs
This commit is contained in:
BIN
packages/matrix/dist/matrix.wasm
vendored
BIN
packages/matrix/dist/matrix.wasm
vendored
Binary file not shown.
0
packages/rendering/camera.ts
Normal file
0
packages/rendering/camera.ts
Normal file
@@ -1,2 +1,4 @@
|
|||||||
export type Point = [number, number];
|
export type Vec3 = [number, number, number];
|
||||||
export type Vector = [Point, Point, Point];
|
export type Vec4 = [number, number, number, number];
|
||||||
|
|
||||||
|
export type Mat4 = [Vec4, Vec4, Vec4, Vec4];
|
||||||
|
|||||||
@@ -21,26 +21,48 @@ onMounted(() => {
|
|||||||
const centerX = sizeX / 2;
|
const centerX = sizeX / 2;
|
||||||
const centerY = sizeY / 2;
|
const centerY = sizeY / 2;
|
||||||
|
|
||||||
|
const figureSizeH = 200;
|
||||||
|
const figureSizeW = 200;
|
||||||
|
|
||||||
const points: Point[] = [
|
const points: Point[] = [
|
||||||
[500, 500, 500, 1],
|
[0, 0, figureSizeH, 1],
|
||||||
[500, 500, 700, 1],
|
[0, 100, 0, 1],
|
||||||
[500, 700, 500, 1],
|
[95.1, 30.9, 0, 1],
|
||||||
[500, 700, 700, 1],
|
[58.8, -80.9, 0, 1],
|
||||||
[700, 500, 500, 1],
|
[-58.8, -80.9, 0, 1],
|
||||||
[700, 500, 700, 1],
|
[-95.1, 30.9, 0, 1],
|
||||||
[700, 700, 500, 1],
|
|
||||||
[700, 700, 700, 1],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const faces: [number, number, number, number][] = [
|
const faces = [
|
||||||
[0, 1, 3, 2],
|
[0, 1, 2],
|
||||||
[0, 1, 5, 4],
|
[0, 2, 3],
|
||||||
[0, 2, 6, 4],
|
[0, 3, 4],
|
||||||
[1, 3, 7, 5],
|
[0, 4, 5],
|
||||||
[2, 3, 7, 6],
|
[0, 5, 1],
|
||||||
[4, 5, 7, 6],
|
[1, 2, 3, 4, 5],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Cube
|
||||||
|
// const points: Point[] = [
|
||||||
|
// [0, 0, 0, 1],
|
||||||
|
// [0, 0, 100, 1],
|
||||||
|
// [0, 100, 0, 1],
|
||||||
|
// [0, 100, 100, 1],
|
||||||
|
// [100, 0, 0, 1],
|
||||||
|
// [100, 0, 100, 1],
|
||||||
|
// [100, 100, 0, 1],
|
||||||
|
// [100, 100, 100, 1],
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// const faces = [
|
||||||
|
// [0, 1, 3, 2],
|
||||||
|
// [0, 1, 5, 4],
|
||||||
|
// [0, 2, 6, 4],
|
||||||
|
// [1, 3, 7, 5],
|
||||||
|
// [2, 3, 7, 6],
|
||||||
|
// [4, 5, 7, 6],
|
||||||
|
// ];
|
||||||
|
|
||||||
// Rotate around X axis
|
// Rotate around X axis
|
||||||
const rotateX = (angle: number): Point[] => {
|
const rotateX = (angle: number): Point[] => {
|
||||||
const rad = (angle * Math.PI) / 180;
|
const rad = (angle * Math.PI) / 180;
|
||||||
@@ -83,12 +105,35 @@ onMounted(() => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rotate = (x: number, y: number, z: number): Point[] => {
|
||||||
|
return mul([rotateX(x), rotateY(y), rotateZ(z)]);
|
||||||
|
};
|
||||||
|
|
||||||
// Translate
|
// Translate
|
||||||
const translate = (x: number, y: number, z: number): Point[] => {
|
const translate = (x: number, y: number, z: number): Point[] => {
|
||||||
return [
|
return [
|
||||||
[1, 0, 0, x],
|
[1, 0, 0, 0],
|
||||||
[0, 1, 0, y],
|
[0, 1, 0, 0],
|
||||||
[0, 0, 1, z],
|
[0, 0, 1, 0],
|
||||||
|
[x, y, z, 1],
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
const scaleMatrix = (x: number, y: number, z: number): Point[] => {
|
||||||
|
return [
|
||||||
|
[x, 0, 0, 0],
|
||||||
|
[0, y, 0, 0],
|
||||||
|
[0, 0, z, 0],
|
||||||
|
[0, 0, 0, 1],
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const axonometryIsometric = (): Point[] => {
|
||||||
|
return [
|
||||||
|
[0.707, -0.408, 0, 0],
|
||||||
|
[0, 0.816, 0, 0],
|
||||||
|
[-0.707, -0.408, 1, 0],
|
||||||
[0, 0, 0, 1],
|
[0, 0, 0, 1],
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -116,46 +161,52 @@ onMounted(() => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mul = (matrices: Point[][]) => {
|
||||||
|
let result = matrices[0];
|
||||||
|
|
||||||
|
for (let i = 1; i < matrices.length; i++) {
|
||||||
|
result = multiply(result, matrices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
// Draw figure
|
// Draw figure
|
||||||
const drawFigure = (
|
const drawFigure = (points: Point[], faces: number[][]) => {
|
||||||
points: Point[],
|
|
||||||
faces: [number, number, number, number][]
|
|
||||||
) => {
|
|
||||||
ctx.clearRect(0, 0, sizeX, sizeY);
|
ctx.clearRect(0, 0, sizeX, sizeY);
|
||||||
|
|
||||||
faces.forEach((face) => {
|
for (const face of faces) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(points[face[0]][0], points[face[0]][1]);
|
|
||||||
ctx.lineTo(points[face[1]][0], points[face[1]][1]);
|
for (let i = 0; i < face.length; i++) {
|
||||||
ctx.lineTo(points[face[2]][0], points[face[2]][1]);
|
const [x, y] = points[face[i]];
|
||||||
ctx.lineTo(points[face[3]][0], points[face[3]][1]);
|
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rotation animation all axis in the center of the canvas
|
useTransformations((translation, rotation, scale) => {
|
||||||
let angle = 0;
|
drawFigure(
|
||||||
|
mul([
|
||||||
const animate = () => {
|
points,
|
||||||
const matrix = multiply(
|
scaleMatrix(scale[0], scale[1], scale[2]),
|
||||||
multiply(
|
rotate(rotation[0], rotation[1], rotation[2]),
|
||||||
multiply(translate(centerX, centerY, 0), rotateX(angle)),
|
translate(translation[0], translation[1], translation[2]),
|
||||||
rotateY(angle)
|
rotateX(-90),
|
||||||
),
|
axonometryIsometric(),
|
||||||
rotateZ(angle)
|
translate(centerX, centerY + figureSizeH / 2, 0),
|
||||||
|
]),
|
||||||
|
faces
|
||||||
);
|
);
|
||||||
|
});
|
||||||
const rotatedPoints = multiply(points, matrix);
|
|
||||||
|
|
||||||
drawFigure(rotatedPoints, faces);
|
|
||||||
|
|
||||||
angle += angle < 360 ? 1 : -360;
|
|
||||||
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
};
|
|
||||||
|
|
||||||
animate();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ const onChange = (event: Event) => {
|
|||||||
value.value = target.valueAsNumber;
|
value.value = target.valueAsNumber;
|
||||||
emit('change', value.value);
|
emit('change', value.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
value.value = defaultValue;
|
||||||
|
emit('change', value.value);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -51,7 +56,7 @@ const onChange = (event: Event) => {
|
|||||||
class="range__input"
|
class="range__input"
|
||||||
type="range"
|
type="range"
|
||||||
@input="onChange"
|
@input="onChange"
|
||||||
@dblclick="value = defaultValue"
|
@click.middle="reset"
|
||||||
/>
|
/>
|
||||||
<div class="range__border">{{ max }}</div>
|
<div class="range__border">{{ max }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
34
src/composables/states.ts
Normal file
34
src/composables/states.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export type XYZ = [number, number, number];
|
||||||
|
|
||||||
|
type fn = ((translation: XYZ, rotation: XYZ, scale: XYZ) => void) | null;
|
||||||
|
|
||||||
|
const translation = reactive<XYZ>([0, 0, 0]);
|
||||||
|
const rotation = reactive<XYZ>([0, 0, 0]);
|
||||||
|
const scale = reactive<XYZ>([1, 1, 1]);
|
||||||
|
|
||||||
|
const setTranslation = (axis: number, value: number) => {
|
||||||
|
translation[axis] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setRotation = (axis: number, value: number) => {
|
||||||
|
rotation[axis] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setScale = (axis: number, value: number) => {
|
||||||
|
scale[axis] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTransformations = (onUpdate: fn = null) => {
|
||||||
|
watchEffect(() => {
|
||||||
|
if (onUpdate) onUpdate(translation, rotation, scale);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
translation,
|
||||||
|
rotation,
|
||||||
|
scale,
|
||||||
|
setTranslation,
|
||||||
|
setRotation,
|
||||||
|
setScale,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -10,39 +10,44 @@ const projections = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const activeProjection = ref<number>(0);
|
const activeProjection = ref<number>(0);
|
||||||
|
|
||||||
|
const transformations = useTransformations();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Accordion title="Перемещение">
|
<Accordion title="Перемещение">
|
||||||
<FormRange
|
<FormRange
|
||||||
v-for="axis in axes"
|
v-for="(axis, i) in axes"
|
||||||
:label="`${axis} =`"
|
:label="`${axis} =`"
|
||||||
:min="-10"
|
:min="-100"
|
||||||
:max="10"
|
:max="100"
|
||||||
:step="0.1"
|
:step="1"
|
||||||
:defaultValue="0"
|
:defaultValue="0"
|
||||||
|
@change="transformations.setTranslation(i, $event)"
|
||||||
/>
|
/>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Вращение">
|
<Accordion title="Вращение">
|
||||||
<FormRange
|
<FormRange
|
||||||
v-for="axis in axes"
|
v-for="(axis, i) in axes"
|
||||||
:label="`${axis} =`"
|
:label="`${axis} =`"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="359"
|
:max="359"
|
||||||
:step="1"
|
:step="1"
|
||||||
:defaultValue="0"
|
:defaultValue="0"
|
||||||
|
@change="transformations.setRotation(i, $event)"
|
||||||
/>
|
/>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Масштабирование">
|
<Accordion title="Масштабирование">
|
||||||
<FormRange
|
<FormRange
|
||||||
v-for="axis in axes"
|
v-for="(axis, i) in axes"
|
||||||
:key="axis"
|
:key="axis"
|
||||||
:label="`${axis} =`"
|
:label="`${axis} =`"
|
||||||
:min="0.1"
|
:min="0.1"
|
||||||
:max="5"
|
:max="5"
|
||||||
:step="0.1"
|
:step="0.1"
|
||||||
:defaultValue="1"
|
:defaultValue="1"
|
||||||
|
@change="transformations.setScale(i, $event)"
|
||||||
/>
|
/>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Проекции">
|
<Accordion title="Проекции">
|
||||||
|
|||||||
Reference in New Issue
Block a user