示例#1
0
int main( void )
{

	// init random seed equal to current time
	std::srand(std::time(0));

	const unsigned MAX_NR_RECTANGLES = 100;

	// windows size
	int windowWidth = 600;
	int windowHeight = 600;

	// Initialise GLFW
	if(!glfwInit()) {
		std::cerr << "Failed to initialize GLFW" << std::endl;
		return -1;
	}

	glfwWindowHint(GLFW_SAMPLES, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// Open a window and create its OpenGL context
	// window = glfwCreateWindow( 1024, 768, "Tutorial 03 - Matrices", NULL, NULL);
	window = glfwCreateWindow(windowWidth, windowHeight, "Mondrian", NULL, NULL);
	if(window == NULL) {
		std::cerr << "Failed to open GLFW window.";
		std::cerr << " If you have an Intel GPU, they are not 3.3 compatible.";
		std::cerr << " Try the 2.1 version of the tutorials." << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	// Initialize GLEW
	glewExperimental = true; // Needed for core profile
	if (glewInit() != GLEW_OK) {
		std::cerr << "Failed to initialize GLEW" << std::endl;
		return -1;
	}

	// enable z-buffer
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	// Ensure we can capture the escape key being pressed below
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
	glfwSetMouseButtonCallback(window, mouseButtonCallback);

	// white background
	glm::vec4 whiteColor (1.0f, 1.0f, 1.0f, 0.0f);
	glClearColor(whiteColor.r, whiteColor.g, whiteColor.b, whiteColor.a);

	// nr rectangles * 2 triangles each * 3 vertices * 4 floats
	float cpuBufferDataPoints[MAX_NR_RECTANGLES * 2 * 3 * 4];
	float cpuBufferColors[MAX_NR_RECTANGLES * 2 * 3 * 4];
	// nr rectangles * 2 triangles each * 3 vertices * 4x4 floats
	float cpuBufferMVProw1[MAX_NR_RECTANGLES * 2 * 3 * 4];
	float cpuBufferMVProw2[MAX_NR_RECTANGLES * 2 * 3 * 4];
	float cpuBufferMVProw3[MAX_NR_RECTANGLES * 2 * 3 * 4];
	float cpuBufferMVProw4[MAX_NR_RECTANGLES * 2 * 3 * 4];
	float *cpuBufferLines;

	// Create and compile our GLSL program from the shaders
	GLuint rectangleProgram = LoadShaders( "rectangleVertexShader.glsl", "rectangleFragmentShader.glsl" );
	GLuint lineProgram = LoadShaders( "lineVertexShader.glsl", "lineFragmentShader.glsl" );

	// Use our shader (it's not required to be using a program to bind VAOs)
	glUseProgram(rectangleProgram);

	// VAOs
	GLuint rectangleVAO;
	GLuint lineVAO;
	glGenVertexArrays(1, &rectangleVAO);
	glGenVertexArrays(1, &lineVAO);
	glBindVertexArray(rectangleVAO);

	// left, right, bottom, top, angle1, angle2
	// glm::mat4 Projection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f,0.0001f,10.0f); // In world coordinates
	glm::mat4 Projection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f); // In world coordinates
	for (unsigned row = 0; row < 4; row++) {
		for (unsigned col = 0; col < 4; col++) {
			std::cout << std::setw(10) << Projection[col][row] << " ";
		}
		std::cout << std::endl;
	}

	glm::vec4 testvertex;
	for (int i = -10; i <= 10; i++) {
		testvertex = glm::vec4(1.0);
		testvertex.z = i;
		testvertex = Projection * testvertex;
		std::cout << "z = " << std::setw(3) << i << " " << testvertex.z << std::endl;
	}
	std::cout << std::endl;

	// glm::mat4 Projection = glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f,1.0f,100.0f); // In world coordinates
	/*
	glm::mat4 Projection = glm::perspective(
		45.0f,         // The horizontal Field of View, in degrees : the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in)
		4.0f / 4.0f, // Aspect Ratio. Depends on the size of your window. Notice that 4/3 == 800/600 == 1280/960, sounds familiar ?
		0.1f,        // Near clipping plane. Keep as big as possible, or you'll get precision issues.
		100.0f       // Far clipping plane. Keep as little as possible.
	);		
	*/
	// Camera matrix
	/*
	 * view es una matriz cuadrada almacenada por columnas (no por filas)
	 * al poner la camara mirando al origen en una posicion z=2 (positivo),
	 * lo que hace, es generar una matriz de transformación que moverá el objeto
	 * en el eje z dos unidades de "w" */
	glm::mat4 View = glm::lookAt(
		glm::vec3(0,0,2), // Camera is at (4,3,3), in World Space
		glm::vec3(0,0,0), // and looks at the origin
		glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
	);
	// glm::mat4 View = glm::mat4(1.0f);
	/*
	for (unsigned row = 0; row < 4; row++) {
		for (unsigned col = 0; col < 4; col++) {
			std::cout << std::setw(3) << View[col][row] << " ";
		}
		std::cout << std::endl;
	}
	*/

	GLuint rectangleVertexBuffer;
	glGenBuffers(1, &rectangleVertexBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, rectangleVertexBuffer);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(
		0,					// attribute. No particular reason for 0, but must match the layout in the shader.
		4,					// size
		GL_FLOAT,			// type
		GL_FALSE,			// normalized?
		0,					// stride
		(void*)0			// array buffer offset
	);

	GLuint rectangleColorBuffer;
	glGenBuffers(1, &rectangleColorBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, rectangleColorBuffer);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(
		1,					// attribute. No particular reason for 0, but must match the layout in the shader.
		4,					// size
		GL_FLOAT,			// type
		GL_FALSE,			// normalized?
		0,					// stride
		(void*)0			// array buffer offset
	);

	// GLuint rectangleMVPBuffer;
	GLuint rectangleMVProw1Buffer;
	GLuint rectangleMVProw2Buffer;
	GLuint rectangleMVProw3Buffer;
	GLuint rectangleMVProw4Buffer;
	glGenBuffers(1, &rectangleMVProw1Buffer);
	glGenBuffers(1, &rectangleMVProw2Buffer);
	glGenBuffers(1, &rectangleMVProw3Buffer);
	glGenBuffers(1, &rectangleMVProw4Buffer);
	glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw1Buffer);
	glEnableVertexAttribArray(2);
	glVertexAttribPointer(
		2,										// attribute. No particular reason for 0, but must match the layout in the shader.
		4,												// size
		GL_FLOAT,								// type
		GL_FALSE,								// normalized?
		0,		// stride
		(void*)(0)								// array buffer offset
	);
	glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw2Buffer);
	glEnableVertexAttribArray(3);
	glVertexAttribPointer(
		3,										// attribute. No particular reason for 0, but must match the layout in the shader.
		4,												// size
		GL_FLOAT,								// type
		GL_FALSE,								// normalized?
		0,		// stride
		(void*)(0)								// array buffer offset
	);
	glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw3Buffer);
	glEnableVertexAttribArray(4);
	glVertexAttribPointer(
		4,										// attribute. No particular reason for 0, but must match the layout in the shader.
		4,												// size
		GL_FLOAT,								// type
		GL_FALSE,								// normalized?
		0,		// stride
		(void*)(0)								// array buffer offset
	);
	glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw4Buffer);
	glEnableVertexAttribArray(5);
	glVertexAttribPointer(
		5,										// attribute. No particular reason for 0, but must match the layout in the shader.
		4,												// size
		GL_FLOAT,								// type
		GL_FALSE,								// normalized?
		0,		// stride
		(void*)(0)								// array buffer offset
	);

	glBindVertexArray(lineVAO);
	GLuint lineVertexBuffer;
	glGenBuffers(1, &lineVertexBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, lineVertexBuffer);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(
		0,					// attribute. No particular reason for 0, but must match the layout in the shader.
		2,					// size
		GL_FLOAT,			// type
		GL_FALSE,			// normalized?
		0,					// stride
		(void*)0			// array buffer offset
	);

	// get a handle for MVP uniform matrix in line program (it's not required
	// to be using the line program right now
	// GLuint lineMVPID = glGetUniformLocation(lineProgram, "MVP");

	glBindVertexArray(rectangleVAO);


	std::set<float> verticalLines;
	std::set<float> horizontalLines;
	unsigned nrLines = 0;

	unsigned simulationTime = 0;
	bool justEnded = true;

	double lineThickness = 0.1;
		
	do {

		// update window's title to show fps
		updateFPSCounter(window);

		// Clear the screen
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// update viewport 
		glfwGetWindowSize(window, &windowWidth, &windowHeight);
		glViewport(0, 0, windowWidth, windowHeight);	// (x,y) offset from lower left; (width, height)

		// std::cout << "nr rectangles: " << rectangles.size() << std::endl;

		// every 75 steps, create a new rectangle
		if (simulationTime % 500 == 0 && !endSimulation && rectangles.size() < MAX_NR_RECTANGLES) {
				// create rectangle
				Rectangle rectangle;
				// insert rectangle into array
				rectangles.push_back(rectangle);
		}

		// update cpu buffers

		// fill up new cpu position buffers
		float rectangleCoords[2 * 3 * 4];
		unsigned coordCounter = 0;
		for (unsigned i = 0; i < rectangles.size(); i++) {
				rectangles[i].getCoords(rectangleCoords);
				for (unsigned j = 0; j < 2*3*4; j++) {
						cpuBufferDataPoints[coordCounter] = rectangleCoords[j];
						coordCounter++;
				}
				// std::cout << "rectangle ID: " << rectangles[i].getID() << std::endl;
				// std::cout << "horizontal: [" << rectangles[i].getLeft() << " : " << rectangles[i].getRight() << "]" << std::endl;
				// std::cout << "vertical: [" << rectangles[i].getBottom() << " : " << rectangles[i].getTop() << "]" << std::endl;
		}

		// fill up new cpu colors buffers
		float rectangleColorComponents[2 * 3 * 4];
		coordCounter = 0;
		for (unsigned i = 0; i < rectangles.size(); i++) {
				rectangles[i].getColorComponents(rectangleColorComponents);
				for (unsigned j = 0; j < 2*3*4; j++) {
						cpuBufferColors[coordCounter] = rectangleColorComponents[j];
						coordCounter++;
				}
		}

		// fill up new cpu mvp buffers
		unsigned counter = 0;
		for (unsigned i = 0; i < rectangles.size(); i++) {
			glm::mat4 Model = rectangles[i].getModel();
			// glm::mat4 MVP = Projection * View * Model;
			glm::mat4 MVP = Projection * Model * View;
			// TODO: try to transfer glm::mat MVP to buffer directly with glBufferData
			// glm::vec4 vertexa = Model * rectangles[i].getVertexA();
			// glm::vec4 vertexb = Model * rectangles[i].getVertexB();
			// glm::vec4 vertexc = Model * rectangles[i].getVertexC();
			// glm::vec4 vertexd = Model * rectangles[i].getVertexD();

			// std::cout << "final positions: " << std::endl;
			// std::cout << vertexa.x << " " << vertexa.y << " " << vertexa.z << std::endl;
			// std::cout << vertexb.x << " " << vertexb.y << " " << vertexb.z << std::endl;
			// std::cout << vertexc.x << " " << vertexc.y << " " << vertexc.z << std::endl;
			// std::cout << vertexd.x << " " << vertexd.y << " " << vertexd.z << std::endl;

			for (unsigned vertex = 0; vertex < 6; vertex++) {
				for (unsigned element = 0; element < 4; element++) {
					// cpuBufferMVProw1[counter] = static_cast<int>(MVP[0][element]);
					// cpuBufferMVProw2[counter] = static_cast<int>(MVP[1][element]);
					// cpuBufferMVProw3[counter] = static_cast<int>(MVP[2][element]);
					// cpuBufferMVProw4[counter] = static_cast<int>(MVP[3][element]);

					// TODO swap these lines
					cpuBufferMVProw1[counter] = MVP[0][element];
					cpuBufferMVProw2[counter] = MVP[1][element];
					cpuBufferMVProw3[counter] = MVP[2][element];
					cpuBufferMVProw4[counter] = MVP[3][element];

					// for these ones
					// cpuBufferMVProw1[counter] = MVP[element][0];
					// cpuBufferMVProw2[counter] = MVP[element][1];
					// cpuBufferMVProw3[counter] = MVP[element][2];
					// cpuBufferMVProw4[counter] = MVP[element][3];

					// std::cout << "double: " << std::endl;
					// std::cout << MVP[0][element] << " ";
					// std::cout << MVP[1][element] << " ";
					// std::cout << MVP[2][element] << " ";
					// std::cout << MVP[3][element] << std::endl;
					// std::cout << "int: " << std::endl;
					// std::cout << cpuBufferMVProw1[counter] << " ";
					// std::cout << cpuBufferMVProw2[counter] << " ";
					// std::cout << cpuBufferMVProw3[counter] << " ";
					// std::cout << cpuBufferMVProw4[counter] << std::endl;
					counter++;
				}
			}
		}

		glUseProgram(rectangleProgram);
		glBindVertexArray(rectangleVAO);

		// transfer data to position and color buffers
		glBindBuffer(GL_ARRAY_BUFFER, rectangleVertexBuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float) * rectangles.size() * 2 * 3 * 4, cpuBufferDataPoints, GL_STREAM_DRAW);

		glBindBuffer(GL_ARRAY_BUFFER, rectangleColorBuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float) * rectangles.size() * 2 * 3 * 4, cpuBufferColors, GL_STREAM_DRAW);

		glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw1Buffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float) * rectangles.size() * 2 * 3 * 4, cpuBufferMVProw1, GL_STREAM_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw2Buffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float) * rectangles.size() * 2 * 3 * 4, cpuBufferMVProw2, GL_STREAM_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw3Buffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float) * rectangles.size() * 2 * 3 * 4, cpuBufferMVProw3, GL_STREAM_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, rectangleMVProw4Buffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float) * rectangles.size() * 2 * 3 * 4, cpuBufferMVProw4, GL_STREAM_DRAW);

		// draw rectangles
		glDrawArrays(GL_TRIANGLES, 0, rectangles.size()*2*3); // 3 indices starting at 0 -> 1 triangle

		for (unsigned i = 0; i < rectangles.size(); i++) {
			// std::cout << "updating rectangle id: " << i << std::endl;
			rectangles[i].updateModel();
			rectangles[i].shouldBeAlive();
		}

		for (std::vector<Rectangle>::iterator it = rectangles.begin(); it != rectangles.end();) {
			if (it->isAlive()) {
				it++;
			} else {
				it = rectangles.erase(it);
			}
		}

		if (endSimulation && !justEnded) {
			glUseProgram(lineProgram);
			glBindVertexArray(lineVAO);
			// std::cout << "drawing lines..." << std::endl;
			glDrawArrays(GL_TRIANGLES, 0, nrLines * 2 * 3); // 3 indices starting at 0 -> 1 triangle
		}

		if (endSimulation && justEnded) {

			std::cout << "creating lines..." << std::endl;
			justEnded = false;

			// load data in cpuBufferLines just the very first time

			// get vertical and horizontal coords of every line that should be drawn
			for (unsigned i = 0; i < rectangles.size(); i++) {
				if (rectangles[i].getIsPinned()) {
					// values in {-10:10}
					int left = static_cast<int>(round(rectangles[i].getLeft()));
					int right = static_cast<int>(round(rectangles[i].getRight()));
					int bottom = static_cast<int>(round(rectangles[i].getBottom()));
					int top = static_cast<int>(round(rectangles[i].getTop()));
					std::cout << "left: " << left << " right: " << right << " top: " << top << " bottom: " << bottom << std::endl;
					verticalLines.insert(left);
					verticalLines.insert(right);
					horizontalLines.insert(bottom);
					horizontalLines.insert(top);
				}
			}

			unsigned nrVerticalLines = verticalLines.size();
			unsigned nrHorizontalLines = horizontalLines.size();
			nrLines = nrVerticalLines + nrHorizontalLines;
			std::cout << "nr lines to draw: " << nrLines << std::endl;
			// 1 line = 2 triangles; 1 triangle = 3 points each; 1 point = 2 coord each;
			cpuBufferLines = new float[nrLines * 2 * 3 * 2];
			unsigned counter = 0;
			glm::mat4 PV = Projection * View;
			for (std::set<float>::iterator it = verticalLines.begin(); it != verticalLines.end(); it++) {
				glm::vec4 vertexA (*it - lineThickness, 10, 0, 1);
				glm::vec4 vertexB (*it + lineThickness, 10, 0, 1);
				glm::vec4 vertexC (*it - lineThickness, -10, 0, 1);
				glm::vec4 vertexD (*it + lineThickness, -10, 0, 1);
				vertexA = PV * vertexA;
				vertexB = PV * vertexB;
				vertexC = PV * vertexC;
				vertexD = PV * vertexD;
				// glm::vec4 firstPointMVP = Projection * View * firstPoint;
				// glm::vec4 secondPointMVP = Projection * View * secondPoint;
				// sent the points in {-10:10} domain
				// first triangle ABC
				cpuBufferLines[counter] = vertexA.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexA.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexB.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexB.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexC.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexC.y;		// y
				counter++;
				// second triangle BCD
				cpuBufferLines[counter] = vertexB.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexB.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexC.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexC.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexD.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexD.y;		// y
				counter++;
				// std::cout << "vertical line: " << std::endl;
				// std::cout << "(" << firstPointMVP.x << "," << firstPointMVP.y << ")" << std::endl;
				// std::cout << "(" << secondPointMVP.x << "," << secondPointMVP.y << ")" << std::endl;
			}

			for (std::set<float>::iterator it = horizontalLines.begin(); it != horizontalLines.end(); it++) {
				glm::vec4 vertexA (-10, *it + lineThickness, 0, 1);
				glm::vec4 vertexB (10, *it + lineThickness, 0, 1);
				glm::vec4 vertexC (-10, *it - lineThickness, 0, 1);
				glm::vec4 vertexD (10, *it - lineThickness, 0, 1);
				vertexA = PV * vertexA;
				vertexB = PV * vertexB;
				vertexC = PV * vertexC;
				vertexD = PV * vertexD;
				// glm::vec4 firstPointMVP = Projection * View * firstPoint;
				// glm::vec4 secondPointMVP = Projection * View * secondPoint;
				// first triangle ABC
				cpuBufferLines[counter] = vertexA.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexA.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexB.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexB.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexC.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexC.y;		// y
				counter++;
				// second triangle BCD
				cpuBufferLines[counter] = vertexB.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexB.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexC.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexC.y;		// y
				counter++;
				cpuBufferLines[counter] = vertexD.x;		// x
				counter++;
				cpuBufferLines[counter] = vertexD.y;		// y
				counter++;
				// std::cout << "horizontal line: " << std::endl;
				// std::cout << "(" << firstPointMVP.x << "," << firstPointMVP.y << ")" << std::endl;
				// std::cout << "(" << secondPointMVP.x << "," << secondPointMVP.y << ")" << std::endl;
			}

			glBindBuffer(GL_ARRAY_BUFFER, lineVertexBuffer);
			glBufferData(GL_ARRAY_BUFFER, sizeof(float) * nrLines * 2 * 3 * 2, cpuBufferLines, GL_STATIC_DRAW);


			// send MVP matrix to line program
			// TODO: check if i need to be using line program and to have bound line VAO
			// glUniformMatrix4fv(lineMVPID, 1, GL_FALSE, &PV[0][0]);
		}

		// Swap buffers
		glfwSwapBuffers(window);
		glfwPollEvents();
		// usleep(500000);
		// usleep(100000);

		simulationTime++;

		// int dummy;
		// std::cin >> dummy;

	} while ( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && glfwWindowShouldClose(window) == 0 );


	glBindVertexArray(rectangleVAO);
	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);

	glBindVertexArray(lineVAO);
	glDisableVertexAttribArray(0);

	// Cleanup VBO and shader
	glDeleteBuffers(1, &rectangleVertexBuffer);
	glDeleteBuffers(1, &rectangleColorBuffer);
	glDeleteBuffers(1, &rectangleMVProw1Buffer);
	glDeleteBuffers(1, &rectangleMVProw2Buffer);
	glDeleteBuffers(1, &rectangleMVProw3Buffer);
	glDeleteBuffers(1, &rectangleMVProw4Buffer);
	glDeleteBuffers(1, &lineVertexBuffer);
	glDeleteProgram(rectangleProgram);
	glDeleteProgram(lineProgram);
	glDeleteVertexArrays(1, &rectangleVAO);
	glDeleteVertexArrays(1, &lineVAO);

	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	delete [] cpuBufferLines;				// make sure new was executed, i.e: right click event

	return 0;
}
示例#2
0
void PrevailingWindDemo::createPalmTree ( hkpWorld* world, const hkpWind* wind, const hkVector4& pos )
{
	const hkReal trunkHeight = 4.0f;
	const hkReal trunkBottomRadius = 0.5f;
	const hkReal trunkTopRadius = 0.2f;
	const hkReal trunkStiffness = 0.1f;
	const hkReal segmentMass = 0.6f;
	const int numberOfSegments = 4;
	const hkReal segmentGap = 0.2f;
	const int numberOfFronds = 6;
	const hkReal frondWidth = 2.0f;
	const hkReal frondLength = 3.0f;
	const hkReal frondMass = 0.4f;
	
	// The trunk
	hkArray<hkpRigidBody*> trunk;

	const hkReal segmentHeight = (trunkHeight - ((numberOfSegments - 1) * segmentGap)) / numberOfSegments;
	const hkReal radiusIncrement = (trunkBottomRadius - trunkTopRadius) / numberOfSegments;

	for ( int i = 0; i < numberOfSegments; i++ )
	{
		hkpShape* segmentShape;
		hkpRigidBodyCinfo info;
		{
			hkVector4 bottom( 0.0f, (segmentHeight + segmentGap) * i, 0.0f );
			hkVector4 top( 0.0f, (segmentHeight + segmentGap) * i + segmentHeight, 0.0f );
			hkReal radius = trunkBottomRadius - (radiusIncrement * i);
			segmentShape = new hkpCylinderShape( bottom, top, radius, 0.03f );
				
			info.m_shape = segmentShape;
			info.m_position = pos;
			
			if (i == 0)
			{
				info.m_motionType = hkpMotion::MOTION_FIXED;
			}
			else
			{
				hkpMassProperties massProperties;
				{			
					hkpInertiaTensorComputer::computeCylinderVolumeMassProperties( bottom, top, radius, segmentMass, massProperties );
				}
				info.m_motionType = hkpMotion::MOTION_DYNAMIC;
				info.m_mass = massProperties.m_mass;
				info.m_inertiaTensor = massProperties.m_inertiaTensor;
				info.m_centerOfMass = massProperties.m_centerOfMass;
			}
		}
		
		hkpRigidBody* segment = new hkpRigidBody( info );
		segmentShape->removeReference();
		
		trunk.pushBack( segment );
		world->addEntity( segment );
		segment->removeReference();

		if (i > 0)
		{
			hkpWindAction* action = new hkpWindAction( segment, wind, 0.1f );
			world->addAction(action);
			action->removeReference();
		}
	}

	
	for ( int i = 1; i < numberOfSegments; i++ )
	{
		// We model the connection between the segments with a ragdoll constraint.
		hkpRagdollConstraintData* rdc;
		{
			hkReal planeMin = HK_REAL_PI * -0.025f;
			hkReal planeMax = HK_REAL_PI *  0.025f;
			hkReal twistMin = HK_REAL_PI * -0.025f;
			hkReal twistMax = HK_REAL_PI *  0.025f;
			hkReal coneMin  = HK_REAL_PI * -0.05f;
			hkReal coneMax  = HK_REAL_PI *  0.05f;

			rdc = new hkpRagdollConstraintData();

			rdc->setPlaneMinAngularLimit( planeMin );
			rdc->setPlaneMaxAngularLimit( planeMax );
			rdc->setTwistMinAngularLimit( twistMin );
			rdc->setTwistMaxAngularLimit( twistMax );

			hkVector4 twistAxis( 0.0f, 1.0f, 0.0f );
			hkVector4 planeAxis( 0.0f, 0.0f, 1.0f );
			hkVector4 pivot( 0.0f, (segmentHeight + segmentGap) * i, 0.0f );

			rdc->setInBodySpace( pivot, pivot, planeAxis, planeAxis, twistAxis, twistAxis );
			rdc->setAsymmetricConeAngle( coneMin, coneMax );

			//world->createAndAddConstraintInstance( trunk[i - 1], trunk[i], rdc )->removeReference();

			hkpConstraintInstance* constraint = new hkpConstraintInstance( trunk[i - 1], trunk[i], rdc );
			world->addConstraint(constraint);

			hkpPositionConstraintMotor* motor = new hkpPositionConstraintMotor( 0 );
			motor->m_tau = trunkStiffness;
			motor->m_maxForce = 1000.0f;
			motor->m_constantRecoveryVelocity = 0.1f;

			rdc->setTwistMotor( motor ); 
			rdc->setConeMotor( motor ); 
			rdc->setPlaneMotor( motor ); 
			rdc->setMotorsActive(constraint, true);

			motor->removeReference();

			constraint->removeReference();
			rdc->removeReference();
		}
	}

	// The angle that the leaves make with the ground in their half lifted position.
	hkQuaternion tilt;
	{
		hkVector4 axis( 0.0f, 0.0f, 1.0f );
		tilt.setAxisAngle( axis, HK_REAL_PI * 0.1f );
	}
	hkQuaternion tiltRot;

	// The fronds
	for ( int i = 0; i < numberOfFronds; i++ )
	{
		hkQuaternion rotation;
		{
			hkVector4 axis( 0.0f, 1.0f, 0.0f );
			rotation.setAxisAngle( axis, HK_REAL_PI * 2.0f * ( i / (hkReal) numberOfFronds ) );
			rotation.normalize();
		}

		hkpShape* frondShape;
		hkpRigidBodyCinfo info;
		{
			hkVector4 vertexA( 0.0f, 0.0f, 0.0f );
			hkVector4 vertexB( frondLength, 0.0f, frondWidth / 2.0f );
			hkVector4 vertexC( frondLength, 0.0f, - frondWidth / 2.0f );
				
			frondShape = new hkpTriangleShape( vertexA, vertexB, vertexC, 0.01f );
			info.m_shape = frondShape;
			
			hkVector4 relPos;
			relPos.setRotatedDir( rotation, hkVector4( trunkTopRadius + 0.3f, trunkHeight, 0.0f ) );

			info.m_position.setAdd4( pos, relPos );
			
			hkpMassProperties massProperties;
			{
				hkReal mass = frondMass;
				hkpInertiaTensorComputer::computeTriangleSurfaceMassProperties( vertexA, vertexB, vertexC, mass, 0.01f, massProperties );
			}

			info.m_motionType = hkpMotion::MOTION_DYNAMIC;
			info.m_mass = massProperties.m_mass;
			info.m_inertiaTensor = massProperties.m_inertiaTensor;
			info.m_centerOfMass = massProperties.m_centerOfMass;

			tiltRot.setMul( rotation, tilt );
			info.m_rotation = tiltRot;
		}
		
		hkpRigidBody* frond = new hkpRigidBody( info );
		frondShape->removeReference();
		
		world->addEntity( frond );

		hkpWindAction* action = new hkpWindAction( frond, wind, 0.1f );
		world->addAction(action);
		action->removeReference();

		
		// We model the connection between the fronds and the trunk with a ragdoll constraint.
		hkpRagdollConstraintData* rdc;
		{
			hkReal planeMin = HK_REAL_PI * -0.005f;
			hkReal planeMax = HK_REAL_PI *  0.005f;
			hkReal twistMin = HK_REAL_PI * -0.05f;
			hkReal twistMax = HK_REAL_PI *  0.05f;
			hkReal coneMin  = HK_REAL_PI * -0.2f;
			hkReal coneMax  = HK_REAL_PI *  0.2f;

			rdc = new hkpRagdollConstraintData();

			rdc->setPlaneMinAngularLimit( planeMin );
			rdc->setPlaneMaxAngularLimit( planeMax );
			rdc->setTwistMinAngularLimit( twistMin );
			rdc->setTwistMaxAngularLimit( twistMax );

			hkVector4 twistAxisFrond( 1.0f, 0.0f, 0.0f );
			hkVector4 twistAxisTrunk;
			twistAxisTrunk.setRotatedDir( tiltRot, twistAxisFrond );
			
			hkVector4 planeAxisFrond( 0.0f, 0.0f, 1.0f );
			hkVector4 planeAxisTrunk;
			planeAxisTrunk.setRotatedDir( tiltRot, planeAxisFrond );
			
			hkVector4 pivotFrond( 0.0f, 0.0f, 0.0f );
			hkVector4 pivotTrunk;
			pivotTrunk.setRotatedDir( rotation, hkVector4( trunkTopRadius + 0.3f, trunkHeight, 0.0f ) );

			rdc->setInBodySpace( pivotTrunk, pivotFrond, planeAxisTrunk, planeAxisFrond, twistAxisTrunk, twistAxisFrond );
			rdc->setAsymmetricConeAngle( coneMin, coneMax );

			world->createAndAddConstraintInstance( trunk[ numberOfSegments - 1 ], frond, rdc )->removeReference();
			rdc->removeReference();
			frond->removeReference();
		}
	}
}