├── .gitignore ├── images ├── 01_Cube.png ├── 02_Chair.png ├── 05_Donut.png ├── 04_Airplane.png └── 03_ParametricChair.png ├── .gitmodules ├── CMakeLists.txt ├── src ├── 01_Cube.cpp ├── 02_Chair.cpp ├── 05_Donut.cpp ├── 03_ParametricChair.cpp └── 04_Airplane.cpp ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | /.vs 3 | /.idea 4 | /cmake-build-debug 5 | /.vscode -------------------------------------------------------------------------------- /images/01_Cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaExamples/HEAD/images/01_Cube.png -------------------------------------------------------------------------------- /images/02_Chair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaExamples/HEAD/images/02_Chair.png -------------------------------------------------------------------------------- /images/05_Donut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaExamples/HEAD/images/05_Donut.png -------------------------------------------------------------------------------- /images/04_Airplane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaExamples/HEAD/images/04_Airplane.png -------------------------------------------------------------------------------- /images/03_ParametricChair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaExamples/HEAD/images/03_ParametricChair.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/AobaAPI"] 2 | path = libs/AobaAPI 3 | url = https://github.com/lsimic/AobaAPI.git 4 | branch = develop 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(AobaExamples) 3 | add_subdirectory(libs/AobaAPI) 4 | 5 | file(GLOB APP_SOURCES src/*.cpp) 6 | foreach(sourcefile ${APP_SOURCES}) 7 | file(RELATIVE_PATH file ${CMAKE_CURRENT_SOURCE_DIR}/src ${sourcefile}) 8 | string(REPLACE ".cpp" "" filename ${file} ) 9 | add_executable(${filename} ${sourcefile}) 10 | set_property(TARGET ${filename} PROPERTY CXX_STANDARD 17) 11 | target_link_libraries(${filename} "AobaAPI" "${AOBAAPI_LIBRARIES}") 12 | # target_include_directories(${file} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/libs/AobaAPI/include") 13 | endforeach(sourcefile ${APP_SOURCES}) 14 | -------------------------------------------------------------------------------- /src/01_Cube.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/AobaAPI.hpp" 2 | 3 | int main() { 4 | // crate a new mesh 5 | Aoba::Core::Mesh* mesh = new Aoba::Core::Mesh(); 6 | 7 | // create a new vertex inside the mesh 8 | Aoba::Core::Vert* vert = Aoba::Ops::CreateVert(mesh, Aoba::Math::Vec3(-1, -1, -1)); 9 | 10 | // extrude the vertex into an edge, translate the new vertex 11 | auto evResult = Aoba::Ops::ExtrudeVerts(mesh, {vert}); 12 | Aoba::Ops::Translate(mesh, evResult.verts, Aoba::Math::Vec3(2, 0, 0)); 13 | 14 | // extrude the edge from the previous step into a face, translate new vertices 15 | auto eeResult = Aoba::Ops::ExtrudeEdges(mesh, evResult.edges); 16 | Aoba::Ops::Translate(mesh, eeResult.verts, Aoba::Math::Vec3(0, 2, 0)); 17 | 18 | // extrude the face from the previous step to create a volume(cube), translate new vertices 19 | auto efResult = Aoba::Ops::ExtrudeFaces(mesh, eeResult.faces, true); 20 | Aoba::Ops::Translate(mesh, efResult.verts, Aoba::Math::Vec3(0, 0, 2)); 21 | 22 | // export the mesh 23 | Aoba::IO::ExportObj("cube.obj", mesh); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Luka Šimić 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example uses of the procedural modeling API 2 | 3 | ## 01_Cube 4 | ![01_Cube](images/01_Cube.png) 5 | Create a cube using extrusion, starting from a single vertex 6 | 7 | ## 02_Chair 8 | ![02_Chair](images/02_Chair.png) 9 | Create a chair 10 | 11 | ## 03_ParametricChair 12 | ![03_ParametricChair](images/03_ParametricChair.png) 13 | Create a program which generates chair variations based on a random seed. 14 | For seeds [0-10] the back is not generated. for seeds [11-20] the back is generated. The random seed just works out that way and it's not a bug. 15 | 16 | ## 04_Airplane 17 | ![04_Airplane](images/04_Airplane.png) 18 | Create a complex airplane mesh, with fuselage, wings, vertical and horizontal stabilizers. 19 | 20 | ## 05_Donut 21 | ![05_Donut](images/05_Donut.png) 22 | Create a donut, with frosting and sprinkles. 23 | 24 | ## build and install 25 | ### Clone the repo, update library (using git submodules) 26 | `> git clone https://github.com/lsimic/AobaExamples.git` 27 | `> cd AobaExamples` 28 | `> git submodule update --init` 29 | `> git submodule update --remote` 30 | 31 | ### Windows specific 32 | Tested with Visual Studio Community 2019, open and build as any other CMake project 33 | 34 | ### LInux specific 35 | `> cd out` 36 | `> cmake ..` 37 | `> make` -------------------------------------------------------------------------------- /src/02_Chair.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/AobaAPI.hpp" 2 | 3 | #include 4 | 5 | int main() { 6 | // create a new mesh 7 | Aoba::Core::Mesh* mesh = new Aoba::Core::Mesh(); 8 | 9 | // use a 3x3 face grid as a starting point 10 | auto grid = Aoba::Ops::CreateGrid(mesh, 2, 2, 0.5f, 0.5f); 11 | 12 | // find verts which are close to the Y axis, scale them on X axis 13 | // find them using a lambda expression 14 | auto vx = mesh->Verts([](const Aoba::Core::Vert* v) { 15 | return fabsf(v->co.x) < 0.5f / 3.0f; 16 | }); 17 | Aoba::Ops::Scale(mesh, vx, Aoba::Math::Vec3(0.0f, 0.0f, 0.0f), Aoba::Math::Vec3(2.5f, 1.0f, 1.0f)); 18 | 19 | // find verts which are close to the X axis, scale them on Y axis 20 | // find them using a lambda expression 21 | auto vy = mesh->Verts([](const Aoba::Core::Vert* v) { 22 | return fabsf(v->co.y) < 0.5f / 3.0f; 23 | }); 24 | Aoba::Ops::Scale(mesh, vy, Aoba::Math::Vec3(0.0f, 0.0f, 0.0f), Aoba::Math::Vec3(1.0f, 2.5f, 1.0f)); 25 | 26 | // seat faces - faces which will be extruded to make the seat - all faces currently in the mesh 27 | auto seatFaces = mesh->Faces(); 28 | 29 | // leg faces - faces which will be extruded to make the chair legs - the four corner faces 30 | // find them using a lambda expression 31 | auto legFaces = mesh->Faces([](const Aoba::Core::Face* f) { 32 | return fabsf(f->CalcCenterAverage().x) > 0.01f && fabsf(f->CalcCenterAverage().y) > 0.01f; 33 | }); 34 | 35 | // create the seat by extruding the seat faces and translating the new vertices 36 | auto seat = Aoba::Ops::ExtrudeFaceRegion(mesh, seatFaces, true); 37 | Aoba::Ops::Translate(mesh, seat.verts, Aoba::Math::Vec3(0.0f, 0.0f, 0.05f)); 38 | 39 | // create the legs by extruding the leg faces and translating the new vertices 40 | auto legs = Aoba::Ops::ExtrudeFaces(mesh, legFaces, false); 41 | Aoba::Ops::Translate(mesh, legs.verts, Aoba::Math::Vec3(0.0f, 0.0f, -0.5f)); 42 | 43 | // back faces - faces which will be extruded to make the back of the chair 44 | auto backFaces = mesh->Faces([](const Aoba::Core::Face* f) { 45 | return f->CalcCenterAverage().y < -0.5f / 3.0f && f->CalcCenterAverage().z > 0.05f / 1.5f; 46 | }); 47 | 48 | // extrude the back faces and translate the vertices to chreate the back of the chair 49 | auto back = Aoba::Ops::ExtrudeFaceRegion(mesh, backFaces, false); 50 | Aoba::Ops::Translate(mesh, back.verts, Aoba::Math::Vec3(0.0f, 0.0f, 0.5f)); 51 | 52 | // export the mesh 53 | Aoba::IO::ExportObj("chair.obj", mesh); 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /src/05_Donut.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/AobaAPI.hpp" 2 | #include 3 | 4 | int main() { 5 | std::default_random_engine generator(10); 6 | std::uniform_real_distribution distribution(0.0f, 1.0f); 7 | 8 | // crate a new mesh 9 | Aoba::Core::Mesh* mesh = new Aoba::Core::Mesh(); 10 | 11 | // Start with a circle 12 | auto circle = Aoba::Ops::CreateCircle(mesh, 8, 0.5f); 13 | 14 | // extrude circle and scale inwards, then extrude upwards to create a torus 15 | auto eeResult = Aoba::Ops::ExtrudeEdges(mesh, mesh->Edges()); 16 | Aoba::Ops::Scale(mesh, eeResult.verts, Aoba::Math::Vec3(0, 0, 0), 17 | Aoba::Math::Vec3(3.0f, 3.0f, 1.0f)); 18 | auto efrResult = Aoba::Ops::ExtrudeFaceRegion(mesh, mesh->Faces(), true); 19 | Aoba::Ops::Translate(mesh, efrResult.verts, Aoba::Math::Vec3(0, 0, 1.0f)); 20 | Aoba::Ops::ReverseFaceOrder(mesh, efrResult.horizontalFaces); 21 | Aoba::Ops::ReverseFaceOrder(mesh, efrResult.verticalFaces); 22 | 23 | // subdivide the geometry twice to create a smoother torus shape 24 | auto subdResult = Aoba::Ops::SubdivideSmooth(mesh, {}, mesh->Faces()); 25 | subdResult = Aoba::Ops::SubdivideSmooth(mesh, {}, mesh->Faces()); 26 | 27 | // select all faces with average vertex coords > 0.5 28 | // if face is between 0.3-0.5, select it at random 29 | std::vector frostingFaces; 30 | frostingFaces = mesh->Faces([](const Aoba::Core::Face* const f) { 31 | auto center = f->CalcCenterAverage(); 32 | if (center.z > 0.5) { 33 | return true; 34 | } 35 | return false; 36 | }); 37 | auto moreFaces = mesh->Faces([](const Aoba::Core::Face* const f) { 38 | auto center = f->CalcCenterAverage(); 39 | if (center.z > 0.3f && center.z < 0.5f) { 40 | return true; 41 | } 42 | return false; 43 | }); 44 | for (Aoba::Core::Face* f : moreFaces) { 45 | float rand = distribution(generator); 46 | if (rand > 0.5) { 47 | frostingFaces.push_back(f); 48 | } 49 | } 50 | 51 | // duplicate frosting faces and move them on face normals 52 | auto dupes = Aoba::Ops::Duplicate(mesh, {}, {}, frostingFaces); 53 | // mark faces used for calculation 54 | for (Aoba::Core::Face* f : dupes.faces) { 55 | f->NormalUpdate(); 56 | f->flags = 1; 57 | } 58 | for (Aoba::Core::Vert* v : dupes.verts) { 59 | auto vertFaces = 60 | v->Faces([](const Aoba::Core::Face* const f) { return f->flags == 1; }); 61 | auto avgNo = Aoba::Math::Vec3(); 62 | for (Aoba::Core::Face* f : vertFaces) { 63 | avgNo += f->no; 64 | } 65 | avgNo.Normalize(); 66 | avgNo.Negate(); 67 | v->co += 0.05f * avgNo; 68 | } 69 | 70 | // find, extrude boundary edges, 71 | auto boundary = mesh->Edges( 72 | [](const Aoba::Core::Edge* const e) { return e->IsBoundary(); }); 73 | auto extResult = Aoba::Ops::ExtrudeEdges(mesh, boundary); 74 | 75 | // move frosting verts out. 76 | for (Aoba::Core::Vert* v : dupes.verts) { 77 | auto vertFaces = 78 | v->Faces([](const Aoba::Core::Face* const f) { return f->flags == 1; }); 79 | auto avgNo = Aoba::Math::Vec3(0, 0, 0); 80 | for (Aoba::Core::Face* f : vertFaces) { 81 | f->NormalUpdate(); 82 | avgNo += f->no; 83 | } 84 | avgNo.Normalize(); 85 | v->co += 0.1f * avgNo; 86 | } 87 | 88 | // subdivide more 89 | subdResult = Aoba::Ops::SubdivideSmooth(mesh, {}, mesh->Faces()); 90 | subdResult = Aoba::Ops::SubdivideSmooth(mesh, {}, mesh->Faces()); 91 | 92 | // add slight variation to each vertex, to make the donut less smooth 93 | for (Aoba::Core::Vert* v : mesh->Verts()) { 94 | auto avgNo = Aoba::Math::Vec3(0, 0, 0); 95 | for (Aoba::Core::Face* f : v->Faces()) { 96 | f->NormalUpdate(); 97 | avgNo += f->no; 98 | } 99 | avgNo.Normalize(); 100 | v->co += distribution(generator) * 0.005f * avgNo; 101 | } 102 | 103 | // add sprinkles 104 | auto sph = Aoba::Ops::CreateUVSphere(mesh, 0.02f, 4, 6); 105 | for (Aoba::Core::Face* f : mesh->Faces()) { 106 | f->NormalUpdate(); 107 | if (f->no.z > 0.2f) { 108 | if (distribution(generator) > 0.8f) { 109 | auto dup = Aoba::Ops::Duplicate(mesh, {}, {}, sph.Faces); 110 | Aoba::Ops::Scale(mesh, dup.verts, Aoba::Math::Vec3(0, 0, 0), 111 | Aoba::Math::Vec3(distribution(generator) * 2, 112 | distribution(generator) * 2, 113 | distribution(generator) * 2)); 114 | Aoba::Ops::Translate(mesh, dup.verts, f->CalcCenterAverage()); 115 | } 116 | } 117 | } 118 | Aoba::Ops::Delete(mesh, sph.verts, {}, {}, Aoba::Ops::DeleteMode::All); 119 | 120 | // export the mesh 121 | Aoba::IO::ExportObj("05_Donut.obj", mesh); 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /src/03_ParametricChair.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/AobaAPI.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // chair params class, which holds the properties which define the chair 9 | class ChairParams { 10 | public: 11 | float width; 12 | float depth; 13 | float seatHeight; 14 | float seatThickness; 15 | float backHeight; 16 | float legScale; 17 | bool hasBack; 18 | 19 | ChairParams(int seed) { 20 | std::default_random_engine generator(seed); 21 | std::uniform_real_distribution distribution(0.0f, 1.0f); 22 | width = 0.4f + distribution(generator) * 0.2f; 23 | depth = 0.4f + distribution(generator) * 0.2f; 24 | seatHeight = 0.4f + distribution(generator) * 0.2f; 25 | seatThickness = 0.02f + distribution(generator) * 0.06f; 26 | backHeight = 0.5f + distribution(generator) * 0.4f; 27 | legScale = 2.0f + distribution(generator) * 1.0f; 28 | hasBack = (distribution(generator) > 0.5f) ? true : false; 29 | } 30 | }; 31 | 32 | // extract the chair generation into a separate function 33 | Aoba::Core::Mesh* MakeChair(ChairParams params) { 34 | // create a new mesh 35 | Aoba::Core::Mesh* mesh = new Aoba::Core::Mesh(); 36 | 37 | // use a 3x3 face grid as a starting point 38 | auto grid = Aoba::Ops::CreateGrid(mesh, 2, 2, params.width, params.depth); 39 | 40 | // find verts which are close to the Y axis, scale them on X axis 41 | // find them using a lambda expression 42 | auto vx = mesh->Verts([params](const Aoba::Core::Vert* v) { 43 | return fabsf(v->co.x) < params.width / 3.0f; 44 | }); 45 | Aoba::Ops::Scale(mesh, vx, Aoba::Math::Vec3(0.0f, 0.0f, 0.0f), Aoba::Math::Vec3(params.legScale, 1.0f, 1.0f)); 46 | 47 | // find verts which are close to the X axis, scale them on Y axis 48 | // find them using a lambda expression 49 | auto vy = mesh->Verts([params](const Aoba::Core::Vert* v) { 50 | return fabsf(v->co.y) < params.depth / 3.0f; 51 | }); 52 | Aoba::Ops::Scale(mesh, vy, Aoba::Math::Vec3(0.0f, 0.0f, 0.0f), Aoba::Math::Vec3(1.0f, params.legScale, 1.0f)); 53 | 54 | // seat faces - faces which will be extruded to make the seat - all faces currently in the mesh 55 | auto seatFaces = mesh->Faces(); 56 | 57 | // leg faces - faces which will be extruded to make the chair legs - the four corner faces 58 | // find them using a lambda expression 59 | auto legFaces = mesh->Faces([](const Aoba::Core::Face* f) { 60 | return fabsf(f->CalcCenterAverage().x) > 0.01f && fabsf(f->CalcCenterAverage().y) > 0.01f; 61 | }); 62 | 63 | // create the seat by extruding the seat faces and translating the new vertices 64 | auto seat = Aoba::Ops::ExtrudeFaceRegion(mesh, seatFaces, true); 65 | Aoba::Ops::Translate(mesh, seat.verts, Aoba::Math::Vec3(0.0f, 0.0f, params.seatThickness)); 66 | 67 | // create the legs by extruding the leg faces and translating the new vertices 68 | auto legs = Aoba::Ops::ExtrudeFaces(mesh, legFaces, false); 69 | Aoba::Ops::Translate(mesh, legs.verts, Aoba::Math::Vec3(0.0f, 0.0f, -params.seatHeight)); 70 | 71 | // will the back of the chair be generated or not? 72 | if(params.hasBack) { 73 | // back faces - faces which will be extruded to make the back of the chair 74 | auto backFaces = mesh->Faces([params](const Aoba::Core::Face* f) { 75 | return f->CalcCenterAverage().y < -params.depth / 3.0f && f->CalcCenterAverage().z > params.seatThickness / 1.5f; 76 | }); 77 | 78 | // extrude the back faces and translate the vertices to chreate the back of the chair 79 | auto back = Aoba::Ops::ExtrudeFaceRegion(mesh, backFaces, false); 80 | Aoba::Ops::Translate(mesh, back.verts, Aoba::Math::Vec3(0.0f, 0.0f, params.backHeight)); 81 | } 82 | 83 | // move the mesh upwards so that legs stand on the x-y plane 84 | Aoba::Ops::Translate(mesh, mesh->Verts(), Aoba::Math::Vec3(0, 0, params.seatHeight)); 85 | 86 | // return the resulting mesh 87 | return mesh; 88 | } 89 | 90 | int main() { 91 | // prompt the user to enter the seed 92 | int seed; 93 | std::cout<<"Enter random seed: "; 94 | std::cin>>seed; 95 | 96 | // generate params using the seed, generate the chair mesh using the params 97 | ChairParams params = ChairParams(seed); 98 | auto mesh = MakeChair(params); 99 | 100 | // create filename using the seed 101 | std::ostringstream stringStream; 102 | stringStream << "chair_"; 103 | stringStream << seed; 104 | stringStream << ".obj"; 105 | std::string name = stringStream.str(); 106 | 107 | // export mesh, kill mesh(free the memory) 108 | Aoba::IO::ExportObj(name, mesh); 109 | Aoba::Core::KillMesh(mesh); 110 | 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /src/04_Airplane.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/AobaAPI.hpp" 2 | 3 | #include 4 | 5 | // not the prettiest code I've written 6 | int main() { 7 | // create a new mesh 8 | Aoba::Core::Mesh* mesh = new Aoba::Core::Mesh(); 9 | 10 | 11 | // create the fusleage by extruding, translating and scaling a circle 12 | auto circle = Aoba::Ops::CreateCircle(mesh, 24, 2.75); 13 | Aoba::Ops::Rotate( mesh, circle.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Mat3::Rotation(Aoba::Math::Vec3(1, 0, 0), 3.1415926f / 2)); 14 | Aoba::Ops::Translate(mesh, circle.verts, Aoba::Math::Vec3(0, -20, 0)); 15 | std::vector vals = {5.0f, 30.0f, 5.0f}; 16 | std::vector last = circle.edges; 17 | for (int i = 0; i < vals.size(); ++i) { 18 | auto extrudeResult = Aoba::Ops::ExtrudeEdges(mesh, last); 19 | Aoba::Ops::Translate(mesh, extrudeResult.verts, 20 | Aoba::Math::Vec3(0, vals.at(i), 0)); 21 | last = extrudeResult.horizontalEdges; 22 | } 23 | 24 | std::vector yTrans = {4.0f, 3.5f, 0.7f}; 25 | std::vector zTrans = {-0.5f, -0.63f, 0.0f}; 26 | std::vector scale = {0.72f, 0.43f, 0.5f}; 27 | float yCent = 0; 28 | float zCent = 0; 29 | for (int i = 0; i < yTrans.size(); ++i) { 30 | auto extrudeResult = Aoba::Ops::ExtrudeEdges(mesh, last); 31 | Aoba::Ops::Translate(mesh, extrudeResult.verts, Aoba::Math::Vec3(0, yTrans.at(i), zTrans.at(i))); 32 | last = extrudeResult.horizontalEdges; 33 | yCent += yTrans.at(i); 34 | zCent += zTrans.at(i); 35 | Aoba::Ops::Scale(mesh, extrudeResult.verts, Aoba::Math::Vec3(0, yCent, zCent), Aoba::Math::Vec3(scale.at(i), 1, scale.at(i))); 36 | } 37 | Aoba::Ops::MakeFace(mesh, last); 38 | 39 | yTrans = {-13.0f, -0.35f}; 40 | zTrans = {1.6f, 0.0f}; 41 | scale = {0.13f, 0.7f}; 42 | yCent = 0; 43 | zCent = 0; 44 | last = circle.edges; 45 | for (int i = 0; i < yTrans.size(); ++i) { 46 | auto extrudeResult = Aoba::Ops::ExtrudeEdges(mesh, last); 47 | Aoba::Ops::Translate(mesh, extrudeResult.verts, Aoba::Math::Vec3(0, yTrans.at(i), zTrans.at(i))); 48 | last = extrudeResult.horizontalEdges; 49 | yCent += yTrans.at(i); 50 | zCent += zTrans.at(i); 51 | Aoba::Ops::Scale(mesh, extrudeResult.verts, Aoba::Math::Vec3(0, yCent, zCent), Aoba::Math::Vec3(scale.at(i), 1, scale.at(i))); 52 | } 53 | Aoba::Ops::MakeFace(mesh, last); 54 | 55 | 56 | // create wings by extruding, scaling and translating a circle, and mirroring to create the other side 57 | circle = Aoba::Ops::CreateCircle(mesh, 24, 6.75); 58 | Aoba::Ops::Scale(mesh, circle.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Vec3(0.09f, 1.0f, 1.0f)); 59 | Aoba::Ops::Rotate(mesh, circle.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Mat3::Rotation(Aoba::Math::Vec3(0, 1, 0), 3.1415926f / 2)); 60 | Aoba::Ops::Translate(mesh, circle.verts, Aoba::Math::Vec3(0, 0, -1.8f)); 61 | auto wingVerts = circle.verts; 62 | 63 | auto extrudeResult = Aoba::Ops::ExtrudeEdges(mesh, circle.edges); 64 | Aoba::Ops::Translate(mesh, extrudeResult.verts, Aoba::Math::Vec3(3.5f, 0, 0)); 65 | std::vector facesToDuplicate = extrudeResult.faces; 66 | wingVerts.insert(wingVerts.end(), extrudeResult.verts.begin(), extrudeResult.verts.end()); 67 | 68 | extrudeResult = Aoba::Ops::ExtrudeEdges(mesh, extrudeResult.horizontalEdges); 69 | Aoba::Ops::Scale(mesh, extrudeResult.verts, Aoba::Math::Vec3(3.5f, 0, -1.8f), Aoba::Math::Vec3(0.15f, 0.15f, 0.15f)); 70 | Aoba::Ops::Translate(mesh, extrudeResult.verts, Aoba::Math::Vec3(25.0f, -12.0f, 0.0f)); 71 | facesToDuplicate.insert(facesToDuplicate.end(), extrudeResult.faces.begin(), extrudeResult.faces.end()); 72 | wingVerts.insert(wingVerts.end(), extrudeResult.verts.begin(), extrudeResult.verts.end()); 73 | 74 | auto face = Aoba::Ops::MakeFace(mesh, extrudeResult.horizontalEdges); 75 | facesToDuplicate.push_back(face); 76 | // rotate wing 77 | Aoba::Ops::Rotate( mesh, wingVerts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Mat3::Rotation(Aoba::Math::Vec3(0, 1, 0), -0.05f)); 78 | // mirror 79 | auto mir = Aoba::Ops::Mirror(mesh, {}, {}, facesToDuplicate, Aoba::Math::Vec3(1, 0, 0), Aoba::Math::Vec3(0, 0, 0), true, 0.2f); 80 | 81 | 82 | // vertical stabilizer 83 | circle = Aoba::Ops::CreateCircle(mesh, 24, 4.15f); 84 | Aoba::Ops::Scale(mesh, circle.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Vec3(0.12f, 1, 1)); 85 | wingVerts = circle.verts; 86 | 87 | extrudeResult = Aoba::Ops::ExtrudeEdges(mesh, circle.edges); 88 | Aoba::Ops::Scale(mesh, extrudeResult.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Vec3(0.3f, 0.3f, 0.3f)); 89 | Aoba::Ops::Translate(mesh, extrudeResult.verts, Aoba::Math::Vec3(0, -6.6f, 11)); 90 | wingVerts.insert(wingVerts.end(), extrudeResult.verts.begin(), extrudeResult.verts.end()); 91 | facesToDuplicate = extrudeResult.faces; 92 | face = Aoba::Ops::MakeFace(mesh, extrudeResult.horizontalEdges); 93 | facesToDuplicate.push_back(face); 94 | 95 | 96 | // duplicate vertical stabilizer to make horizontal stabilizer 97 | auto duplicateResult = Aoba::Ops::Duplicate(mesh, std::vector(), std::vector(), facesToDuplicate); 98 | Aoba::Ops::Rotate(mesh, duplicateResult.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Mat3::Rotation(Aoba::Math::Vec3(0, 1, 0), 1.5)); 99 | wingVerts.insert(wingVerts.end(), duplicateResult.verts.begin(), duplicateResult.verts.end()); 100 | 101 | duplicateResult = Aoba::Ops::Duplicate(mesh, std::vector(), std::vector(), facesToDuplicate); 102 | Aoba::Ops::Rotate(mesh, duplicateResult.verts, Aoba::Math::Vec3(0, 0, 0), Aoba::Math::Mat3::Rotation(Aoba::Math::Vec3(0, 1, 0), -1.5)); 103 | wingVerts.insert(wingVerts.end(), duplicateResult.verts.begin(), duplicateResult.verts.end()); 104 | 105 | Aoba::Ops::Translate(mesh, wingVerts, Aoba::Math::Vec3(0, -26.5f, 1.2f)); 106 | 107 | // export the mesh 108 | Aoba::IO::ExportObj("airplane.obj", mesh); 109 | } --------------------------------------------------------------------------------