* brush.icosahedron.truncate option

This commit is contained in:
Garux 2020-12-14 08:46:36 +03:00
parent e1f3b25bc6
commit 8b460b5318
3 changed files with 84 additions and 67 deletions

View File

@ -327,75 +327,94 @@ void Brush_ConstructRock( Brush& brush, const AABB& bounds, std::size_t sides, c
}
}
#include "quickhull/QuickHull.hpp"
namespace icosahedron{
#define X .525731112119133606
#define Z .850650808352039932
static float vdata[12][3] = {
constexpr double X = .525731112119133606;
constexpr double Z = .850650808352039932;
// 12 vertices
static const DoubleVector3 vdata[12] = {
{ -X, 0.0, Z}, {X, 0.0, Z}, { -X, 0.0, -Z}, {X, 0.0, -Z},
{0.0, Z, X}, {0.0, Z, -X}, {0.0, -Z, X}, {0.0, -Z, -X},
{Z, X, 0.0}, { -Z, X, 0.0}, {Z, -X, 0.0}, { -Z, -X, 0.0}
};
static unsigned int tindices[20][3] = {
// 20 faces
static constexpr unsigned int tindices[20][3] = {
{0, 4, 1}, {0, 9, 4}, {9, 5, 4}, {4, 5, 8}, {4, 8, 1},
{8, 10, 1}, {8, 3, 10}, {5, 3, 8}, {5, 2, 3}, {2, 7, 3},
{7, 10, 3}, {7, 6, 10}, {7, 11, 6}, {11, 0, 6}, {0, 1, 6},
{6, 1, 10}, {9, 0, 11}, {9, 11, 2}, {9, 2, 5}, {7, 2, 11}
};
void normalize( float* a ) {
float d = sqrt( a[0] * a[0] + a[1] * a[1] + a[2] * a[2] );
a[0] /= d;
a[1] /= d;
a[2] /= d;
}
void drawtri( float* a, float* b, float* c, std::size_t subdivisions, Brush& brush, float radius, const Vector3& mid, const char* shader, const TextureProjection& projection ) {
if( subdivisions <= 0 ) {
brush.addPlane( Vector3( a[0], a[1], a[2] ) * radius + mid,
Vector3( b[0], b[1], b[2] ) * radius + mid,
Vector3( c[0], c[1], c[2] ) * radius + mid,
shader, projection );
void drawtri( const DoubleVector3& a, const DoubleVector3& b, const DoubleVector3& c, std::size_t subdivisions, bool truncate, std::vector<quickhull::Vector3<double>>& pointCloud ) {
if( subdivisions == 0 ) {
const auto push = [&pointCloud]( const DoubleVector3& p ){
pointCloud.push_back( quickhull::Vector3<double>( p.x(), p.y(), p.z() ) );
};
if( truncate ){ // this is not quite correct after subdivision performed :thinking:
push( ( a * ( 2.0 / 3.0 ) ) + ( b * ( 1.0 / 3.0 ) ) );
push( ( a * ( 1.0 / 3.0 ) ) + ( b * ( 2.0 / 3.0 ) ) );
push( ( a * ( 2.0 / 3.0 ) ) + ( c * ( 1.0 / 3.0 ) ) );
push( ( a * ( 1.0 / 3.0 ) ) + ( c * ( 2.0 / 3.0 ) ) );
push( ( b * ( 2.0 / 3.0 ) ) + ( c * ( 1.0 / 3.0 ) ) );
push( ( b * ( 1.0 / 3.0 ) ) + ( c * ( 2.0 / 3.0 ) ) );
}
else{
float ab[3], ac[3], bc[3];
for( int i = 0; i < 3; i++ ) {
ab[i] = ( a[i] + b[i] ) / 2;
ac[i] = ( a[i] + c[i] ) / 2;
bc[i] = ( b[i] + c[i] ) / 2;
push( a );
push( b );
push( c );
}
normalize( ab );
normalize( ac );
normalize( bc );
drawtri( a, ab, ac, subdivisions - 1, brush, radius, mid, shader, projection );
drawtri( b, bc, ab, subdivisions - 1, brush, radius, mid, shader, projection );
drawtri( c, ac, bc, subdivisions - 1, brush, radius, mid, shader, projection );
drawtri( ab, bc, ac, subdivisions - 1, brush, radius, mid, shader, projection ); //<--Comment this line and sphere looks really cool!
}
else{
const DoubleVector3 ab = vector3_normalised( a + b );
const DoubleVector3 ac = vector3_normalised( a + c );
const DoubleVector3 bc = vector3_normalised( b + c );
drawtri( a, ab, ac, subdivisions - 1, truncate, pointCloud );
drawtri( b, bc, ab, subdivisions - 1, truncate, pointCloud );
drawtri( c, ac, bc, subdivisions - 1, truncate, pointCloud );
drawtri( ab, bc, ac, subdivisions - 1, truncate, pointCloud ); //<--Comment this line and sphere looks really cool!
}
}
void Brush_ConstructIcosahedron( Brush& brush, const AABB& bounds, std::size_t subdivisions, const char* shader, const TextureProjection& projection ){
if ( Unsigned( 20 * ( 4 << subdivisions ) ) > c_brush_maxFaces ) {
globalErrorStream() << "brushIcosahedron" << ": subdivisions " << Unsigned( subdivisions ) << ": wrong sides count requested\n";
return;
}
void Brush_ConstructIcosahedron( Brush& brush, const AABB& bounds, std::size_t subdivisions, bool truncate, const char* shader, const TextureProjection& projection ){
brush.clear();
brush.reserve( 20 * ( 4 << subdivisions ) );
float radius = max_extent( bounds.extents );
const float radius = max_extent( bounds.extents );
const Vector3& mid = bounds.origin;
std::vector<quickhull::Vector3<double>> pointCloud;
for( int i = 0; i < 20; i++ ){
drawtri( vdata[tindices[i][0]], vdata[tindices[i][1]], vdata[tindices[i][2]], subdivisions, brush, radius, mid, shader, projection );
drawtri( vdata[tindices[i][0]], vdata[tindices[i][1]], vdata[tindices[i][2]], subdivisions, truncate, pointCloud );
}
quickhull::QuickHull<double> quickhull;
auto hull = quickhull.getConvexHull( pointCloud, true, true );
const auto& indexBuffer = hull.getIndexBuffer();
const size_t triangleCount = indexBuffer.size() / 3;
std::vector<Plane3> planes;
for( size_t i = 0; i < triangleCount; ++i ) {
DoubleVector3 p[3];
for( size_t j = 0; j < 3; ++j ){
p[j] = DoubleVector3( pointCloud[indexBuffer[i * 3 + j]].x,
pointCloud[indexBuffer[i * 3 + j]].y,
pointCloud[indexBuffer[i * 3 + j]].z );
}
const Plane3 plane = plane3_for_points( p );
if( plane3_valid( plane ) ){
if( std::find_if( planes.begin(), planes.end(), [&plane]( const Plane3& pla ){ return plane3_equal( plane, pla ); } ) == planes.end() ){
planes.push_back( plane );
brush.addPlane( p[0] * radius + mid, p[1] * radius + mid, p[2] * radius + mid, shader, projection );
}
}
}
}
} //namespace icosahedron
void Brush_ConstructPrefab( Brush& brush, EBrushPrefab type, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
void Brush_ConstructPrefab( Brush& brush, EBrushPrefab type, const AABB& bounds, std::size_t sides, bool option, const char* shader, const TextureProjection& projection ){
switch ( type )
{
case eBrushCuboid:
@ -448,7 +467,7 @@ void Brush_ConstructPrefab( Brush& brush, EBrushPrefab type, const AABB& bounds,
command << "brushIcosahedron" << " -subdivisions " << Unsigned( sides );
UndoableCommand undo( command.c_str() );
icosahedron::Brush_ConstructIcosahedron( brush, bounds, sides, shader, projection );
icosahedron::Brush_ConstructIcosahedron( brush, bounds, sides, option, shader, projection );
}
break;
}
@ -802,14 +821,14 @@ const TextureProjection& TextureTransform_getDefault(){
return g_defaultTextureProjection;
}
void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, const char* shader ){
void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, bool option, const char* shader ){
if ( GlobalSelectionSystem().countSelected() != 0 ) {
const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
Brush* brush = Node_getBrush( path.top() );
if ( brush != 0 ) {
AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified
Brush_ConstructPrefab( *brush, type, bounds, sides, shader, TextureTransform_getDefault() );
const AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified
Brush_ConstructPrefab( *brush, type, bounds, sides, option, shader, TextureTransform_getDefault() );
SceneChangeNotify();
}
}
@ -1447,7 +1466,7 @@ BrushMakeSided( std::size_t count )
: m_count( count ){
}
void set(){
Scene_BrushConstructPrefab( GlobalSceneGraph(), eBrushPrism, m_count, TextureBrowser_GetSelectedShader() );
Scene_BrushConstructPrefab( GlobalSceneGraph(), eBrushPrism, m_count, false, TextureBrowser_GetSelectedShader() );
}
typedef MemberCaller<BrushMakeSided, &BrushMakeSided::set> SetCaller;
};

View File

@ -43,7 +43,7 @@ namespace scene
class Graph;
class Node;
}
void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, const char* shader );
void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, bool option, const char* shader );
class AABB;
void Scene_BrushResize_Cuboid( scene::Node*& node, const AABB& bounds );
void Brush_ConstructPlacehoderCuboid( scene::Node& node, const AABB& bounds );

View File

@ -366,8 +366,7 @@ void DoProjectSettings(){
void DoSides( int type, int axis ){
ModalDialog dialog;
//GtkEntry* sides_entry;
GtkWidget* sides_spin;
GtkWidget* spin, *toggle;
GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Arbitrary sides", G_CALLBACK( dialog_delete_callback ), &dialog );
@ -375,20 +374,21 @@ void DoSides( int type, int axis ){
gtk_window_add_accel_group( window, accel );
{
GtkVBox* vbox = create_dialog_vbox( 0, 0 );
gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
GtkHBox* hbox = create_dialog_hbox( 4, 4 );
gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, FALSE, 0 );
GtkLabel* label;
{
GtkLabel* label = GTK_LABEL( gtk_label_new( "Sides:" ) );
label = GTK_LABEL( gtk_label_new( "Sides:" ) );
gtk_widget_show( GTK_WIDGET( label ) );
gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( label ), FALSE, FALSE, 0 );
}
// {
// GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
// gtk_widget_show( GTK_WIDGET( entry ) );
// gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( entry ), FALSE, FALSE, 0 );
// sides_entry = entry;
// gtk_widget_grab_focus( GTK_WIDGET( entry ) );
// }
{
toggle = gtk_check_button_new_with_label( "Truncate" );
gtk_box_pack_end( GTK_BOX( vbox ), toggle, FALSE, FALSE, 0 );
}
{
GtkAdjustment* adj;
EBrushPrefab BrushPrefabType = (EBrushPrefab)type;
@ -406,19 +406,19 @@ void DoSides( int type, int axis ){
break;
case eBrushIcosahedron :
adj = GTK_ADJUSTMENT( gtk_adjustment_new( 1, 0, 2, 1, 10, 0 ) ); //possible with 3, but buggy
gtk_widget_show( toggle );
gtk_label_set_label( label, "Subdivisions" );
break;
default:
adj = GTK_ADJUSTMENT( gtk_adjustment_new( 8, 3, 31, 1, 10, 0 ) );
break;
}
GtkWidget* spin = gtk_spin_button_new( adj, 1, 0 );
spin = gtk_spin_button_new( adj, 1, 0 );
gtk_widget_show( spin );
gtk_box_pack_start( GTK_BOX( hbox ), spin, FALSE, FALSE, 0 );
gtk_widget_set_size_request( spin, 64, -1 );
gtk_spin_button_set_numeric( GTK_SPIN_BUTTON( spin ), TRUE );
sides_spin = spin;
}
{
GtkVBox* vbox = create_dialog_vbox( 4 );
@ -438,12 +438,10 @@ void DoSides( int type, int axis ){
}
if ( modal_dialog_show( window, dialog ) == eIDOK ) {
// const char *str = gtk_entry_get_text( sides_entry );
// Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, atoi( str ), TextureBrowser_GetSelectedShader() );
gtk_spin_button_update ( GTK_SPIN_BUTTON( sides_spin ) );
int sides = static_cast<int>( gtk_spin_button_get_value( GTK_SPIN_BUTTON( sides_spin ) ) );
Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, sides, TextureBrowser_GetSelectedShader() );
gtk_spin_button_update ( GTK_SPIN_BUTTON( spin ) );
const int sides = static_cast<int>( gtk_spin_button_get_value( GTK_SPIN_BUTTON( spin ) ) );
const bool option = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) );
Scene_BrushConstructPrefab( GlobalSceneGraph(), (EBrushPrefab)type, sides, option, TextureBrowser_GetSelectedShader() );
}
gtk_widget_destroy( GTK_WIDGET( window ) );