<commit_before>//
// Coypright (c) 2022 Singular Inversions Inc. (facegen.com)
// Use, modification and distribution is subject to the MIT License,
// see accompanying file LICENSE.txt or facegen.com/base_library_license.txt
//
// Command to software-render meshes to image files.
//

#include "stdafx.h"

#include "FgCommand.hpp"
#include "FgFileSystem.hpp"
#include "Fg3dMeshIo.hpp"
#include "Fg3dCamera.hpp"
#include "FgSoftRender.hpp"
#include "FgTime.hpp"
#include "FgSyntax.hpp"
#include "FgMetaFormat.hpp"
#include "FgImgDisplay.hpp"
#include "FgParse.hpp"
#include "FgTestUtils.hpp"
#include "FgBuild.hpp"
#include "FgGridTriangles.hpp"

using namespace std;

namespace Fg {

namespace {

struct      ModelFile
{
    String              meshFilename;
    Strings             imgFilenames;
    bool                shiny = false;
    FG_SERIALIZE3(meshFilename,imgFilenames,shiny)
};
typedef Svec<ModelFile>     ModelFiles;

struct      RenderArgs
{
    ModelFiles              models;
    double                  roll = 0,       // Z axis rotation, radians, in order:
                            tilt = 0,       // X axis rotation
                            pan = 0;        // Y axis rotation
    SimilarityD             modelview;      // Transform world to openGL eye coordinate system (OECS)
    // transform from projected coordinates (image tangent CS) to image unit CS (x,y in [0,1]):
    AffineEw2D              itcsToIucs;
    Vec2UI                  imagePixelSize = Vec2UI(512,512);
    RenderOptions           options;

    FG_SERIALIZE8(models,roll,tilt,pan,modelview,itcsToIucs,imagePixelSize,options);
};

Meshes              loadMeshes(ModelFiles const & mfs)
{
    Meshes          meshes;
    for (ModelFile const & mf : mfs) {
        meshes.push_back(loadMesh(mf.meshFilename));
        for (size_t ss=0; ss<mf.imgFilenames.size(); ++ss) {
            if (ss < meshes.back().surfaces.size()) {
                meshes.back().surfaces[ss].setAlbedoMap(loadImage(mf.imgFilenames[ss]));
                meshes.back().surfaces[ss].material.shiny = mf.shiny;
            }
            else
                fgout << "WARNING: more images specified than surfaces for mesh " << mf.meshFilename;
        }
    }
    return meshes;
}

void                cmdRenderSetup(CLArgs const & args)
{
    Syntax              syn {args,
        R"(<name>.xml (-<option> <value>)* (<mesh>.<ext> [<albedo>.<img>])*
    <option>        - (roll | tilt | pan) - specify object pose, with <value> given in degrees
    <ext>           - )" + getMeshLoadExtsCLDescription() + R"(
    <img>           - )" + getImageFileExtCLDescriptions() + R"(
OUTPUT:
    <name>.xml      - render configuration file
NOTES:
    * creates default rendering parameters for the given models
    * fields in <name>.xml can be modified as long as the XML structure remains valid
    <name>.xml description:
        <models>
            The list of mesh and related albedo map files to be rendered.
        <roll>,<tilt>,<pan>
            Euler angle rotations in radians applied to the models, in this order, before
            application of <modelview> below. Can be used to orient your meshes for proper viewing
            (FG head coordinate system is X - head's left, Y - head's up, Z - head's forward),
            or to orient the head as desired. Defaults are 0.
        <modelview>
            The similarity transform from mesh coordinates to openGL eye coordinates,
            consisting of scale, rotation (as a quaternion) and translation, applied in that order.
            Default places subject at a viewing distance from the camera to mostly fill the frame.
        <itcsToIucs>
            The 2D transform from projected coordinates ("image tangent CS") to the
            "image unit CS" in which x,y are in [0,1] with origin at top left of image.
        <imagePixelSize>
            Pixel width, height
        <options>
            <lighting> all color component values below are in the range [0,1]:
                <ambient> light coming from all directions.
                <lights>
                    <colour> RGB order
                    <direction> in order of X,Y,Z in OpenGL Eye Coordinates.
            <backgroundColor> RGBA order, values in [0,1].
            <antiAliasBitDepth> 3 is high quality (equivalent to 8x FSAA) without running too slowly
            <renderSurfPoints> 0 - don't, 1 - when visible, 2 - always.
                They are rendered as single-pixel green dots over the image.)"
    };
    string              name = syn.next();
    RenderArgs          rend;
    while (syn.more()) {
        if (syn.peekNext()[0] == '-') {         // option
            String          opt = syn.next();
            if (opt == "-roll")
                rend.roll = degToRad(syn.nextAs<float>());
            else if (opt == "-tilt")
                rend.tilt = degToRad(syn.nextAs<float>());
            else if (opt == "-pan")
                rend.pan = degToRad(syn.nextAs<float>());
            else
                syn.error("Unrecognized option",opt);
        }
        //! Set up the default render options from the arguments:
        ModelFile           mf;
        mf.meshFilename = syn.next();
        while (syn.more() && hasImageFileExt(syn.peekNext()))
            mf.imgFilenames.push_back(syn.next());
        rend.models.push_back(mf);
    }
    // Load data from files to validate and set modelview:
    Meshes              meshes = loadMeshes(rend.models);
    Mat32F              bounds = cBounds(meshes);
    CameraParams        cps {fgF2D(bounds)};
    cps.logRelScale = std::log(0.9);
    cps.fovMaxDeg = 17.0;
    Camera              cam = cps.camera(rend.imagePixelSize);
    rend.modelview = cam.modelview;<commit_msg>fix cmd render setup options parse<commit_after>//
// Coypright (c) 2022 Singular Inversions Inc. (facegen.com)
// Use, modification and distribution is subject to the MIT License,
// see accompanying file LICENSE.txt or facegen.com/base_library_license.txt
//
// Command to software-render meshes to image files.
//

#include "stdafx.h"

#include "FgCommand.hpp"
#include "FgFileSystem.hpp"
#include "Fg3dMeshIo.hpp"
#include "Fg3dCamera.hpp"
#include "FgSoftRender.hpp"
#include "FgTime.hpp"
#include "FgSyntax.hpp"
#include "FgMetaFormat.hpp"
#include "FgImgDisplay.hpp"
#include "FgParse.hpp"
#include "FgTestUtils.hpp"
#include "FgBuild.hpp"
#include "FgGridTriangles.hpp"

using namespace std;

namespace Fg {

namespace {

struct      ModelFile
{
    String              meshFilename;
    Strings             imgFilenames;
    bool                shiny = false;
    FG_SERIALIZE3(meshFilename,imgFilenames,shiny)
};
typedef Svec<ModelFile>     ModelFiles;

struct      RenderArgs
{
    ModelFiles              models;
    double                  roll = 0,       // Z axis rotation, radians, in order:
                            tilt = 0,       // X axis rotation
                            pan = 0;        // Y axis rotation
    SimilarityD             modelview;      // Transform world to openGL eye coordinate system (OECS)
    // transform from projected coordinates (image tangent CS) to image unit CS (x,y in [0,1]):
    AffineEw2D              itcsToIucs;
    Vec2UI                  imagePixelSize = Vec2UI(512,512);
    RenderOptions           options;

    FG_SERIALIZE8(models,roll,tilt,pan,modelview,itcsToIucs,imagePixelSize,options);
};

Meshes              loadMeshes(ModelFiles const & mfs)
{
    Meshes          meshes;
    for (ModelFile const & mf : mfs) {
        meshes.push_back(loadMesh(mf.meshFilename));
        for (size_t ss=0; ss<mf.imgFilenames.size(); ++ss) {
            if (ss < meshes.back().surfaces.size()) {
                meshes.back().surfaces[ss].setAlbedoMap(loadImage(mf.imgFilenames[ss]));
                meshes.back().surfaces[ss].material.shiny = mf.shiny;
            }
            else
                fgout << "WARNING: more images specified than surfaces for mesh " << mf.meshFilename;
        }
    }
    return meshes;
}

void                cmdRenderSetup(CLArgs const & args)
{
    Syntax              syn {args,
        R"(<name>.xml (-<option> <value>)* (<mesh>.<ext> [<albedo>.<img>])*
    <option>        - (roll | tilt | pan) - specify object pose, with <value> given in degrees
    <ext>           - )" + getMeshLoadExtsCLDescription() + R"(
    <img>           - )" + getImageFileExtCLDescriptions() + R"(
OUTPUT:
    <name>.xml      - render configuration file
NOTES:
    * creates default rendering parameters for the given models
    * fields in <name>.xml can be modified as long as the XML structure remains valid
    <name>.xml description:
        <models>
            The list of mesh and related albedo map files to be rendered.
        <roll>,<tilt>,<pan>
            Euler angle rotations in radians applied to the models, in this order, before
            application of <modelview> below. Can be used to orient your meshes for proper viewing
            (FG head coordinate system is X - head's left, Y - head's up, Z - head's forward),
            or to orient the head as desired. Defaults are 0.
        <modelview>
            The similarity transform from mesh coordinates to openGL eye coordinates,
            consisting of scale, rotation (as a quaternion) and translation, applied in that order.
            Default places subject at a viewing distance from the camera to mostly fill the frame.
        <itcsToIucs>
            The 2D transform from projected coordinates ("image tangent CS") to the
            "image unit CS" in which x,y are in [0,1] with origin at top left of image.
        <imagePixelSize>
            Pixel width, height
        <options>
            <lighting> all color component values below are in the range [0,1]:
                <ambient> light coming from all directions.
                <lights>
                    <colour> RGB order
                    <direction> in order of X,Y,Z in OpenGL Eye Coordinates.
            <backgroundColor> RGBA order, values in [0,1].
            <antiAliasBitDepth> 3 is high quality (equivalent to 8x FSAA) without running too slowly
            <renderSurfPoints> 0 - don't, 1 - when visible, 2 - always.
                They are rendered as single-pixel green dots over the image.)"
    };
    string              name = syn.next();
    RenderArgs          rend;
    while (syn.more() && (syn.peekNext()[0] == '-')) {          // options
        String          opt = syn.next();
        if (opt == "-roll")
            rend.roll = degToRad(syn.nextAs<float>());
        else if (opt == "-tilt")
            rend.tilt = degToRad(syn.nextAs<float>());
        else if (opt == "-pan")
            rend.pan = degToRad(syn.nextAs<float>());
        else
            syn.error("Unrecognized option",opt);
    }
    while (syn.more()) {                                        // meshes & maps
        ModelFile           mf;
        mf.meshFilename = syn.next();
        while (syn.more() && hasImageFileExt(syn.peekNext()))
            mf.imgFilenames.push_back(syn.next());
        rend.models.push_back(mf);
    }
    // Load data from files to validate and set modelview:
    Meshes              meshes = loadMeshes(rend.models);
    Mat32F              bounds = cBounds(meshes);
    CameraParams        cps {fgF2D(bounds)};
    cps.logRelScale = std::log(0.9);
    cps.fovMaxDeg = 17.0;
    Camera              cam = cps.camera(rend.imagePixelSize);
    rend.modelview = cam.modelview;
{'fraction_non_alphanumeric': 0.10236357316649287, 'fraction_numerical': 0.007820646506777894, 'mean_word_length': 2.027887398053144, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '112444', 'n_tokens_mistral': 3428, 'n_tokens_neox': 3280, 'n_words': 1020}