Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions cvcpkg/recipes/libcvc/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ depends:
- name: yaml
- name: levmar
- name: libiimod
# Native mesh/model import (Phase 3: cvc::model + read_model/read_geometry for
# obj/ply/stl/fbx/gltf/... via Assimp). Guarded by CVC_ENABLE_ASSIMP; the
# bundle links libassimp so it must be present in the flat prefix. (libcvc's
# CMake rewrites assimp's exported absolute-zlib-path quirk to ZLIB::ZLIB.)
#
# Scoped to the platforms assimp is published for (linux/macos-arm64/freebsd/
# netbsd). It is NOT yet built for windows, and `cvcpkg install-deps` reads
# THIS recipe's closure to set up the build prefix — an unconditional assimp
# dep would fail the windows deps-install (no bundle) for both CI and publish.
# Where assimp is absent, find_package(assimp CONFIG) misses and CVC_ENABLE_
# ASSIMP flips OFF, so libcvc still builds (without the model loader) — add
# windows here once an assimp windows bundle is published.
- name: assimp
platforms: [linux, macos, freebsd, netbsd]
- name: openblas
- name: openssl
- name: c-ares
Expand Down
98 changes: 98 additions & 0 deletions inc/cvc/model/model.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright 2024 The University of Texas at Austin

This file is part of libcvc.

libcvc is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License version 2.1 as published by the
Free Software Foundation.
*/

#ifndef __CVC_MODEL_H__
#define __CVC_MODEL_H__

// cvc::model — a small scene value type (Phase-3 mesh/model surface). A single
// mesh file (OBJ/glTF/FBX/...) can carry several meshes plus their materials and
// textures, so a model bundles a vector of meshes (each a cvc::geometry + a
// material index) and a vector of cvc::material. Textures decode BELOW the VTK
// line via cvc::image, so cvcGL, pycvc, and the mesh loaders consume already
// decoded pixels. Like cvc::geometry / cvc::image this is a plain, copyable
// value type; the reference-counted sharing lives inside geometry/image.

#include <boost/array.hpp>
#include <boost/cstdint.hpp>
#include <cvc/geometry/geometry.h>
#include <cvc/image/image.h>
#include <cvc/volume/bounding_box.h>
#include <string>
#include <vector>

namespace cvc {

// --------
// material
// --------
// Purpose:
// A minimal PBR-ish material description. Values are normalized to the glTF
// metallic/roughness model; OBJ/other importers map their fields onto it
// (Kd -> base_color rgb, d/opacity -> base_color a, etc.). base_color is a
// multiplier applied on top of base_color_texture when one is present.
// ---- Change History ----
// 2024 -- Joe R. -- Creation (Phase-3 model surface).
struct material {
std::string name;
boost::array<double, 4> base_color = {
{1, 1, 1, 1}}; // RGBA multiplier (glTF baseColorFactor / obj Kd+d)
double metallic = 0.0;
double roughness = 1.0;
boost::array<double, 3> emissive = {{0, 0, 0}};
std::string
base_color_texture_path; // path exactly as referenced in the file (may be relative or empty)
image base_color_texture; // the LOADED texture (image.empty() if none / unresolved)

bool has_base_color_texture() const { return !base_color_texture.empty(); }
};

// -----
// model
// -----
// Purpose:
// A scene: several meshes and the materials they reference. Each mesh pairs a
// cvc::geometry with an index into materials (-1 when the mesh has no material).
// ---- Change History ----
// 2024 -- Joe R. -- Creation (Phase-3 model surface).
struct model {
struct mesh {
geometry geom;
int material = -1; // index into model::materials, or -1 for none
std::string name;
};

std::vector<mesh> meshes;
std::vector<material> materials;

bool empty() const { return meshes.empty(); }

// ---------------
// model::merged
// ---------------
// Purpose:
// Concatenate every mesh into a single cvc::geometry, offsetting each mesh's
// triangle (and line/quad) indices by the running vertex count so the merged
// index buffer is correct. Per-vertex attributes (normals/colors/uvs/tangents)
// are appended in the same order. Material assignment is not preserved.
geometry merged() const;

// ---------------
// model::extents
// ---------------
// Purpose:
// Union of every mesh's bounding box (reuses geometry's bbox facility).
bounding_box extents() const;

boost::uint64_t num_meshes() const { return meshes.size(); }
};

} // namespace cvc

#endif // __CVC_MODEL_H__
196 changes: 196 additions & 0 deletions inc/cvc/model/model_file_io.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
Copyright 2024 The University of Texas at Austin

This file is part of libcvc.

libcvc is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License version 2.1 as published by the
Free Software Foundation.
*/

#ifndef __CVC_MODEL_FILE_IO__
#define __CVC_MODEL_FILE_IO__

#include <boost/shared_ptr.hpp>
#include <cvc/core/exception.h>
#include <cvc/model/model.h>
#include <cvc/volume/bounding_box.h>
#include <list>
#include <map>
#include <string>
#include <vector>

namespace cvc {
CVC_DEF_EXCEPTION(unsupported_model_file_type);

// -------------
// model_file_io
// -------------
// Purpose:
// Provides I/O for model (multi-mesh scene) data. Mirrors geometry_file_io /
// volume_file_io: handlers declare the extensions they support and are
// dispatched by extension, first handler that succeeds wins.
// ---- Change History ----
// 2024 -- Joe R. -- Creation (Phase-3 model surface).
struct model_file_io {
static const char *FILE_EXTENSION_EXPR;

// -----------------
// model_file_io::id
// -----------------
// Purpose:
// Returns a string that identifies this model_file_io object. This should
// be unique, but is freeform.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
virtual const std::string &id() const = 0;

typedef std::list<std::string> extension_list;

// -------------------------
// model_file_io::extensions
// -------------------------
// Purpose:
// Returns a list of extensions that this model_file_io object supports.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
virtual const extension_list &extensions() const = 0;

// -------------------
// model_file_io::read
// -------------------
// Purpose:
// Reads a file and outputs a model object.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
virtual model read(const std::string &filename) const = 0;

// --------------------
// model_file_io::write
// --------------------
// Purpose:
// Writes a model to file. Export is optional; a handler that cannot write
// throws unsupported_model_file_type.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
virtual void write(const model &m, const std::string &filename) const = 0;

// ----------------------
// model_file_io::extents
// ----------------------
// Purpose:
// Returns the smallest bounding box that includes all vertices in the file.
// Default implementation reads the entire file and computes it.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
virtual bounding_box extents(const std::string &filename);

virtual ~model_file_io() {}

typedef boost::shared_ptr<model_file_io> ptr;
typedef std::vector<ptr> handlers;
typedef std::map<std::string, /* file extension */
handlers>
handler_map;

// -------------------------
// model_file_io::get_handlers
// -------------------------
// Purpose:
// Static initialization of handler map. Clients use the handler_map
// to add themselves to the collection of objects that are to be used
// to perform model file i/o operations.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static handler_map &get_handlers();

// -----------------------------
// model_file_io::insert_handler
// -----------------------------
// Purpose:
// Convenience function for adding objects to the map.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static void insert_handler(const ptr &mfio);

// -----------------------------
// model_file_io::remove_handler
// -----------------------------
// Purpose:
// Convenience function for removing objects from the map.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static void remove_handler(const ptr &mfio);

// -----------------------------
// model_file_io::remove_handler
// -----------------------------
// Purpose:
// Convenience function for removing objects from the map.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static void remove_handler(const std::string &name);

// -----------------------------
// model_file_io::get_extensions
// -----------------------------
// Purpose:
// Returns the list of supported file extensions.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static std::vector<std::string> get_extensions();

private:
// -----------------------------
// model_file_io::initialize_map
// -----------------------------
// Purpose:
// Adds the standard model_file_io objects to a new handler_map object
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static handler_map *initialize_map();

// -----------------------------
// model_file_io::insert_handler
// -----------------------------
// Purpose:
// Convenience function for adding objects to the specified map.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
static void insert_handler(handler_map &hm, const ptr &mfio);
};

// ----------
// read_model
// ----------
// Purpose:
// The main read model function. Refers to the handler map to choose
// an appropriate IO object for reading the requested model file.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
model read_model(const std::string &filename);

// -----------
// write_model
// -----------
// Purpose:
// The main write model function. Refers to the handler map to choose
// an appropriate IO object for writing the requested model file.
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
void write_model(const model &m, const std::string &filename);

// ----------------------------------
// register_default_model_handlers
// ----------------------------------
// Purpose:
// Register the built-in model I/O handlers without requiring a cvc::app
// instance. Called from model_file_io::get_handlers() the first time the
// handler map is requested. Registers nothing when no import backend is
// compiled in (e.g. CVC_ENABLE_ASSIMP off).
// ---- Change History ----
// 2024 -- Joe R. -- Creation.
void register_default_model_handlers();
} // namespace cvc

#endif
7 changes: 7 additions & 0 deletions inc/cvc/volume/io_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ void register_bunny_io();
void register_off_io();
void register_cvcraw_io();

// Conditionally compiled geometry handler. The Assimp geometry flattening
// handler (defined in model/assimp_io.cpp) registers itself into the
// geometry_file_io registry so read_geometry("foo.obj") works.
#ifdef CVC_ENABLE_ASSIMP
void register_assimp_geometry_io();
#endif

// Convenience entry point that calls each register_*_io() above. Used
// by geometry_file_io::get_handlers() to populate the handler map on
// first access without requiring a cvc::app instance to exist.
Expand Down
Loading
Loading