1
0
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:
2022-11-26 04:48:51 +07:00
parent bca8e0aada
commit ae9661f4b1
7 changed files with 155 additions and 58 deletions

Binary file not shown.

View File

View 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];

View File

@@ -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>

View File

@@ -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
View 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,
};
};

View File

@@ -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="Проекции">