Ok so here is my try to write a small example on creating a basic mesh dynamically.
I have done it by reading what I could find about the topic and a lot of experimenting.
Please let me know if I have done some mistakes in my asumptions here, thanks!
Also, how can I get the camera to mimic the result/view from setMatricesWindow( getWindowSize() ) ?
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/CinderImGui.h"
using namespace ci;
using namespace ci::app;
/*
Creates a basic Mesh as a TriMesh or a VboMesh.
-----------------------------------------------
1) Defines 3 vertex positions and then defines a triangle polygon from them.
2) A Left Mouse Click -> Creates another vertex and another triangle,
using the new vertex and two of the existing vertices.
3) A Right Mouse Click deletes the second triangle and the last vertex in this mesh.
*/
class BasicMeshApp : public App {
public:
void setup() override;
void update() override;
void draw() override;
void mouseDown(MouseEvent event) override;
void resize() override;
void buildTriMesh();
void buildVboMesh();
vec2 mousePos;
std::list <vec3> mousePoints; // List with XYZ positions
bool useTriMesh = false;
bool useVboMesh = false;
bool useAnimation = true;
bool useWireframe = false;
// TriMesh
TriMesh mTriMesh; // Handle to the TriMesh
/// VboMesh
gl::VboMeshRef mVboMesh; // Reference to the VboMesh
std::vector<vec3> positions; // Vector(List) for XYZ positions
std::vector<ColorA> colors; // Vector(List) for colors
std::vector<uint32_t> indices; // Vector(List) for indices (to create polygons) ( uint32_t is a fixed width integer type )
gl::VboRef indexBufferObj; // Reference to a Vertex Buffer to store the indices
gl::BatchRef batchVbo; // Reference to the Batch
};
void BasicMeshApp::resize() {
// Rebuild the mesh just to fit the window...
buildTriMesh(); // Create the TriMesh
buildVboMesh(); // Create the VboMesh
update(); // Keep drawing the ImGui interface while resizing
}
void BasicMeshApp::mouseDown(MouseEvent event)
{
// Add or remove points:
if (event.isLeft()) { // Left mouse-click?
mousePoints.clear(); // Clear the list (Just to keep it simple)
// Add a new point to the list:
mousePoints.push_front(vec3(event.getPos().x, event.getPos().y, 0));
}
else if (event.isRight()) { // Right mouse-click?
if (mousePoints.size() > 0) { // If there still points in the List...
// Remove one point from the list:
mousePoints.pop_front();
}
}
buildTriMesh(); // Recreate the mesh
buildVboMesh(); // Recreate the mesh
}
void BasicMeshApp::buildTriMesh()
{
// Remove old data
mTriMesh.clear();
// Setup the TriMesh format: (Choose the attributes that you will need)
mTriMesh = TriMesh(
TriMesh::Format() // This TriMesh shall contain:
.positions() // Position
.colors(3) // RGB Color ?? Couldn't use .colors(4) Why...?
);
// *** Build the first triangle polygon ***
// Vertex 1
mTriMesh.appendPosition(vec3(getWindowCenter().x, getWindowCenter().y -20, 0)); // Position
mTriMesh.appendColorRgb(Color(0, 0, 1)); // Color ?? Couldn't use ColorA(0, 0, 1, 0.5) Why...?
// Vertex 2
mTriMesh.appendPosition(vec3(0, 0, 0));
mTriMesh.appendColorRgb(Color(0, 1, 1));
// Vertex 3
mTriMesh.appendPosition(vec3(getWindowWidth(), 0, 0));
mTriMesh.appendColorRgb(Color(1, 0, 1));
// Create the first triangle polygon (from the 3 first vertices/indicies)
mTriMesh.appendTriangle(0, 1, 2); // Use indices/vertices 1,2,3
// *** Build the second triangle polygon ***
if (mousePoints.size() > 0) { // If there have been drawn an extra vertex...
// Vertex 4 (Takes the position from the list with mouse-clicks)
mTriMesh.appendPosition(vec3(mousePoints.front().x, mousePoints.front().y, 0));
mTriMesh.appendColorRgb(Color(0.5, 0.5, 0.5));
// Create the second triangle polygon (from the 2 first vertices and the new 4:th vertex)
mTriMesh.appendTriangle(0, 1, 3); // Use indices/vertices 1,2 and 4
}
}
void BasicMeshApp::buildVboMesh() {
// Clear all previous mesh data: <-- (Just to keep it simple)
positions.clear();
colors.clear();
indices.clear();
// Define a VboMesh Layout: (Choose the attributes that you will need)
std::vector<gl::VboMesh::Layout> layout = {
gl::VboMesh::Layout().usage(GL_DYNAMIC_DRAW).attrib(geom::Attrib::POSITION, 3), // *Dynamic* positions (Will be animated)
gl::VboMesh::Layout().usage(GL_STATIC_DRAW).attrib(geom::Attrib::COLOR, 4) // *Static* Colors (Will not animate)
// .attrib(geom::TEX_COORD_0, 2)
// .attrib(geom::NORMAL, 3)
};
// *** Build the first triangle polygon ***
// Vertex 1
positions.push_back(vec3(getWindowCenter().x, getWindowCenter().y +20, 0)); // Add a vertex position element to the positions vector/List
colors.push_back(ColorA(1, 0, 0, 1)); // Add a vertex color element to the color vector/List
indices.push_back(0); // Add first index for the first triangle polygon
// Vertex 2
positions.push_back(vec3(ci::app::getWindowWidth(), ci::app::getWindowHeight(), 0));
colors.push_back(ColorA(0.5, 0.5, 0, 1));
indices.push_back(1); // Second index for the first triangle polygon
// Vertex 3
positions.push_back(vec3(0, ci::app::getWindowHeight(), 0));
colors.push_back(ColorA(0, 0.5, 0.5, 1));
indices.push_back(2); // Last index for the first triangle polygon
// *** Build the second triangle polygon ***
if (mousePoints.size() > 0) { // If there have been drawn an extra vertex
// Vertex 4 (Takes the position from the list with mouse-clicks)
positions.push_back( vec3( mousePoints.front().x, mousePoints.front().y, 0));
colors.push_back(ColorA(0.5, 0.5, 0.5, 1));
// Create the second triangle polygon from these indices:
indices.push_back(0); // First index for the second polygon/triangle (Vertex 1) (Using same position as vertex 1)
indices.push_back(1); // Second index for the second polygon/triangle (Vertex 2) (Using same position as vertex 2)
indices.push_back(3); // Last index for the second polygon/triangle (Vertex 4) (Using same position as vertex 4, wich was the mouse click)
}
// Allocate a Vertex Buffer to store the indices on the GPU (So the Vbo-mesh knows from wich vertices it should build its triagles)
indexBufferObj = gl::Vbo::create(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW); // ? GL_STATIC_DRAW ? GL_DYNAMIC_DRAW GL_ARRAY_BUFFER
// Create the Vbo-mesh and pass the index-BufferObject
mVboMesh = gl::VboMesh::create(positions.size(), GL_TRIANGLES, { layout }, indices.size(), GL_UNSIGNED_INT, indexBufferObj);
// GL_LINES GL_TRIANGLES GL_POINTS GL_UNSIGNED_SHORT
mVboMesh->bufferAttrib<vec3>(geom::POSITION, positions); // Hook up the positions vector/List to the VBO-mesh
mVboMesh->bufferAttrib<ColorA>(geom::COLOR, colors); // Hook up the color vector/List to the VBO-mesh
batchVbo = gl::Batch::create(mVboMesh, gl::getStockShader(gl::ShaderDef().color())); // Create a (faster) Batch object from the Vbo-mesh (A Batch combines the mesh with the shader)
//batchVbo = gl::Batch::create(mVboMesh, gl::getStockShader( gl::ShaderDef().lambert().color()) ); // Alternative batch that use the built in Lambert shader
}
void BasicMeshApp::setup()
{
ImGui::Initialize();
gl::pointSize(6);
buildTriMesh(); // Create the TriMesh
buildVboMesh(); // Create the VboMesh
}
void BasicMeshApp::update()
{
// ImGui interface
ImGui::Begin("Settings - Mesh");
ImGui::SetWindowPos(ImVec2(10, 10), ImGuiCond_FirstUseEver);
ImGui::SetWindowSize(ImVec2(190, 140), ImGuiCond_FirstUseEver);
static int e = 0;
ImGui::RadioButton("Use TriMesh (CPU-mem)", &e, 0);
ImGui::RadioButton("Use VboMesh (GPU-mem)", &e, 1);
if (e == 0)
{
useTriMesh = true;
useVboMesh = false;
}
else
{
useTriMesh = false;
useVboMesh = true;
}
ImGui::NewLine();
ImGui::Checkbox("Animate Vbo", &useAnimation);
ImGui::Checkbox("Wireframe", &useWireframe);
ImGui::End();
}
void BasicMeshApp::draw()
{
gl::clear();
// reset the matrices
gl::setMatricesWindow(getWindowSize());
/* CameraPersp cam;
cam.lookAt(vec3(0, 0, -900), vec3(0)); // ?? How to "flip/mirror" the cam to mimic the result from gl::setMatricesWindow(getWindowSize()) ??
gl::setMatrices(cam); */
if (useWireframe) gl::enableWireframe();
else gl::disableWireframe();
// Draw the TriMesh
if (useTriMesh)
{
gl::draw(mTriMesh); // Draw the TriMesh
}
// Draw the VboMesh
if (useVboMesh)
{
if (useAnimation) // First animate the Vbo mesh vertex positions
{
// Get a Position Attribute reference to the GPU buffer
auto mappedPosAttrib = mVboMesh->mapAttrib3f(geom::Attrib::POSITION, false);
// Add animated displacement to the positionAttribute for the first vertex position in the buffer
mappedPosAttrib->y = getWindowCenter().y + sin(getElapsedSeconds() *8) *10;
mappedPosAttrib->x = getWindowCenter().x + cos(getElapsedSeconds() *8) *10;
mappedPosAttrib.unmap();
}
// gl::draw(mVboMesh); // Draw the VboMesh
batchVbo->draw(); // Draw the VboMesh (faster) with a "Batch"
}
// Draw text
if (useWireframe) gl::disableWireframe(); // (Turn off wireframe now while drawing text)
if (mousePoints.size() == 0) gl::drawString("Left click to: \nAdd a vertex", vec3(20, getWindowCenter().y -20, 0), ColorA(1, 1, 1, 0.5), Font("verdana", 20.f));
if (mousePoints.size() >= 1) gl::drawString("Left click to: \nSet a new position", vec3(20, getWindowCenter().y - 20, 0), ColorA(1, 1, 1, 0.5), Font("verdana", 20.f));
if (mousePoints.size() >= 1)gl::drawStringCentered("Right click to: \nRemove the vertex", vec3(getWindowWidth()-100, getWindowCenter().y - 20, 0), ColorA(1, 1, 1, 0.5), Font("verdana", 20.f));
if (useTriMesh) gl::drawStringCentered("TriMesh", vec3(getWindowCenter().x, 20, 0), Color(1, 1, 1), Font("verdana", 50.f));
if (useVboMesh) gl::drawStringCentered("VboMesh", vec3(getWindowCenter().x, getWindowHeight() -50, 0), Color(1, 1, 1), Font("verdana", 50.f));
gl::color(Color(1, 1, 0.0)); // Set a yellow draw color
// Draw Points (from the mousePoints list)
gl::begin( GL_POINTS );
for each( vec3 point in mousePoints ) {
gl::vertex( point ); // Draw each point in the list
}
/*
// Draw all points in the TriMesh
if (useTriMesh)
{
vec3* allVertexPositions;
allVertexPositions = mTriMesh.getPositions<3>();
for (int i = 0; i < mTriMesh.getBufferPositions().size() / 3; i++)
{
gl::vertex(allVertexPositions->x, allVertexPositions->y, 0); // Draw this vertex position as a Point
allVertexPositions++;
}
}
// Draw all points in the VboMesh
if (useVboMesh)
{
gl::VboMesh::MappedAttrib allVertexPositions = mVboMesh->mapAttrib3f(geom::Attrib::POSITION, false);
for (int i = 0; i < mVboMesh->getNumVertices() ; i++)
{
gl::vertex(allVertexPositions->x, allVertexPositions->y, 0); // Draw this vertex position as a Point
allVertexPositions++;
}
allVertexPositions.unmap();
}*/
gl::end();
}
CINDER_APP( BasicMeshApp, RendererGl( RendererGl::Options().msaa( 0 ) ) )