SAINode* ESceneAIMapTool::GetNode(Fvector vAt, bool bIC) // return node's index { // *** Test if we can travel this path SnapXZ (vAt,m_Params.fPatchSize); // *** set up xr_new<node SAINode* N = xr_new<SAINode>(); SAINode* R = 0; if (CreateNode(vAt,*N,bIC)){ R = FindNode(N->Pos); xr_delete (N); } xr_delete(N); return R; }
SAINode* ESceneAIMapTool::FindNeighbor(SAINode* N, int side, bool bIgnoreConstraints) { Fvector Pos; Pos.set (N->Pos); SnapXZ (Pos,m_Params.fPatchSize); switch (side){ case 0: Pos.x -= m_Params.fPatchSize; break; case 1: Pos.z += m_Params.fPatchSize; break; case 2: Pos.x += m_Params.fPatchSize; break; case 3: Pos.z -= m_Params.fPatchSize; break; } AINodeVec* nodes = HashMap(Pos); SAINode* R = 0; if (nodes) if (bIgnoreConstraints){ float dy= flt_max; for (AINodeIt I=nodes->begin(); I!=nodes->end(); I++) if (fsimilar((*I)->Pos.x,Pos.x,EPS_L)&&fsimilar((*I)->Pos.z,Pos.z,EPS_L)){ float _dy = _abs((*I)->Pos.y-Pos.y); if (_dy<dy){dy=_dy; R=*I;} } }else{ SAINode* R_up = 0; SAINode* R_down = 0; float dy_up = flt_max; float dy_down = flt_max; for (AINodeIt I=nodes->begin(); I!=nodes->end(); I++){ if (fsimilar((*I)->Pos.x,Pos.x,EPS_L)&&fsimilar((*I)->Pos.z,Pos.z,EPS_L)){ float _dy = (*I)->Pos.y-Pos.y; float _ady= _abs(_dy); if (_dy>=0.f){ if ((_ady<m_Params.fCanUP)&&(_ady<dy_up)) {dy_up=_ady; R_up=*I;} }else{ if ((_ady<m_Params.fCanDOWN)&&(_ady<dy_down)) {dy_down=_ady; R_down=*I;} } } } if (dy_down<=dy_up) R = R_down; else R = R_up; } return R; }
u32 BuildNode(Fvector& vFrom, Fvector& vAt) // return node's index { // *** Test if we can travel this path SnapXZ (vAt); if (!CanTravel(vFrom, vAt)) return InvalidNode; // *** set up xr_new<node vertex N; if (CreateNode(vAt,N)) { //*** check if similar node exists u32 old = FindNode(N.Pos); if (old==InvalidNode) { // register xr_new<node RegisterNode(N); return g_nodes.size()-1; } else { // where already was node - return it return old; } } else return InvalidNode; }
SAINode* ESceneAIMapTool::BuildNode(Fvector& vFrom, Fvector& vAt, bool bIC, bool bSuperIC) // return node's index { // *** Test if we can travel this path SnapXZ (vAt,m_Params.fPatchSize); if (!(bIC||CanTravel(vFrom, vAt))) return 0; // *** set up node SAINode N; BOOL bRes = CreateNode(vAt,N,bIC); if (!bRes&&bIC&&bSuperIC){ Fvector D = {0,1,0}; N.Plane.build(vAt,D); // build plane N.Plane.intersectRayPoint(vAt,D,N.Pos); // "project" position bRes = TRUE; } if (bRes) { //*** check if similar node exists SAINode* old = FindNode(N.Pos); if (!old){ // register xr_new<node AINodeVec* V = HashMap(N.Pos); if (V){ m_Nodes.push_back (xr_new<SAINode>(N)); V->push_back (m_Nodes.back()); return m_Nodes.back(); }else return 0; }else{ // where already was node - return it return old; } }else{ return 0; } }
void xrBuildNodes() { // begin XRC.box_options (CDB::OPT_FULL_TEST); XRC.ray_options (CDB::OPT_CULL | CDB::OPT_ONLYNEAREST); g_nodes.reserve (1024*1024); // Initialize hash hash_Initialize (); for (u32 em=0; em<Emitters.size(); em++) { // Align emitter Fvector Pos = Emitters[em]; SnapXZ (Pos); Pos.y +=1; Fvector Dir; Dir.set(0,-1,0); XRC.ray_query (&Level,Pos,Dir,3); if (XRC.r_count()==0) { Msg ("Can't align emitter"); abort (); } else { CDB::RESULT& R = *XRC.r_begin(); Pos.y = Pos.y - R.range; } // Build first node int oldcount = g_nodes.size(); int start = BuildNode (Pos,Pos); if (start==InvalidNode) continue; if (start<oldcount) continue; // Estimate nodes Fvector LevelSize; LevelBB.getsize (LevelSize); u32 estimated_nodes = iFloor(LevelSize.x/g_params.fPatchSize)*iFloor(LevelSize.z/g_params.fPatchSize); // General cycle for (u32 i=0; i<g_nodes.size(); i++) { // left if (g_nodes[i].n1==UnkonnectedNode) { Pos.set (g_nodes[i].Pos); Pos.x -= g_params.fPatchSize; int id = BuildNode(g_nodes[i].Pos,Pos); g_nodes[i].n1 = id; } // fwd if (g_nodes[i].n2==UnkonnectedNode) { Pos.set (g_nodes[i].Pos); Pos.z += g_params.fPatchSize; int id = BuildNode(g_nodes[i].Pos,Pos); g_nodes[i].n2 = id; } // right if (g_nodes[i].n3==UnkonnectedNode) { Pos.set (g_nodes[i].Pos); Pos.x += g_params.fPatchSize; int id = BuildNode(g_nodes[i].Pos,Pos); g_nodes[i].n3 = id; } // back if (g_nodes[i].n4==UnkonnectedNode) { Pos.set (g_nodes[i].Pos); Pos.z -= g_params.fPatchSize; int id = BuildNode(g_nodes[i].Pos,Pos); g_nodes[i].n4 = id; } if (i%512==0) { Status("%d / %d nodes created.",g_nodes.size()-i,g_nodes.size()); float p1 = float(i)/float(g_nodes.size()); float p2 = float(g_nodes.size())/estimated_nodes; float p = 0.1f*p1+0.9f*p2; clamp (p,0.f,1.f); Progress(p); } } } Msg("Freeing memory..."); hash_Destroy (); }
BOOL CreateNode(Fvector& vAt, vertex& N) { // *** Query and cache polygons for ray-casting Fvector PointUp; PointUp.set(vAt); PointUp.y += RCAST_Depth; SnapXZ (PointUp); Fvector PointDown; PointDown.set(vAt); PointDown.y -= RCAST_Depth; SnapXZ (PointDown); Fbox BB; BB.set (PointUp,PointUp); BB.grow(g_params.fPatchSize/2); // box 1 Fbox B2; B2.set (PointDown,PointDown); B2.grow(g_params.fPatchSize/2); // box 2 BB.merge(B2 ); BoxQuery(BB,false ); u32 dwCount = XRC.r_count(); if (dwCount==0) { // Log("chasm1"); return FALSE; // chasm? } // *** Transfer triangles and compute sector R_ASSERT(dwCount<RCAST_MaxTris); static svector<tri,RCAST_MaxTris> tris; tris.clear(); for (u32 i=0; i<dwCount; i++) { tri& D = tris.last(); CDB::RESULT &rp = XRC.r_begin()[i]; CDB::TRI& T = *(Level.get_tris()+rp.id); D.v[0].set (rp.verts[0]); D.v[1].set (rp.verts[1]); D.v[2].set (rp.verts[2]); D.sector = T.sector; D.N.mknormal(D.v[0],D.v[1],D.v[2]); if (D.N.y<=0) continue; tris.inc (); } if (tris.size()==0) { // Log("chasm2"); return FALSE; // chasm? } // *** Perform ray-casts and calculate sector WORD Sector = 0xfffe; // mark as first time static svector<Fvector,RCAST_Total> points; points.clear(); static svector<Fvector,RCAST_Total> normals; normals.clear(); Fvector P,D; D.set(0,-1,0); float coeff = 0.5f*g_params.fPatchSize/float(RCAST_Count); for (int x=-RCAST_Count; x<=RCAST_Count; x++) { P.x = vAt.x + coeff*float(x); for (int z=-RCAST_Count; z<=RCAST_Count; z++) { P.z = vAt.z + coeff*float(z); P.y = vAt.y + 10.f; float tri_min_range = flt_max; int tri_selected = -1; float range,u,v; for (i=0; i<u32(tris.size()); i++) { if (CDB::TestRayTri(P,D,tris[i].v,u,v,range,false)) { if (range<tri_min_range) { tri_min_range = range; tri_selected = i; } } } if (tri_selected>=0) { P.y -= tri_min_range; points.push_back(P); normals.push_back(tris[tri_selected].N); WORD TS = WORD(tris[tri_selected].sector); if (Sector==0xfffe) Sector = TS; else if (Sector!=TS) Sector=InvalidSector; } } } if (points.size()<3) { // Msg ("Failed to create node at [%f,%f,%f].",vAt.x,vAt.y,vAt.z); return FALSE; } if (float(points.size())/float(RCAST_Total) < 0.7f) { // Msg ("Partial chasm at [%f,%f,%f].",vAt.x,vAt.y,vAt.z); return FALSE; } // *** Calc normal Fvector vNorm; vNorm.set(0,0,0); for (u32 n=0; n<normals.size(); n++) vNorm.add(normals[n]); vNorm.div(float(normals.size())); vNorm.normalize(); /* { // second algorithm (Magic) Fvector N,O; N.set(vNorm); O.set(points[0]); Mgc::OrthogonalPlaneFit( points.size(),(Mgc::Vector3*)points.begin(), *((Mgc::Vector3*)&O), *((Mgc::Vector3*)&N) ); if (N.y<0) N.invert(); N.normalize(); vNorm.lerp(vNorm,N,.3f); vNorm.normalize(); } */ // *** Align plane Fvector vOffs; vOffs.set(0,-1000,0); Fplane PL; PL.build(vOffs,vNorm); for (u32 p=0; p<points.size(); p++) { float dist = PL.classify(points[p]); if (dist>0) { vOffs = points[p]; PL.build(vOffs,vNorm); } } // *** Create node and register it N.Sector =Sector; // sector N.Plane.build (vOffs,vNorm); // build plane D.set (0,1,0); N.Plane.intersectRayPoint(PointDown,D,N.Pos); // "project" position // *** Validate results vNorm.set(0,1,0); if (vNorm.dotproduct(N.Plane.n)<_cos(deg2rad(60.f))) return FALSE; float y_old = vAt.y; float y_new = N.Pos.y; if (y_old>y_new) { // down if (y_old-y_new > g_params.fCanDOWN ) return FALSE; } else { // up if (y_new-y_old > g_params.fCanUP ) return FALSE; } // *** Validate plane { Fvector PLP; D.set(0,-1,0); int num_successed_rays = 0; for (int x=-RCAST_Count; x<=RCAST_Count; x++) { P.x = N.Pos.x + coeff*float(x); for (int z=-RCAST_Count; z<=RCAST_Count; z++) { P.z = N.Pos.z + coeff*float(z); P.y = N.Pos.y; N.Plane.intersectRayPoint(P,D,PLP); // "project" position P.y = PLP.y+RCAST_VALID*0.01f; float tri_min_range = flt_max; int tri_selected = -1; float range,u,v; for (i=0; i<float(tris.size()); i++) { if (CDB::TestRayTri(P,D,tris[i].v,u,v,range,false)) { if (range<tri_min_range) { tri_min_range = range; tri_selected = i; } } } if (tri_selected>=0) { if (tri_min_range<RCAST_VALID) num_successed_rays++; } } } float perc = float(num_successed_rays)/float(RCAST_Total); if (perc < 0.5f) { // Msg ("Floating node."); return FALSE; } } // *** Mask check // ??? return TRUE; }
int ESceneAIMapTool::BuildNodes(const Fvector& pos, int sz, bool bIC) { // Align emitter Fvector Pos = pos; SnapXZ (Pos,m_Params.fPatchSize); Pos.y += 1; Fvector Dir; Dir.set(0,-1,0); int cnt = 0; if (m_CFModel) cnt=Scene->RayQuery(PQ,Pos,Dir,3,CDB::OPT_ONLYNEAREST|CDB::OPT_CULL,m_CFModel); else cnt=Scene->RayQuery(PQ,Pos,Dir,3,CDB::OPT_ONLYNEAREST|CDB::OPT_CULL,GetSnapList()); if (0==cnt) { ELog.Msg (mtInformation,"Can't align position."); return 0; } else { Pos.y = Pos.y - PQ.r_begin()->range; } // Build first node int oldcount = m_Nodes.size(); SAINode* start = BuildNode(Pos,Pos,bIC); if (!start) return 0; // Estimate nodes float estimated_nodes = (2*sz-1)*(2*sz-1); SPBItem* pb = 0; if (estimated_nodes>1024) pb = UI->ProgressStart(1, "Building nodes..."); float radius = sz*m_Params.fPatchSize-EPS_L; // General cycle for (int k=0; k<(int)m_Nodes.size(); k++){ SAINode* N = m_Nodes[k]; // left if (0==N->n1){ Pos.set (N->Pos); Pos.x -= m_Params.fPatchSize; if (Pos.distance_to(start->Pos)<=radius) N->n1 = BuildNode(N->Pos,Pos,bIC); } // fwd if (0==N->n2){ Pos.set (N->Pos); Pos.z += m_Params.fPatchSize; if (Pos.distance_to(start->Pos)<=radius) N->n2 = BuildNode(N->Pos,Pos,bIC); } // right if (0==N->n3){ Pos.set (N->Pos); Pos.x += m_Params.fPatchSize; if (Pos.distance_to(start->Pos)<=radius) N->n3 = BuildNode(N->Pos,Pos,bIC); } // back if (0==N->n4){ Pos.set (N->Pos); Pos.z -= m_Params.fPatchSize; if (Pos.distance_to(start->Pos)<=radius) N->n4 = BuildNode(N->Pos,Pos,bIC); } if (estimated_nodes>1024){ if (k%128==0) { float p1 = float(k)/float(m_Nodes.size()); float p2 = float(m_Nodes.size())/estimated_nodes; float p = 0.1f*p1+0.9f*p2; clamp (p,0.f,1.f); pb->Update(p); // check need abort && redraw if (UI->NeedAbort()) break; } } } if (estimated_nodes>1024) UI->ProgressEnd(pb); return oldcount-m_Nodes.size(); }
BOOL ESceneAIMapTool::CreateNode(Fvector& vAt, SAINode& N, bool bIC) { // *** Query and cache polygons for ray-casting Fvector PointUp; PointUp.set(vAt); PointUp.y += RCAST_Depth; SnapXZ (PointUp,m_Params.fPatchSize); Fvector PointDown; PointDown.set(vAt); PointDown.y -= RCAST_Depth; SnapXZ (PointDown,m_Params.fPatchSize); Fbox BB; BB.set (PointUp,PointUp); BB.grow(m_Params.fPatchSize/2); // box 1 Fbox B2; B2.set (PointDown,PointDown); B2.grow(m_Params.fPatchSize/2); // box 2 BB.merge (B2); if (m_CFModel) { /* for(u32 i=0; i<m_CFModel->get_tris_count(); ++i) { CDB::TRI* tri = (m_CFModel->get_tris()+i); if(tri->material!=0) Msg("non-default material"); } */ Scene->BoxQuery(PQ,BB,CDB::OPT_FULL_TEST,m_CFModel); }else Scene->BoxQuery(PQ,BB,CDB::OPT_FULL_TEST,GetSnapList()); DWORD dwCount = PQ.r_count(); if (dwCount==0){ // Log("chasm1"); return FALSE; // chasm? } // *** Transfer triangles and compute sector // R_ASSERT(dwCount<RCAST_MaxTris); static xr_vector<tri> tris; tris.reserve(RCAST_MaxTris); tris.clear(); for (DWORD i=0; i<dwCount; i++) { SPickQuery::SResult* R = PQ.r_begin()+i; if (R->e_obj&&R->e_mesh) { CSurface* surf = R->e_mesh->GetSurfaceByFaceID(R->tag); //. SGameMtl* mtl = GMLib.GetMaterialByID(surf->_GameMtl()); //. if (mtl->Flags.is(SGameMtl::flPassable))continue; Shader_xrLC* c_sh = Device.ShaderXRLC.Get(surf->_ShaderXRLCName()); if (!c_sh->flags.bCollision) continue; } /* if(m_CFModel) { u16 mtl_id = R->material; if(std::find(m_ignored_materials.begin(), m_ignored_materials.end(), mtl_id) != m_ignored_materials.end() ) { //. Msg("--ignore"); continue; } } */ tris.push_back (tri()); tri& D = tris.back(); Fvector* V = R->verts; D.v[0] = &V[0]; D.v[1] = &V[1]; D.v[2] = &V[2]; D.N.mknormal(*D.v[0],*D.v[1],*D.v[2]); if (D.N.y<=0) tris.pop_back (); } if (tris.size()==0){ // Log("chasm2"); return FALSE; // chasm? } static xr_vector<Fvector> points; points.reserve(RCAST_Total); points.clear(); static xr_vector<Fvector> normals; normals.reserve(RCAST_Total);normals.clear(); Fvector P,D; D.set(0,-1,0); float coeff = 0.5f*m_Params.fPatchSize/float(RCAST_Count); for (int x=-RCAST_Count; x<=RCAST_Count; x++) { P.x = vAt.x + coeff*float(x); for (int z=-RCAST_Count; z<=RCAST_Count; z++) { P.z = vAt.z + coeff*float(z); P.y = vAt.y + 10.f; float tri_min_range = flt_max; int tri_selected = -1; float range,u,v; for (i=0; i<DWORD(tris.size()); i++){ if (ETOOLS::TestRayTriA(P,D,tris[i].v,u,v,range,false)){ if (range<tri_min_range){ tri_min_range = range; tri_selected = i; } } } if (tri_selected>=0) { P.y -= tri_min_range; points.push_back(P); normals.push_back(tris[tri_selected].N); } } } if (points.size()<3) { // Msg ("Failed to create node at [%f,%f,%f].",vAt.x,vAt.y,vAt.z); return FALSE; } //. float rc_lim = bIC?0.015f:0.7f; if (float(points.size())/float(RCAST_Total) < rc_lim) { // Msg ("Partial chasm at [%f,%f,%f].",vAt.x,vAt.y,vAt.z); return FALSE; } // *** Calc normal Fvector vNorm; vNorm.set(0,0,0); for (DWORD n=0; n<normals.size(); n++) vNorm.add(normals[n]); vNorm.div(float(normals.size())); vNorm.normalize(); /* { // second algorithm (Magic) Fvector N,O; N.set(vNorm); O.set(points[0]); Mgc::OrthogonalPlaneFit( points.size(),(Mgc::Vector3*)points.begin(), *((Mgc::Vector3*)&O), *((Mgc::Vector3*)&N) ); if (N.y<0) N.invert(); N.normalize(); vNorm.lerp(vNorm,N,.3f); vNorm.normalize(); } */ // *** Align plane Fvector vOffs; vOffs.set(0,-1000,0); Fplane PL; PL.build(vOffs,vNorm); for (DWORD p=0; p<points.size(); p++) { float dist = PL.classify(points[p]); if (dist>0) { vOffs = points[p]; PL.build(vOffs,vNorm); } } // *** Create node and register it N.Plane.build (vOffs,vNorm); // build plane D.set (0,1,0); N.Plane.intersectRayPoint(PointDown,D,N.Pos); // "project" position // *** Validate results vNorm.set(0,1,0); if (vNorm.dotproduct(N.Plane.n)<_cos(deg2rad(60.f))) return FALSE; float y_old = vAt.y; float y_new = N.Pos.y; if (y_old>y_new) { // down if (y_old-y_new > m_Params.fCanDOWN ) return FALSE; } else { // up if (y_new-y_old > m_Params.fCanUP ) return FALSE; } // *** Validate plane { Fvector PLP; D.set(0,-1,0); int num_successed_rays = 0; for (int x=-RCAST_Count; x<=RCAST_Count; x++) { P.x = N.Pos.x + coeff*float(x); for (int z=-RCAST_Count; z<=RCAST_Count; z++) { P.z = N.Pos.z + coeff*float(z); P.y = N.Pos.y; N.Plane.intersectRayPoint(P,D,PLP); // "project" position P.y = PLP.y+RCAST_VALID*0.01f; float tri_min_range = flt_max; int tri_selected = -1; float range,u,v; for (i=0; i<tris.size(); i++){ if (ETOOLS::TestRayTriA(P,D,tris[i].v,u,v,range,false)){ if (range<tri_min_range){ tri_min_range = range; tri_selected = i; } } } if (tri_selected>=0){ if (tri_min_range<RCAST_VALID) num_successed_rays++; } } } float perc = float(num_successed_rays)/float(RCAST_Total); //. if (!bIC&&(perc < 0.5f)){ float perc_lim = bIC?0.015f:0.5f; if (perc < perc_lim){ // Msg ("Floating node."); return FALSE; } } // *** Mask check // ??? return TRUE; }