Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimized iridescence spheres #6

Merged
merged 3 commits into from
Aug 24, 2023

Conversation

javagl
Copy link
Contributor

@javagl javagl commented Jul 3, 2023

The IridescenceDielectricSpheres and IridescenceMetallicSpheres models contain 343 spheres that only differ in the material that is applied. The .bin file of these assets had >10MB, because the geometry data for these spheres was stored 343 times.

This PR replaces the asset with one where the .bin file contains the geometry data only once, and only has 41 KB (!). There is no visual difference between the assets, as far as I can tell.

The assets have been created with the following snippet, using the dedup() function of glTF-Transform:

import path from "path";
import fs from "fs";
import { NodeIO } from "@gltf-transform/core";
import { dedup } from "@gltf-transform/functions";
import { KHRONOS_EXTENSIONS } from "@gltf-transform/extensions";

const baseDir = "C:/glTF-Sample-Assets/Models/";

function ensureDirectoryExists(fileName: string) {
  const directory = path.dirname(fileName);
  if (!fs.existsSync(directory)) {
    fs.mkdirSync(directory, { recursive: true });
  }
}

async function runOptimize(modelName: string) {
  const inputDir = baseDir + modelName + "/glTF/";
  const outputDir = baseDir + modelName + "/glTF-Optimized/";

  const inputFileName = inputDir + modelName + ".gltf";
  const outputFileName = outputDir + modelName + ".gltf";

  const io = new NodeIO().registerExtensions(KHRONOS_EXTENSIONS);
  const document = await io.read(inputFileName);
  await document.transform(dedup());
  const jsonDocument = await io.writeJSON(document);

  ensureDirectoryExists(outputFileName);
  fs.writeFileSync(outputFileName, JSON.stringify(jsonDocument.json, null, 2));
  for (const uri of Object.keys(jsonDocument.resources)) {
    const resource = jsonDocument.resources[uri];
    const resourceFileName = path.join(outputDir, uri);
    ensureDirectoryExists(resourceFileName);
    fs.writeFileSync(resourceFileName, resource);
  }
}

async function run() {
  await runOptimize("IridescenceDielectricSpheres");
  await runOptimize("IridescenceMetallicSpheres");
}

run();

@echadwick-artist
Copy link
Contributor

Thanks for these optimizations!

Both have several issues reported in the glTF Validator:

    "issues": {
        "numErrors": 1,
        "numWarnings": 0,
        "numInfos": 343,
        "numHints": 0,
        "messages": [
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_0"
            },
...
            {
                "code": "IO_ERROR",
                "message": "Resource not found (textures/guides.png).",
                "severity": 0,
                "pointer": "/images/0/uri"
            }
        ],

I wonder if the texcoords could be removed, and perhaps packing the asset into a GLB might solve the texture error?

@javagl
Copy link
Contributor Author

javagl commented Jul 7, 2023

That texture error surprises me a bit. This is actually not caused by an error in the model. It is caused by the fact that the textures are contained in a /textures subdirectory. I thought that the validator was able to handle that.

When dragging-and-dropping the asset (i.e. the .gltf and .bin file, together with the textures directory) into https://gltf-viewer.donmccurdy.com/ , then the validation report does not contain this error.

I opened KhronosGroup/glTF-Validator#208 for tracking this.


The point about the UNUSED_OBJECT messages is valid. To be fair: Before this optimization, the models caused a whopping 343 infos and 1040 (!) hints in the validation report, so that already was an improvement. But when I'm already at it anyhow, I also removed the unnecessary texture coordinates. With the new state, the models should cause no issues and no warnings (and of course, no errors, e.g. when they are dragged into the https://gltf-viewer.donmccurdy.com/ that properly resolves the textures).


For completeness, here's the modified code (with the somewhat hacky part to remove the TEXCOORD_0 attributes) to create the optimized versions from the original ones.

import path from "path";
import fs from "fs";
import { NodeIO } from "@gltf-transform/core";
import { dedup, prune } from "@gltf-transform/functions";
import { KHRONOS_EXTENSIONS } from "@gltf-transform/extensions";

const baseDir = "C:/glTF-Sample-Assets/Models/";

function ensureDirectoryExists(fileName: string) {
  const directory = path.dirname(fileName);
  if (!fs.existsSync(directory)) {
    fs.mkdirSync(directory, { recursive: true });
  }
}

async function runOptimize(modelName: string) {
  const inputDir = baseDir + modelName + "/glTF/";
  const outputDir = baseDir + modelName + "/glTF-Optimized/";

  const inputFileName = inputDir + modelName + ".gltf";
  const outputFileName = outputDir + modelName + ".gltf";

  const io = new NodeIO().registerExtensions(KHRONOS_EXTENSIONS);
  const document = await io.read(inputFileName);
  await document.transform(dedup());
  await document.transform(prune());
  const meshes = document.getRoot().listMeshes();
  for (const mesh of meshes) {
    const primitives = mesh.listPrimitives();
    for (const primitive of primitives) {
      if (primitive.getMaterial()?.getBaseColorTexture() === null) {
        primitive.setAttribute("TEXCOORD_0", null);
      }
    }
  }
  const jsonDocument = await io.writeJSON(document);

  ensureDirectoryExists(outputFileName);
  fs.writeFileSync(outputFileName, JSON.stringify(jsonDocument.json, null, 2));
  for (const uri of Object.keys(jsonDocument.resources)) {
    const resource = jsonDocument.resources[uri];
    const resourceFileName = path.join(outputDir, uri);
    ensureDirectoryExists(resourceFileName);
    fs.writeFileSync(resourceFileName, resource);
  }
}

async function run() {
  await runOptimize("IridescenceDielectricSpheres");
  await runOptimize("IridescenceMetallicSpheres");
}

run();

@javagl javagl mentioned this pull request Jul 17, 2023
@echadwick-artist echadwick-artist merged commit 36995f1 into KhronosGroup:main Aug 24, 2023
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants