3DCoat Core API
The 3DCoat API documentation.
auto_export.cpp

The auto-export example, same as used in the 3DCoat

// The auto-export example, same as used in the 3DCoat
#include <CoreAPI.h>
//If you need the release build at full speed please change the Debug below to Release
//@config: Debug
class AutoExport : public BaseClass {
public:
AutoExport() {
TexRes = 4;
DesiredPolycount = 40000;
Enumerator* E = ENUM.Get("EXPPRESETS");
expPreset = E->Get("PBR+EXR-displacement");
ReductionPercent = 80;
DecimateFromAllVolumes = false;
pDecimateFromAllVolumes = false;
auto vol = coat::Scene::current().Volume();
CurrentPolycount = vol.getPolycount();
EachVolumeToSeparateAsset = false;
ScanDepthPercent = 1.0;
CenterEachAssetAroundBoundBox = false;
EachAssetToOwnFolder = false;
DropToBlender = false;
UE5_optimized = false;
RegularExport = true;
SkipFilenamePreffix = true;
}
int TexRes;
int DesiredPolycount;
int expPreset;
coat::str exportMesh;
coat::str texturesExportPath;
float ReductionPercent;
int CurrentPolycount;
bool DecimateFromAllVolumes;
bool pDecimateFromAllVolumes;
bool EachVolumeToSeparateAsset;
bool CenterEachAssetAroundBoundBox;
bool EachAssetToOwnFolder;
float ScanDepthPercent;
bool DropToBlender;
bool UE5_optimized;
bool RegularExport;
bool SkipFilenamePreffix;
SERIALIZE() {
Enumerator* E = ENUM.Get("EXSIZE");
if (E->GetAmount() == 0) {
E->Add("#256x256");
E->Add("#512x512");
E->Add("#1024x1024");
E->Add("#2048x2048");
E->Add("#4096x4096");
E->Add("#8192x8192");
}
CHK_GROUP(1) REG_AUTO(RegularExport);
CHK_GROUP(1) REG_AUTO(DropToBlender);
CHK_GROUP(1) REG_AUTO(UE5_optimized);
DELIMITER;
REG_AUTO(EachVolumeToSeparateAsset);
REG_AUTO(CenterEachAssetAroundBoundBox);
if (!EachVolumeToSeparateAsset) {
REG_AUTO(DecimateFromAllVolumes);
}
else {
REG_AUTO(EachAssetToOwnFolder);
if (!EachAssetToOwnFolder) {
REG_AUTO(SkipFilenamePreffix);
}
}
// gather all supported export extensions
if(UE5_optimized) {
FILEPATH(exportMesh, "exportMesh", "save:*.usdz,*.usd")
} else
if (DropToBlender) {
FILEPATH(exportMesh, "exportMesh", "save:*.fbx");
}
else {
FILEPATH(exportMesh, "exportMesh", meshes.ToCharPtr());
}
FILEPATH(texturesExportPath, "texturesExportPath", "folder");
REG_DROPLIST(TexRes, "TexRes", "EXSIZE");
if (RegularExport) {
REG_DROPLIST(expPreset, "expPreset", "EXPPRESETS");
}
DELIMITER;
//^ means read-only field, in this case it will be shown as value without name
REG_AUTO(CurrentPolycount, "^CurrentPolycount");
FSLIDER(ReductionPercent, "ReductionPercent", 0, 99, 1, false);
UI_LAYOUT("[] 6");
//maticon refers icons in data/material.io/icons/black/
TEXTMSG2("{maticon arrow_forward}");
REG_AUTO(DesiredPolycount, "ReducedPolycount");
FSLIDER(ScanDepthPercent, "ScanDepthPercent", 0.01, 100, 1, 0);
}
// this function called each frame when the class editor open
bool ProcessInEditor(BaseClass* Parent) override {
if (CurrentPolycount > 0) {
ReductionPercent = 100.0f - DesiredPolycount * 100.0f / CurrentPolycount;
}
if (DecimateFromAllVolumes != pDecimateFromAllVolumes) {
pDecimateFromAllVolumes = DecimateFromAllVolumes;
// calculate the poly-count
if (DecimateFromAllVolumes) {
CurrentPolycount = 0;
auto root = coat::Scene::sculptRoot();
root.iterateVisibleSubtree(
[&](coat::SceneElement e)->bool {
CurrentPolycount += e.Volume().getPolycount();
return false;
});
}
else {
auto vol = coat::Scene::current().Volume();
CurrentPolycount = vol.getPolycount();
}
}
return false;
}
// called when any field changed by user
bool OnChangeMember(BaseClass* MembClass, void* MembPtr, void* MembExtra, const char* MembName) override {
if(strstr(MembName, "ReductionPercent")) {
DesiredPolycount = CurrentPolycount * (100.0 - ReductionPercent) / 100.0;
}
return false;
}
};
void RemovePaintObjects() {
// switch to paint room
coat::ui::toRoom("Paint");
for (int k = 0; k < 10; k++) {
// delete the first object in the list
if (coat::ui::cmd("$OneSubObject::&OneSubObject::DeleteElm[0]",
[&] {
coat::ui::cmd("$DialogButton#1");
})) {
// just a step to refresh the UI
}
}
}
// EXPORT_COMMAND used to simplify usage of this command withion the 3DCoat sources and
// insert it directly into the Coat's UI system
EXPORT_COMMAND(DecimateAutoMapExport) {
AutoExport se;
if (coat::io::fileExists("UserPrefs/CoreAPI/auto_export.json")) {
se.ReadFromFile("UserPrefs/CoreAPI/auto_export.json");
}
// show the parameters dialog
if (coat::dialog().ok().cancel().text("SimpSculptExportHint").caption("SimpSculptExportHintCap").params(&se).show() == 1) {
if (coat::dialog().ok().cancel().text("RunBakeScriptInfo").caption("SimpSculptExportHintCap").dontShowAgainCheckbox().show() <= 1) {
// hide annoying message about textures locking
coat::ui::hideDontShowAgainMessage("AttachTextureHint");
// don't fade background, but keep it' state, auto_keep allows to save that value in the current scope,
// change it and restore after the exit of the scope
if (se.DropToBlender) {
// the blender applink toes not support embedding
coat::ui::setOption("EmbedTexturesToFBX", "false");
// set blender's specific normalmapping settings
// Generally, you may use just English text itens below
// but I decided to use the identifiers instead
coat::ui::setOption("SoftwarePreset", "Blender");
coat::ui::setOption("NormalsCalculationMethod", "nm_AngleWeighed");
coat::ui::setOption("NMAP_EXPORT_TYPE", "NM_MAYA");
coat::ui::setOption("TriangulationMethod", "DelaunayTriangulation");
coat::ui::setOption("TBNMethod", "MikkTSpace");
}
if (se.UE5_optimized) {
// set UE5 specific normalmapping settings
coat::ui::setOption("SoftwarePreset", "UnrealEngine");
coat::ui::setOption("NormalsCalculationMethod", "nm_AngleWeighed");
coat::ui::setOption("NMAP_EXPORT_TYPE", "NM_3DMAX");
coat::ui::setOption("TriangulationMethod", "NaiveTriangulation");
coat::ui::setOption("TBNMethod", "MikkTSpace");
Enumerator* E = ENUM.Get("EXPPRESETS");
se.expPreset = E->Get("USD (PBR Standard)");
}
// save settings
se.WriteToFile("UserPrefs/CoreAPI/auto_export.json");
// create the list of volumes we want to handle
auto_keep(se.DecimateFromAllVolumes);
if (se.EachVolumeToSeparateAsset) {
se.DecimateFromAllVolumes = false;
[&](auto el) -> bool {
volumes.Add(el.Volume());
return false;
});
}
else {
volumes.Add(coat::Scene::current().Volume());
}
// go through all volumes we want to operate
for (int i = 0; i < volumes.Count(); i++) {
auto vol = volumes[i];
// select the volume
if (se.EachVolumeToSeparateAsset)vol.inScene().selectOne();
comms::cBounds ab;
ab.SetEmpty();
if (!se.DecimateFromAllVolumes) {
ab = vol.calcWorldSpaceAABB();
}
else {
[&](auto el) -> bool {
ab.AddBounds(el.Volume().calcWorldSpaceAABB());
return false;
});
}
coat::vec3 center = ab.GetCenter();
auto transform = [&](const coat::mat4& t) {
if (se.CenterEachAssetAroundBoundBox) {
if (!se.DecimateFromAllVolumes) {
vol.inScene().transform_single(t);
}
else {
[&](auto el) -> bool {
el.transform_single(t);
return false;
});
}
}
};
transform(coat::mat4::Translation(-center));
float d = ab.GetDiagonal() * se.ScanDepthPercent / 100.0;
int cp = vol.getPolycount();
if (cp > 0) {
// calculating the decimation percent
float per = 100.0f - se.DesiredPolycount * 100.0f / float(cp);
if (per > 100.0f)per = 99.0f;
if (per < 0.0f)per = 0.0f;
// get the name of the export preset, there is index
// of the preset in the se.expPreset
Enumerator* E = ENUM.Get("EXPPRESETS");
coat::str preset = E->Get(se.expPreset);
RemovePaintObjects();
coat::ui::toRoom("Retopo");
bool nameCorr = coat::ui::getBoolField("$UseNamesCorrespondence");
coat::ui::setBoolValue("$UseNamesCorrespondence", true);
coat::ui::cmd("$ClearTM");
coat::ui::toRoom("Sculpt");
coat::ui::cmd(se.DecimateFromAllVolumes ? "$DecimateAllToRetopo" : "$DecimateToRetopo",
[&] {
coat::ui::setSliderValue("$DecimationParams::ReductionPercent", per);
coat::ui::cmd("$DialogButton#1");
});
// switch to retopo room
coat::ui::toRoom("Retopo");
// refresh UI
// closest along normal to keep shape
coat::ui::cmd("$SnapToNearestAlongNormal");
// relax the mesh, snapping rurned ON
coat::ui::cmd("$ApplyTSm");
// refresh UI
// the string that contains texture size
coat::str texSize = coat::str::ToString(256 << se.TexRes);
// set the baking scan depth, the depth is auto-cacculated and substituted by Coat
coat::ui::setOption("BakeScanDepthOut", d);
coat::ui::setOption("BakeScanDepthIn", d);
// call Bake->Bake with normal map+flat displacement
coat::ui::cmd("$MergeForDPNM_flatdisp", [&] {
// set texture width when the field accessible
coat::ui::cmd("$COMBOBOX_TEXTURE_SIZE_X" + texSize);
// set texture height
coat::ui::cmd("$COMBOBOX_TEXTURE_SIZE_Y" + texSize);
// press OK at the end
coat::ui::cmd("$DialogButton#1");
});
coat::ui::setBoolValue("$UseNamesCorrespondence", nameCorr);
coat::ui::toRoom("Paint");
// fill the mesh name
coat::str name = se.exportMesh;
if (se.EachVolumeToSeparateAsset) {
coat::str ext = name.GetFileExtension();
name.RemoveFileExtension();
if (se.EachAssetToOwnFolder) {
name += "/";
name += vol.inScene().name();
name += "/";
name += vol.inScene().name();
name += ".";
name += ext;
}
else {
if(se.SkipFilenamePreffix) {
name.RemoveFileName();
name.EnsureTrailingSlash();
} else name += "_";
name += vol.inScene().name();
name += ".";
name += ext;
}
}
if(se.UE5_optimized) {
cStr ext = name.GetFileExtension();
if (ext.IndexOf("usd") == -1) {
name.RemoveFileExtension();
name += ".usdz";
}
}
if (se.DropToBlender) {
name.RemoveFileExtension();
name += ".fbx";
// call the export dialog
coat::ui::cmd("$Blender",
[&] {
coat::ui::cmd("$DialogButton#1");
});
}
else {
coat::ui::cmd("$EXPORTOBJECT", [&] {
// set export preset
coat::ui::cmd("$COMBOBOX_" + preset);
coat::ui::setEditBoxValue("$ExportOpt::ExportMeshName", name);
// enable mesh export
coat::ui::setBoolValue("$ExportOpt::ExportTextures", true);
// enable textures export
coat::ui::setBoolValue("$ExportOpt::ExportGeometry", true);
// set the textures folder path
if (coat::ui::setEditBoxValue("$ExportOpt::PathForTextures", se.texturesExportPath)) {
// press Export at the end
coat::ui::cmd("$DialogButton#1");
}
});
}
// remove all paint objects
RemovePaintObjects();
coat::ui::toRoom("Retopo");
// clear all retopo objects
coat::ui::cmd("$ClearTM");
coat::ui::toRoom("Sculpt");
}
transform(coat::mat4::Translation(center));
}
}
}
};
Use this class for build a class for UI or serialization. see class_reg.h for details about the class...
Definition: BaseClass.h:90
virtual bool ProcessInEditor(BaseClass *Parent)
Editor will call next function always when it is visible in tree. Return true if overriden.
Definition: BaseClass.h:324
virtual bool OnChangeMember(BaseClass *MembClass, void *MembPtr, void *MembExtra, const char *MembName)
Editor will call it every time when member changes and editor is able to recognise it.
Definition: BaseClass.h:336
Definition: cStr.h:6
The scene element, like sculpt object or curve.
Definition: CoreAPI.h:380
bool iterateVisibleSubtree(const std::function< bool(SceneElement)> &fn) const
iterate over the visible subtree
static SceneElement current()
returns the current sculpt object
static SceneElement sculptRoot()
get the root of all sculpt objects
the rich dialog. You may customize it, show your custom parameters and custom buttons.
Definition: CoreAPI.h:1305
static bool & fadeDialogsBackground()
returns the reference to the global property - fade modal dialogs background (true) or not (false)
static void step(int count=1)
perform rendering cycles
static bool fileExists(const char *path)
check if file exists
static const char * supportedMeshesFormats()
returns the list of supported images formats
static bool setBoolValue(const char *id, bool value)
Set the value for the checkbox in UI.
static void toRoom(const char *name)
switch to the room
static void setFileForFileDialog(const char *filename)
Set the file for the next file dialog that will be triggered by user. If you will use coat::ui:cmd(....
static bool getBoolField(const char *id)
Get the bool field from the checkbox in UI.
static bool cmd(const char *id, std::function< void()> process_in_modal_dialog=0)
perform some command in UI.
static void hideDontShowAgainMessage(const char *id)
Hides the "Don't show again dialog" for the current session (not forever)
static bool setOption(const char *id, const char *value)
set the value to preferences
static bool setEditBoxValue(const char *id, const char *value)
set the edit box value
comms::cVec3 vec3
3D - float vector, see the cVec3
Definition: CoreAPI.h:16
comms::cMat4 mat4
4x4 float matrix, see the cMat4
Definition: CoreAPI.h:22
comms::cStr str
the string that is compatible with the 3DCoat engine, see the cStr
Definition: CoreAPI.h:28
comms::cList< X > list
the array template, see cList
Definition: CoreAPI.h:31