// Given a scene node for a terrain, find the manual object on that scene node and // update the manual object with the heightmap passed. If there is no manual object on // the scene node, remove all it's attachments and add the manual object. // The heightmap is passed in a 1D array ordered by width rows (for(width) {for(length) {hm[w,l]}}) // This must be called between frames since it touches the scene graph // BETWEEN FRAME OPERATION void Region::UpdateTerrain(const int hmWidth, const int hmLength, const float* hm) { Ogre::SceneNode* node = this->TerrainSceneNode; LG::Log("Region::UpdateTerrain: updating terrain for region %s", this->Name.c_str()); if (node == NULL) { LG::Log("Region::UpdateTerrain: terrain scene node doesn't exist. Not updating terrain."); return; } // Find the movable object attached to the scene node. If not found remove all. if (node->numAttachedObjects() > 0) { Ogre::MovableObject* attached = node->getAttachedObject(0); if (attached->getMovableType() != "ManualObject") { // don't know why this would ever happen but clean out the odd stuff LG::Log("Found extra stuff on terrain scene node"); node->detachAllObjects(); } } // if there is not a manual object on the node, create a new one if (node->numAttachedObjects() == 0) { LG::Log("Region::UpdateTerrain: creating terrain ManualObject for region %s", this->Name.c_str()); // if no attached objects, we add our dynamic ManualObject Ogre::ManualObject* mob = LG::RendererOgre::Instance()->m_sceneMgr->createManualObject("ManualObject/" + node->getName()); mob->addQueryFlags(Ogre::SceneManager::WORLD_GEOMETRY_TYPE_MASK); mob->setDynamic(true); mob->setCastShadows(true); mob->setVisible(true); node->attachObject(mob); // m_visCalc->RecalculateVisibility(); } Ogre::ManualObject* mo = (Ogre::ManualObject*)node->getAttachedObject(0); // stuff our heightmap information into the dynamic manual object mo->estimateVertexCount(hmWidth * hmLength); mo->estimateIndexCount(hmWidth * hmLength * 6); if (mo->getNumSections() == 0) { // if first time mo->begin(LG::GetParameter("Renderer.Ogre.DefaultTerrainMaterial")); } else { mo->beginUpdate(0); // we've been here before } int loc = 0; for (int xx = 0; xx < hmWidth; xx++) { for (int yy = 0; yy < hmLength; yy++) { mo->position((Ogre::Real)xx, (Ogre::Real)yy, hm[loc++]); mo->textureCoord((float)xx / (float)hmWidth, (float)yy / (float)hmLength); mo->normal(0.0, 1.0, 0.0); // always up (for the moment) } } for (int px = 0; px < hmLength-1; px++) { for (int py = 0; py < hmWidth-1; py++) { mo->quad(px + py * hmWidth, px + (py + 1) * hmWidth, (px + 1) + (py + 1) * hmWidth, (px + 1) + py * hmWidth ); } } mo->end(); return; }
/* Generates the decal and returns a Decal object. The caller is responsible for NULL-checking the manual object. This gigantic function should probably be chopped into smaller bite-sized pieces, but I'm just doo darn lazy. @param mesh The mesh to project the decal onto. @param pos The position of the decal @param width The width of the decal @param height The height of the decal Note: The aspect ratio defined by width/height should match the texture, otherwise it will appear stretched. @param materialName The name of the material to use for the decal @param flipTexture Will randomly flip the texture to introduce variety (useful for blood splatter, explosion decals, etc.) @param decalObject If NULL, this function will automatically create a new manual object (default). Otherwise, it will re-use the one passed in. For dynamic decals (generating one every frame), it is much more efficient to reuse the same manual object, as long as the material doesn't change. */ Decal DecalGenerator::createDecal( TriangleMesh* mesh, const Ogre::Vector3& pos, float width, float height, const Ogre::String& materialName, bool flipTexture, Ogre::ManualObject* decalObject ) { //Variable de error para dar de salida en el object un NULL y que no pete bool bError = false; /// Clear out any old left-over stuff from the fridge. triangles.clear(); uniquePoints.clear(); finalPolys.clear(); polygon_points.clear(); float depth = max(width, height); /// Define our AABB Ogre::Vector3 aabbMin = pos + Ogre::Vector3( -depth, -depth, -depth ); Ogre::Vector3 aabbMax = pos + Ogre::Vector3( depth, depth, depth); /// We're gonna need triangles. Lot's of triangles. mesh->findTrianglesInAABB( aabbMin, aabbMax, triangles ); if (triangles.empty()) { /// No triangles were found, return an empty Decal /// Note that the caller is responsible for verifying the returned object return Decal(); } std::vector< Triangle >::iterator iter; Ogre::Vector3 averageN(0, 0, 0); /// Calculate the average normal of all the triangles gathered from our AABB for (iter = triangles.begin(); iter != triangles.end(); ++iter) { averageN += iter->normal; } /// This average normal length is too close to zero, which is a bad omen. Get out while we still can! if (averageN.length() < VEC_EPSILON) { return Decal(); } averageN.normalise(); Ogre::Vector3 right, up; /// Calculate a coordinate space from the the average of all the triangle normals /// We're creating the projection box that will be used to clip the triangles if (averageN == Ogre::Vector3(0, 1, 0)) { right = Ogre::Vector3(1, 0, 0); } else if (averageN == Ogre::Vector3(0, -1, 0)) { right = Ogre::Vector3(-1, 0, 0); } else { right = (-averageN).crossProduct( Ogre::Vector3(0, 1, 0) ); } right.normalise(); up = right.crossProduct( -averageN ); up.normalise(); /// Now that we have our coordinate space, let's define some planes. No silly, not the ones that fly in the sky! // These are the clipping planes! Be careful, because you might get cut. const int NUM_EDGE_PLANES = 6; Ogre::Vector4 edgePlanes[NUM_EDGE_PLANES]; Ogre::Vector3 planeR[6]; Ogre::Vector3 planeN[6]; planeN[0] = averageN; planeN[1] = -averageN; planeN[2] = right; planeN[3] = -right; planeN[4] = up; planeN[5] = -up; /// These are to ensure that certain points are not "out of bounds" double distanceLimit = sqrt( (depth * depth) + (depth * depth) ) * 1.25; double edgeLimitX = sqrt( (width * width) + (width * width) ) * 1.05; double edgeLimitY = sqrt( (height * height) + (height * height) ) * 1.05; /// A point for each plane planeR[0] = pos + (planeN[0] * depth); planeR[1] = pos + (planeN[1] * depth); planeR[2] = pos + (planeN[2] * width); planeR[3] = pos + (planeN[3] * width); planeR[4] = pos + (planeN[4] * height); planeR[5] = pos + (planeN[5] * height); /// Set up each edge plane as a four dimensional vector defined in interstellar space. Carl Sagan would be all over this. for (int i = 0; i < NUM_EDGE_PLANES; ++i) { edgePlanes[i] = Ogre::Vector4(planeN[i].x, planeN[i].y, planeN[i].z, planeR[i].dotProduct( planeN[i] ) ); } Ogre::Vector3 averageNormal(0, 0, 0); double totalPoints = 0; Ogre::Vector3 averagePoint; /// Loop through each triangle to find the meaning of life for (iter = triangles.begin(); iter != triangles.end(); ++iter) { polygon_points.clear(); polygon_points.push_back( iter->v[0] ); polygon_points.push_back( iter->v[1] ); polygon_points.push_back( iter->v[2] ); Ogre::Vector3 n = iter->normal; Ogre::Vector3 polygonNormal = n; /// Clip this triangle against each edge for (int edge = 0; edge < NUM_EDGE_PLANES; ++edge) { /// Clip the polygon against the edge plane. /// Why is this function returning duplicate points? //std::cout << "Cuando peta, peta aqui: " << polygon_points.size() << std::endl; if (polygon_points.size() > 0) { int clipped_count = plane_clip_polygon( edgePlanes[ edge ], &(polygon_points[0]), polygon_points.size(), clippedPoints ); polygon_points.clear(); int index = 0; for (int i = 0; i < clipped_count; ++i) { Ogre::Vector3 p = clippedPoints[ i ]; /// Do not use any duplicate points returned by plane_clip_polygon() if (!isDuplicate( polygon_points, p )) { polygon_points.push_back( p ); /// If this was the last edge plane we checked against, then we have all of our clipped points for this triangle if (edge == NUM_EDGE_PLANES - 1) { /// Check if this point was clipped by comparing it to each original vertex of the triangle. /// If this point is an original triangle vertex, then it wasn't clipped. if (( p - iter->v[0] ).length() < VEC_EPSILON || (p - iter->v[1]).length() < VEC_EPSILON || (p - iter->v[2]).length() < VEC_EPSILON) { pointWasClipped[ index ] = false; } else { pointWasClipped[ index ] = true; } } ++index; } } }//if points else { bError = true; std::cout << "Hay ERROR triangulando para el decal" << std::endl; break; } }// for edge /// If we ended up with less points than what we started out with, then we're in big horse doo-doo if (polygon_points.size() < 3) continue; finalPolys.push_back( DecalPolygon( polygonNormal ) ); int size = polygon_points.size(); double area = 1; /// Find the area of our freshly clipped polygon; used for generating a "better" vertex normal if (polygon_points.size() >= 3) { polygon_points.push_back( polygon_points[0] ); polygon_points.push_back( polygon_points[1] ); area = area3D_Polygon( size, &(polygon_points[0]), n ); } /// Loop through each point in our clipped polygon, and find the unique ones. for (int i = 0; i < polygon_points.size() - 2; ++i) { Ogre::Vector3 p = polygon_points[i]; averagePoint += p; ++totalPoints; /// Make sure this point is not "out of bounds" if ((p - pos).length() < distanceLimit) { /// Check to see if this point is a duplicate /// Assuming this point is not a duplicate, set uniqueIndex to be the point added next int uniqueIndex = uniquePoints.size(); /// If a duplicate is found, uniqueIndex will be set to that point if ( !isDuplicate( uniquePoints, p, uniqueIndex ) ) { uniquePoints.push_back( UniquePoint( p, pointWasClipped[i] ) ); } /// Make sure we're still OK assert( uniqueIndex >= 0 && uniqueIndex < uniquePoints.size() ); // Update the normal for this point (we'll normalize it later). NormaLIZE. uniquePoints[ uniqueIndex ].normal += (polygonNormal * area); finalPolys.back().points.push_back( uniqueIndex ); } } averageNormal += (n * area); }//for loop triangles if (!bError) { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// /// We intterupt this program to give you a brief word from our sponsers... /// /// /// /// Congratulations. You've made it this far. Half the battle is over. At this point, we have all of our final clipped points. /// /// Now we need to project those points to 2D so we can calculate the UV coordinates. Don't worry, there's more fudge on the way. /// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// averageNormal.normalise(); averagePoint /= totalPoints; Ogre::Vector3 projectionPlaneNormal = -averageN; Ogre::Vector3 projectionPlaneRight = right; projectionPlaneRight.normalise(); Ogre::Vector3 projectionPlaneUp = up; projectionPlaneUp.normalise(); Ogre::Quaternion planeOrien( projectionPlaneRight, projectionPlaneUp, -projectionPlaneNormal ); Ogre::Quaternion finalOrien( Ogre::Vector3(1, 0, 0), Ogre::Vector3(0, 1, 0), Ogre::Vector3(0, 0, -1) ); planeOrien.normalise(); finalOrien.normalise(); /// This is the quaternion we'll use to tranform each point so we can project them onto the XY plane /// This quaternion takes the projection plane's coordinate system (which is indentical our clipping box) /// and rotates it so that it is parrallel with the XY plane, at which point we nuke the Z axis on every point to /// project them into 2D. Ogre::Quaternion finalQuat = planeOrien.UnitInverse() * finalOrien; /// My mom always said, you can never normalize too much. Except when you spell it wrong. finalQuat.normalise(); std::vector< DecalPolygon >::iterator pIter; double leftMost, rightMost, topMost, bottomMost; bool initCorners = false; Ogre::Vector3 center = finalQuat * pos; Ogre::Vector2 centerPoint( center.x, center.y ); std::vector< UniquePoint >::iterator pointIter; /// Loop through all of our points and project them into 2D. for (pointIter = uniquePoints.begin(); pointIter != uniquePoints.end(); ++pointIter) { /// Rotate each point so that we can project them onto the XY plane /// finalQuat transforms the projection plane so that it's parallel with the XY plane Ogre::Vector3 v = finalQuat * pointIter->p; /// Project/flatten the point by eliminating the Z coordinate Ogre::Vector2 projectedPoint(v.x, v.y); pointIter->uvCoord = projectedPoint; /// Find the left, right, top, and bottom edge if (pointIter->isEdge) { Ogre::Vector2 diff = (projectedPoint - centerPoint); /// More fudge-checking to make sure nothing is out of bounds if ( fabs( diff.x ) < edgeLimitX && fabs( diff.y ) < edgeLimitY ) { if (!initCorners) { leftMost = rightMost = projectedPoint.x; topMost = bottomMost = projectedPoint.y; initCorners = true; } if (projectedPoint.x < leftMost) leftMost = projectedPoint.x; if (projectedPoint.x > rightMost) rightMost = projectedPoint.x; if (projectedPoint.y > topMost) topMost = projectedPoint.y; if (projectedPoint.y < bottomMost) bottomMost = projectedPoint.y; } } } /// The hardest part is over. Now we get to fudge the UV coords, because you can never have too much fudge. std::vector< UniquePoint >::iterator topRight, bottomRight, topLeft, bottomLeft; Ogre::Vector2 cornerTopLeft( leftMost, topMost ); Ogre::Vector2 cornerTopRight( rightMost, topMost ); Ogre::Vector2 cornerBottomLeft( leftMost, bottomMost ); Ogre::Vector2 cornerBottomRight( rightMost, bottomMost ); bool initValues = false; double minDistanceTopLeft, minDistanceTopRight, minDistanceBottomLeft, minDistanceBottomRight; std::vector< UniquePoint >::iterator projectedPointIter; /// Loop throgh all of our (2D) points and figure out which points are nearest to each of the four corners. /// Why do we do this? Because I said so. Do not question my infinite wisdom. for (projectedPointIter = uniquePoints.begin(); projectedPointIter != uniquePoints.end(); ++projectedPointIter) { Ogre::Vector2 p = projectedPointIter->uvCoord; double distanceTopLeft = (p - cornerTopLeft).length(); double distanceTopRight = (p - cornerTopRight).length(); double distanceBottomLeft = (p - cornerBottomLeft).length(); double distanceBottomRight = (p - cornerBottomRight).length(); if (!initValues) { initValues = true; topRight = projectedPointIter; topLeft = projectedPointIter; bottomRight = projectedPointIter; bottomLeft = projectedPointIter; minDistanceTopLeft = distanceTopLeft; minDistanceTopRight = distanceTopRight; minDistanceBottomLeft = distanceBottomLeft; minDistanceBottomRight = distanceBottomRight; } else { if (distanceTopLeft < minDistanceTopLeft) { minDistanceTopLeft = distanceTopLeft; topLeft = projectedPointIter; } if (distanceTopRight < minDistanceTopRight) { minDistanceTopRight = distanceTopRight; topRight = projectedPointIter; } if (distanceBottomLeft < minDistanceBottomLeft) { minDistanceBottomLeft = distanceBottomLeft; bottomLeft = projectedPointIter; } if (distanceBottomRight < minDistanceBottomRight) { minDistanceBottomRight = distanceBottomRight; bottomRight = projectedPointIter; } } } /// Here we calculate (via fudge factor) the UV edges which are used to determine the UV coords. /// This is the fudge motherload. /// I could try to explain why we're doing this, but it would just sound like I have no idea what I'm talking about. /// Which is probably true. rightMost = average( average( topRight->uvCoord.x, bottomRight->uvCoord.x ), rightMost ); leftMost = average( average( topLeft->uvCoord.x, bottomLeft->uvCoord.x ), leftMost ); topMost = average( average( topLeft->uvCoord.y, topRight->uvCoord.y ), topMost ); bottomMost = average( average(bottomRight->uvCoord.y, bottomLeft->uvCoord.y), bottomMost ); /// Calcuate the width and height used to calculate UV coords. width = rightMost - leftMost; height = topMost - bottomMost; int randomFlipType = 0; if (flipTexture) randomFlipType = rand()%8; /// Ok, now we get to the final UV coords. For real this time, no kidding. for (projectedPointIter = uniquePoints.begin(); projectedPointIter != uniquePoints.end(); ++projectedPointIter) { Ogre::Vector2 p = projectedPointIter->uvCoord; double uvMin = 0; double uvMax = 1; /// Ta da! double u = (p.x - leftMost) / width; double v = (p.y - bottomMost) / height; /// Oops, we wouldn't want any rebel coordiantes wreaking havoc. if (u < uvMin) u = uvMin; else if (u > uvMax) u = uvMax; if ( v < uvMin ) v = uvMin; else if (v > uvMax) v = uvMax; projectedPointIter->uvCoord.x = u; projectedPointIter->uvCoord.y = (1 - v); /// Randomly flip UV coords between 8 different ways /// This might be desirable for things like blood splatter, explosions, etc to simply introduce variety if (flipTexture && randomFlipType > 0) { switch (randomFlipType) { case 1: { projectedPointIter->uvCoord.x = (1 - u); projectedPointIter->uvCoord.y = (1 - v); } case 2: { projectedPointIter->uvCoord.x = (1 - u); projectedPointIter->uvCoord.y = v; } case 3: { projectedPointIter->uvCoord.x = u; projectedPointIter->uvCoord.y = v; } case 4: { projectedPointIter->uvCoord.y = (1 - u); projectedPointIter->uvCoord.x = (1 - v); } case 5: { projectedPointIter->uvCoord.y = (1 - u); projectedPointIter->uvCoord.x = v; } case 6: { projectedPointIter->uvCoord.y = u; projectedPointIter->uvCoord.x = v; } case 7: { projectedPointIter->uvCoord.y = u; projectedPointIter->uvCoord.x = (1 - v); } } } } /// All of the final UV coords have been generated. Now it's rendering time. /// What are you waiting for? Let's make that manual object that you've been dreaming about. std::vector<UniquePoint>::iterator uniqueIter; /// Debug drawing stuff if (DEBUG_ENABLED) { Ogre::ManualObject* moDebug = sceneMgr->createManualObject(); moDebug->begin("debug_draw", Ogre::RenderOperation::OT_TRIANGLE_LIST); int indexOffset = 0; for (uniqueIter = uniquePoints.begin(); uniqueIter != uniquePoints.end(); ++uniqueIter) { createCubeMesh( sceneMgr, uniqueIter->p, 0.25, Ogre::ColourValue::Red, moDebug, indexOffset); indexOffset += 8; } moDebug->end(); if (!mDebugVisible) moDebug->setVisible( false ); mDebugNode->attachObject( moDebug ); } /// "lines" is used for debug drawing triangles Ogre::ManualObject* lines = 0; if (DEBUG_ENABLED) { lines = sceneMgr->createManualObject(); lines->begin("debug_draw", Ogre::RenderOperation::OT_LINE_LIST); } /// Create a new manual object if this one doesn't exist if (!decalObject) { decalObject = sceneMgr->createManualObject(); } else { /// Make sure the decal object can be dynmically updated if (!decalObject->getDynamic()) decalObject->setDynamic( true ); } Ogre::String material = materialName; if (decalObject->getDynamic() && decalObject->getNumSections() > 0) { /// Update the existng decal instead of starting a new one decalObject->beginUpdate( 0 ); } else { /// Start a new decal decalObject->begin(material, Ogre::RenderOperation::OT_TRIANGLE_LIST); } /// If we're sharing vertices, then simply set up all of the vertex info ahead of time if ( SHARE_VERTEX_NORMALS ) { for (uniqueIter = uniquePoints.begin(); uniqueIter != uniquePoints.end(); ++uniqueIter) { uniqueIter->normal.normalise(); decalObject->position( uniqueIter->p ); decalObject->textureCoord( uniqueIter->uvCoord ); decalObject->normal( uniqueIter->normal ); } } int p1, p2, p3; p1 = p2 = p3 = 0; // Each of these finalPolys is a just list of indicies into the uniquePoints vector for (pIter = finalPolys.begin(); pIter != finalPolys.end(); ++pIter) { std::vector< int >::iterator iter; Ogre::Vector3 norm = pIter->norm; if (pIter->points.size() >= 3) { p2 = p1 + 1; p3 = p2 + 1; int t = 0; for (iter = pIter->points.begin(); iter != pIter->points.end(); ++iter) { if (!SHARE_VERTEX_NORMALS) { decalObject->position( uniquePoints[ *iter ].p ); decalObject->textureCoord( uniquePoints[ *iter ].uvCoord ); decalObject->normal( norm ); } if (t >= 3) { ++p2; ++p3; AddTriangle( decalObject, pIter->points, p1, p2, p3, lines ); } ++t; if (t == 3) { AddTriangle( decalObject, pIter->points, p1, p2, p3, lines ); } } p1 = p3 + 1; } } decalObject->end(); /// Finish debug drawing stuff if (DEBUG_ENABLED) { lines->end(); if (!mDebugVisible) lines->setVisible( false ); mDebugNode->attachObject( lines ); } } /// And were done. Phew. Decal decal; decal.object = decalObject; if (bError) decal.object = NULL; return decal; }