#include <CoreAPI.h>
static const char* backup = "data/Temp/TreesGenerator.json";
const char* GeneratorID = "TreesGenerator";
class TreesGenerator : public SculptGenerator{
public:
TreesGenerator() {
defaults();
collision.setUnit(8);
}
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;
LeafsScale = 1;
LeafsDensity = 1;
LeafsScattering = 1;
LeafsColor1 = 0xFF8BA055;
LeafsColor2 = 0xFF519E2A;
LeafsType = 0;
UpdateLeafsEnum();
}
Enumerator* leafsEnum() {
static Enumerator* LEAFS = ENUM.Get("LEAFSTYPES");
return LEAFS;
}
void UpdateLeafsEnum() {
Enumerator* LEAFS = leafsEnum();
LEAFS->Clear();
FileList FL;
CreateSortedFoldersList("UserPrefs/ToolsSettings/leafs/", FL);
for (auto* l : FL) {
s.RemoveFilePath();
LEAFS->Add(s);
}
}
const char* GetLeafName() {
Enumerator* LEAFS = leafsEnum();
return LEAFS->GetStr(LeafsType);
}
float GrowUpStrength;
float sign;
float noiseAmpliicator;
float chunkLengthAmplificator;
float overallDensity;
float radiusDecrement;
float gravity;
float transition;
float start_transition;
float ends_distortion;
float Level0_Randomness;
int Level0_branches;
float Level0_StartBranchesLevel;
float Level0_Proportions;
float BranchesRandomness;
float BranchesStartAngle;
float BranchesEndAngle;
float BranchesProportions;
float MinimalBranchRadius;
bool MakeRoots;
float RootsRandomness;
float RootsLength;
float RootsComplexity;
float RootsStartGrowHeight;
int RootsCount;
int TreeGenSeed;
bool addLeafs;
int LeafsType;
float LeafsScale;
float LeafsDensity;
float LeafsScattering;
DWORD LeafsColor1;
DWORD LeafsColor2;
void RandomizeTree() {
TreeGenSeed = rand();
NotifyChanges();
}
void SaveTreePreset() {
if(io::saveFileDialog("*.tree",fn)) {
WriteToFile(fn);
}
}
void LoadTreePreset() {
if (io::openFileDialog("*.tree", fn)) {
defaults();
ReadFromFile(fn);
}
NotifyChanges();
}
virtual const char* getDefaultObjectName() {
return "Tree";
}
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);
REG_DROPLIST(LeafsType, "LeafsType", "LEAFSTYPES");
FUNCTION_CALL(AddLeafsType);
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 << leafsEnum()->GetAmount();
}
}
void AddLeafsType() {
if (io::openFileDialog("*.png;*.tga", fn)) {
str name = fn.GetFileName();
str ext = name.GetFileExtension();
name.RemoveFileExtension();
str dest =
"UserPrefs/ToolsSettings/Leafs/" + name +
"/color." + ext;
while(io::fileExists(dest)) {
name += "_";
name += str::ToString(__abs(int(GetTickCount())) % 1000);
dest = "UserPrefs/ToolsSettings/Leafs/" + name + "/color." + ext;
}
__copyfile(fn, dest, false);
UpdateLeafsEnum();
LeafsType = leafsEnum()->Get(name);
}
}
float checkVoxelsCollision(
Volume& v,
const vec3& pos,
float radius) {
return collision.
collides(pos, radius).LengthSq() > 0;
}
void create(
int level,
Volume v,
vec3 start,
vec3 start_direction,
vec3 direction,
float radius,
float rdiff,
float relative_length,
float randomness) {
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;
float best_cost = FLT_MAX;
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;
for (int k = 0; k < nattempts; k++) {
direction = dir0;
mat4 rot = level > 0 ? mat4::RotationAt(start0, start_direction, comms::cMath::Rand(0, 360)) : mat4::Identity;
direction.TransformNormal(rot);
start = start0 + ort * rdiff;
vec3 out = start0 + ort * (rdiff + radius);
start.TransformCoordinate(rot);
out.TransformCoordinate(rot);
float cost = 0;
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);
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;
direction.y += gravity;
direction += vec3::RandNormal() * random_level * (1.0 + float(i) * ends_distortion / 20);
direction.Normalize();
}
if (cost < best_cost) {
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()));
};
const int nsub = int(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);
}
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);
}
}
}
}
if (level == 0 && sign > 0) {
mainTrunk.Clear();
mainTrunk.Add(
vec4(a->SpacePos, a->Radius));
}
}
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);
}
}
void randomGrow(
int level,
Volume v,
Curve& cu,
Mesh& m,
vec3 outPos,
int count,
float relative_length,
float randomness) {
for (int i = 0; i < count; i++) {
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;
if (!checkVoxelsCollision(v, a->SpacePos, a->Radius * 1.1)) {
pts.Add(p);
}
}
}
for (int i = 0; i < nr; i++) {
}
summ += m;
if (BranchesEndAngle < BranchesStartAngle)std::swap(BranchesEndAngle, BranchesStartAngle);
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];
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);
}
}
if(leafs.Volume().valid()) {
leafs.
Volume().clearNoUndo();
}
else leafs = parent.
addChild(
"Leafs");
v41 -= v40;
coat::str leafName =
str(
"UserPrefs/ToolsSettings/Leafs/") + GetLeafName() +
"/color.png";
comms::cImage im;
comms::cIO::LoadImage(leafName, &im);
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(y, 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();
auto& pos = lf.
geometry()->GetPositions();
auto& uv = lf.
geometry()->GetTexCoords();
auto& vcol = lf.
geometry()->GetVertexColor();
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 = V4D2DW(vc);
if (CL == 0xFF808080)CL = 0xFF818181;
if (CL == 0xFFFFFFFF)CL = 0xFFFEFEfE;
vcol.Add(CL, 4);
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));
};
lf.
geometry()->GetMaterials()[0].Tex[0].FileName = leafName;
float r = 3 * LeafsScale;
float r2 = r/3;
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() > 0) {
quad(lp, a.second);
break;
}
}
}
leafs.Volume().clear();
leafs.Volume().mergeMeshWithTexture(lf);
leafs.Volume().vo()->RenderJustFacture = true;
}
}
}
void generate(
Volume v,
bool Preview) {
forLeafs.Clear();
comms::cMath::Randomize(TreeGenSeed);
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++) {
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);
}
}
for (int k = 0; k < relaxSpheres.Count(); k++) {
v.
relaxGpu(relaxSpheres[k].ToVec3(), relaxSpheres[k].w * 2, 1);
}
}
virtual void GeneratePreview() {
auto v = t.Volume();
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);
}
generate(t.Volume(), true);
if (addLeafs) {
generateLeafs(s);
}
WriteToFile(backup);
};
virtual void GenerateFinalObject() {
if (s.Volume().valid()) {
generate(t.Volume(), true);
for (int k = 0; k < relaxSpheres.Count(); k++) {
v.
relaxGpu(relaxSpheres[k].ToVec3(), relaxSpheres[k].w * 2, 1);
}
if (addLeafs) {
generateLeafs(s);
}
}
}
};
#ifdef _WINDOWS
EXPORT_EXTENSION(TreesGenerator) {
TreesGenerator* tg = new TreesGenerator;
tg->ReadFromFile(backup);
VoxelExtension::Register(tg);
}
#endif
Definition CoreAPI.h:1058
void add(const Vector3D &p, const Vector3D &normal, float Radius)
add the point to the curve without the direct setting 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 mesh reference.
Definition CoreAPI.h:60
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:397
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
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:1199
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:615
void clearNoUndo()
Clear quickly, without affecting the Undo queue.
void relaxGpu(const vec3 ¢er, 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:1493
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()
Show the dialog. This is usually the last command in the chain.
dialog & text(const char *id)
pass the header text of the dialog
static bool cmd(const char *id, std::function< void()> process_in_modal_dialog=0)
perform some command in UI.
The coat namespace used for most 3DCoat API calls except low-level internal structures.
Definition CoreAPI.h:14
comms::cVec3 vec3
3D - float vector, see the cVec3
Definition CoreAPI.h:19
comms::cVec4 vec4
4D - float vector, see the cVec4
Definition CoreAPI.h:16
comms::cVec2 vec2
2D - vector, see the cVec2
Definition CoreAPI.h:25
comms::cMat4 mat4
4x4 float matrix, see the cMat4
Definition CoreAPI.h:28
comms::cVec3i vec3i
3D - int vector, see the cVec3i
Definition CoreAPI.h:22
comms::cStr str
the string that is compatible with the 3DCoat engine, see the cStr
Definition CoreAPI.h:34
comms::cList< X > list
the array template, see cList
Definition CoreAPI.h:37