Skip to content

Commit

Permalink
🐛 Fixing lookAt to work with rotateY
Browse files Browse the repository at this point in the history
  • Loading branch information
WoodNeck committed Jul 26, 2019
1 parent ecb2141 commit d7947b3
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 29 deletions.
3 changes: 2 additions & 1 deletion docs/css/common.css
Expand Up @@ -81,9 +81,10 @@ body {
}
#rotated {
transform-style: preserve-3d;
transform: rotateY(30deg);
transform: rotateY(100deg);
width: 300px;
height: 300px;
/* transform-origin: 100% 0%; */
background: aquamarine;
}
#rotated2 {
Expand Down
6 changes: 3 additions & 3 deletions docs/test.html
Expand Up @@ -8,7 +8,7 @@
<body>
<div id="space">
<div id="rotated">
<div id="rotated2"></div>
<!-- <div id="rotated2"></div> -->
</div>
<div class="cube" id="cube1">
<div class="cube-side front"></div>
Expand Down Expand Up @@ -44,9 +44,9 @@
const back = document.querySelector(".back");
const right = document.querySelector(".right");
const rotated1 = document.querySelector("#rotated");
const rotated2 = document.querySelector("#rotated2");
// const rotated2 = document.querySelector("#rotated2");

camera.lookAt(rotated);
camera.lookAt(rotated1);
camera.update();
// const loop = () => {
// setTimeout(() => {
Expand Down
43 changes: 36 additions & 7 deletions src/Camera.ts
@@ -1,7 +1,8 @@
import { mat4, vec3, quat } from 'gl-matrix';
import Transform from './Transform';
import { getElement, applyCSS, getTransformMatrix, findIndex, quatToEuler } from './utils/helper';
import { getElement, applyCSS, getTransformMatrix, findIndex, quatToEuler, getOffsetFromParent, getRotateOffset, translateMat } from './utils/helper';
import DEFAULT from './constants/default';
import { Offset } from './types';

abstract class Camera {
private _element: HTMLElement;
Expand Down Expand Up @@ -57,11 +58,14 @@ abstract class Camera {

const eulerAngle = quatToEuler(rotation);

vec3.negate(eulerAngle, eulerAngle);

this.transform.rotation = eulerAngle;
this.transform.position = translation;
}

public getFocusMatrix(element: HTMLElement): mat4 {
const elements = [];
const elements: HTMLElement[] = [];
while (element) {
elements.push(element);
if (element === this._element) break;
Expand All @@ -73,18 +77,43 @@ abstract class Camera {

const elStyles = elements.map(el => window.getComputedStyle(el));

// From this._element to element's first parent
// Find most element that transform-style is not preserve-3d
// Find first element that transform-style is not preserve-3d
// As all childs of that element is affected by its matrix
const firstFlatIndex = findIndex(elStyles, style => style.transformStyle !== 'preserve-3d');
if (firstFlatIndex > 0) { // el doesn't have to be preserve-3d'ed
elStyles.splice(firstFlatIndex + 1);
}

let matrix = mat4.create();
let parentOffset: Offset = {
left: 0,
top: 0,
width: this.cameraEl.offsetWidth,
height: this.cameraEl.offsetHeight,
};
const position = vec3.fromValues(0, 0, this.transform.perspective);
const matrix = mat4.create();
mat4.identity(matrix);
elStyles.forEach(style => {
matrix = mat4.mul(matrix, matrix, getTransformMatrix(style)) ;
mat4.translate(matrix, matrix, position);
elStyles.forEach((style, idx) => {
const el = elements[idx];
const currentOffset = {
left: el.offsetLeft,
top: el.offsetTop,
width: el.offsetWidth,
height: el.offsetHeight,
};
const rotateOffset = getRotateOffset(style, currentOffset);
const offsetFromParent = getOffsetFromParent(currentOffset, parentOffset);

vec3.negate(rotateOffset, rotateOffset);
translateMat(matrix, rotateOffset);
mat4.mul(matrix, getTransformMatrix(style), matrix);
vec3.negate(rotateOffset, rotateOffset);
translateMat(matrix, rotateOffset);

translateMat(matrix, offsetFromParent);

parentOffset = currentOffset;
});

return matrix;
Expand Down
3 changes: 1 addition & 2 deletions src/Transform.ts
Expand Up @@ -21,10 +21,9 @@ class Transform {
}

public get worldCSS() {
const perspective = this._perspective;
const position = this._position;

return `translate3d(${-position[0]}px, ${-position[1]}px, ${-position[2] - perspective}px)`;
return `translate3d(${-position[0]}px, ${-position[1]}px, ${-position[2]}px)`;
}

public set position(val: vec3) { this._position = val; }
Expand Down
8 changes: 0 additions & 8 deletions src/constants/math.ts

This file was deleted.

7 changes: 7 additions & 0 deletions src/types.ts
Expand Up @@ -4,3 +4,10 @@ export type Matrix4x4 = [
number, number, number, number,
number, number, number, number,
];

export interface Offset {
left: number;
top: number;
width: number;
height: number;
}
49 changes: 42 additions & 7 deletions src/utils/helper.ts
@@ -1,6 +1,6 @@
import { mat4, quat, vec3, glMatrix } from 'gl-matrix';
import { BASE_ELEMENT_NOT_EXIST, MUST_STRING_OR_ELEMENT } from '../constants/error';
import { Matrix4x4 } from '../types';
import { Matrix4x4, Offset } from '../types';

export const getElement = (el: string | HTMLElement): HTMLElement => {
if (typeof el === 'string') {
Expand Down Expand Up @@ -28,9 +28,39 @@ export function getTransformMatrix(elStyle: CSSStyleDeclaration): mat4 {
const matrixVal = transformStr
? transformStr[1].split(',').map(val => parseFloat(val)) as Matrix4x4
: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] as Matrix4x4;
const matrix = mat4.fromValues(...matrixVal);
if (matrixVal.length === 16 ) {
return mat4.fromValues(...matrixVal);
} else {
// Convert 2d matrix(length 6) to 3d
const matrix = mat4.create();
mat4.identity(matrix);

matrix[0] = matrixVal[0];
matrix[1] = matrixVal[1];
matrix[4] = matrixVal[2];
matrix[5] = matrixVal[3];
matrix[12] = matrixVal[4];
matrix[13] = matrixVal[5];

return matrix;
}
}

export function getOffsetFromParent(currentOffset: Offset, parentOffset: Offset): vec3 {
const offsetLeft = currentOffset.left + (currentOffset.width - parentOffset.width) / 2;
const offsetTop = currentOffset.top + (currentOffset.height - parentOffset.height) / 2;

return matrix;
return vec3.fromValues(offsetLeft, offsetTop, 0);
}

export function getRotateOffset(elStyle: CSSStyleDeclaration, currentOffset: Offset): vec3 {
const axis = (elStyle.transformOrigin as string)
.split(' ')
.map(str => parseFloat(str.substring(0, str.length - 2)));
const ax = axis[0] - currentOffset.width / 2;
const ay = axis[1] - currentOffset.height / 2;

return vec3.fromValues(ax, ay, 0);
}

export function findIndex<T>(iterable: T[], callback: (el: T) => boolean): number {
Expand Down Expand Up @@ -61,7 +91,6 @@ export function radToDeg(rad: number): number {
return 180 * rad / Math.PI;
}

// From https://github.com/toji/gl-matrix/issues/329
export function quatToEuler(q: quat): vec3 {
const x = q[0];
const y = q[1];
Expand All @@ -77,21 +106,27 @@ export function quatToEuler(q: quat): vec3 {
let out = vec3.create();
if (test > glMatrix.EPSILON * unit) {
// singularity at the north pole
out[0] = Math.PI;
out[0] = Math.PI / 2;
out[1] = 2 * Math.atan2(y, x);
out[2] = 0;
} else if (test < -glMatrix.EPSILON * unit) {
// singularity at the south pole
out[0] = -Math.PI;
out[0] = -Math.PI / 2;
out[1] = 2 * Math.atan2(y, x);
out[2] = 0;
} else {
out[0] = Math.atan2(2 * (x * w + y * z), 1 - 2 * (x2 + y2));
out[1] = Math.asin(2 * (x * z - w * y));
out[1] = Math.asin(2 * (w * y - x * z));
out[2] = Math.atan2(2 * (x * y + z * w), 1 - 2 * (y2 + z2));
}

out = out.map(val => radToDeg(val)) as vec3;

return out;
}

export function translateMat(mat: mat4, vec: vec3): void {
mat[12] += vec[0];
mat[13] += vec[1];
mat[14] += vec[2];
}
4 changes: 3 additions & 1 deletion tslint.json
Expand Up @@ -18,7 +18,9 @@
"no-console": false,
"curly": false,
"adjacent-overload-signatures": false,
"no-bitwise": false
"no-bitwise": false,
"interface-name": false,
"max-line-length": false
},
"linterOptions": {
"exclude": [
Expand Down

0 comments on commit d7947b3

Please sign in to comment.