void CalculateRelSet(Fvector &pos, vecW &rel_set) { // calculate volume in local level space Fbox Volume; Volume.set(pos,pos); float Vsize = (g_params.m_relevance+g_params.m_sample_step)/2.f; Volume.min.sub(Vsize); Volume.max.add(Vsize); Volume.min.sub(g_TREE_ROOT->bbox.min); Volume.max.sub(g_TREE_ROOT->bbox.min); // scale it to sample grid space Volume.min.div(g_params.m_sample_step); Volume.max.div(g_params.m_sample_step); // calc volume in grid-space int minX,minY,minZ; int maxX,maxY,maxZ; minX = iROUND(floorf(Volume.min.x)); clamp(minX, 0, int(g_pvs_X)-1); minY = iROUND(floorf(Volume.min.y)); clamp(minY, 0, int(g_pvs_Y)-1); minZ = iROUND(floorf(Volume.min.z)); clamp(minZ, 0, int(g_pvs_Z)-1); maxX = iROUND(ceilf(Volume.max.x)); clamp(maxX, 0, int(g_pvs_X)-1); maxY = iROUND(ceilf(Volume.max.y)); clamp(maxY, 0, int(g_pvs_Y)-1); maxZ = iROUND(ceilf(Volume.max.z)); clamp(maxZ, 0, int(g_pvs_Z)-1); /* clMsg("- Selected BB: [%d,%d,%d]-[%d,%d,%d]", minX,minY,minZ, maxX,maxY,maxZ ); */ // merge data for (int z=minZ; z<=maxZ; z++) { for (int x=minX; x<=maxX; x++) { for (int y=minY; y<=maxY; y++) { u32 cell = z*g_pvs_X*g_pvs_Y + x*g_pvs_Y + y; // clMsg("* Sample #%d",cell); int ptr = g_pvs_map_vm[cell]; if (ptr>=0) { rel_set.insert(rel_set.end(),g_pvs[ptr].begin(),g_pvs[ptr].end()); } } } } if (rel_set.size()>1) { std::sort(rel_set.begin(),rel_set.end()); vecW_IT I = std::unique(rel_set.begin(),rel_set.end()); rel_set.erase(I,rel_set.end()); } }
void CBuild::BuildPVS() { Fvector size; Fvector pos; Fvector ground_dir; Status("Preparing..."); g_TREE_ROOT->bbox.getsize(size); g_pvs_X = iROUND(ceilf(size.x/g_params.m_sample_step))+1; g_pvs_Y = iROUND(ceilf(size.y/g_params.m_sample_step))+1; g_pvs_Z = iROUND(ceilf(size.z/g_params.m_sample_step))+1; clMsg("Ceiling dimensions: [%3d,%3d,%3d]",g_pvs_X, g_pvs_Y, g_pvs_Z); // ground pick setup XRC.RayMode (RAY_ONLYFIRST|RAY_CULL); ground_dir.set (0,-1,0); // reserve memory CFS_File pvs_map ("pvs.temp"); u32 dwSlot = 0; u32 dwSlotsTotal= g_pvs_X*g_pvs_Y*g_pvs_Z; u32 pvs_reserve = dwSlotsTotal/1024 + 512; clMsg("PVS: %d M", (pvs_reserve*sizeof(vecW))/(1024*1024)); g_pvs.reserve (pvs_reserve); // begin! Status("Processing..."); u32 dwStartTime = timeGetTime(); for (int z=0; z<g_pvs_Z; z++) { for (int x=0; x<g_pvs_X; x++) { for (int y=0; y<g_pvs_Y; y++) { pos.set(x,y,z); pos.mul(g_params.m_sample_step); pos.add(g_TREE_ROOT->bbox.min); dwSlot++; // ground pick XRC.RayPick(precalc_identity,1.f,&RCAST_Model,pos,ground_dir,g_params.m_sample_break); if (XRC.GetRayContactCount()==0) { // don't calculate PVS for this point int tmp = -1; pvs_map.write(&tmp,4); continue; } // Sample PVS data g_TREE_ROOT->VisUnroll(pos,g_selected); if (!g_selected.empty()) { g_result.resize(g_selected.size()); ZeroMemory(g_result.begin(),g_result.size()*sizeof(BOOL)); ORM_Process(g_selected.size(),pos,g_selected.begin(),g_result.begin()); // Exclude invisible for (int i=0; i<g_selected.size(); i++) { if (!g_result[i]) { if (g_tree[g_selected[i]]->isPatch) continue; g_selected.erase(g_selected.begin()+i); g_result.erase(g_result.begin()+i); i--; } } } // Compress and record PVS sample pvs_map.w_u32(CompressSelected()); g_selected.clear(); // Statistic if (dwSlot%64 == 63) { Progress(float(dwSlot)/float(dwSlotsTotal)); u32 dwCurrentTime = timeGetTime(); Status("Sample #%d\nSpeed %3.1f samples per second\nPVS entrys: %d", dwSlot,1000.f*float(dwSlot)/float(dwCurrentTime-dwStartTime), g_pvs.size() ); } } } } ORM_Destroy(); clMsg("* PVS entrys: %d", g_pvs.size()); clMsg("* Aver. Speed: %3.1f", 1000.f*float(dwSlot)/float(timeGetTime()-dwStartTime)); }
void CBuild::BuildRelevance(IWriter &fs) { static Fvector size; static u32 nx,ny,nz; Status("Preparing..."); R_ASSERT(g_TREE_ROOT); g_TREE_ROOT->bbox.getsize(size); clMsg("Level dimensions: [%3.1f,%3.1f,%3.1f]",size.x,size.y,size.z); nx = iROUND(ceilf(size.x/g_params.m_relevance)); ny = iROUND(ceilf(size.y/g_params.m_relevance)); nz = iROUND(ceilf(size.z/g_params.m_relevance)); clMsg("Ceiling dimensions: [%3d,%3d,%3d]",nx, ny, nz); fs.open_chunk(fsL_VISIBILITY); fs.open_chunk(fsV_HEADER); V_Header H; H.nX = nx; H.nY = ny; H.nZ = nz; H.relevance = g_params.m_relevance; H.min.set (g_TREE_ROOT->bbox.min); fs.write (&H,sizeof(H)); fs.close_chunk (); // Build Visibility static xr_vector< u32 > slots; static xr_vector< vecW > vis_nodes; static xr_vector< vecW > vis_lights; static xr_vector< vecW > vis_glows; static xr_vector< vecW > vis_occluders; static vecW rel_set; static vecW unroll; CVirtualFileStream* pvs_map_stream=0; if (g_params.m_bTestOcclusion) { pvs_map_stream = xr_new<CVirtualFileStream> ("pvs.temp"); g_pvs_map_vm = (int *)pvs_map_stream->Pointer(); } static u32 dwSlot = 0; for (int z=0; z<nz; z++) { for (int x=0; x<nx; x++) { for (int y=0; y<ny; y++) { Status("Volume #%d...",dwSlot); static Fvector pos; pos.set(x,y,z); pos.add(.5f); pos.mul(g_params.m_relevance); pos.add(g_TREE_ROOT->bbox.min); // ******* Nodes relevance if (g_params.m_bTestOcclusion) { CalculateRelSet(pos,unroll); if (unroll.empty() || g_TREE_ROOT->VisCapture(unroll,rel_set)) rel_set.push_back(g_tree.size()-1); unroll.clear(); slots.push_back(PlaceData(vis_nodes,rel_set)); } else { // Unroll hierrarhy VERIFY(g_TREE_ROOT); g_TREE_ROOT->VisUnroll(pos,unroll); if (unroll.size()>1) std::sort(unroll.begin(),unroll.end()); // Capture results if (g_TREE_ROOT->VisCapture(unroll,rel_set)) rel_set.push_back(g_tree.size()-1); unroll.clear(); // Register in container slots.push_back(PlaceData(vis_nodes,rel_set)); } // Lights relevance for (int i=0; i<lights.size(); i++) { if ((pos.distance_to(lights[i].position) - lights[i].range) < g_params.m_viewdist) { rel_set.push_back(i); } } slots.push_back(PlaceData(vis_lights,rel_set)); // Glows relevance for (i=0; i<glows.size(); i++) { if ((pos.distance_to(glows[i].P) - glows[i].size) < g_params.m_viewdist) { rel_set.push_back(i); } } slots.push_back(PlaceData(vis_glows,rel_set)); // Occluders relevance for (i=0; i<occluders.size(); i++) { Fvector P; float R=-1; float T; P.add(occluders[i].V2,occluders[i].V4); P.mul(.5f); T = P.distance_to(occluders[i].V1); if (T>R) R=T; T = P.distance_to(occluders[i].V2); if (T>R) R=T; T = P.distance_to(occluders[i].V4); if (T>R) R=T; if ((pos.distance_to(P) - R) < g_params.m_viewdist*g_params.m_occluder_rel_scale) { rel_set.push_back(i); } } slots.push_back(PlaceData(vis_occluders,rel_set)); dwSlot++; Progress(float(dwSlot)/float(nz*nx*ny)); } } } xr_delete (pvs_map_stream); fs.open_chunk (fsV_NODES); SaveDATA (fs,vis_nodes); fs.close_chunk (); fs.open_chunk(fsV_LIGHTS); SaveDATA(fs,vis_lights); fs.close_chunk(); fs.open_chunk(fsV_GLOWS); SaveDATA(fs,vis_glows); fs.close_chunk(); fs.open_chunk(fsV_OCCLUDERS); SaveDATA(fs,vis_occluders); fs.close_chunk(); fs.open_chunk(fsV_MAP); fs.write(slots.begin(),slots.size()*sizeof(u32)); fs.close_chunk(); fs.close_chunk(); }