// // Given a node and a link, return the two points that the link // will need in order to hook up with the node. // void NodeGeom::FindVerticesForLink(TLink *pL, bool bStart, FPoint3 &p0, FPoint3 &p1) { if (NumLinks() == 1) { p0 = m_v[0]; p1 = m_v[1]; } else if (NumLinks() == 2) { if (pL == m_connect[0]) { p0 = m_v[1]; p1 = m_v[0]; } else { p0 = m_v[0]; p1 = m_v[1]; } } else { for (int i = 0; i < NumLinks(); i++) { if (m_connect[i] == pL) { p0 = m_v[i*2]; p1 = m_v[i*2+1]; return; } } // Should not get here! This node does not reference the link passed ; // (put a breakpoint here) } }
//------------------------------------------------------------------------------ // Purpose : Used to get links in different order each time. // Call ShuffleLinks() first // Input : // Output : //------------------------------------------------------------------------------ CAI_Link* CAI_Node::GetShuffeledLink(int nNum) { int nLinkID = m_iFirstShuffledLink + nNum; if (nLinkID >= NumLinks()) { nLinkID -= NumLinks(); } return m_Links[nLinkID]; }
//------------------------------------------------------------------------------ // Purpose : Called before GetShuffeledLinks to change the order in which // links are returned // Input : // Output : //------------------------------------------------------------------------------ void CAI_Node::ShuffleLinks(void) { m_iFirstShuffledLink++; if (m_iFirstShuffledLink >= NumLinks()) { m_iFirstShuffledLink = 0; } }
vtMesh *NodeGeom::GenerateGeometry(const VirtualTexture &vt) { if (NumLinks() < 3) return NULL; FPoint2 uv; const FPoint3 upvector(0.0f, 1.0f, 0.0f); vtMesh *pMesh = new vtMesh(osg::PrimitiveSet::TRIANGLE_FAN, VT_TexCoords | VT_Normals, NumLinks()*2 + 1); int verts = 0; vt.Adapt(FPoint2(0.5f, 0.5f), uv); pMesh->SetVtxPUV(verts, m_p3, uv.x, uv.y); pMesh->SetVtxNormal(verts, upvector); verts++; for (int j = 0; j < NumLinks(); j++) { vt.Adapt(FPoint2(0.0f, 1.0f), uv); pMesh->SetVtxPUV(verts, m_v[j*2+1], uv.x, uv.y); vt.Adapt(FPoint2(1.0f, 1.0f), uv); pMesh->SetVtxPUV(verts+1, m_v[j*2], uv.x, uv.y); pMesh->SetVtxNormal(verts, upvector); pMesh->SetVtxNormal(verts+1, upvector); verts += 2; } // create triangles verts = 0; int idx[100]; idx[verts++] = 0; for (int j = 0; j < NumLinks(); j++) { idx[verts++] = (j*2+1); idx[verts++] = (j*2+2); } idx[verts++] = 1; // close it pMesh->AddFan(idx, verts); return pMesh; }
//----------------------------------------------------------------------------- // Purpose: Returns link if node has a link to node of the given nNodeID. // Otherwise returns NULL // Input : // Output : //----------------------------------------------------------------------------- CAI_Link* CAI_Node::HasLink(int nNodeID) { for (int link=0;link<NumLinks();link++) { // If node has link to myself, than add link to my list of links if (m_Links[link]->DestNodeID(m_iID) == nNodeID) { return m_Links[link]; } } return NULL; }
//----------------------------------------------------------------------------- // Purpose: Add a link to this node // Input : // Output : //----------------------------------------------------------------------------- void CAI_Node::AddLink(CAI_Link *newLink) { if ( NumLinks() == AI_MAX_NODE_LINKS ) { DevMsg( "Node %d has too many links\n", m_iID ); return; } #ifdef _DEBUG for (int link=0;link<NumLinks();link++) { if (m_Links[link] == newLink) { AssertMsgOnce( 0, "Link added to node multiple times!" ); return; } } AssertMsg( newLink->m_iDestID == m_iID || newLink->m_iSrcID == m_iID, "Added link to node that doesn't reference the node" ); #endif m_Links.AddToTail( newLink ); }
CAI_Link *CAI_Node::GetLink( int destNodeId ) { // Now make sure this node still has a link to the destID for ( int link = 0; link < NumLinks(); link++ ) { // If we find the link the dynamic link is valid if ( m_Links[link]->DestNodeID(m_iID) == destNodeId ) { return m_Links[link]; } } return NULL; }
void NodeGeom::ComputeIntersectionVertices() { FPoint3 v, v_next, v_prev; FPoint3 pn0, pn1; float w; // link width SortLinksByAngle(); // how many links meet here? if (NumLinks() == 0) { ; // bogus case (put a breakpoint here) } else if (NumLinks() == 1) { // dead end: only need 2 vertices for this node m_iVerts = 2; m_v.SetSize(2); // get info about the link LinkGeom *r = GetLink(0); w = (r->m_fLeftWidth + r->m_fRightWidth) / 2; pn1 = GetAdjacentRoadpoint(0); v = CreateRoadVector(m_p3, pn1, w); m_v[0].Set(m_p3.x + v.z, m_p3.y, m_p3.z - v.x); m_v[1].Set(m_p3.x - v.z, m_p3.y, m_p3.z + v.x); one++; } else if (NumLinks() == 2) { // only need 2 vertices for this node; no intersection m_iVerts = 2; m_v.SetSize(2); // get info about the links w = (GetLink(0)->m_fLeftWidth + GetLink(1)->m_fRightWidth) / 2.0f; pn0 = GetAdjacentRoadpoint(0); pn1 = GetAdjacentRoadpoint(1); v = CreateRoadVector(pn0, pn1, w); m_v[0].Set(m_p3.x + v.z, m_p3.y, m_p3.z - v.x); m_v[1].Set(m_p3.x - v.z, m_p3.y, m_p3.z + v.x); two++; } else { // intersection: need 2 vertices for each link meeting here m_iVerts = 2 * NumLinks(); m_v.SetSize(m_iVerts); // For each pairs of links, find the places where the link edges // intersect as they approach this node. // The following is an array of float triples, used as follows: // x = minimum distance which avoids intersection with next link // y = minimum distance which avoids intersection with previous link // z = greater of x or y. FLine3 distance_to_intersection(NumLinks()); // Go through the links once, collecting the minimum distances for (int i = 0; i < NumLinks(); i++) { // indices of the next and previous links const int i_next = (i == NumLinks()-1) ? 0 : i+1; const TLink *pL = GetLink(i); const TLink *pL_next = GetLink(i_next); const float width1 = pL->m_fLeftWidth; const float width2 = pL_next->m_fRightWidth; const FPoint3 linkv1 = GetUnitLinkVector(i); const FPoint3 linkv2 = GetUnitLinkVector(i_next); // Use 2D vectors for the following math // Compute two vectors: left road edge of this link, right road // edge of the following link, compute where they intersect, in // terms of the ua and ub factors, which are the distance along // each input vector to the intersection point. const FPoint2 v1(linkv1.x, linkv1.z); const FPoint2 v2(linkv2.x, linkv2.z); const float denom = v2.y*v1.x - v2.x*v1.y; if (fabs(denom) < 0.01) { // too parallel, pick a safety value distance_to_intersection[i].x = 1.0f; distance_to_intersection[i_next].y = 1.0f; } else { FPoint2 norm1(linkv1.z, -linkv1.x); FPoint2 norm2(linkv2.z, -linkv2.x); norm1.Normalize(); norm2.Normalize(); const FPoint2 center(m_p3.x, m_p3.z); const FPoint2 p1 = center + norm1 * width1; const FPoint2 p2 = center - norm2 * width2; const float ua = (v2.x*(p1.y - p2.y) - v2.y*(p1.x - p2.x)) / denom; const float ub = (v1.x*(p1.y - p2.y) - v1.y*(p1.x - p2.x)) / denom; distance_to_intersection[i].x = ua; distance_to_intersection[i_next].y = ub; } } // Go through the links again, picking the largest minimum for (int i = 0; i < NumLinks(); i++) { distance_to_intersection[i].z = std::max(distance_to_intersection[i].x, distance_to_intersection[i].y); } // Now we can finally set the two points where this link meets the // intersection without overlapping with the other links for (int i = 0; i < NumLinks(); i++) { const TLink *pL = GetLink(i); v = GetUnitLinkVector(i); FPoint3 norm(v.z, 0, -v.x); norm.Normalize(); norm *= pL->m_fLeftWidth; const float dist = distance_to_intersection[i].z; m_v[i * 2 + 0] = m_p3 + norm + (v * dist); m_v[i * 2 + 1] = m_p3 - norm + (v * dist); } many++; } }
vtTransform *vtRoadMap3d::GenerateGeometry(bool do_texture, bool bHwy, bool bPaved, bool bDirt, bool progress_callback(int)) { VTLOG(" vtRoadMap3d::GenerateGeometry\n"); VTLOG(" Nodes %d, Links %d\n", NumNodes(), NumLinks()); _CreateMaterials(do_texture); m_pGroup = new vtGroup; m_pGroup->setName("Roads"); m_pTransform = new vtTransform; m_pTransform->addChild(m_pGroup); m_pTransform->SetTrans(FPoint3(0, m_fGroundOffset, 0)); // We wrap the roads' geometry with an array of simple LOD nodes int a, b; for (a = 0; a < ROAD_CLUSTER; a++) for (b = 0; b < ROAD_CLUSTER; b++) { m_pRoads[a][b] = NULL; } _GatherExtents(); #if 0 vtGeode *pGeode = CreateLineGridGeom(m_pMats, 0, m_extents.min, m_extents.max, ROAD_CLUSTER); m_pGroup->addChild(pGeode); #endif vtMesh *pMesh; int count = 0, total = NumLinks() + NumNodes(); for (LinkGeom *pL = GetFirstLink(); pL; pL = pL->GetNext()) { // Decide whether to construct this link bool include = false; if (bHwy && bPaved && bDirt) include = true; else { bool bIsDirt = (pL->m_Surface == SURFT_2TRACK || pL->m_Surface == SURFT_DIRT); if (bHwy && pL->m_iHwy != -1) include = true; if (bPaved && !bIsDirt) include = true; if (bDirt && bIsDirt) include = true; } if (include) pL->GenerateGeometry(this); count++; if (progress_callback != NULL) progress_callback(count * 100 / total); } count = 0; for (NodeGeom *pN = GetFirstNode(); pN; pN = pN->GetNext()) { // What material to use? We used to simply use "pavement", but that is // bad when they are e.g. trail or stone. Now, we will try to guess // what to use by looking at the links here. int node_vti = VTI_PAVEMENT; for (int i = 0; i < pN->NumLinks(); i++) { LinkGeom *pL = pN->GetLink(i); switch (pL->m_vti) { case VTI_RAIL: case VTI_4WD: case VTI_TRAIL: case VTI_GRAVEL: case VTI_STONE: node_vti = pL->m_vti; } } VirtualTexture &vt = m_vt[node_vti]; pMesh = pN->GenerateGeometry(vt); if (pMesh) AddMeshToGrid(pMesh, vt.m_idx); count++; if (progress_callback != NULL) progress_callback(count * 100 / total); } // return the top group, ready to be added to scene graph return m_pTransform; }