How-to: Apply texture to separate faces of a shell

Language: C++

The following example leverages the feature of adding textures to discrete faces of a shell:

#include <string>

#include "hoops_license.h"
#include "sc_store.h"
#include "sc_assemblytree.h"

#include "ginger_bread_jpg.h"

class ApplicationLogger: public SC::Store::Logger {
public:
    virtual void
    Message(const char* message) const
    {
        printf("%s\n", message);
    }
};

static void
AddSquareFace(SC::Store::Mesh& mesh, uint32_t const* point_indices, uint32_t const* uv_indices)
{
    mesh.face_elements.emplace_back();
    SC::Store::MeshElement& face = mesh.face_elements.back();

    // Add first face triangle
    for (size_t i = 0; i < 3; ++i) {
        face.indices.push_back(point_indices[i]);
        face.indices.push_back(uv_indices[i]);
    }

    // Add second face triangle
    for (size_t i = 0; i < 3; ++i) {
        auto index = (i + 2) % 4;
        face.indices.push_back(point_indices[index]);
        face.indices.push_back(uv_indices[index]);
    }
}

static SC::Store::MeshKey
CreateCubeMesh(SC::Store::Model& model)
{
    using SC::Store::Mesh;

    Mesh mesh;
    mesh.flags = (Mesh::Bits)(Mesh::ClockwiseWinding | Mesh::Manifold | Mesh::FaceUVs);

    SC::Store::Point points[] = {
        { 0, 0, 1 }, // 0
        { 1, 0, 1 }, // 1
        { 0, 0, 0 }, // 2
        { 1, 0, 0 }, // 3
        { 1, 1, 1 }, // 4
        { 1, 1, 0 }, // 5
        { 0, 1, 1 }, // 6
        { 0, 1, 0 }, // 7
    };
    SC::Store::UV uvs[] = {
        { 0, 0 }, // 0
        { 1, 0 }, // 1
        { 1, 1 }, // 2
        { 0, 1 }, // 3
    };
    mesh.points      = points;
    mesh.point_count = 8;
    mesh.uvs         = uvs;
    mesh.uv_count    = 4;

    uint32_t uv_indices[] = { 0, 1, 2, 3 };

    {
        uint32_t point_indices[] = { 0, 1, 3, 2 };
        AddSquareFace(mesh, point_indices, uv_indices);
    }
    {
        uint32_t point_indices[] = { 6, 4, 1, 0 };
        AddSquareFace(mesh, point_indices, uv_indices);
    }
    {
        uint32_t point_indices[] = { 7, 5, 4, 6 };
        AddSquareFace(mesh, point_indices, uv_indices);
    }
    {
        uint32_t point_indices[] = { 2, 3, 5, 7 };
        AddSquareFace(mesh, point_indices, uv_indices);
    }
    {
        uint32_t point_indices[] = { 1, 4, 5, 3 };
        AddSquareFace(mesh, point_indices, uv_indices);
    }
    {
        uint32_t point_indices[] = { 6, 0, 2, 7 };
        AddSquareFace(mesh, point_indices, uv_indices);
    }

    {
        // Create a material map to color 5 of the 6 cube faces.
        // The 6th face will get colored by the instance material.

        SC::Store::Color colors[] = {
            SC::Store::Color(1.0f, 0.0f, 0.0f, 1.0f), //
            SC::Store::Color(0.0f, 1.0f, 0.0f, 1.0f), //
            SC::Store::Color(0.0f, 0.0f, 1.0f, 1.0f), //
            SC::Store::Color(1.0f, 0.0f, 1.0f, 1.0f), //
        };

        SC::Store::MaterialKeys material_keys;

        for (SC::Store::Color const& color: colors) {
            material_keys.push_back(model.Insert(color));
        }

        {
            SC::Store::ImageKey image_key_jpeg =
                model.Insert(ginger_bread_jpg_size, ginger_bread_jpg, SC::Store::ImageFormat::JPEG);

            uint32_t const packedFlags = SC::Store::Texture::PackFlags(
                SC::Store::Texture::Clamp, SC::Store::Texture::InterpolationOn,
                SC::Store::Texture::MipMappingOn, SC::Store::Texture::UV, SC::Store::Texture::None);

            SC::Store::Material material;
            material.diffuse_textures.push_back(
                SC::Store::Texture(image_key_jpeg, SC::Store::MatrixKey(), packedFlags));

            material_keys.push_back(model.Insert(material));
        }

        mesh.mesh_face_element_material_map = model.Insert(material_keys);
    }

    return model.Insert(mesh);
}

int
MaterialMapSample(const std::string& model_path)
{
    std::string model_name = "material_map_sample";

    ApplicationLogger logger;

    try {
        // Open the cache.
        SC::Store::Database::SetLicense(HOOPS_LICENSE);
        SC::Store::Cache cache = SC::Store::Database::Open(logger);

        std::string file_path_string = model_path;
        file_path_string += "/";
        file_path_string += model_name;

        // Does the model in question exist?
        if (cache.Exists(file_path_string.c_str())) {
            printf("Model already exists.\n");
            return 1;
        }

        // Create and open the model we care about.
        SC::Store::Model model = cache.Open(file_path_string.c_str());

        model.Include(model);

        SC::Store::MeshKey mesh_key = CreateCubeMesh(model);
        {
            SC::Store::Matrix3d matrix = {
                1, 0, 0, //
                0, 1, 0, //
                0, 0, 1, //
                0, 0, 0, //
            };
            SC::Store::MatrixKey matrix_key = model.Insert(matrix);

            SC::Store::Color color(
                0, 0, 0,
                1); // Colors the instance's face that does not have a material map applied.
            SC::Store::MaterialKey material_key = model.Insert(color);

            model.Instance(mesh_key, matrix_key, material_key, material_key);
        }

        // Prepare the model for streaming.
        model.PrepareStream();
    } catch (std::exception const& e) {
        std::string message("Exception: ");
        message.append(e.what());
        message.append("\n");
        logger.Message(message.c_str());
        return 1;
    }

    return 0;
}

Also attaching the .cpp file which can be transferred to the following directory to replace the existing libsc example:
HOOPS_Communicator_2021_SP1\authoring\libsc\examples

sc_store_material_map.cpp (5.5 KB)

This is a screenshot of the resulting single-face textured cube as viewed in the Web Viewer: