mirror of
https://github.com/misode/misode.github.io.git
synced 2026-05-05 07:01:48 +00:00
Use deepslate matrix abstraction
This commit is contained in:
Generated
+7
-7
@@ -24,7 +24,7 @@
|
||||
"brace": "^0.11.1",
|
||||
"buffer": "^6.0.3",
|
||||
"comment-json": "^4.1.1",
|
||||
"deepslate": "^0.17.1",
|
||||
"deepslate": "^0.17.2",
|
||||
"deepslate-1.18": "npm:deepslate@^0.9.0-beta.9",
|
||||
"deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13",
|
||||
"highlight.js": "^11.5.1",
|
||||
@@ -1985,9 +1985,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/deepslate": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.1.tgz",
|
||||
"integrity": "sha512-/NL5JIZTSrbWRxbkSKDZPxDHmB49VuFj0ybj83Ae47JyTN1cfmIMPH7liK3lYfhkjLnxqElpif/2sf3wEjXGig==",
|
||||
"version": "0.17.2",
|
||||
"resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.2.tgz",
|
||||
"integrity": "sha512-kQDX2mm0ieKN6HLI+fu+0BBiDqZdTjPsv0uUbpePoDIQd9JxMbvugEpZluQa8ellyEyK9vJOCTT1Wnz+RV8fBQ==",
|
||||
"dependencies": {
|
||||
"gl-matrix": "^3.3.0",
|
||||
"md5": "^2.3.0",
|
||||
@@ -6752,9 +6752,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"deepslate": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.1.tgz",
|
||||
"integrity": "sha512-/NL5JIZTSrbWRxbkSKDZPxDHmB49VuFj0ybj83Ae47JyTN1cfmIMPH7liK3lYfhkjLnxqElpif/2sf3wEjXGig==",
|
||||
"version": "0.17.2",
|
||||
"resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.17.2.tgz",
|
||||
"integrity": "sha512-kQDX2mm0ieKN6HLI+fu+0BBiDqZdTjPsv0uUbpePoDIQd9JxMbvugEpZluQa8ellyEyK9vJOCTT1Wnz+RV8fBQ==",
|
||||
"requires": {
|
||||
"gl-matrix": "^3.3.0",
|
||||
"md5": "^2.3.0",
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
"brace": "^0.11.1",
|
||||
"buffer": "^6.0.3",
|
||||
"comment-json": "^4.1.1",
|
||||
"deepslate": "^0.17.1",
|
||||
"deepslate": "^0.17.2",
|
||||
"deepslate-1.18": "npm:deepslate@^0.9.0-beta.9",
|
||||
"deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13",
|
||||
"highlight.js": "^11.5.1",
|
||||
|
||||
+73
-84
@@ -1,8 +1,10 @@
|
||||
import type { DataModel } from '@mcschema/core'
|
||||
import { Path } from '@mcschema/core'
|
||||
import * as zip from '@zip.js/zip.js'
|
||||
import type { Random } from 'deepslate/core'
|
||||
import { mat3, mat4, quat, vec2, vec3 } from 'gl-matrix'
|
||||
import type { Random } from 'deepslate'
|
||||
import { Matrix3, Matrix4, Vector } from 'deepslate'
|
||||
import type { mat3 } from 'gl-matrix'
|
||||
import { quat, vec2 } from 'gl-matrix'
|
||||
import yaml from 'js-yaml'
|
||||
import { route } from 'preact-router'
|
||||
import rfdc from 'rfdc'
|
||||
@@ -406,28 +408,27 @@ function qrGivensQuat(a: number, b: number) {
|
||||
}
|
||||
|
||||
// modifies the passed mat3
|
||||
function stepJacobi(m: mat3): quat {
|
||||
const n = mat3.create()
|
||||
function stepJacobi(m: Matrix3): quat {
|
||||
const n = new Matrix3()
|
||||
const q = quat.create()
|
||||
if (m[1] * m[1] + m[3] * m[3] > 1e-6) {
|
||||
const [a, b] = approxGivensQuat(m[0], 0.5 * (m[1] + m[3]), m[4])
|
||||
if (m.m01 * m.m01 + m.m10 * m.m10 > 1e-6) {
|
||||
const [a, b] = approxGivensQuat(m.m00, 0.5 * (m.m01 + m.m10), m.m11)
|
||||
const r = quat.fromValues(0, 0, a, b)
|
||||
const c = b * b - a * a
|
||||
const d = -2 * a * b
|
||||
const e = b * b + a * a
|
||||
quat.mul(q, q, r)
|
||||
n[0] = c
|
||||
n[4] = c
|
||||
n[1] = -d
|
||||
n[3] = d
|
||||
n[8] = e
|
||||
mat3.mul(m, m, n)
|
||||
mat3.transpose(n, n)
|
||||
mat3.mul(n, n, m)
|
||||
mat3.copy(m, n)
|
||||
n.m00 = c
|
||||
n.m11 = c
|
||||
n.m01 = -d
|
||||
n.m10 = d
|
||||
n.m22 = e
|
||||
m.mul(n)
|
||||
n.transpose().mul(m)
|
||||
m.copy(n)
|
||||
}
|
||||
if (m[2] * m[2] + m[6] * m[6] > 1e-6) {
|
||||
const pair = approxGivensQuat(m[0], 0.5 * (m[2] + m[6]), m[8])
|
||||
if (m.m02 * m.m02 + m.m20 * m.m20 > 1e-6) {
|
||||
const pair = approxGivensQuat(m.m00, 0.5 * (m.m02 + m.m20), m.m22)
|
||||
const a = -pair[0]
|
||||
const b = pair[1]
|
||||
const r = quat.fromValues(0, a, 0, b)
|
||||
@@ -435,68 +436,65 @@ function stepJacobi(m: mat3): quat {
|
||||
const d = -2 * a * b
|
||||
const e = b * b + a * a
|
||||
quat.mul(q, q, r)
|
||||
n[0] = c
|
||||
n[8] = c
|
||||
n[2] = d
|
||||
n[6] = -d
|
||||
n[4] = e
|
||||
mat3.mul(m, m, n)
|
||||
mat3.transpose(n, n)
|
||||
mat3.mul(n, n, m)
|
||||
mat3.copy(m, n)
|
||||
n.m00 = c
|
||||
n.m22 = c
|
||||
n.m02 = d
|
||||
n.m20 = -d
|
||||
n.m11 = e
|
||||
m.mul(n)
|
||||
n.transpose().mul(m)
|
||||
m.copy(n)
|
||||
}
|
||||
if (m[5] * m[5] + m[7] * m[7] > 1e-6) {
|
||||
const [a, b] = approxGivensQuat(m[4], 0.5 * (m[5] + m[7]), m[8])
|
||||
if (m.m12 * m.m12 + m.m21 * m.m21 > 1e-6) {
|
||||
const [a, b] = approxGivensQuat(m.m11, 0.5 * (m.m12 + m.m21), m.m22)
|
||||
const r = quat.fromValues(a, 0, 0, b)
|
||||
const c = b * b - a * a
|
||||
const d = -2 * a * b
|
||||
const e = b * b + a * a
|
||||
quat.mul(q, q, r)
|
||||
n[4] = c
|
||||
n[8] = c
|
||||
n[5] = -d
|
||||
n[7] = d
|
||||
n[0] = e
|
||||
mat3.mul(m, m, n)
|
||||
mat3.transpose(n, n)
|
||||
mat3.mul(n, n, m)
|
||||
mat3.copy(m, n)
|
||||
n.m11 = c
|
||||
n.m22 = c
|
||||
n.m12 = -d
|
||||
n.m21 = d
|
||||
n.m00 = e
|
||||
m.mul(n)
|
||||
n.transpose().mul(m)
|
||||
m.copy(n)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
export function svdDecompose(m: mat3): [quat, vec3, quat] {
|
||||
export function svdDecompose(m: Matrix3): [quat, Vector, quat] {
|
||||
const q = quat.create()
|
||||
const r = quat.create()
|
||||
const n = mat3.create()
|
||||
mat3.transpose(n, m)
|
||||
mat3.mul(n, n, m)
|
||||
const n = m.clone()
|
||||
.transpose()
|
||||
.mul(m)
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
quat.mul(r, r, stepJacobi(n))
|
||||
}
|
||||
quat.normalize(r, r)
|
||||
const p0 = mat3.create()
|
||||
mat3.fromQuat(p0, r)
|
||||
mat3.mul(p0, m, p0)
|
||||
const p0 = m.clone()
|
||||
.mul(Matrix3.fromQuat(r))
|
||||
let f = 1
|
||||
|
||||
const [a1, b1] = qrGivensQuat(p0[0], p0[1])
|
||||
const [a1, b1] = qrGivensQuat(p0.m00, p0.m01)
|
||||
const c1 = b1 * b1 - a1 * a1
|
||||
const d1 = -2 * a1 * b1
|
||||
const e1 = b1 * b1 + a1 * a1
|
||||
const s1 = quat.fromValues(0, 0, a1, b1)
|
||||
quat.mul(q, q, s1)
|
||||
const p1 = mat3.create()
|
||||
p1[0] = c1
|
||||
p1[4] = c1
|
||||
p1[1] = d1
|
||||
p1[3] = -d1
|
||||
p1[8] = e1
|
||||
const p1 = new Matrix3()
|
||||
p1.m00 = c1
|
||||
p1.m11 = c1
|
||||
p1.m01 = d1
|
||||
p1.m10 = -d1
|
||||
p1.m22 = e1
|
||||
f *= e1
|
||||
mat3.mul(p1, p1, p0)
|
||||
p1.mul(p0)
|
||||
|
||||
const pair = qrGivensQuat(p1[0], p1[2])
|
||||
const pair = qrGivensQuat(p1.m00, p1.m02)
|
||||
const a2 = -pair[0]
|
||||
const b2 = pair[1]
|
||||
const c2 = b2 * b2 - a2 * a2
|
||||
@@ -504,49 +502,40 @@ export function svdDecompose(m: mat3): [quat, vec3, quat] {
|
||||
const e2 = b2 * b2 + a2 * a2
|
||||
const s2 = quat.fromValues(0, a2, 0, b2)
|
||||
quat.mul(q, q, s2)
|
||||
const p2 = mat3.create()
|
||||
p2[0] = c2
|
||||
p2[8] = c2
|
||||
p2[2] = -d2
|
||||
p2[6] = d2
|
||||
p2[4] = e2
|
||||
const p2 = new Matrix3()
|
||||
p2.m00 = c2
|
||||
p2.m22 = c2
|
||||
p2.m02 = -d2
|
||||
p2.m20 = d2
|
||||
p2.m11 = e2
|
||||
f *= e2
|
||||
mat3.mul(p2, p2, p1)
|
||||
p2.mul(p1)
|
||||
|
||||
const [a3, b3] = qrGivensQuat(p2[4], p2[5])
|
||||
const [a3, b3] = qrGivensQuat(p2.m11, p2.m12)
|
||||
const c3 = b3 * b3 - a3 * a3
|
||||
const d3 = -2 * a3 * b3
|
||||
const e3 = b3 * b3 + a3 * a3
|
||||
const s3 = quat.fromValues(a3, 0, 0, b3)
|
||||
quat.mul(q, q, s3)
|
||||
const p3 = mat3.create()
|
||||
p3[4] = c3
|
||||
p3[8] = c3
|
||||
p3[5] = d3
|
||||
p3[7] = -d3
|
||||
p3[0] = e3
|
||||
const p3 = new Matrix3()
|
||||
p3.m11 = c3
|
||||
p3.m22 = c3
|
||||
p3.m12 = d3
|
||||
p3.m21 = -d3
|
||||
p3.m00 = e3
|
||||
f *= e3
|
||||
mat3.mul(p3, p3, p2)
|
||||
p3.mul(p2)
|
||||
|
||||
f = 1 / f
|
||||
quat.scale(q, q, Math.sqrt(f))
|
||||
const scale = vec3.fromValues(p3[0] * f, p3[4] * f, p3[8] * f)
|
||||
const scale = new Vector(p3.m00 * f, p3.m11 * f, p3.m22 * f)
|
||||
return [q, scale, r]
|
||||
}
|
||||
|
||||
export function toAffine(m: mat4): mat4 {
|
||||
if (m[15] === 0) m[15] = 1
|
||||
const a = 1 / m[15]
|
||||
const n = mat4.clone(m)
|
||||
mat4.scale(n, n, [a, a, a])
|
||||
return n
|
||||
}
|
||||
|
||||
export function composeMatrix(translation: vec3, leftRotation: quat, scale: vec3, rightRotation: quat) {
|
||||
const m = mat4.create()
|
||||
mat4.translate(m, m, translation)
|
||||
mat4.mul(m, m, mat4.fromQuat(mat4.create(), leftRotation))
|
||||
mat4.scale(m, m, scale)
|
||||
mat4.mul(m, m, mat4.fromQuat(mat4.create(), rightRotation))
|
||||
return m
|
||||
export function composeMatrix(translation: Vector, leftRotation: quat, scale: Vector, rightRotation: quat) {
|
||||
return new Matrix4()
|
||||
.translate(translation)
|
||||
.mul(Matrix4.fromQuat(leftRotation))
|
||||
.scale(scale)
|
||||
.mul(Matrix4.fromQuat(rightRotation))
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Mesh, Quad, Renderer, ShaderProgram, Vector, Vertex } from 'deepslate'
|
||||
import { mat3, mat4, quat, vec3 } from 'gl-matrix'
|
||||
import { Matrix3, Matrix4, Mesh, Quad, Renderer, ShaderProgram, Vector, Vertex } from 'deepslate'
|
||||
import { mat4, quat } from 'gl-matrix'
|
||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
|
||||
import { Footer, NumberInput, Octicon, RangeInput } from '../components/index.js'
|
||||
import { InteractiveCanvas3D } from '../components/previews/InteractiveCanvas3D.jsx'
|
||||
import { useLocale, useTitle } from '../contexts/index.js'
|
||||
import { useAsync } from '../hooks/useAsync.js'
|
||||
import { loadImage } from '../services/DataFetcher.js'
|
||||
import { composeMatrix, svdDecompose, toAffine } from '../Utils.js'
|
||||
import { composeMatrix, svdDecompose } from '../Utils.js'
|
||||
|
||||
const XYZ = ['x', 'y', 'z'] as const
|
||||
type XYZ = typeof XYZ[number]
|
||||
|
||||
interface Props {
|
||||
path?: string,
|
||||
@@ -26,11 +29,11 @@ export function Transformation({}: Props) {
|
||||
return data
|
||||
})
|
||||
|
||||
const [matrix, setMatrix] = useState<mat4>(mat4.create())
|
||||
const [translation, setTranslation] = useState<vec3>(vec3.create())
|
||||
const [leftRotation, setLeftRotation] = useState<quat>(quat.create())
|
||||
const [scale, setScale] = useState<vec3>(vec3.fromValues(1, 1, 1))
|
||||
const [rightRotation, setRightRotation] = useState<quat>(quat.create())
|
||||
const [matrix, setMatrix] = useState(new Matrix4())
|
||||
const [translation, setTranslation] = useState(new Vector(0, 0, 0))
|
||||
const [leftRotation, setLeftRotation] = useState(quat.create())
|
||||
const [scale, setScale] = useState(new Vector(1, 1, 1))
|
||||
const [rightRotation, setRightRotation] = useState(quat.create())
|
||||
|
||||
const [normalizeLeft, setNormalizeLeft] = useState(true)
|
||||
const [normalizeRight, setNormalizeRight] = useState(true)
|
||||
@@ -44,10 +47,10 @@ export function Transformation({}: Props) {
|
||||
return composeMatrix(translation, leftRotation, scale, rightRotation)
|
||||
}, [matrix, useMatrixOverride])
|
||||
|
||||
const updateMatrix = useCallback((m: mat4) => {
|
||||
const affine = toAffine(m)
|
||||
const newTranslation = mat4.getTranslation(vec3.create(), affine)
|
||||
const [newLeftRotation, newScale, newRightRotation] = svdDecompose(mat3.fromMat4(mat3.create(), affine))
|
||||
const updateMatrix = useCallback((m: Matrix4) => {
|
||||
const affine = m.clone().affine()
|
||||
const newTranslation = affine.getTranslation()
|
||||
const [newLeftRotation, newScale, newRightRotation] = svdDecompose(Matrix3.fromMatrix4(affine))
|
||||
setTranslation(newTranslation)
|
||||
setLeftRotation(newLeftRotation)
|
||||
setScale(newScale)
|
||||
@@ -56,20 +59,18 @@ export function Transformation({}: Props) {
|
||||
}, [])
|
||||
|
||||
const changeMatrix = useCallback((i: number, value: number) => {
|
||||
const m = mat4.clone(matrix)
|
||||
m[i] = value
|
||||
const m = matrix.clone()
|
||||
m.data[i] = value
|
||||
updateMatrix(m)
|
||||
}, [matrix])
|
||||
|
||||
const updateTranslation = useCallback((value: vec3) => {
|
||||
const updateTranslation = useCallback((value: Vector) => {
|
||||
setTranslation(value)
|
||||
setMatrix(composeMatrix(value, leftRotation, scale, rightRotation))
|
||||
}, [leftRotation, scale, rightRotation])
|
||||
|
||||
const changeTranslation = useCallback((i: number, value: number) => {
|
||||
const copy = vec3.clone(translation)
|
||||
copy[i] = value
|
||||
updateTranslation(copy)
|
||||
const changeTranslation = useCallback((c: XYZ, v: number) => {
|
||||
updateTranslation(new Vector(c === 'x' ? v : translation.x, c === 'y' ? v : translation.y, c === 'z' ? v : translation.z))
|
||||
}, [translation, updateTranslation])
|
||||
|
||||
const updateLeftRotation = useCallback((value: quat) => {
|
||||
@@ -84,16 +85,13 @@ export function Transformation({}: Props) {
|
||||
updateLeftRotation(copy)
|
||||
}, [leftRotation, normalizeLeft, updateLeftRotation])
|
||||
|
||||
const updateScale = useCallback((value: vec3) => {
|
||||
const updateScale = useCallback((value: Vector) => {
|
||||
setScale(value)
|
||||
setMatrix(composeMatrix(translation, leftRotation, value, rightRotation))
|
||||
}, [translation, leftRotation, rightRotation])
|
||||
|
||||
const changeScale = useCallback((i: number, value: number) => {
|
||||
const copy = vec3.clone(scale)
|
||||
copy[i] = value
|
||||
setScale(copy)
|
||||
setMatrix(composeMatrix(translation, leftRotation, scale, rightRotation))
|
||||
const changeScale = useCallback((c: XYZ, v: number) => {
|
||||
updateScale(new Vector(c === 'x' ? v : scale.x, c === 'y' ? v : scale.y, c === 'z' ? v : scale.z))
|
||||
}, [scale, updateScale])
|
||||
|
||||
const updateRightRotation = useCallback((value: quat) => {
|
||||
@@ -120,7 +118,7 @@ export function Transformation({}: Props) {
|
||||
renderer.current?.setViewport(0, 0, width, height)
|
||||
}, [cubeTexture])
|
||||
const onDraw = useCallback((view: mat4) => {
|
||||
renderer.current?.draw(view, usedMatrix)
|
||||
renderer.current?.draw(view, usedMatrix.data)
|
||||
}, [usedMatrix])
|
||||
|
||||
return <main class="has-preview">
|
||||
@@ -129,11 +127,11 @@ export function Transformation({}: Props) {
|
||||
<div class="transformation-section">
|
||||
<div class="transformation-title">
|
||||
<span>{locale('transformation.translation')}</span>
|
||||
<button class="tooltipped tip-se" aria-label={locale('reset')} onClick={() => updateTranslation(vec3.create())}>{Octicon['history']}</button>
|
||||
<button class="tooltipped tip-se" aria-label={locale('reset')} onClick={() => updateTranslation(new Vector(0, 0, 0))}>{Octicon['history']}</button>
|
||||
</div>
|
||||
{Array(3).fill(0).map((_, i) => <div class="transformation-input">
|
||||
<NumberInput value={translation[i].toFixed(3)} onChange={v => changeTranslation(i, v)} />
|
||||
<RangeInput min={-1} max={1} step={0.01} value={translation[i]} onChange={v => changeTranslation(i, v)} />
|
||||
{XYZ.map((c) => <div class="transformation-input">
|
||||
<NumberInput value={translation[c].toFixed(3)} onChange={v => changeTranslation(c, v)} />
|
||||
<RangeInput min={-1} max={1} step={0.01} value={translation[c]} onChange={v => changeTranslation(c, v)} />
|
||||
</div>)}
|
||||
</div>
|
||||
<div class="transformation-section">
|
||||
@@ -150,11 +148,11 @@ export function Transformation({}: Props) {
|
||||
<div class="transformation-section">
|
||||
<div class="transformation-title">
|
||||
<span>{locale('transformation.scale')}</span>
|
||||
<button class="tooltipped tip-se" aria-label={locale('reset')} onClick={() => updateScale(vec3.fromValues(1, 1, 1))}>{Octicon['history']}</button>
|
||||
<button class="tooltipped tip-se" aria-label={locale('reset')} onClick={() => updateScale(new Vector(1, 1, 1))}>{Octicon['history']}</button>
|
||||
</div>
|
||||
{Array(3).fill(0).map((_, i) => <div class="transformation-input">
|
||||
<NumberInput value={scale[i].toFixed(3)} onChange={v => changeScale(i, v)} />
|
||||
<RangeInput min={-1} max={1} step={0.01} value={scale[i]} onChange={v => changeScale(i, v)} />
|
||||
{XYZ.map((c) => <div class="transformation-input">
|
||||
<NumberInput value={scale[c].toFixed(3)} onChange={v => changeScale(c, v)} />
|
||||
<RangeInput min={-1} max={1} step={0.01} value={scale[c]} onChange={v => changeScale(c, v)} />
|
||||
</div>)}
|
||||
</div>
|
||||
<div class="transformation-section">
|
||||
@@ -173,12 +171,12 @@ export function Transformation({}: Props) {
|
||||
<div class="transformation-section">
|
||||
<div class="transformation-title">
|
||||
<span>{locale('transformation.matrix')}</span>
|
||||
<button class="tooltipped tip-se" aria-label={locale('reset')} onClick={() => updateMatrix(mat4.create())}>{Octicon['history']}</button>
|
||||
<button class="tooltipped tip-se" aria-label={locale('reset')} onClick={() => updateMatrix(new Matrix4())}>{Octicon['history']}</button>
|
||||
<button class="tooltipped tip-se" aria-label={`${useMatrixOverride ? 'Expected' : 'Current'} behavior (see MC-259853)`} onClick={() => setUseMatrixOverride(!useMatrixOverride)}>{Octicon['info']}</button>
|
||||
</div>
|
||||
{Array(16).fill(0).map((_, i) => <div class="transformation-input">
|
||||
<NumberInput value={matrix[i].toFixed(3)} onChange={v => changeMatrix(i, v)} />
|
||||
<RangeInput min={-1} max={1} step={0.01} value={matrix[i]} onChange={v => changeMatrix(i, v)} />
|
||||
<NumberInput value={matrix.data[i].toFixed(3)} onChange={v => changeMatrix(i, v)} />
|
||||
<RangeInput min={-1} max={1} step={0.01} value={matrix.data[i]} onChange={v => changeMatrix(i, v)} />
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user