void CCollisionManager::ClipFaceFaceVerts(vec3* verts0, int* vertIndexs0, vec3* verts1, int* vertIndexs1, vec3* vertsX, int* numVertsX) { SortVertices(verts0, vertIndexs0); SortVertices(verts1, vertIndexs1); // Work out the normal for the face vec3 v0 = verts0[1] - verts0[0]; vec3 v1 = verts0[2] - verts0[0]; vec3 n = cross(v1, v0); n = normalize(n); // Project all the vertices onto a shared plane, plane0 vec3 vertsTemp1[4]; for (int i=0; i<4; i++) { vertsTemp1[i] = verts1[i] + (n * dot(n, verts0[0]-verts1[i])); } static vec3 temp[50]; int numVerts = 0; for (int c=0; c<2; c++) { vec3* vertA = vertsTemp1; vec3* vertB = verts0; if (c==1) { vertA = verts0; vertB = vertsTemp1; } // Work out the normal for the face vec3 v0 = vertA[1] - vertA[0]; vec3 v1 = vertA[2] - vertA[0]; vec3 n = cross(v1, v0); n = normalize(n); for (int i=0; i<4; i++) { vec3 s0 = vertA[i]; vec3 s1 = vertA[(i+1)%4]; vec3 sx = s0 + n*10.0f; // Work out the normal for the face vec3 sv0 = s1 - s0; vec3 sv1 = sx - s0; vec3 sn = cross(sv1, sv0); sn = normalize(sn); float d = dot(s0, sn); for (int j=0; j<4; j++) { vec3 p0 = vertB[j]; vec3 p1 = vertB[(j+1)%4]; // Loops back to the 0th for the last one float d0 = dot(p0, sn) - d; float d1 = dot(p1, sn) - d; // Check there on opposite sides of the plane if ( (d0 * d1) < 0.0f) { vec3 pX = p0 + ( dot(sn, (s0-p0)) / dot(sn, (p1-p0)) ) * (p1 - p0); if (VertInsideFace(vertA, pX, 0.1f)) { temp[numVerts] = pX; numVerts++; } } if (VertInsideFace(vertA, p0)) { temp[numVerts] = p0; numVerts++; } } } } // Remove verts which are very close to each other for (int i=0; i<numVerts; i++) { for (int j=i; j<numVerts; j++) { if (i!=j) { auto squaredLength = length(temp[i] - temp[j]); squaredLength *= squaredLength; float dist = squaredLength; if (dist < 6.5f) { for (int k=j; k<numVerts; k++) { temp[k] = temp[k+1]; } numVerts--; } } } } *numVertsX = numVerts; for (int i=0; i<numVerts; i++) { vertsX[i] = temp[i]; } }
/*!*************************************************************************** @Function PVRTGeometrySort @Modified pVtxData Pointer to array of vertices @Modified pwIdx Pointer to array of indices @Input nStride Size of a vertex (in bytes) @Input nVertNum Number of vertices. Length of pVtxData array @Input nTriNum Number of triangles. Length of pwIdx array is 3* this @Input nBufferVtxLimit Number of vertices that can be stored in a buffer @Input nBufferTriLimit Number of triangles that can be stored in a buffer @Input dwFlags PVRTGEOMETRY_SORT_* flags @Description Triangle sorter *****************************************************************************/ void PVRTGeometrySort( void * const pVtxData, PVRTGEOMETRY_IDX * const pwIdx, const int nStride, const int nVertNum, const int nTriNum, const int nBufferVtxLimit, const int nBufferTriLimit, const unsigned int dwFlags) { CObject sOb(pwIdx, nVertNum, nTriNum, nBufferVtxLimit, nBufferTriLimit); CBlock sBlock(nBufferVtxLimit, nBufferTriLimit); PVRTGEOMETRY_IDX *pwIdxOut; int nTriCnt, nVtxCnt; int nOutTriCnt, nOutVtxCnt, nOutBlockCnt; int nMeshToResize; #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS int i; int pnBlockTriCnt[PVRVGPBLOCKTEST_MAX_BLOCKS]; SVGPModel sVGPMdlBefore; SVGPModel sVGPMdlAfter; #endif if(dwFlags & PVRTGEOMETRY_SORT_VERTEXCACHE) { #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS VGP590Test(&sVGPMdlBefore, pwIdx, nTriNum); _RPT4(_CRT_WARN, "OptimiseTriListPVR() Before: Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nTriNum, sVGPMdlBefore.nVtxCnt, (float)sVGPMdlBefore.nVtxCnt / (float)nTriNum, sVGPMdlBefore.nBlockCnt); #endif pwIdxOut = (PVRTGEOMETRY_IDX*)malloc(nTriNum * 3 * sizeof(*pwIdxOut)); _ASSERT(pwIdxOut); // Sort geometry into blocks nOutTriCnt = 0; nOutVtxCnt = 0; nOutBlockCnt = 0; do { // Clear & fill the block sBlock.Clear(); nMeshToResize = sBlock.Fill(&sOb); // Copy indices into output sBlock.Output(&pwIdxOut[3*nOutTriCnt], &nVtxCnt, &nTriCnt, &sOb); sOb.m_nTriNumFree -= nTriCnt; nOutTriCnt += nTriCnt; if(nMeshToResize >= 0) { SMesh *pMesh; _ASSERT(nMeshToResize <= (nBufferVtxLimit-3)); pMesh = &sOb.m_pvMesh[nMeshToResize].back(); sOb.ResizeMesh(pMesh->nVtxNum, pMesh->ppVtx); sOb.m_pvMesh[nMeshToResize].pop_back(); } _ASSERT(nVtxCnt <= nBufferVtxLimit); _ASSERT(nTriCnt <= nBufferTriLimit); #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS _ASSERT(nOutBlockCnt < PVRVGPBLOCKTEST_MAX_BLOCKS); pnBlockTriCnt[nOutBlockCnt] = nTriCnt; #endif nOutVtxCnt += nVtxCnt; nOutBlockCnt++; // _RPT4(_CRT_WARN, "%d/%d tris (+%d), %d blocks\n", nOutTriCnt, nTriNum, nTriCnt, nOutBlockCnt); _ASSERT(nTriCnt == nBufferTriLimit || (nBufferVtxLimit - nVtxCnt) < 3 || nOutTriCnt == nTriNum); } while(nOutTriCnt < nTriNum); _ASSERT(nOutTriCnt == nTriNum); // The following will fail if optimising a subset of the mesh (e.g. a bone batching) //_ASSERT(nOutVtxCnt >= nVertNum); // Done! memcpy(pwIdx, pwIdxOut, nTriNum * 3 * sizeof(*pwIdx)); FREE(pwIdxOut); _RPT3(_CRT_WARN, "OptimiseTriListPVR() In: Tri: %d, Vtx: %d, vtx/tri=%f\n", nTriNum, nVertNum, (float)nVertNum / (float)nTriNum); _RPT4(_CRT_WARN, "OptimiseTriListPVR() HW: Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nOutTriCnt, nOutVtxCnt, (float)nOutVtxCnt / (float)nOutTriCnt, nOutBlockCnt); #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS VGP590Test(&sVGPMdlAfter, pwIdx, nTriNum); _RPT4(_CRT_WARN, "OptimiseTriListPVR() After : Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nTriNum, sVGPMdlAfter.nVtxCnt, (float)sVGPMdlAfter.nVtxCnt / (float)nTriNum, sVGPMdlAfter.nBlockCnt); _ASSERTE(sVGPMdlAfter.nVtxCnt <= sVGPMdlBefore.nVtxCnt); _ASSERTE(sVGPMdlAfter.nBlockCnt <= sVGPMdlBefore.nBlockCnt); for(i = 0; i < nOutBlockCnt; ++i) { _ASSERT(pnBlockTriCnt[i] == sVGPMdlAfter.pnBlockTriCnt[i]); } #endif } if(!(dwFlags & PVRTGEOMETRY_SORT_IGNOREVERTS)) { // Re-order the vertices so maybe they're accessed in a more linear // manner. Should cut page-breaks on the initial memory read of // vertices. Affects both the order of vertices, and the values // of indices, but the triangle order is unchanged. SortVertices(pVtxData, pwIdx, nStride, nVertNum, nTriNum*3); } }
void sreLODModel::UploadToGPU(int attribute_mask, int dynamic_flags) { // Check that all attributes defined in attribute_mask are present in the model // (enabled in sreBaseModel::flags). if ((attribute_mask & flags) != attribute_mask) sreFatalError("Error (sreLODModel::UploadToGPU): Not all requested attributes are present the base model."); if (attribute_mask == 0) sreFatalError("Error (sreLODModel::UploadToGPU): attribute_mask = 0 (unexpected)."); // This is not the best place to initialize this table (which is used when drawing objects). if (!attribute_list_table_initialized) sreGenerateAttributeListTable(); bool shadow = (sre_internal_rendering_flags & SRE_RENDERING_FLAG_SHADOW_VOLUME_SUPPORT) && !(flags & SRE_LOD_MODEL_NO_SHADOW_VOLUME_SUPPORT) && (flags & SRE_LOD_MODEL_IS_SHADOW_VOLUME_MODEL); if (flags & SRE_LOD_MODEL_BILLBOARD) { // Special case for billboards; little has to be uploaded yet. glGenBuffers(1, &GL_attribute_buffer[SRE_ATTRIBUTE_POSITION]); if (flags & SRE_LOD_MODEL_LIGHT_HALO) glGenBuffers(1, &GL_attribute_buffer[SRE_ATTRIBUTE_NORMAL]); if (attribute_mask & SRE_TEXCOORDS_MASK) { // Any texture coordinates (for use with an emission map) have to be uploaded. glGenBuffers(1, &GL_attribute_buffer[SRE_ATTRIBUTE_TEXCOORDS]); glBufferData(GL_ARRAY_BUFFER, nu_vertices * sizeof(float) * 2, texcoords, GL_STATIC_DRAW); } // Set the non-interleaved attribute info (just the lower 8 bits are used). // The interleaved information is set to zero. attribute_info.attribute_masks = attribute_mask; if (nu_triangles == 0) // For single billboards, no triangles are allocated (they always consist // of a two triangle fan with the order of the vertex data). return; goto copy_indices; } //DEBUG // cache_coherency_sorting_hint = 0; // Determine a sorting order that optimizes cache coherency. { uint64_t best_cost = UINT64_MAX; int best_sorting_dimension = 0; const char *predefined_str; if (cache_coherency_sorting_hint != SRE_SORTING_HINT_UNDEFINED) { best_sorting_dimension = cache_coherency_sorting_hint; if (cache_coherency_sorting_hint == SRE_SORTING_HINT_DO_NOT_SORT) predefined_str = "predefined, keep original order"; else predefined_str = "predefined"; } else { uint64_t preexisting_cost = CalculateCacheCoherency(); sreBaseModel *clone = new sreBaseModel; CloneGeometry(clone); for (int dim = 0; dim < 48; dim++) { clone->SortVertices(dim); uint64_t cost = clone->CalculateCacheCoherency(); // sreMessage(SRE_MESSAGE_INFO, "Sorting dimension = %d, cost = %llu.", dim, cost); if (cost < best_cost) { best_cost = cost; best_sorting_dimension = dim; } } delete clone; if (preexisting_cost <= best_cost) { best_sorting_dimension = SRE_SORTING_HINT_DO_NOT_SORT; predefined_str = "kept original order"; } else predefined_str = "calculated"; } if (best_sorting_dimension != SRE_SORTING_HINT_DO_NOT_SORT) SortVertices(best_sorting_dimension); sreMessage(SRE_MESSAGE_LOG, "sreLODModel::UploadToGPU: Model %d sorting order %d (%s).", id, best_sorting_dimension, predefined_str); } int total_nu_vertices; sreLODModelShadowVolume *model_shadow_volume; if (shadow) { model_shadow_volume = (sreLODModelShadowVolume *)this; total_nu_vertices = nu_vertices * 2; // Times two for extruded vertices. } else total_nu_vertices = nu_vertices; Vector4D *positions_4D; if (attribute_mask & SRE_POSITION_MASK) { // Create 4D array for vertex position buffer from aligned 3D positions in // sreBaseModel geometry. positions_4D = new Vector4D[total_nu_vertices]; // Copy vertex positions, adding a w component of 1.0 for the shaders. for (int i = 0; i < nu_vertices; i++) positions_4D[i] = Vector4D(vertex[i], 1.0f); // Create vertices extruded to infinity for shadow volumes. if (shadow) { for (int i = 0; i < nu_vertices; i++) // w = 0 for extruded vertices. positions_4D[i + nu_vertices] = Vector4D(vertex[i], 0.0f); model_shadow_volume->vertex_index_shadow_offset = nu_vertices; } } // Interleaving vertex attributes may increase GPU cache/memory performance, especially // on low-end GPUs. // Currently it isn't really optimal when extruded vertex positions for shadow volumes // are used, because the second half of the buffer will contain gaps for the non-position // attributes. // The current behaviour, when SRE_INTERLEAVED_BUFFERS_ENABLED is set, is to simply // combine all the model's attributes in one interleaved buffer. However, support is // in place for any combination of non-interleaved and up to three interleaved vertex // buffers. // When extruded shadow volume vertices are required, or dynamic_flags is set, no // interleaved buffers are created. if (sre_internal_interleaved_vertex_buffers_mode == SRE_INTERLEAVED_BUFFERS_ENABLED && dynamic_flags == 0 && !shadow && !OnlyOneAttributeSet(attribute_mask)) { // Interleave attribute data. NewVertexBufferInterleaved(attribute_mask, positions_4D, shadow); // Set the interleaved attribute info. Interleaved slot 0 is used, located // at bits 8-15. The non-interleaved information is set to zero. attribute_info.attribute_masks = attribute_mask << 8; goto finish; } // The attribute arrays for vertex normals, texcoords, tangents and colors in the // model data structure are already properly formatted to be forwarded to the GPU. NewVertexBuffers(attribute_mask, dynamic_flags, positions_4D, shadow); // Set the non-interleaved attribute info (just the lower 8 bits are used). // The interleaved information is set to zero. attribute_info.attribute_masks = attribute_mask; finish : if (attribute_mask & SRE_POSITION_MASK) // 4D positions no longer required. delete [] positions_4D; sreMessage(SRE_MESSAGE_LOG, "sreLODModel::UploadToGPU: Uploading model %d, attribute_mask 0x%02X.", id, attribute_mask); // If the model is in any way instanced (at least one attribute shared), then we can // be sure that the triangle vertex indices are already present on the GPU and will // also be shared. if ((attribute_mask ^ flags) & attribute_mask) { if (shadow && (attribute_mask & SRE_POSITION_MASK)) goto calculate_edges; // Finished. return; } copy_indices: // Copy triangle vertex indices. unsigned short *triangle_vertex_indices; // Decide whether to use short indices (range 0 - 65535); // when PRIMITIVE_RESTART is allowed for drawing shadow volumes, the highest index // is reserved. int max_short_index; max_short_index = 65535; #ifndef NO_PRIMITIVE_RESTART if (shadow && GLEW_NV_primitive_restart) max_short_index = 65534; #endif if (total_nu_vertices > max_short_index) { // Keep new/delete happy by using unsigned short. triangle_vertex_indices = new unsigned short[nu_triangles * 3 * 2]; unsigned int *indices = (unsigned int *)triangle_vertex_indices; for (int i = 0; i < nu_triangles; i++) for (int j = 0; j < 3; j++) indices[i * 3 + j] = triangle[i].vertex_index[j]; GL_indexsize = 4; } else { triangle_vertex_indices = new unsigned short[nu_triangles * 3 * 2]; unsigned short *indices16 = triangle_vertex_indices; for (int i = 0; i < nu_triangles; i++) for (int j = 0; j < 3; j++) indices16[i * 3 + j] = triangle[i].vertex_index[j]; GL_indexsize = 2; sreMessage(SRE_MESSAGE_LOG, "Less or equal to %d vertices in object (including extruded shadow vertices), " "using 16-bit indices.", max_short_index + 1); } // Upload triangle vertex indices. glGenBuffers(1, &GL_element_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_element_buffer); sreCheckGLError("OpenGL error before element array buffer creation.\n"); glBufferData(GL_ELEMENT_ARRAY_BUFFER, nu_triangles * 3 * GL_indexsize, triangle_vertex_indices, GL_STATIC_DRAW); if (glGetError() != GL_NO_ERROR) sreFatalError("OpenGL error occurred during element array buffer creation."); delete [] triangle_vertex_indices; if (!shadow) return; // Finished. calculate_edges : // Create edge array for shadow silhouette determination (shadow volumes). if (model_shadow_volume->nu_edges == 0) model_shadow_volume->CalculateEdges(); else sreMessage(SRE_MESSAGE_WARNING, "Warning: sreLODModel::UploadToGPU: edges already calculated " "(shouldn't happen)."); }