3DCoat Core API
The 3DCoat API documentation.
Loading...
Searching...
No Matches
TreesGenerator.cpp

The trees generator example

// The trees generator example
#include <CoreAPI.h>
//@config: Release
using namespace coat;
static const char* backup = "data/Temp/TreesGenerator.json";
static const char *LeafsPath = "UserPrefs/ToolsSettings/Leafs/";
const char* GeneratorID = "TreesGenerator";
// This is the tree trunk generator class.
// It ir registered as tool in the current room toolset as the first tool in list.
// In voxels additional smoothing spheres are applied to make smooth transition between branches.
// This tool is derived from the SculptGenerator, it means that we make non-destructive object
class TreesGenerator : public SculptGenerator{
public:
TreesGenerator() {
defaults();
collision.setUnit(8);
}
// returns the name/ID of the tool in the toolset
virtual const char* GetID() {
return GeneratorID;
}
void defaults() {
sign = 1;
noiseAmpliicator = 1.2;
chunkLengthAmplificator = 1.3;
overallDensity = 1.0;
radiusDecrement = 0.75;
gravity = 0.05;
transition = 4;
start_transition = 0;
ends_distortion = 0.125;
Level0_branches = 1;
Level0_Randomness = 1;
Level0_StartBranchesLevel = 0;
Level0_Proportions = 1;
GrowUpStrength = 1;
BranchesRandomness = 1;
BranchesStartAngle = 100;
BranchesEndAngle = 0;
BranchesProportions = 1;
MinimalBranchRadius = 0.9;
MakeRoots = true;
RootsRandomness = 1;
RootsLength = 1;
RootsComplexity = 0.5;
RootsCount = 8;
TreeGenSeed = 0;
RootsStartGrowHeight = 0.05;
addLeafs = true;
addNarrowTrunks = false;
LeafsScale = 1;
LeafsDensity = 1;
LeafsScattering = 1;
LeafsColor1 = 0xFF8BA055;
LeafsColor2 = 0xFF519E2A;
LeafsType = 0;
pLeafsType = -1;
UpdateLeafsEnum();
}
// the Enumerator object for leafs
const char* leafsEnum() {
return "LEAFSTYPES";
}
// Update the enumerator from the folders list
void UpdateLeafsEnum() {
utils::clearEnum(leafsEnum());
io::ListFolders(LeafsPath, FL);
for (auto& s : FL) {
s.RemoveFilePath();
utils::addEnumValue(leafsEnum(), s);
}
}
// get the current leaf name
const char* GetLeafName() {
return utils::getEnumValueByIndex(leafsEnum(), LeafsType);
}
// the summary mesh
Mesh summ;
Mesh smallTrunks;
// the spheres to relax the voxels after merging
list<vec4> relaxSpheres;
// the collision space to avoid self-intersections
SphericalCollision collision;
// the strength of grow-up
float GrowUpStrength;
// the points of the main trunk (uniform and dense enough)
list<vec4> mainTrunk;
// positive for trunk, negative for roots
float sign;
// the noise multiplied by this value when you generate next level of branches
float noiseAmpliicator;
// the branches become relatively lengthy when you generate next level
float chunkLengthAmplificator;
// overall density, it increases the branches frequency
float overallDensity;
// how the rediu decreases when we generate next level
float radiusDecrement;
// the anti-gravity level, how much the branches are pulled up
float gravity;
// the bigger value means faster transition of the branch direction from the parent direction to the destination direction
float transition;
// the initial transition
float start_transition;
// ends of branches are more distorted
float ends_distortion;
// randomness of the main branch
float Level0_Randomness;
// amount of main branches
int Level0_branches;
// the level (0..1) of the first branch
float Level0_StartBranchesLevel;
// the main branch proportions
float Level0_Proportions;
// secondary branches curvature
float BranchesRandomness;
// initial angle of growing the secondary branches (angle taken between 2 values)
float BranchesStartAngle;
// the end value of the angle
float BranchesEndAngle;
// seconday branches proportions
float BranchesProportions;
// the threshold for the branch radius
float MinimalBranchRadius;
// need roots?
bool MakeRoots;
// the roots curvature
float RootsRandomness;
// the roots proportions (in comparison to default)
float RootsLength;
// the sub-branches frequency
float RootsComplexity;
// the initial height where roots start growing
float RootsStartGrowHeight;
// the count of roots
int RootsCount;
// the seed for the random generator
int TreeGenSeed;
// add the leafs to the tree
bool addLeafs;
bool addNarrowTrunks;
// previous leafs type, required for the update purposes
int pLeafsType;
// leafs type
int LeafsType;
// leafs size
float LeafsScale;
// density
float LeafsDensity;
// scattering, randomness
float LeafsScattering;
// colors modulators
DWORD LeafsColor1;
DWORD LeafsColor2;
// backfaces culling extracted from the settings
bool bfCulling;
// the folder to remove when user wants to remove the leafs type
str FolderToRemove;
// the array of the potential places for leafs
// the dice
void RandomizeTree() {
TreeGenSeed = rand();
NotifyChanges();
}
// save the tree preset
void SaveTreePreset() {
str fn;
if(io::saveFileDialog("*.tree",fn)) {
WriteToFile(fn);
}
}
// load the tree preset
void LoadTreePreset() {
str fn;
if (io::openFileDialog("*.tree", fn)) {
defaults();
ReadFromFile(fn);
}
NotifyChanges();
}
// the name for the generator
virtual const char* getDefaultObjectName() {
return "Tree";
}
// register the object for serialization and UI
SERIALIZE() {
FUNCTION_CALL(CreateNewObject,"CreateNewTree");
if (getObject()) {
FSLIDER(overallDensity, "overallDensity", 0, 2, 100, 0);
FSLIDER(GrowUpStrength, "GrowUpStrength", -5, 5, 100, 0);
SLIDER(Level0_branches, "Level0_branches", 1, 8);
FSLIDER(Level0_Randomness, "Level0_Randomness", 0, 3, 100, 0);
FSLIDER(Level0_StartBranchesLevel, "Level0_StartBranchesLevel", 0, 0.8, 100, 0);
FSLIDER(Level0_Proportions, "Level0_Proportions", 0, 3, 100, 0);
DELIMITER;
FSLIDER(BranchesProportions, "BranchesProportions", 0, 3, 100, 0);
FSLIDER(radiusDecrement, "radiusDecrement", 0, 0.85, 100, 0);
FSLIDER(BranchesRandomness, "BranchesRandomness", 0, 3, 100, 0);
FSLIDER(BranchesStartAngle, "BranchesStartAngle", 30, 160, 1, 0);
FSLIDER(BranchesEndAngle, "BranchesEndAngle", 30, 160, 1, 0);
FSLIDER(MinimalBranchRadius, "MinimalBranchRadius", 0.5, 2, 1, 0);
DELIMITER;
REG_AUTO(MakeRoots);
if (MakeRoots) {
FSLIDER(RootsRandomness, "RootsRandomness", 0, 3, 100, 0);
FSLIDER(RootsLength, "RootsLength", 0, 3, 100, 0);
FSLIDER(RootsComplexity, "RootsComplexity", 0, 2, 100, 0);
FSLIDER(RootsStartGrowHeight, "RootsStartGrowHeight", 0, 0.5, 100, 0);
SLIDER(RootsCount, "RootsCount", 2, 16);
}
DELIMITER;
REG_AUTO(addLeafs);
FSLIDER(LeafsScale, "LeafsScale", 0.1, 4, 100, 0);
FSLIDER(LeafsDensity, "LeafsDensity", 0.1, 4, 100, 0);
FSLIDER(LeafsScattering, "LeafsScattering", 0.1, 4, 100, 0);
REG_AUTO(LeafsColor1);
REG_AUTO(LeafsColor2);
if (FolderToRemove.Length()) {
UI_LAYOUT("1[]");
REG_DROPLIST(LeafsType, "LeafsType", "LEAFSTYPES");
ICON_BUTTON("delete", removeLeafFolder, "REMOVELEAF_HINT");
}else {
REG_DROPLIST(LeafsType, "LeafsType", "LEAFSTYPES");
}
FUNCTION_CALL(AddLeafsType);
if(bfCulling) {
TEXTMSG2("TurnOffCulling");
if (hash)*hash << 1.0f;
}
DELIMITER;
UI_LAYOUT("1[]");
REG_AUTO(TreeGenSeed);
FUNCTION_CALL(RandomizeTree, "{maticon dice}");
UI_LAYOUT("2");
FUNCTION_CALL(SaveTreePreset);
FUNCTION_CALL(LoadTreePreset);
FUNCTION_CALL(Generate, "GenerateTreeInGoodQuality");
REG_AUTO(TransformObject);
if(TransformObject) {
NOHASH{
REG_AUTO(Transform);
}
}
if (hash)*hash << utils::getEnumValuesCount(leafsEnum());
}
}
// process called each frame
void Process() override {
bfCulling = ui::getOption("BackfaceCulling") == "true";
if(LeafsType !=pLeafsType) {
pLeafsType = LeafsType;
FolderToRemove.Clear();
if (utils::getEnumValuesCount(leafsEnum()) > 1) {
str name = utils::getEnumValueByIndex(leafsEnum(), LeafsType);
str fn = LeafsPath + name + "/";
io::convertToWritablePath(&fn);
io::ListFiles(fn, "*.*", FL, false);
if (FL.Count()) {
FolderToRemove = fn;
}
}
}
SculptGenerator::Process();
}
// remove the leafs folder
void removeLeafFolder() {
if (utils::getEnumValuesCount(leafsEnum()) > 1) {
if (dialog().text("SureRemoveLeaf").yes().no().show() == 1) {
io::removeFolder(FolderToRemove);
pLeafsType = -1;
UpdateLeafsEnum();
if (LeafsType >= utils::getEnumValuesCount(leafsEnum())) {
LeafsType = utils::getEnumValuesCount(leafsEnum()) - 1;
}
NotifyChanges();
}
}
}
// add leafs type
void AddLeafsType() {
dialog().dontShowAgainCheckbox().text("AddLeafInfo").ok().show();
str fn;
if (io::openFileDialog("*.png;*.tga", fn)) {
str name = fn.GetFileName();
str ext = name.GetFileExtension();
name.RemoveFileExtension();
str dest = LeafsPath + name + "/color." + ext;
while(io::fileExists(dest)) {
name += "_";
name += str::ToString(abs(int(GetTickCount())) % 1000);
dest = LeafsPath + name + "/color." + ext;
}
coat::io::copyFile(fn, dest);
UpdateLeafsEnum();
LeafsType = utils::getEnumValueIndex(leafsEnum(), name);
NotifyChanges();
}
}
// check the sphere collision
float checkVoxelsCollision(Volume& v, const vec3& pos, float radius) {
return collision.collides(pos, radius).LengthSq() > 0;
}
// create the branch
void create(int level, Volume v, vec3 start, vec3 start_direction, vec3 direction, float radius, float rdiff, float relative_length, float randomness) {
Curve cu;
vec3 N = start_direction.GetOrthonormal();
int npoints = 20;
float L = radius * relative_length / npoints;
if (sign < 0)L *= RootsLength;
else if (level == 0)L *= Level0_Proportions;
else L *= BranchesProportions;
list<vec3> best;
float best_cost = FLT_MAX;
vec3 best_out;
vec3 dir0 = direction;
vec3 start0 = start;
vec3 ort = direction;
ort.MakeOrthonormalTo(start_direction);
float random_level = randomness;
if (sign < 0)random_level *= RootsRandomness;
else if (level == 0)random_level *= Level0_Randomness;
else random_level *= BranchesRandomness;
float this_transition = transition;
float this_start_transition = start_transition;
if(level == 0 && sign > 0) {
this_transition = 0;
this_start_transition = 1;
}
int nattempts = (level == 0 && Level0_branches <= 1) && sign > 0 ? 1 : 30;
// we are trying many times choosing the random branch that collides with the existing spheres minimally
for (int k = 0; k < nattempts; k++) {
direction = dir0;
// we rotate branch direction randomly, except level 0
mat4 rot = level > 0 ? mat4::RotationAt(start0, start_direction, comms::cMath::Rand(0, 360)) : mat4::Identity;
direction.TransformNormal(rot);
start = start0 + ort * rdiff;
// we start growing a bit offside, not from the center of the parent
vec3 out = start0 + ort * (rdiff + radius);
start.TransformCoordinate(rot);
out.TransformCoordinate(rot);
list<vec3> current;
float cost = 0;
// the initial growth direction
vec3 dir = start_direction;
for (int i = 0; i < npoints; i++) {
float r = radius * (npoints - i) / npoints;
if (start.y * sign < 0)cost -= start.y * 1000 * sign;
current.Add(start);
cost += checkVoxelsCollision(v, start, r * 1.2);
// the transition degree between initial parent trunk diretion and the destination direction
float t = float(i) * this_transition / npoints + this_start_transition;
if (t > 1)t = 1;
dir = start_direction * (1 - t) + direction * t;
start += L * dir;
// apply the gravity
direction.y += gravity;
direction += vec3::RandNormal() * random_level * (1.0 + float(i) * ends_distortion / 20);
direction.Normalize();
}
if (cost < best_cost) {
// we choose minimally collided branch
best_cost = cost;
best = current;
best_out = out + start_direction * radius * 2;
}
}
auto addleaf = [&](const vec3& pt, const vec3& dir) {
forLeafs.Add(std::pair(pt, (dir + Vector3D::RandNormal()).ToNormal()));
};
// add points to the curve, we need to canculate curve normal as well
const int nsub = int((addNarrowTrunks ? 4 : 32) * LeafsDensity);
for (int i = 0; i < best.Count(); i++) {
float r = radius * (npoints + 1 - i) / (npoints + 1);
int ip = i - 1;
int in = i + 1;
if (ip < 0)ip = 0;
if (in > best.Count() - 1)in = best.Count() - 1;
vec3 d = (best[in] - best[ip]).ToNormal();
if (r > MinimalBranchRadius) {
N.MakeOrthonormalTo(d);
cu.add(best[i], N, r);
}
if(addLeafs && sign > 0 && r<MinimalBranchRadius*3) {
addleaf(best[i],d);
if (i < best.Count() - 1) {
for (int k = 1; k < nsub; k++) {
addleaf((best[i + 1] * k + best[i]*(nsub-k)) / nsub, d);
}
}
}
}
// get the render points of the curve (dense)
if (level == 0 && sign > 0) {
mainTrunk.Clear();
for (int i = 0; i < cu.renderPointsCount(); i++) {
auto* a = cu.renderPoint(i);
mainTrunk.Add(vec4(a->SpacePos, a->Radius));
}
}
Mesh m;
cu.tubeToMesh(m, true);
int ns = int(radius * overallDensity / 1.2);
if (sign < 0)ns = int(radius * RootsComplexity / 1.2);
if (ns > 0) {
randomGrow(level + 1, v, cu, m, best_out, ns, relative_length, randomness);
}
}
// grow next level branches
void randomGrow(int level, Volume v, Curve& cu, Mesh& m, vec3 outPos, int count, float relative_length, float randomness) {
list<int> pts;
// we gather the points-candidates for growing, they should not be inside the existing branches (excett parent of course), so we check collision
for (int i = 0; i < count; i++) {
int nr = cu.renderPointsCount();
if (nr > 8) {
int p0 = Level0_StartBranchesLevel * nr;
if (p0 < 1)p0 = 1;
if (level > 1 || sign < 0)p0 = 1;
int s = nr / 16;
int p = p0 + i * (nr - p0 - 4) / count;
auto* a = cu.renderPoint(p);
if (a && !checkVoxelsCollision(v, a->SpacePos, a->Radius * 1.1)) {
pts.Add(p);
}
}
}
// we add collision spheres of the parent branch
int nr = cu.pointsCount();
for (int i = 0; i < nr; i++) {
auto* p = cu.point(i);
collision.addSphere(p->SpacePos, p->Radius);
}
// add the mesh to the resulting mesh
summ += m;
if (BranchesEndAngle < BranchesStartAngle)std::swap(BranchesEndAngle, BranchesStartAngle);
auto* p0 = cu.point(0);
if (p0 && p0->Radius > 1) {
float r = p0->Radius;
if (sign > 0)r *= 2;
else r *= 1.25;
relaxSpheres.Add(vec4(outPos, r));
}
for(int i=0;i<pts.Count();i++){
int p = pts[i];
auto* a_prev = cu.renderPoint(p - 1);
auto* a_curr = cu.renderPoint(p);
auto* a_next = cu.renderPoint(p + 1);
vec3 dir = (a_next->SpacePos - a_prev->SpacePos).ToNormal();
vec3 N = a_curr->TempNormal;
vec3 pos = a_curr->SpacePos;
mat4 rot = mat4::RotationAt(pos, dir, comms::cMath::Rand(0, 360));
float ang = float(BranchesStartAngle + comms::cMath::Rand01() * (BranchesEndAngle - BranchesStartAngle)) * comms::cMath::Pi / 180;
float _cos = cos(ang);
vec3 tdir = (dir * cos(ang) + N * sin(ang)).ToNormal();
tdir.TransformNormal(rot);
create(level + 1, v, pos, dir, tdir
, a_curr->Radius * radiusDecrement
, a_curr->Radius * (1.0f - radiusDecrement)
, relative_length * chunkLengthAmplificator, randomness * noiseAmpliicator);
}
}
SceneElement generateLeafs(SceneElement parent) {
auto leafs = parent.findInSubtree("Leafs");
if(leafs.Volume().valid()) {
leafs.Volume().clearNoUndo();
} else leafs = parent.addChild("Leafs");
vec4 v40 = utils::dwordToVec4(LeafsColor1);
vec4 v41 = utils::dwordToVec4(LeafsColor2);
v41 -= v40;
str leafName = str(LeafsPath) + GetLeafName() + "/color.png";
Image im;
im.Read(leafName);
int w = im.GetWidth();
int h = im.GetHeight();
if (w > 0 && h > 0) {
int minX = w - 1;
int maxX = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
auto pix = im.GetPixelRgba8(x, y);
if (pix.a > 0) {
minX = std::min(x, minX);
maxX = std::max(x, maxX);
}
}
}
float u0 = float(minX) / w;
float u1 = float(maxX) / w;
float dx1 = 1.0f - u0 * 2.0f;
float dx2 = u1 * 2.0f - 1.0f;
if (leafs.Volume().vo()) {
leafs.Volume().toSurface();
Mesh lf;
auto& pos = lf.geometry()->GetPositions();
auto& uv = lf.geometry()->GetTexCoords();
auto& raw = lf.geometry()->GetRaw();
auto& vcol = lf.geometry()->GetVertexColor();
auto& tpos = smallTrunks.geometry()->GetPositions();
auto& traw = smallTrunks.geometry()->GetRaw();
auto quad = [&](const vec3& start, const vec3& dir) {
Vector3D ort;
do {
ort = Vector3D::RandNormal();
ort -= dir * ort.dot(dir);
if (ort.Length() > 0.1) {
ort.Normalize();
break;
}
} while (true);
ort *= LeafsScale * 3;
Vector3D dd = dir * 6 * LeafsScale;
int ps = pos.Count();
pos.Add(start + ort * dx2);
pos.Add(start + ort * dx2 + dd);
pos.Add(start - ort * dx1 + dd);
pos.Add(start - ort * dx1);
coat::vec4 vc = v40 + v41 * comms::cMath::Rand01();
DWORD CL = utils::vec4ToDword(vc);
if (CL == 0xFF808080)CL = 0xFF818181;
if (CL == 0xFFFFFFFF)CL = 0xFFFEFEfE;
vcol.Add(CL, 4);
uv.Add(vec2(u1, 0));
uv.Add(vec2(u1, 1));
uv.Add(vec2(u0, 1));
uv.Add(vec2(u0, 0));
raw.Add(vec3i(4, 0, 0));
raw.Add(vec3i(ps, ps, -1));
raw.Add(vec3i(ps + 1, ps + 1, -1));
raw.Add(vec3i(ps + 2, ps + 2, -1));
raw.Add(vec3i(ps + 3, ps + 3, -1));
};
auto rode = [&](const vec3& start, const vec3& end, float r1, float r2) {
vec3 dir = end - start;
float L = dir.Length();
if (L > 0) {
dir /= L;
auto ort = dir.GetOrthonormalPair();
int nbase = 6;
const float da = cMath::Pi * 2 / nbase;
int p0 = tpos.Count();
for (int i = 0; i < nbase; i++) {
float ang = i * da;
vec3 dd = ort.first * cos(ang) + ort.second * sin(ang);
tpos.Add(start + dd * r1);
tpos.Add(end + dd * r2);
int in = (i + 1) % nbase;
in *= 2;
int ic = i * 2;
traw.Add(cVec3i(4, 0, 0));
traw.Add(cVec3i(p0 + ic, -1, -1));
traw.Add(cVec3i(p0 + ic + 1, -1, -1));
traw.Add(cVec3i(p0 + in + 1, -1, -1));
traw.Add(cVec3i(p0 + in, -1, -1));
}
}
};
lf.geometry()->SetDefaultObjMtl();
lf.geometry()->GetMaterials()[0].Tex[0].FileName = leafName;
float r = 3 * LeafsScale;
float r2 = r/3;
if(addNarrowTrunks) {
smallTrunks.geometry()->SetDefaultObjMtl();
if (addNarrowTrunks) {
for (auto& a : forLeafs) {
vec3 lp = a.first + a.second * comms::cMath::Rand01() * LeafsScale * 16 * LeafsScattering;
rode(a.first, lp, MinimalBranchRadius / 1.5, MinimalBranchRadius / 4);
quad(lp, (lp - a.first).ToNormal());
}
}
}
else {
for (auto& a : forLeafs) {
for (int k = 0; k < 8; k++) {
vec3 lp = a.first + a.second * comms::cMath::Rand01() * LeafsScale * 16 * LeafsScattering;
vec3 c = lp + a.second * r;
if (!collision.collides(c, r2).LengthSq()) {
collision.addSphere(c, r2);
quad(lp, a.second);
break;
}
}
}
}
leafs.Volume().clear();
leafs.Volume().mergeMeshWithTexture(lf);
leafs.Volume().vo()->RenderJustFacture = true;
}
}
return leafs;
}
void generate(Volume v, bool Preview) {
forLeafs.Clear();
comms::cMath::Randomize(TreeGenSeed);
summ.geometry()->Clear();
smallTrunks.geometry()->Clear();
collision.clear();
relaxSpheres.Clear();
mainTrunk.Clear();
Save(t, this);
gravity = 0.05 * GrowUpStrength;
for (int i = 0; i < Level0_branches; i++) {
create(0, v, vec3::Zero, vec3::AxisY, vec3::AxisY, 20, 0, 20, 0.2);
}
if (MakeRoots && RootsCount && mainTrunk.Count() > 4) {
overallDensity *= 0.7;
sign = -10;
gravity = 0.01;
transition = 2;
start_transition = 0.15;
ends_distortion = 2.0;
int a0 = 360 / RootsCount;
for (int r = 0; r < RootsCount; r++) {
vec3 ax(1, 0, 0);
ax *= mat3::Rotation(vec3::AxisY, r * a0 + comms::cMath::Rand(-45, 45));
ax.Normalize();
int n0 = mainTrunk.Count() / 30 + 1;
int g = n0 + int(RootsStartGrowHeight * comms::cMath::Rand(0, mainTrunk.Count() - n0 - 2));
vec3 d = (mainTrunk[g - 1].ToVec3() - mainTrunk[g + 1].ToVec3()).ToNormal();
float rad = mainTrunk[g].w;
create(0, v, mainTrunk[g].ToVec3(), d, ax, rad * 0.6, rad * 0.4, 20, 0.2);
}
defaults();
Load(t, this);
}
v.mergeMesh(summ);
}
void finish(Volume v) {
v.mergeMesh(summ);
for (int k = 0; k < relaxSpheres.Count(); k++) {
v.relaxGpu(relaxSpheres[k].ToVec3(), relaxSpheres[k].w * 2, 1);
}
}
virtual void GeneratePreview() {
SceneElement s(getObject());
SceneElement trunks;
t = s.addChild("Trunk");
auto v = t.Volume();
auto setShader = [](Volume v) {
v.assignShader("#Jama Clay/JamaClay1");
v.setFloatShaderProperty("Textures scale", 1.0);
v.setFloatShaderProperty("Bumpness", 4.0);
v.setFloatShaderProperty("Gloss", 0.4);
v.setFloatShaderProperty("Side rotation [1]", comms::cMath::Rad(0.0f));
v.setFloatShaderProperty("Side rotation [2]", comms::cMath::Rad(90.0f));
v.setColorShaderProperty("Color", 0xFF935B38);
v.setColorShaderProperty("Cavity color", 0xFF2F1D12);
v.setColorShaderProperty("Bulge color", 0xFF51331D);
v.setBoolShaderProperty("Flat shading", false);
};
setShader(v);
if (addLeafs && addNarrowTrunks) {
trunks = s.addChild("Narrow");
trunks.Volume().toSurface();
auto vt = trunks.Volume();
setShader(vt);
}
v.toSurface();
t.setTransform(mat4::Identity);
trunks.setTransform(mat4::Identity);
generate(t.Volume(), true);
if (addLeafs) {
auto l = generateLeafs(s);
l.setTransform(l.getTransform() * s.getTransform());
if (addNarrowTrunks) {
trunks.Volume().mergeMesh(smallTrunks);
trunks.setTransform(trunks.getTransform() * s.getTransform());
}
}
WriteToFile(backup);
s.selectOne();
};
virtual void GenerateFinalObject() {
SceneElement s(getObject());
if (s.Volume().valid()) {
if (s.childCount() == 0)t = s.addChild("Trunk");
else t = s.child(0);
Volume v = t.Volume();
v.toVoxels();
t.setTransform(mat4::Identity);
generate(t.Volume(), true);
for (int k = 0; k < relaxSpheres.Count(); k++) {
v.relaxGpu(relaxSpheres[k].ToVec3(), relaxSpheres[k].w * 2, 1);
}
if (addLeafs) {
auto l = generateLeafs(s);
l.setTransform(l.getTransform()*s.getTransform());
}
s.selectOne();
}
}
};
EXPORT_EXTENSION(TreesGenerator) {
TreesGenerator* tg = new TreesGenerator;
tg->ReadFromFile(backup);
VoxelExtension::Register(tg);
coat::ui::cmd(str("[extension]") + tg->GetID());
}
This class intended to load/save the class derived from the BaseClass as XML/JSON....
Definition TagsList.h:21
Definition CoreAPI.h:2792
void add(const Vector3D &p, const Vector3D &normal, float Radius)
add the point to the curve without the direct options the tangents
int pointsCount()
get the base points cout in the curve
OneSelPoint * point(int idx)
get the base point pointer
int renderPointsCount()
returns the visual points count. Visual points used to render the curve in the viewport as set of str...
void updatePoints()
update the visual points if need. Use this function if you cahnge the curve. Change the multiple para...
void tubeToMesh(Mesh &mesh, bool hemisphere)
create the solid tube around the curve using the points radius
OneSelPoint * renderPoint(int idx)
returns the visual point reference
The image references. Look the cImage for the list of allowed operations.
Definition CoreAPI.h:1293
bool Read(const char *name)
Read the image from the file.
The mesh reference.
Definition CoreAPI.h:401
comms::cMeshContainer * geometry()
The low-level mesh reference allows to create, operate over individual faces, vertices,...
The scene element, like sculpt object or curve.
Definition CoreAPI.h:1609
void selectOne() const
unselect all similar elements and select this one
SceneElement findInSubtree(const char *name) const
find the element in subtree by name
SceneElement child(int index) const
returns child element by index
mat4 getTransform() const
get the scene element transform
void removeSubtree() const
remove the whole subtree
int childCount() const
returns the child elements count
const SceneElement & setTransform(const mat4 &Transform) const
Set the transform matrix.
SceneElement addChild(const char *name) const
add the child element of the same nature
The class intended to place spheres in space and identify if there are spheres around....
Definition CoreAPI.h:2933
int addSphere(const vec3 &p, float radius)
add the sphere into the space
void clear()
remove all spheres
vec3 collides(const vec3 &p, float radius)
check if sphere intersects other spheres in the space
The class allows to operate over voxels/surface on the relatively low-level.
Definition CoreAPI.h:1899
void clearNoUndo()
Clear quickly, without affecting the Undo queue.
void relaxGpu(const vec3 &center, float Radius, float degree)
fast voxel-based relax within the sphere with the gradual falloff. It works only in voxel mode.
void mergeMesh(Mesh &mesh, const mat4 &transform=mat4::Identity, BoolOpType op=BOOL_MERGE)
merge the mesh into scene
void toVoxels()
turn to voxels, auto-voxelize
void assignShader(const char *shaderName)
set the shader for the Volume
void toSurface()
turn to surface mode, the triangles will be tangentially relaxed
the rich dialog. You may customize it, show your custom parameters and custom buttons.
Definition CoreAPI.h:3388
dialog & dontShowAgainCheckbox()
show the checkbox "Don't show again". If user checks if the dialog will net be shown next time and sh...
dialog & ok()
add Ok button
int show()
pass the function/lambda that will be called when the button will be pressed. The button index (start...
dialog & text(const char *id)
pass the header text of the dialog
static void copyFile(const char *src, const char *dest)
copy the file from src to dest. If the src or dest is relative, it is relative to the documents folde...
text primitive
Definition CorePrimAPI.h:1735
static bool cmd(const char *id, std::function< void()> process_in_modal_dialog=0)
execute some action in UI as if you pressed on some control
The UV API. The mesh is taken from the current room. If paint or UV rooms is active,...
Definition CoreAPI.h:4246
The coat namespace used for most 3DCoat API calls except low-level internal structures.
Definition CoreAPI.h:43
comms::cVec3 vec3
3D - float vector, see the cVec3
Definition CoreAPI.h:50
comms::cVec4 vec4
4D - float vector, see the cVec4
Definition CoreAPI.h:47
comms::cVec2 vec2
2D - vector, see the cVec2
Definition CoreAPI.h:56
comms::cMat4 mat4
4x4 float matrix, see the cMat4
Definition CoreAPI.h:59
comms::cVec3i vec3i
3D - int vector, see the cVec3i
Definition CoreAPI.h:53
comms::cStr str
the string that is compatible with the 3DCoat engine, see the cStr
Definition CoreAPI.h:67
comms::cList< X > list
the array template, see cList
Definition CoreAPI.h:70