зеркало из https://github.com/mozilla/Spoke.git
Make PR review changes.
This commit is contained in:
Родитель
efda58eb00
Коммит
8ba71a83fd
23
.eslintrc.js
23
.eslintrc.js
|
@ -9,7 +9,7 @@ module.exports = {
|
|||
rules: {
|
||||
"prettier/prettier": "error",
|
||||
"prefer-const": "error",
|
||||
"no-use-before-define": ["error", { "functions": false, "classes": false, "variables": true }],
|
||||
"no-use-before-define": ["error", { functions: false, classes: false, variables: true }],
|
||||
"no-var": "error",
|
||||
"no-throw-literal": "error",
|
||||
// Light console usage is useful but remove debug logs before merging to master.
|
||||
|
@ -17,16 +17,17 @@ module.exports = {
|
|||
"lines-between-class-members": 2,
|
||||
"padding-line-between-statements": [
|
||||
"error",
|
||||
{ "blankLine": "always", "prev": "function", "next": "function" },
|
||||
{ "blankLine": "always", "prev": "function", "next": "class" },
|
||||
{ "blankLine": "always", "prev": "class", "next": "function" },
|
||||
{ "blankLine": "always", "prev": "class", "next": "export" },
|
||||
{ "blankLine": "always", "prev": "export", "next": "function" },
|
||||
{ "blankLine": "always", "prev": "export", "next": "class" },
|
||||
{ "blankLine": "always", "prev": "export", "next": "export" },
|
||||
{ "blankLine": "always", "prev": "import", "next": "function" },
|
||||
{ "blankLine": "always", "prev": "import", "next": "class" },
|
||||
]
|
||||
{ blankLine: "always", prev: "function", next: "function" },
|
||||
{ blankLine: "always", prev: "function", next: "class" },
|
||||
{ blankLine: "always", prev: "class", next: "function" },
|
||||
{ blankLine: "always", prev: "class", next: "export" },
|
||||
{ blankLine: "always", prev: "export", next: "function" },
|
||||
{ blankLine: "always", prev: "export", next: "class" },
|
||||
{ blankLine: "always", prev: "export", next: "export" },
|
||||
{ blankLine: "always", prev: "import", next: "function" },
|
||||
{ blankLine: "always", prev: "import", next: "class" }
|
||||
],
|
||||
"no-unused-vars": ["error", { varsIgnorePattern: "^_", argsIgnorePattern: "^_", ignoreRestSiblings: true }]
|
||||
},
|
||||
extends: ["prettier", "plugin:react/recommended", "eslint:recommended"]
|
||||
};
|
||||
|
|
|
@ -943,7 +943,7 @@ export default class Editor {
|
|||
}
|
||||
});
|
||||
|
||||
MeshCombinationGroup.combineMeshes(clonedScene);
|
||||
await MeshCombinationGroup.combineMeshes(clonedScene);
|
||||
|
||||
const animations = [];
|
||||
|
||||
|
|
|
@ -1,41 +1,40 @@
|
|||
import THREE from "./three";
|
||||
import { isStatic, computeAndSetStaticModes } from "./StaticMode";
|
||||
import asyncTraverse from "./utils/asyncTraverse";
|
||||
import keysEqual from "./utils/keysEqual";
|
||||
import hashImage from "./utils/getImageData";
|
||||
|
||||
function getBase64Image(img) {
|
||||
// Create an empty canvas element
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
export async function getImageHash(hashCache, img) {
|
||||
let hash = hashCache.get(img);
|
||||
|
||||
// Copy the image contents to the canvas
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// Get the data-URL formatted image
|
||||
// Firefox supports PNG and JPEG. You could check img.src to
|
||||
// guess the original format, but be aware the using "image/jpg"
|
||||
// will re-encode the image.
|
||||
const dataURL = canvas.toDataURL("image/png");
|
||||
|
||||
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
|
||||
}
|
||||
|
||||
export function compareImages(a, b) {
|
||||
if (a && b) {
|
||||
return a.src === b.src || getBase64Image(a) === getBase64Image(b);
|
||||
if (!hash) {
|
||||
hash = await hashImage(img);
|
||||
hashCache.set(img, hash);
|
||||
}
|
||||
|
||||
return a === b;
|
||||
return hash;
|
||||
}
|
||||
|
||||
export function compareTextures(a, b) {
|
||||
export async function compareImages(hashCache, a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (await getImageHash(hashCache, a)) === (await getImageHash(hashCache, b));
|
||||
}
|
||||
|
||||
export async function compareTextures(hashCache, a, b) {
|
||||
if (a && b) {
|
||||
return (
|
||||
compareImages(a, b) &&
|
||||
a.wrapS === b.wrapS &&
|
||||
a.wrapT === b.wrapT &&
|
||||
a.magFilter === b.magFilter &&
|
||||
a.minFilter === b.minFilter
|
||||
a.minFilter === b.minFilter &&
|
||||
(await compareImages(hashCache, a.image, b.image))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -44,7 +43,9 @@ export function compareTextures(a, b) {
|
|||
|
||||
export default class MeshCombinationGroup {
|
||||
static MaterialComparators = {
|
||||
MeshStandardMaterial: (a, b) => {
|
||||
MeshStandardMaterial: async (group, a, b) => {
|
||||
const imageHashes = group.imageHashes;
|
||||
|
||||
return (
|
||||
a.opacity === b.opacity &&
|
||||
a.roughness === b.roughness &&
|
||||
|
@ -53,27 +54,27 @@ export default class MeshCombinationGroup {
|
|||
a.normalScale.equals(b.normalScale) &&
|
||||
a.color.equals(b.color) &&
|
||||
a.emissive.equals(b.emissive) &&
|
||||
compareTextures(a.map, b.map) &&
|
||||
compareTextures(a.roughnessMap, b.roughnessMap) &&
|
||||
compareTextures(a.metalnessMap, b.metalnessMap) &&
|
||||
compareTextures(a.aoMap, b.aoMap) &&
|
||||
compareTextures(a.normalMap, b.normalMap) &&
|
||||
compareTextures(a.emissiveMap, b.emissiveMap)
|
||||
(await compareTextures(imageHashes, a.map, b.map)) &&
|
||||
(await compareTextures(imageHashes, a.roughnessMap, b.roughnessMap)) &&
|
||||
(await compareTextures(imageHashes, a.metalnessMap, b.metalnessMap)) &&
|
||||
(await compareTextures(imageHashes, a.aoMap, b.aoMap)) &&
|
||||
(await compareTextures(imageHashes, a.normalMap, b.normalMap)) &&
|
||||
(await compareTextures(imageHashes, a.emissiveMap, b.emissiveMap))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
static combineMeshes(rootObject) {
|
||||
static async combineMeshes(rootObject) {
|
||||
computeAndSetStaticModes(rootObject);
|
||||
|
||||
const meshCombinationGroups = [];
|
||||
|
||||
rootObject.traverse(object => {
|
||||
await asyncTraverse(rootObject, async object => {
|
||||
if (isStatic(object) && object.isMesh) {
|
||||
let added = false;
|
||||
|
||||
for (const group of meshCombinationGroups) {
|
||||
if (group.tryAdd(object)) {
|
||||
if (await group._tryAdd(object)) {
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
|
@ -86,7 +87,7 @@ export default class MeshCombinationGroup {
|
|||
});
|
||||
|
||||
for (const group of meshCombinationGroups) {
|
||||
const combinedMesh = group.combine();
|
||||
const combinedMesh = group._combine();
|
||||
|
||||
if (combinedMesh) {
|
||||
rootObject.add(combinedMesh);
|
||||
|
@ -103,9 +104,10 @@ export default class MeshCombinationGroup {
|
|||
|
||||
this.initialObject = initialObject;
|
||||
this.meshes = [initialObject];
|
||||
this.imageHashes = new Map();
|
||||
}
|
||||
|
||||
tryAdd(object) {
|
||||
async _tryAdd(object) {
|
||||
if (!object.isMesh) {
|
||||
return false;
|
||||
}
|
||||
|
@ -116,18 +118,11 @@ export default class MeshCombinationGroup {
|
|||
|
||||
const compareMaterial = MeshCombinationGroup.MaterialComparators[object.material.type];
|
||||
|
||||
if (!(compareMaterial && compareMaterial(this.initialObject.material, object.material))) {
|
||||
if (!(compareMaterial && (await compareMaterial(this, this.initialObject.material, object.material)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const initialGeometryAttributes = Object.keys(this.initialObject.geometry.attributes);
|
||||
const curGeometryAttributes = Object.keys(object.geometry.attributes);
|
||||
|
||||
if (initialGeometryAttributes.length !== curGeometryAttributes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (initialGeometryAttributes.some(attrName => curGeometryAttributes.indexOf(attrName) === -1)) {
|
||||
if (!keysEqual(this.initialObject.geometry.attributes, object.geometry.attributes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -136,7 +131,7 @@ export default class MeshCombinationGroup {
|
|||
return true;
|
||||
}
|
||||
|
||||
combine() {
|
||||
_combine() {
|
||||
const originalMesh = this.meshes[0];
|
||||
|
||||
if (this.meshes.length === 1) {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
export default async function asyncTraverse(object, callback) {
|
||||
await callback(object);
|
||||
|
||||
for (const child of object.children) {
|
||||
await asyncTraverse(child, callback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export default function getImageData(img) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
return ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import getImageData from "./getImageData";
|
||||
|
||||
export default async function hashImage(img) {
|
||||
const imageData = getImageData(img);
|
||||
const digest = await crypto.subtle.digest("SHA-256", imageData.data);
|
||||
const hashArray = Array.from(new Uint8Array(digest));
|
||||
return hashArray.map(b => ("00" + b.toString(16)).slice(-2)).join("");
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
export default function keysEqual(a, b) {
|
||||
let aKeyCount = 0;
|
||||
let bKeyCount = 0;
|
||||
|
||||
for (const key in a) {
|
||||
aKeyCount++;
|
||||
|
||||
if (!b.hasOwnProperty(key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const _bKey in b) {
|
||||
bKeyCount++;
|
||||
}
|
||||
|
||||
return aKeyCount === bKeyCount;
|
||||
}
|
Загрузка…
Ссылка в новой задаче