CesiumGS / 3d-tiles-tools

Apache License 2.0
289 stars 43 forks source link

How to use the pipeline tool #111

Closed lllllleo42 closed 4 months ago

lllllleo42 commented 5 months ago

I want to use the pipeline tool to batch create tileset.json, but I can't find the tutorial. I can only find a step to define a pipeline JSON file image

javagl commented 5 months ago

When you say

... to batch create tileset.json, ...

then it might be that the pipeline functionality is not what you're looking for. The pipeline function (as it was introduced several years ago aimed at performing a sequence of operations on each tile content of an existing tileset (e.g. to optimize each B3DM, and then ZIP it...).

When you say that you want to "batch create tileset.json", then this sounds like you wanted to call the createTilesetJson function for a set of input GLB files. Is that correct?

(If this is correct, then this functionality could probably be implemented with a few (very few) lines of code in a custom script, but I'd like to make sure that I understood this correctly...)

lllllleo42 commented 5 months ago

Thank you for answering my question. Sorry again for my poor English expression. Yes, your understanding is right. I wanted to call the createTilesetJson function for a set of input GLB files. However, I have too many glb files to input, making the command line too long, so I want to use some pipeline tools. Thanks again.

When you say that you want to "batch create tileset.json", then this sounds like you wanted to call the createTilesetJson function for a set of input GLB files. Is that correct?

javagl commented 5 months ago

I have too many glb files to input, making the command line too long, so I want to use some pipeline tools.

The createTilesetJson command can accept a directory as the --input parameter. So when you have a directory like C:\AllMyInputFiles that contains 1000 GLB files, then you can just say

npx 3d-tiles-tools createTilesetJson -i C:/AllMyInputFiles -o C:/AllMyInputFiles/tileset.json

and it will create a single tileset.json that refers to all the 1000 GLB files in that directory.

(Note that the structure of this tileset JSON will be really simple, and it could be possible to create a "better" one, with more information about what the GLB files are, and how they should be combined)


If you want to create one tileset.json file for each GLB, then you could of course run

npx 3d-tiles-tools createTilesetJson -i C:/AllMyInputFiles/input0001.glb -o C:/AllMyInputFiles/tileset0001.json
npx 3d-tiles-tools createTilesetJson -i C:/AllMyInputFiles/input0002.glb -o C:/AllMyInputFiles/tileset0002.json
npx 3d-tiles-tools createTilesetJson -i C:/AllMyInputFiles/input0003.glb -o C:/AllMyInputFiles/tileset0003.json
....

1000 times - but .... you shouldn't do this 🙂 If this is the goal, then you could solve this with a small, custom script (and I'll post an example here if this is what you're trying to do...)

lllllleo42 commented 5 months ago

Thanks. Actually, what I'm trying to do is converting many glb files to a 3dtiles file. However, I need to assign latitude and longitude coordinates to each glb file, Otherwise, there will be many models overlapping in the same location. So I came up with a stupid but simple solution, which is to use the createTilesetJson function for each glb file and generate tileset.json separately. And finally, merg these tileset into a single one. I guess it's kind of stupid😥Thanks again for answering my questions.

javagl commented 5 months ago

It's not stupid. It sounds like a reasonable use-case, and could be a natural extension of the functionality that is already offered by createTilesetJson.

But when adding a new functionality, then one should have a clear idea about the degrees of freedom, and what this function can or cannot do. Right now, the createTilesetJson is targeted at creating a very simple tileset.json for one or more GLB files. Assigning different positions to the GLBs is not covered by that.

If the desired funcitonality was supposed to be added, then I'd have to think this through more thoroughly. And this request does have some overlap to https://github.com/CesiumGS/3d-tiles-tools/issues/84 .

In both cases, one important question is:

Where does the latitude/longitude/height information for each model come from?

So you have your directory with GLB files, like exampleA.glb, exampleB.glb, exampleC.glb.... And each of them has a different latitude/longitude/height. So the pseudocode(!!!) of the function that you would need could be

magic.addContent("exampleA.glb", 35, 140, 10);
magic.addContent("exampleB.glb", 37, 135, 30);
magic.addContent("exampleC.glb", 36, 142, 01);
...
tileset = magic.buildThat();

But this works well only for "few" files. When there are many files (hundreds), then the latitude/longitude/height information is probably contained in some CSV file or some JSON structure (maybe something that is queried via a REST API or so...)


I just started drafting some tests for this.

NOTE: During thest tests, I noticed that https://github.com/CesiumGS/3d-tiles-tools/issues/112 . This is fixed via https://github.com/CesiumGS/3d-tiles-tools/pull/113 , but this is not merged yet.

The approach that you described would probably work, in principle:

This is drafted here.

NOTE: This does use the "library-level" functionality, and not the command line functionality. All this is not "public" yet. This is only a DRAFT.

import fs from "fs";
import path from "path";

import { TilesetJsonCreator } from "./src/tools/tilesetProcessing/TilesetJsonCreator";
import { TilesetOperations } from "./src/tools/tilesetProcessing/TilesetOperations";
import { Paths } from "./src/base";

const baseDir = "./input";
const glbDir = path.resolve(baseDir, "glbFiles");
const tilesetsBaseDir = path.resolve(baseDir, "tilesets");
const mergedFileName = path.resolve(baseDir, "merged/tileset-merged.json");
const combinedFileName = path.resolve(
  baseDir,
  "combined/tileset-combined.json"
);
const inputs = [
  {
    fileName: "exampleA.glb",
    lonLatHeightDegrees: [-75.15, 39.94, 50.0],
  },
  {
    fileName: "exampleB.glb",
    lonLatHeightDegrees: [-75.1502, 39.94, 50.2],
  },
  {
    fileName: "exampleC.glb",
    lonLatHeightDegrees: [-75.1504, 39.94, 50.4],
  },
];

async function run() {
  const overwrite = true;

  const tilesetFileNames: string[] = [];
  for (const input of inputs) {
    const fileName = input.fileName;
    console.log("Create tileset JSON for " + fileName);

    const inputGlbName = path.resolve(glbDir, fileName);
    const tilesetName = "tileset-" + Paths.replaceExtension(fileName, "");
    const tilesetDir = path.resolve(tilesetsBaseDir, tilesetName);
    const outputGlbName = path.resolve(tilesetDir, fileName);
    Paths.ensureDirectoryExists(tilesetDir);
    fs.copyFileSync(inputGlbName, outputGlbName);

    const tileset = await TilesetJsonCreator.createTilesetFromContents(
      tilesetDir,
      [fileName]
    );

    console.log("Assigning position for " + fileName);
    const cartographicPositionDegrees = input.lonLatHeightDegrees;
    const transform =
      TilesetJsonCreator.computeTransformFromCartographicPositionDegrees(
        cartographicPositionDegrees
      );
    tileset.root.transform = transform;
    const tilesetFileName = path.resolve(tilesetDir, "tileset.json");

    console.log("Writing " + tilesetFileName + " for content file " + fileName);
    fs.writeFileSync(tilesetFileName, JSON.stringify(tileset, null, 2));
    tilesetFileNames.push(tilesetFileName);
  }

  console.log("Merging...");
  await TilesetOperations.merge(tilesetFileNames, mergedFileName, overwrite);

  console.log("Combining...");
  await TilesetOperations.combine(mergedFileName, combinedFileName, overwrite);

  console.log("Done, output at " + combinedFileName);
}

run();

I tried this out, with some example GLB files, and it should work in principle:

Cesium Multiple GLB Tileset

But ... this is quirky: It should not be necessary to apply a sequence of three operations, with lots of intermediate steps and input/output files and directories, just to accomplish this task.

There should be a simpler functionality for that. But I'll have to think about how this could be offered sensibly. For example, how to define the cartographic position that each GLB should have. It wouldn't make sense to try to offer this as a pure command-line function. There has to be some sort of input file (CSV or JSON) that defines the position for each GLB. And this means that the implementation should be carefully thought through.

(One could say that we'll have to get our ducks in a row before implementing that 😁 )

lllllleo42 commented 5 months ago

Thanks!!!It really helped me a lot😀

javagl commented 4 months ago

Since the last part is no longer related to the pipeline function, and boiled down to a somewhat specific (but potentially very useful) functionality, I tried to summarize this in https://github.com/CesiumGS/3d-tiles-tools/issues/130