//! Everything in this module is pending to be refactored, turned into idiomatic-rust, and moved to //! other modules. //! The contents of this file are a combination of transpilation and human //! modification to Morten S. Mikkelsen's original tangent space algorithm //! implementation written in C. The original source code can be found at //! //! and includes the following license: //! //! Copyright (C) 2011 by Morten S. Mikkelsen //! //! This software is provided 'as-is', without any express or implied //! warranty. In no event will the authors be held liable for any damages //! arising from the use of this software. //! //! Permission is granted to anyone to use this software for any purpose, //! including commercial applications, and to alter it and redistribute it //! freely, subject to the following restrictions: //! //! 1. The origin of this software must not be misrepresented; you must not //! claim that you wrote the original software. If you use this software //! in a product, an acknowledgment in the product documentation would be //! appreciated but is not required. //! //! 2. Altered source versions must be plainly marked as such, and must not be //! misrepresented as being the original software. //! //! 3. This notice may not be removed or altered from any source distribution. #![allow( clippy::all, clippy::redundant_else, clippy::match_same_arms, clippy::semicolon_if_nothing_returned, clippy::explicit_iter_loop, clippy::map_flatten, dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, non_upper_case_globals, unused_mut, unused_assignments, unused_variables, unsafe_code )] use alloc::{vec, vec::Vec}; use core::ptr::{self, null_mut}; use glam::Vec3; use crate::{face_vert_to_index, get_normal, get_position, get_tex_coord, Geometry}; #[derive(Copy, Clone)] pub struct STSpace { pub vOs: Vec3, pub fMagS: f32, pub vOt: Vec3, pub fMagT: f32, pub iCounter: i32, pub bOrient: bool, } impl STSpace { pub fn zero() -> Self { Self { vOs: Default::default(), fMagS: 0.0, vOt: Default::default(), fMagT: 0.0, iCounter: 0, bOrient: false, } } } // To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the // normal map sampler must use the exact inverse of the pixel shader transformation. // The most efficient transformation we can possibly do in the pixel shader is // achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. // pixel shader (fast transform out) // vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); // where vNt is the tangent space normal. The normal map sampler must likewise use the // interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. // sampler does (exact inverse of pixel shader): // float3 row0 = cross(vB, vN); // float3 row1 = cross(vN, vT); // float3 row2 = cross(vT, vB); // float fSign = dot(vT, row0)<0 ? -1 : 1; // vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); // where vNout is the sampled normal in some chosen 3D space. // // Should you choose to reconstruct the bitangent in the pixel shader instead // of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. // Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of // quads as your renderer then problems will occur since the interpolated tangent spaces will differ // even though the vertex level tangent spaces match. This can be solved either by triangulating before // sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. // However, this must be used both by the sampler and your tools/rendering pipeline. // internal structure #[derive(Copy, Clone)] pub struct STriInfo { pub FaceNeighbors: [i32; 3], pub AssignedGroup: [*mut SGroup; 3], pub vOs: Vec3, pub vOt: Vec3, pub fMagS: f32, pub fMagT: f32, pub iOrgFaceNumber: i32, pub iFlag: i32, pub iTSpacesOffs: i32, pub vert_num: [u8; 4], } impl STriInfo { fn zero() -> Self { Self { FaceNeighbors: [0, 0, 0], AssignedGroup: [null_mut(), null_mut(), null_mut()], vOs: Default::default(), vOt: Default::default(), fMagS: 0.0, fMagT: 0.0, iOrgFaceNumber: 0, iFlag: 0, iTSpacesOffs: 0, vert_num: [0, 0, 0, 0], } } } #[derive(Copy, Clone)] pub struct SGroup { pub iNrFaces: i32, pub pFaceIndices: *mut i32, pub iVertexRepresentative: i32, pub bOrientPreserving: bool, } impl SGroup { fn zero() -> Self { Self { iNrFaces: 0, pFaceIndices: null_mut(), iVertexRepresentative: 0, bOrientPreserving: false, } } } #[derive(Clone)] pub struct SSubGroup { pub iNrFaces: i32, pub pTriMembers: Vec, } impl SSubGroup { fn zero() -> Self { Self { iNrFaces: 0, pTriMembers: Vec::new(), } } } #[derive(Copy, Clone)] pub union SEdge { pub unnamed: unnamed, pub array: [i32; 3], } impl SEdge { fn zero() -> Self { Self { array: [0, 0, 0] } } } #[derive(Copy, Clone)] pub struct unnamed { pub i0: i32, pub i1: i32, pub f: i32, } #[derive(Copy, Clone)] pub struct STmpVert { pub vert: [f32; 3], pub index: i32, } impl STmpVert { fn zero() -> Self { Self { vert: [0.0, 0.0, 0.0], index: 0, } } } pub unsafe fn genTangSpace(geometry: &mut I, fAngularThreshold: f32) -> bool { let mut iNrTrianglesIn = 0; let mut f = 0; let mut t = 0; let mut i = 0; let mut iNrTSPaces = 0; let mut iTotTris = 0; let mut iDegenTriangles = 0; let mut iNrMaxGroups = 0; let mut iNrActiveGroups: i32 = 0i32; let mut index = 0; let iNrFaces = geometry.num_faces(); let mut bRes: bool = false; let fThresCos = cos(fAngularThreshold.to_radians()); f = 0; while f < iNrFaces { let verts = geometry.num_vertices_of_face(f); if verts == 3 { iNrTrianglesIn += 1 } else if verts == 4 { iNrTrianglesIn += 2 } f += 1 } if iNrTrianglesIn <= 0 { return false; } let mut piTriListIn = vec![0i32; 3 * iNrTrianglesIn]; let mut pTriInfos = vec![STriInfo::zero(); iNrTrianglesIn]; iNrTSPaces = GenerateInitialVerticesIndexList( &mut pTriInfos, &mut piTriListIn, geometry, iNrTrianglesIn, ); GenerateSharedVerticesIndexList(piTriListIn.as_mut_ptr(), geometry, iNrTrianglesIn); iTotTris = iNrTrianglesIn; iDegenTriangles = 0; t = 0; while t < iTotTris as usize { let i0 = piTriListIn[t * 3 + 0]; let i1 = piTriListIn[t * 3 + 1]; let i2 = piTriListIn[t * 3 + 2]; let p0 = get_position(geometry, i0 as usize); let p1 = get_position(geometry, i1 as usize); let p2 = get_position(geometry, i2 as usize); if p0 == p1 || p0 == p2 || p1 == p2 { pTriInfos[t].iFlag |= 1i32; iDegenTriangles += 1 } t += 1 } iNrTrianglesIn = iTotTris - iDegenTriangles; if iNrTrianglesIn <= 0 { return false; } DegenPrologue( pTriInfos.as_mut_ptr(), piTriListIn.as_mut_ptr(), iNrTrianglesIn as i32, iTotTris as i32, ); InitTriInfo( pTriInfos.as_mut_ptr(), piTriListIn.as_ptr(), geometry, iNrTrianglesIn, ); iNrMaxGroups = iNrTrianglesIn * 3; let mut pGroups = vec![SGroup::zero(); iNrMaxGroups]; let mut piGroupTrianglesBuffer = vec![0; iNrTrianglesIn * 3]; iNrActiveGroups = Build4RuleGroups( pTriInfos.as_mut_ptr(), pGroups.as_mut_ptr(), piGroupTrianglesBuffer.as_mut_ptr(), piTriListIn.as_ptr(), iNrTrianglesIn as i32, ); let mut psTspace = vec![ STSpace { vOs: Vec3::new(1.0, 0.0, 0.0), fMagS: 1.0, vOt: Vec3::new(0.0, 1.0, 0.0), fMagT: 1.0, ..STSpace::zero() }; iNrTSPaces ]; bRes = GenerateTSpaces( &mut psTspace, pTriInfos.as_ptr(), pGroups.as_ptr(), iNrActiveGroups, piTriListIn.as_ptr(), fThresCos, geometry, ); if !bRes { return false; } DegenEpilogue( psTspace.as_mut_ptr(), pTriInfos.as_mut_ptr(), piTriListIn.as_mut_ptr(), geometry, iNrTrianglesIn as i32, iTotTris as i32, ); index = 0; f = 0; while f < iNrFaces { let verts_0 = geometry.num_vertices_of_face(f); if !(verts_0 != 3 && verts_0 != 4) { i = 0; while i < verts_0 { let mut pTSpace: *const STSpace = &mut psTspace[index] as *mut STSpace; let mut tang = Vec3::new((*pTSpace).vOs.x, (*pTSpace).vOs.y, (*pTSpace).vOs.z); let mut bitang = Vec3::new((*pTSpace).vOt.x, (*pTSpace).vOt.y, (*pTSpace).vOt.z); geometry.set_tangent( tang.into(), bitang.into(), (*pTSpace).fMagS, (*pTSpace).fMagT, (*pTSpace).bOrient, f, i, ); index += 1; i += 1 } } f += 1 } return true; } unsafe fn DegenEpilogue( mut psTspace: *mut STSpace, mut pTriInfos: *mut STriInfo, mut piTriListIn: *mut i32, geometry: &mut I, iNrTrianglesIn: i32, iTotTris: i32, ) { let mut t: i32 = 0i32; let mut i: i32 = 0i32; t = iNrTrianglesIn; while t < iTotTris { let bSkip: bool = if (*pTriInfos.offset(t as isize)).iFlag & 2i32 != 0i32 { true } else { false }; if !bSkip { i = 0i32; while i < 3i32 { let index1: i32 = *piTriListIn.offset((t * 3i32 + i) as isize); let mut bNotFound: bool = true; let mut j: i32 = 0i32; while bNotFound && j < 3i32 * iNrTrianglesIn { let index2: i32 = *piTriListIn.offset(j as isize); if index1 == index2 { bNotFound = false } else { j += 1 } } if !bNotFound { let iTri: i32 = j / 3i32; let iVert: i32 = j % 3i32; let iSrcVert: i32 = (*pTriInfos.offset(iTri as isize)).vert_num[iVert as usize] as i32; let iSrcOffs: i32 = (*pTriInfos.offset(iTri as isize)).iTSpacesOffs; let iDstVert: i32 = (*pTriInfos.offset(t as isize)).vert_num[i as usize] as i32; let iDstOffs: i32 = (*pTriInfos.offset(t as isize)).iTSpacesOffs; *psTspace.offset((iDstOffs + iDstVert) as isize) = *psTspace.offset((iSrcOffs + iSrcVert) as isize) } i += 1 } } t += 1 } t = 0i32; while t < iNrTrianglesIn { if (*pTriInfos.offset(t as isize)).iFlag & 2i32 != 0i32 { let mut vDstP = Vec3::new(0.0, 0.0, 0.0); let mut iOrgF: i32 = -1i32; let mut i_0: i32 = 0i32; let mut bNotFound_0: bool = false; let mut pV: *mut u8 = (*pTriInfos.offset(t as isize)).vert_num.as_mut_ptr(); let mut iFlag: i32 = 1i32 << *pV.offset(0isize) as i32 | 1i32 << *pV.offset(1isize) as i32 | 1i32 << *pV.offset(2isize) as i32; let mut iMissingIndex: i32 = 0i32; if iFlag & 2i32 == 0i32 { iMissingIndex = 1i32 } else if iFlag & 4i32 == 0i32 { iMissingIndex = 2i32 } else if iFlag & 8i32 == 0i32 { iMissingIndex = 3i32 } iOrgF = (*pTriInfos.offset(t as isize)).iOrgFaceNumber; vDstP = get_position( geometry, face_vert_to_index(iOrgF as usize, iMissingIndex as usize), ); bNotFound_0 = true; i_0 = 0i32; while bNotFound_0 && i_0 < 3i32 { let iVert_0: i32 = *pV.offset(i_0 as isize) as i32; let vSrcP = get_position( geometry, face_vert_to_index(iOrgF as usize, iVert_0 as usize), ); if vSrcP == vDstP { let iOffs: i32 = (*pTriInfos.offset(t as isize)).iTSpacesOffs; *psTspace.offset((iOffs + iMissingIndex) as isize) = *psTspace.offset((iOffs + iVert_0) as isize); bNotFound_0 = false } else { i_0 += 1 } } } t += 1 } } unsafe fn GenerateTSpaces( psTspace: &mut [STSpace], mut pTriInfos: *const STriInfo, mut pGroups: *const SGroup, iNrActiveGroups: i32, mut piTriListIn: *const i32, fThresCos: f32, geometry: &mut I, ) -> bool { let mut iMaxNrFaces: usize = 0; let mut iUniqueTspaces = 0; let mut g: i32 = 0i32; let mut i: i32 = 0i32; g = 0i32; while g < iNrActiveGroups { if iMaxNrFaces < (*pGroups.offset(g as isize)).iNrFaces as usize { iMaxNrFaces = (*pGroups.offset(g as isize)).iNrFaces as usize } g += 1 } if iMaxNrFaces == 0 { return true; } let mut pSubGroupTspace = vec![STSpace::zero(); iMaxNrFaces]; let mut pUniSubGroups = vec![SSubGroup::zero(); iMaxNrFaces]; let mut pTmpMembers = vec![0i32; iMaxNrFaces]; iUniqueTspaces = 0; g = 0i32; while g < iNrActiveGroups { let mut pGroup: *const SGroup = &*pGroups.offset(g as isize) as *const SGroup; let mut iUniqueSubGroups = 0; let mut s = 0; i = 0i32; while i < (*pGroup).iNrFaces { let f: i32 = *(*pGroup).pFaceIndices.offset(i as isize); let mut index: i32 = -1i32; let mut iVertIndex: i32 = -1i32; let mut iOF_1: i32 = -1i32; let mut iMembers: usize = 0; let mut j: i32 = 0i32; let mut l: usize = 0; let mut tmp_group: SSubGroup = SSubGroup { iNrFaces: 0, pTriMembers: Vec::new(), }; let mut bFound: bool = false; let mut n = Vec3::new(0.0, 0.0, 0.0); let mut vOs = Vec3::new(0.0, 0.0, 0.0); let mut vOt = Vec3::new(0.0, 0.0, 0.0); if (*pTriInfos.offset(f as isize)).AssignedGroup[0usize] == pGroup as *mut SGroup { index = 0i32 } else if (*pTriInfos.offset(f as isize)).AssignedGroup[1usize] == pGroup as *mut SGroup { index = 1i32 } else if (*pTriInfos.offset(f as isize)).AssignedGroup[2usize] == pGroup as *mut SGroup { index = 2i32 } iVertIndex = *piTriListIn.offset((f * 3i32 + index) as isize); n = get_normal(geometry, iVertIndex as usize); let mut vOs = (*pTriInfos.offset(f as isize)).vOs - (n.dot((*pTriInfos.offset(f as isize)).vOs) * n); let mut vOt = (*pTriInfos.offset(f as isize)).vOt - (n.dot((*pTriInfos.offset(f as isize)).vOt) * n); if VNotZero(vOs) { vOs = Normalize(vOs) } if VNotZero(vOt) { vOt = Normalize(vOt) } iOF_1 = (*pTriInfos.offset(f as isize)).iOrgFaceNumber; iMembers = 0; j = 0i32; while j < (*pGroup).iNrFaces { let t: i32 = *(*pGroup).pFaceIndices.offset(j as isize); let iOF_2: i32 = (*pTriInfos.offset(t as isize)).iOrgFaceNumber; let mut vOs2 = (*pTriInfos.offset(t as isize)).vOs - (n.dot((*pTriInfos.offset(t as isize)).vOs) * n); let mut vOt2 = (*pTriInfos.offset(t as isize)).vOt - (n.dot((*pTriInfos.offset(t as isize)).vOt) * n); if VNotZero(vOs2) { vOs2 = Normalize(vOs2) } if VNotZero(vOt2) { vOt2 = Normalize(vOt2) } let bAny: bool = if ((*pTriInfos.offset(f as isize)).iFlag | (*pTriInfos.offset(t as isize)).iFlag) & 4i32 != 0i32 { true } else { false }; let bSameOrgFace: bool = iOF_1 == iOF_2; let fCosS: f32 = vOs.dot(vOs2); let fCosT: f32 = vOt.dot(vOt2); if bAny || bSameOrgFace || fCosS > fThresCos && fCosT > fThresCos { let fresh0 = iMembers; iMembers = iMembers + 1; pTmpMembers[fresh0] = t } j += 1 } if iMembers > 1 { let mut uSeed: u32 = 39871946i32 as u32; QuickSort(pTmpMembers.as_mut_ptr(), 0i32, (iMembers - 1) as i32, uSeed); } tmp_group.iNrFaces = iMembers as i32; tmp_group.pTriMembers = pTmpMembers.clone(); bFound = false; l = 0; while l < iUniqueSubGroups && !bFound { bFound = CompareSubGroups(&mut tmp_group, &mut pUniSubGroups[l]); if !bFound { l += 1 } } if !bFound { pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers as i32; pUniSubGroups[iUniqueSubGroups].pTriMembers = tmp_group.pTriMembers.clone(); pSubGroupTspace[iUniqueSubGroups] = EvalTspace( tmp_group.pTriMembers.as_mut_ptr(), iMembers as i32, piTriListIn, pTriInfos, geometry, (*pGroup).iVertexRepresentative, ); iUniqueSubGroups += 1 } let iOffs = (*pTriInfos.offset(f as isize)).iTSpacesOffs as usize; let iVert = (*pTriInfos.offset(f as isize)).vert_num[index as usize] as usize; let mut pTS_out: *mut STSpace = &mut psTspace[iOffs + iVert] as *mut STSpace; if (*pTS_out).iCounter == 1i32 { *pTS_out = AvgTSpace(pTS_out, &mut pSubGroupTspace[l]); (*pTS_out).iCounter = 2i32; (*pTS_out).bOrient = (*pGroup).bOrientPreserving } else { *pTS_out = pSubGroupTspace[l]; (*pTS_out).iCounter = 1i32; (*pTS_out).bOrient = (*pGroup).bOrientPreserving } i += 1 } iUniqueTspaces += iUniqueSubGroups; g += 1 } return true; } unsafe fn AvgTSpace(mut pTS0: *const STSpace, mut pTS1: *const STSpace) -> STSpace { let mut ts_res: STSpace = STSpace { vOs: Vec3::new(0.0, 0.0, 0.0), fMagS: 0., vOt: Vec3::new(0.0, 0.0, 0.0), fMagT: 0., iCounter: 0, bOrient: false, }; if (*pTS0).fMagS == (*pTS1).fMagS && (*pTS0).fMagT == (*pTS1).fMagT && (*pTS0).vOs == (*pTS1).vOs && (*pTS0).vOt == (*pTS1).vOt { ts_res.fMagS = (*pTS0).fMagS; ts_res.fMagT = (*pTS0).fMagT; ts_res.vOs = (*pTS0).vOs; ts_res.vOt = (*pTS0).vOt } else { ts_res.fMagS = 0.5f32 * ((*pTS0).fMagS + (*pTS1).fMagS); ts_res.fMagT = 0.5f32 * ((*pTS0).fMagT + (*pTS1).fMagT); ts_res.vOs = (*pTS0).vOs + (*pTS1).vOs; ts_res.vOt = (*pTS0).vOt + (*pTS1).vOt; if VNotZero(ts_res.vOs) { ts_res.vOs = Normalize(ts_res.vOs) } if VNotZero(ts_res.vOt) { ts_res.vOt = Normalize(ts_res.vOt) } } return ts_res; } unsafe fn Normalize(v: Vec3) -> Vec3 { return (1.0 / v.length()) * v; } unsafe fn VNotZero(v: Vec3) -> bool { NotZero(v.x) || NotZero(v.y) || NotZero(v.z) } unsafe fn NotZero(fX: f32) -> bool { abs(fX) > 1.17549435e-38f32 } unsafe fn EvalTspace( mut face_indices: *mut i32, iFaces: i32, mut piTriListIn: *const i32, mut pTriInfos: *const STriInfo, geometry: &mut I, iVertexRepresentative: i32, ) -> STSpace { let mut res: STSpace = STSpace { vOs: Vec3::new(0.0, 0.0, 0.0), fMagS: 0., vOt: Vec3::new(0.0, 0.0, 0.0), fMagT: 0., iCounter: 0, bOrient: false, }; let mut fAngleSum: f32 = 0i32 as f32; let mut face: i32 = 0i32; res.vOs.x = 0.0f32; res.vOs.y = 0.0f32; res.vOs.z = 0.0f32; res.vOt.x = 0.0f32; res.vOt.y = 0.0f32; res.vOt.z = 0.0f32; res.fMagS = 0i32 as f32; res.fMagT = 0i32 as f32; face = 0i32; while face < iFaces { let f: i32 = *face_indices.offset(face as isize); if (*pTriInfos.offset(f as isize)).iFlag & 4i32 == 0i32 { let mut n = Vec3::new(0.0, 0.0, 0.0); let mut vOs = Vec3::new(0.0, 0.0, 0.0); let mut vOt = Vec3::new(0.0, 0.0, 0.0); let mut p0 = Vec3::new(0.0, 0.0, 0.0); let mut p1 = Vec3::new(0.0, 0.0, 0.0); let mut p2 = Vec3::new(0.0, 0.0, 0.0); let mut v1 = Vec3::new(0.0, 0.0, 0.0); let mut v2 = Vec3::new(0.0, 0.0, 0.0); let mut fCos: f32 = 0.; let mut fAngle: f32 = 0.; let mut fMagS: f32 = 0.; let mut fMagT: f32 = 0.; let mut i: i32 = -1i32; let mut index: i32 = -1i32; let mut i0: i32 = -1i32; let mut i1: i32 = -1i32; let mut i2: i32 = -1i32; if *piTriListIn.offset((3i32 * f + 0i32) as isize) == iVertexRepresentative { i = 0i32 } else if *piTriListIn.offset((3i32 * f + 1i32) as isize) == iVertexRepresentative { i = 1i32 } else if *piTriListIn.offset((3i32 * f + 2i32) as isize) == iVertexRepresentative { i = 2i32 } index = *piTriListIn.offset((3i32 * f + i) as isize); n = get_normal(geometry, index as usize); let mut vOs = (*pTriInfos.offset(f as isize)).vOs - (n.dot((*pTriInfos.offset(f as isize)).vOs) * n); let mut vOt = (*pTriInfos.offset(f as isize)).vOt - (n.dot((*pTriInfos.offset(f as isize)).vOt) * n); if VNotZero(vOs) { vOs = Normalize(vOs) } if VNotZero(vOt) { vOt = Normalize(vOt) } i2 = *piTriListIn.offset((3i32 * f + if i < 2i32 { i + 1i32 } else { 0i32 }) as isize); i1 = *piTriListIn.offset((3i32 * f + i) as isize); i0 = *piTriListIn.offset((3i32 * f + if i > 0i32 { i - 1i32 } else { 2i32 }) as isize); p0 = get_position(geometry, i0 as usize); p1 = get_position(geometry, i1 as usize); p2 = get_position(geometry, i2 as usize); v1 = p0 - p1; v2 = p2 - p1; let mut v1 = v1 - (n.dot(v1) * n); if VNotZero(v1) { v1 = Normalize(v1) } let mut v2 = v2 - (n.dot(v2) * n); if VNotZero(v2) { v2 = Normalize(v2) } let fCos = v1.dot(v2); let fCos = if fCos > 1i32 as f32 { 1i32 as f32 } else if fCos < -1i32 as f32 { -1i32 as f32 } else { fCos }; fAngle = acosf64(fCos as f64) as f32; fMagS = (*pTriInfos.offset(f as isize)).fMagS; fMagT = (*pTriInfos.offset(f as isize)).fMagT; res.vOs = res.vOs + (fAngle * vOs); res.vOt = res.vOt + (fAngle * vOt); res.fMagS += fAngle * fMagS; res.fMagT += fAngle * fMagT; fAngleSum += fAngle } face += 1 } if VNotZero(res.vOs) { res.vOs = Normalize(res.vOs) } if VNotZero(res.vOt) { res.vOt = Normalize(res.vOt) } if fAngleSum > 0i32 as f32 { res.fMagS /= fAngleSum; res.fMagT /= fAngleSum } return res; } unsafe fn CompareSubGroups(mut pg1: *const SSubGroup, mut pg2: *const SSubGroup) -> bool { let mut bStillSame: bool = true; let mut i = 0; if (*pg1).iNrFaces != (*pg2).iNrFaces { return false; } while i < (*pg1).iNrFaces as usize && bStillSame { bStillSame = if (*pg1).pTriMembers[i] == (*pg2).pTriMembers[i] { true } else { false }; if bStillSame { i += 1 } } return bStillSame; } unsafe fn QuickSort(mut pSortBuffer: *mut i32, mut iLeft: i32, mut iRight: i32, mut uSeed: u32) { let mut iL: i32 = 0; let mut iR: i32 = 0; let mut n: i32 = 0; let mut index: i32 = 0; let mut iMid: i32 = 0; let mut iTmp: i32 = 0; // Random let mut t: u32 = uSeed & 31i32 as u32; t = uSeed.rotate_left(t) | uSeed.rotate_right((32i32 as u32).wrapping_sub(t)); uSeed = uSeed.wrapping_add(t).wrapping_add(3i32 as u32); // Random end iL = iLeft; iR = iRight; n = iR - iL + 1i32; index = uSeed.wrapping_rem(n as u32) as i32; iMid = *pSortBuffer.offset((index + iL) as isize); loop { while *pSortBuffer.offset(iL as isize) < iMid { iL += 1 } while *pSortBuffer.offset(iR as isize) > iMid { iR -= 1 } if iL <= iR { iTmp = *pSortBuffer.offset(iL as isize); *pSortBuffer.offset(iL as isize) = *pSortBuffer.offset(iR as isize); *pSortBuffer.offset(iR as isize) = iTmp; iL += 1; iR -= 1 } if !(iL <= iR) { break; } } if iLeft < iR { QuickSort(pSortBuffer, iLeft, iR, uSeed); } if iL < iRight { QuickSort(pSortBuffer, iL, iRight, uSeed); }; } unsafe fn Build4RuleGroups( mut pTriInfos: *mut STriInfo, mut pGroups: *mut SGroup, mut piGroupTrianglesBuffer: *mut i32, mut piTriListIn: *const i32, iNrTrianglesIn: i32, ) -> i32 { let iNrMaxGroups: i32 = iNrTrianglesIn * 3i32; let mut iNrActiveGroups: i32 = 0i32; let mut iOffset: i32 = 0i32; let mut f: i32 = 0i32; let mut i: i32 = 0i32; f = 0i32; while f < iNrTrianglesIn { i = 0i32; while i < 3i32 { if (*pTriInfos.offset(f as isize)).iFlag & 4i32 == 0i32 && (*pTriInfos.offset(f as isize)).AssignedGroup[i as usize].is_null() { let mut bOrPre: bool = false; let mut neigh_indexL: i32 = 0; let mut neigh_indexR: i32 = 0; let vert_index: i32 = *piTriListIn.offset((f * 3i32 + i) as isize); let ref mut fresh2 = (*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]; *fresh2 = ptr::from_mut(&mut *pGroups.offset(iNrActiveGroups as isize)); (*(*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]) .iVertexRepresentative = vert_index; (*(*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]).bOrientPreserving = (*pTriInfos.offset(f as isize)).iFlag & 8i32 != 0i32; (*(*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]).iNrFaces = 0i32; let ref mut fresh3 = (*(*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]).pFaceIndices; *fresh3 = ptr::from_mut(&mut *piGroupTrianglesBuffer.offset(iOffset as isize)); iNrActiveGroups += 1; AddTriToGroup((*pTriInfos.offset(f as isize)).AssignedGroup[i as usize], f); bOrPre = if (*pTriInfos.offset(f as isize)).iFlag & 8i32 != 0i32 { true } else { false }; neigh_indexL = (*pTriInfos.offset(f as isize)).FaceNeighbors[i as usize]; neigh_indexR = (*pTriInfos.offset(f as isize)).FaceNeighbors [(if i > 0i32 { i - 1i32 } else { 2i32 }) as usize]; if neigh_indexL >= 0i32 { let bAnswer: bool = AssignRecur( piTriListIn, pTriInfos, neigh_indexL, (*pTriInfos.offset(f as isize)).AssignedGroup[i as usize], ); let bOrPre2: bool = if (*pTriInfos.offset(neigh_indexL as isize)).iFlag & 8i32 != 0i32 { true } else { false }; let bDiff: bool = if bOrPre != bOrPre2 { true } else { false }; } if neigh_indexR >= 0i32 { let bAnswer_0: bool = AssignRecur( piTriListIn, pTriInfos, neigh_indexR, (*pTriInfos.offset(f as isize)).AssignedGroup[i as usize], ); let bOrPre2_0: bool = if (*pTriInfos.offset(neigh_indexR as isize)).iFlag & 8i32 != 0i32 { true } else { false }; let bDiff_0: bool = if bOrPre != bOrPre2_0 { true } else { false }; } iOffset += (*(*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]).iNrFaces } i += 1 } f += 1 } return iNrActiveGroups; } // /////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// unsafe fn AssignRecur( mut piTriListIn: *const i32, mut psTriInfos: *mut STriInfo, iMyTriIndex: i32, mut pGroup: *mut SGroup, ) -> bool { let mut pMyTriInfo: *mut STriInfo = &mut *psTriInfos.offset(iMyTriIndex as isize) as *mut STriInfo; // track down vertex let iVertRep: i32 = (*pGroup).iVertexRepresentative; let mut pVerts: *const i32 = &*piTriListIn.offset((3i32 * iMyTriIndex + 0i32) as isize) as *const i32; let mut i: i32 = -1i32; if *pVerts.offset(0isize) == iVertRep { i = 0i32 } else if *pVerts.offset(1isize) == iVertRep { i = 1i32 } else if *pVerts.offset(2isize) == iVertRep { i = 2i32 } if (*pMyTriInfo).AssignedGroup[i as usize] == pGroup { return true; } else { if !(*pMyTriInfo).AssignedGroup[i as usize].is_null() { return false; } } if (*pMyTriInfo).iFlag & 4i32 != 0i32 { if (*pMyTriInfo).AssignedGroup[0usize].is_null() && (*pMyTriInfo).AssignedGroup[1usize].is_null() && (*pMyTriInfo).AssignedGroup[2usize].is_null() { (*pMyTriInfo).iFlag &= !8i32; (*pMyTriInfo).iFlag |= if (*pGroup).bOrientPreserving { 8i32 } else { 0i32 } } } let bOrient: bool = if (*pMyTriInfo).iFlag & 8i32 != 0i32 { true } else { false }; if bOrient != (*pGroup).bOrientPreserving { return false; } AddTriToGroup(pGroup, iMyTriIndex); (*pMyTriInfo).AssignedGroup[i as usize] = pGroup; let neigh_indexL: i32 = (*pMyTriInfo).FaceNeighbors[i as usize]; let neigh_indexR: i32 = (*pMyTriInfo).FaceNeighbors[(if i > 0i32 { i - 1i32 } else { 2i32 }) as usize]; if neigh_indexL >= 0i32 { AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); } if neigh_indexR >= 0i32 { AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); } return true; } unsafe fn AddTriToGroup(mut pGroup: *mut SGroup, iTriIndex: i32) { *(*pGroup).pFaceIndices.offset((*pGroup).iNrFaces as isize) = iTriIndex; (*pGroup).iNrFaces += 1; } unsafe fn InitTriInfo( mut pTriInfos: *mut STriInfo, mut piTriListIn: *const i32, geometry: &mut I, iNrTrianglesIn: usize, ) { let mut f = 0; let mut i = 0; let mut t = 0; f = 0; while f < iNrTrianglesIn { i = 0i32; while i < 3i32 { (*pTriInfos.offset(f as isize)).FaceNeighbors[i as usize] = -1i32; let ref mut fresh4 = (*pTriInfos.offset(f as isize)).AssignedGroup[i as usize]; *fresh4 = 0 as *mut SGroup; (*pTriInfos.offset(f as isize)).vOs.x = 0.0f32; (*pTriInfos.offset(f as isize)).vOs.y = 0.0f32; (*pTriInfos.offset(f as isize)).vOs.z = 0.0f32; (*pTriInfos.offset(f as isize)).vOt.x = 0.0f32; (*pTriInfos.offset(f as isize)).vOt.y = 0.0f32; (*pTriInfos.offset(f as isize)).vOt.z = 0.0f32; (*pTriInfos.offset(f as isize)).fMagS = 0i32 as f32; (*pTriInfos.offset(f as isize)).fMagT = 0i32 as f32; (*pTriInfos.offset(f as isize)).iFlag |= 4i32; i += 1 } f += 1 } f = 0; while f < iNrTrianglesIn { let v1 = get_position(geometry, *piTriListIn.offset((f * 3 + 0) as isize) as usize); let v2 = get_position(geometry, *piTriListIn.offset((f * 3 + 1) as isize) as usize); let v3 = get_position(geometry, *piTriListIn.offset((f * 3 + 2) as isize) as usize); let t1 = get_tex_coord(geometry, *piTriListIn.offset((f * 3 + 0) as isize) as usize); let t2 = get_tex_coord(geometry, *piTriListIn.offset((f * 3 + 1) as isize) as usize); let t3 = get_tex_coord(geometry, *piTriListIn.offset((f * 3 + 2) as isize) as usize); let t21x: f32 = t2.x - t1.x; let t21y: f32 = t2.y - t1.y; let t31x: f32 = t3.x - t1.x; let t31y: f32 = t3.y - t1.y; let d1 = v2 - v1; let d2 = v3 - v1; let fSignedAreaSTx2: f32 = t21x * t31y - t21y * t31x; let mut vOs = (t31y * d1) - (t21y * d2); let mut vOt = (-t31x * d1) + (t21x * d2); (*pTriInfos.offset(f as isize)).iFlag |= if fSignedAreaSTx2 > 0i32 as f32 { 8i32 } else { 0i32 }; if NotZero(fSignedAreaSTx2) { let fAbsArea: f32 = abs(fSignedAreaSTx2); let fLenOs: f32 = vOs.length(); let fLenOt: f32 = vOt.length(); let fS: f32 = if (*pTriInfos.offset(f as isize)).iFlag & 8i32 == 0i32 { -1.0f32 } else { 1.0f32 }; if NotZero(fLenOs) { (*pTriInfos.offset(f as isize)).vOs = (fS / fLenOs) * vOs } if NotZero(fLenOt) { (*pTriInfos.offset(f as isize)).vOt = (fS / fLenOt) * vOt } (*pTriInfos.offset(f as isize)).fMagS = fLenOs / fAbsArea; (*pTriInfos.offset(f as isize)).fMagT = fLenOt / fAbsArea; if NotZero((*pTriInfos.offset(f as isize)).fMagS) && NotZero((*pTriInfos.offset(f as isize)).fMagT) { (*pTriInfos.offset(f as isize)).iFlag &= !4i32 } } f += 1 } while t + 1 < iNrTrianglesIn { let iFO_a: i32 = (*pTriInfos.offset(t as isize)).iOrgFaceNumber; let iFO_b: i32 = (*pTriInfos.offset((t + 1) as isize)).iOrgFaceNumber; if iFO_a == iFO_b { let bIsDeg_a: bool = if (*pTriInfos.offset(t as isize)).iFlag & 1i32 != 0i32 { true } else { false }; let bIsDeg_b: bool = if (*pTriInfos.offset((t + 1) as isize)).iFlag & 1i32 != 0i32 { true } else { false }; if !(bIsDeg_a || bIsDeg_b) { let bOrientA: bool = if (*pTriInfos.offset(t as isize)).iFlag & 8i32 != 0i32 { true } else { false }; let bOrientB: bool = if (*pTriInfos.offset((t + 1) as isize)).iFlag & 8i32 != 0i32 { true } else { false }; if bOrientA != bOrientB { let mut bChooseOrientFirstTri: bool = false; if (*pTriInfos.offset((t + 1) as isize)).iFlag & 4i32 != 0i32 { bChooseOrientFirstTri = true } else if CalcTexArea(geometry, piTriListIn.offset((t * 3 + 0) as isize)) >= CalcTexArea(geometry, piTriListIn.offset(((t + 1) * 3 + 0) as isize)) { bChooseOrientFirstTri = true } let t0 = if bChooseOrientFirstTri { t } else { t + 1 }; let t1_0 = if bChooseOrientFirstTri { t + 1 } else { t }; (*pTriInfos.offset(t1_0 as isize)).iFlag &= !8i32; (*pTriInfos.offset(t1_0 as isize)).iFlag |= (*pTriInfos.offset(t0 as isize)).iFlag & 8i32 } } t += 2 } else { t += 1 } } let mut pEdges = vec![SEdge::zero(); iNrTrianglesIn * 3]; BuildNeighborsFast( pTriInfos, pEdges.as_mut_ptr(), piTriListIn, iNrTrianglesIn as i32, ); } unsafe fn BuildNeighborsFast( mut pTriInfos: *mut STriInfo, mut pEdges: *mut SEdge, mut piTriListIn: *const i32, iNrTrianglesIn: i32, ) { // build array of edges // could replace with a random seed? let mut uSeed: u32 = 39871946i32 as u32; let mut iEntries: i32 = 0i32; let mut iCurStartIndex: i32 = -1i32; let mut f: i32 = 0i32; let mut i: i32 = 0i32; f = 0i32; while f < iNrTrianglesIn { i = 0i32; while i < 3i32 { let i0: i32 = *piTriListIn.offset((f * 3i32 + i) as isize); let i1: i32 = *piTriListIn.offset((f * 3i32 + if i < 2i32 { i + 1i32 } else { 0i32 }) as isize); (*pEdges.offset((f * 3i32 + i) as isize)).unnamed.i0 = if i0 < i1 { i0 } else { i1 }; (*pEdges.offset((f * 3i32 + i) as isize)).unnamed.i1 = if !(i0 < i1) { i0 } else { i1 }; (*pEdges.offset((f * 3i32 + i) as isize)).unnamed.f = f; i += 1 } f += 1 } QuickSortEdges(pEdges, 0i32, iNrTrianglesIn * 3i32 - 1i32, 0i32, uSeed); iEntries = iNrTrianglesIn * 3i32; iCurStartIndex = 0i32; i = 1i32; while i < iEntries { if (*pEdges.offset(iCurStartIndex as isize)).unnamed.i0 != (*pEdges.offset(i as isize)).unnamed.i0 { let iL: i32 = iCurStartIndex; let iR: i32 = i - 1i32; iCurStartIndex = i; QuickSortEdges(pEdges, iL, iR, 1i32, uSeed); } i += 1 } iCurStartIndex = 0i32; i = 1i32; while i < iEntries { if (*pEdges.offset(iCurStartIndex as isize)).unnamed.i0 != (*pEdges.offset(i as isize)).unnamed.i0 || (*pEdges.offset(iCurStartIndex as isize)).unnamed.i1 != (*pEdges.offset(i as isize)).unnamed.i1 { let iL_0: i32 = iCurStartIndex; let iR_0: i32 = i - 1i32; iCurStartIndex = i; QuickSortEdges(pEdges, iL_0, iR_0, 2i32, uSeed); } i += 1 } i = 0i32; while i < iEntries { let i0_0: i32 = (*pEdges.offset(i as isize)).unnamed.i0; let i1_0: i32 = (*pEdges.offset(i as isize)).unnamed.i1; let f_0: i32 = (*pEdges.offset(i as isize)).unnamed.f; let mut bUnassigned_A: bool = false; let mut i0_A: i32 = 0; let mut i1_A: i32 = 0; let mut edgenum_A: i32 = 0; let mut edgenum_B: i32 = 0i32; GetEdge( &mut i0_A, &mut i1_A, &mut edgenum_A, &*piTriListIn.offset((f_0 * 3i32) as isize), i0_0, i1_0, ); bUnassigned_A = if (*pTriInfos.offset(f_0 as isize)).FaceNeighbors[edgenum_A as usize] == -1i32 { true } else { false }; if bUnassigned_A { let mut j: i32 = i + 1i32; let mut t: i32 = 0; let mut bNotFound: bool = true; while j < iEntries && i0_0 == (*pEdges.offset(j as isize)).unnamed.i0 && i1_0 == (*pEdges.offset(j as isize)).unnamed.i1 && bNotFound { let mut bUnassigned_B: bool = false; let mut i0_B: i32 = 0; let mut i1_B: i32 = 0; t = (*pEdges.offset(j as isize)).unnamed.f; GetEdge( &mut i1_B, &mut i0_B, &mut edgenum_B, &*piTriListIn.offset((t * 3i32) as isize), (*pEdges.offset(j as isize)).unnamed.i0, (*pEdges.offset(j as isize)).unnamed.i1, ); bUnassigned_B = if (*pTriInfos.offset(t as isize)).FaceNeighbors[edgenum_B as usize] == -1i32 { true } else { false }; if i0_A == i0_B && i1_A == i1_B && bUnassigned_B { bNotFound = false } else { j += 1 } } if !bNotFound { let mut t_0: i32 = (*pEdges.offset(j as isize)).unnamed.f; (*pTriInfos.offset(f_0 as isize)).FaceNeighbors[edgenum_A as usize] = t_0; (*pTriInfos.offset(t_0 as isize)).FaceNeighbors[edgenum_B as usize] = f_0 } } i += 1 } } unsafe fn GetEdge( mut i0_out: *mut i32, mut i1_out: *mut i32, mut edgenum_out: *mut i32, mut indices: *const i32, i0_in: i32, i1_in: i32, ) { *edgenum_out = -1i32; if *indices.offset(0isize) == i0_in || *indices.offset(0isize) == i1_in { if *indices.offset(1isize) == i0_in || *indices.offset(1isize) == i1_in { *edgenum_out.offset(0isize) = 0i32; *i0_out.offset(0isize) = *indices.offset(0isize); *i1_out.offset(0isize) = *indices.offset(1isize) } else { *edgenum_out.offset(0isize) = 2i32; *i0_out.offset(0isize) = *indices.offset(2isize); *i1_out.offset(0isize) = *indices.offset(0isize) } } else { *edgenum_out.offset(0isize) = 1i32; *i0_out.offset(0isize) = *indices.offset(1isize); *i1_out.offset(0isize) = *indices.offset(2isize) }; } // /////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// unsafe fn QuickSortEdges( mut pSortBuffer: *mut SEdge, mut iLeft: i32, mut iRight: i32, channel: i32, mut uSeed: u32, ) { let mut t: u32 = 0; let mut iL: i32 = 0; let mut iR: i32 = 0; let mut n: i32 = 0; let mut index: i32 = 0; let mut iMid: i32 = 0; // early out let mut sTmp: SEdge = SEdge { unnamed: unnamed { i0: 0, i1: 0, f: 0 }, }; let iElems: i32 = iRight - iLeft + 1i32; if iElems < 2i32 { return; } else { if iElems == 2i32 { if (*pSortBuffer.offset(iLeft as isize)).array[channel as usize] > (*pSortBuffer.offset(iRight as isize)).array[channel as usize] { sTmp = *pSortBuffer.offset(iLeft as isize); *pSortBuffer.offset(iLeft as isize) = *pSortBuffer.offset(iRight as isize); *pSortBuffer.offset(iRight as isize) = sTmp } return; } } // Random t = uSeed & 31i32 as u32; t = uSeed.rotate_left(t) | uSeed.rotate_right((32i32 as u32).wrapping_sub(t)); uSeed = uSeed.wrapping_add(t).wrapping_add(3i32 as u32); // Random end iL = iLeft; iR = iRight; n = iR - iL + 1i32; index = uSeed.wrapping_rem(n as u32) as i32; iMid = (*pSortBuffer.offset((index + iL) as isize)).array[channel as usize]; loop { while (*pSortBuffer.offset(iL as isize)).array[channel as usize] < iMid { iL += 1 } while (*pSortBuffer.offset(iR as isize)).array[channel as usize] > iMid { iR -= 1 } if iL <= iR { sTmp = *pSortBuffer.offset(iL as isize); *pSortBuffer.offset(iL as isize) = *pSortBuffer.offset(iR as isize); *pSortBuffer.offset(iR as isize) = sTmp; iL += 1; iR -= 1 } if !(iL <= iR) { break; } } if iLeft < iR { QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); } if iL < iRight { QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); }; } // returns the texture area times 2 unsafe fn CalcTexArea(geometry: &mut I, mut indices: *const i32) -> f32 { let t1 = get_tex_coord(geometry, *indices.offset(0isize) as usize); let t2 = get_tex_coord(geometry, *indices.offset(1isize) as usize); let t3 = get_tex_coord(geometry, *indices.offset(2isize) as usize); let t21x: f32 = t2.x - t1.x; let t21y: f32 = t2.y - t1.y; let t31x: f32 = t3.x - t1.x; let t31y: f32 = t3.y - t1.y; let fSignedAreaSTx2: f32 = t21x * t31y - t21y * t31x; return if fSignedAreaSTx2 < 0i32 as f32 { -fSignedAreaSTx2 } else { fSignedAreaSTx2 }; } // degen triangles unsafe fn DegenPrologue( mut pTriInfos: *mut STriInfo, mut piTriList_out: *mut i32, iNrTrianglesIn: i32, iTotTris: i32, ) { let mut iNextGoodTriangleSearchIndex: i32 = -1i32; let mut bStillFindingGoodOnes: bool = false; // locate quads with only one good triangle let mut t: i32 = 0i32; while t < iTotTris - 1i32 { let iFO_a: i32 = (*pTriInfos.offset(t as isize)).iOrgFaceNumber; let iFO_b: i32 = (*pTriInfos.offset((t + 1i32) as isize)).iOrgFaceNumber; if iFO_a == iFO_b { let bIsDeg_a: bool = if (*pTriInfos.offset(t as isize)).iFlag & 1i32 != 0i32 { true } else { false }; let bIsDeg_b: bool = if (*pTriInfos.offset((t + 1i32) as isize)).iFlag & 1i32 != 0i32 { true } else { false }; if bIsDeg_a ^ bIsDeg_b != false { (*pTriInfos.offset(t as isize)).iFlag |= 2i32; (*pTriInfos.offset((t + 1i32) as isize)).iFlag |= 2i32 } t += 2i32 } else { t += 1 } } iNextGoodTriangleSearchIndex = 1i32; t = 0i32; bStillFindingGoodOnes = true; while t < iNrTrianglesIn && bStillFindingGoodOnes { let bIsGood: bool = if (*pTriInfos.offset(t as isize)).iFlag & 1i32 == 0i32 { true } else { false }; if bIsGood { if iNextGoodTriangleSearchIndex < t + 2i32 { iNextGoodTriangleSearchIndex = t + 2i32 } } else { let mut t0: i32 = 0; let mut t1: i32 = 0; let mut bJustADegenerate: bool = true; while bJustADegenerate && iNextGoodTriangleSearchIndex < iTotTris { let bIsGood_0: bool = if (*pTriInfos.offset(iNextGoodTriangleSearchIndex as isize)).iFlag & 1i32 == 0i32 { true } else { false }; if bIsGood_0 { bJustADegenerate = false } else { iNextGoodTriangleSearchIndex += 1 } } t0 = t; t1 = iNextGoodTriangleSearchIndex; iNextGoodTriangleSearchIndex += 1; if !bJustADegenerate { let mut i: i32 = 0i32; i = 0i32; while i < 3i32 { let index: i32 = *piTriList_out.offset((t0 * 3i32 + i) as isize); *piTriList_out.offset((t0 * 3i32 + i) as isize) = *piTriList_out.offset((t1 * 3i32 + i) as isize); *piTriList_out.offset((t1 * 3i32 + i) as isize) = index; i += 1 } let tri_info: STriInfo = *pTriInfos.offset(t0 as isize); *pTriInfos.offset(t0 as isize) = *pTriInfos.offset(t1 as isize); *pTriInfos.offset(t1 as isize) = tri_info } else { bStillFindingGoodOnes = false } } if bStillFindingGoodOnes { t += 1 } } } unsafe fn GenerateSharedVerticesIndexList( mut piTriList_in_and_out: *mut i32, geometry: &mut I, iNrTrianglesIn: usize, ) { let mut i = 0; let mut iChannel: i32 = 0i32; let mut k = 0; let mut e = 0; let mut iMaxCount = 0; let mut vMin = get_position(geometry, 0); let mut vMax = vMin; let mut vDim = Vec3::new(0.0, 0.0, 0.0); let mut fMin: f32 = 0.; let mut fMax: f32 = 0.; i = 1; while i < iNrTrianglesIn * 3 { let index: i32 = *piTriList_in_and_out.offset(i as isize); let vP = get_position(geometry, index as usize); if vMin.x > vP.x { vMin.x = vP.x } else if vMax.x < vP.x { vMax.x = vP.x } if vMin.y > vP.y { vMin.y = vP.y } else if vMax.y < vP.y { vMax.y = vP.y } if vMin.z > vP.z { vMin.z = vP.z } else if vMax.z < vP.z { vMax.z = vP.z } i += 1 } vDim = vMax - vMin; iChannel = 0i32; fMin = vMin.x; fMax = vMax.x; if vDim.y > vDim.x && vDim.y > vDim.z { iChannel = 1i32; fMin = vMin.y; fMax = vMax.y } else if vDim.z > vDim.x { iChannel = 2i32; fMin = vMin.z; fMax = vMax.z } let mut piHashTable = vec![0i32; iNrTrianglesIn * 3]; let mut piHashOffsets = vec![0i32; g_iCells]; let mut piHashCount = vec![0i32; g_iCells]; let mut piHashCount2 = vec![0i32; g_iCells]; i = 0; while i < iNrTrianglesIn * 3 { let index_0: i32 = *piTriList_in_and_out.offset(i as isize); let vP_0 = get_position(geometry, index_0 as usize); let fVal: f32 = if iChannel == 0i32 { vP_0.x } else if iChannel == 1i32 { vP_0.y } else { vP_0.z }; let iCell = FindGridCell(fMin, fMax, fVal); piHashCount[iCell] += 1; i += 1 } piHashOffsets[0] = 0i32; k = 1; while k < g_iCells { piHashOffsets[k] = piHashOffsets[k - 1] + piHashCount[k - 1]; k += 1 } i = 0; while i < iNrTrianglesIn * 3 { let index_1: i32 = *piTriList_in_and_out.offset(i as isize); let vP_1 = get_position(geometry, index_1 as usize); let fVal_0: f32 = if iChannel == 0i32 { vP_1.x } else if iChannel == 1i32 { vP_1.y } else { vP_1.z }; let iCell_0 = FindGridCell(fMin, fMax, fVal_0); piHashTable[(piHashOffsets[iCell_0] + piHashCount2[iCell_0]) as usize] = i as i32; piHashCount2[iCell_0] += 1; i += 1 } k = 0; while k < g_iCells { k += 1 } iMaxCount = piHashCount[0] as usize; k = 1; while k < g_iCells { if iMaxCount < piHashCount[k] as usize { iMaxCount = piHashCount[k] as usize } k += 1 } let mut pTmpVert = vec![STmpVert::zero(); iMaxCount]; k = 0; while k < g_iCells { // extract table of cell k and amount of entries in it let pTable_0 = piHashTable.as_mut_ptr().offset(piHashOffsets[k] as isize); let iEntries = piHashCount[k] as usize; if !(iEntries < 2) { e = 0; while e < iEntries { let mut i_0: i32 = *pTable_0.offset(e as isize); let vP_2 = get_position( geometry, *piTriList_in_and_out.offset(i_0 as isize) as usize, ); pTmpVert[e].vert[0usize] = vP_2.x; pTmpVert[e].vert[1usize] = vP_2.y; pTmpVert[e].vert[2usize] = vP_2.z; pTmpVert[e].index = i_0; e += 1 } MergeVertsFast( piTriList_in_and_out, pTmpVert.as_mut_ptr(), geometry, 0i32, (iEntries - 1) as i32, ); } k += 1 } } unsafe fn MergeVertsFast( mut piTriList_in_and_out: *mut i32, mut pTmpVert: *mut STmpVert, geometry: &mut I, iL_in: i32, iR_in: i32, ) { // make bbox let mut c: i32 = 0i32; let mut l: i32 = 0i32; let mut channel: i32 = 0i32; let mut fvMin: [f32; 3] = [0.; 3]; let mut fvMax: [f32; 3] = [0.; 3]; let mut dx: f32 = 0i32 as f32; let mut dy: f32 = 0i32 as f32; let mut dz: f32 = 0i32 as f32; let mut fSep: f32 = 0i32 as f32; c = 0i32; while c < 3i32 { fvMin[c as usize] = (*pTmpVert.offset(iL_in as isize)).vert[c as usize]; fvMax[c as usize] = fvMin[c as usize]; c += 1 } l = iL_in + 1i32; while l <= iR_in { c = 0i32; while c < 3i32 { if fvMin[c as usize] > (*pTmpVert.offset(l as isize)).vert[c as usize] { fvMin[c as usize] = (*pTmpVert.offset(l as isize)).vert[c as usize] } else if fvMax[c as usize] < (*pTmpVert.offset(l as isize)).vert[c as usize] { fvMax[c as usize] = (*pTmpVert.offset(l as isize)).vert[c as usize] } c += 1 } l += 1 } dx = fvMax[0usize] - fvMin[0usize]; dy = fvMax[1usize] - fvMin[1usize]; dz = fvMax[2usize] - fvMin[2usize]; channel = 0i32; if dy > dx && dy > dz { channel = 1i32 } else if dz > dx { channel = 2i32 } fSep = 0.5f32 * (fvMax[channel as usize] + fvMin[channel as usize]); if fSep >= fvMax[channel as usize] || fSep <= fvMin[channel as usize] { l = iL_in; while l <= iR_in { let mut i: i32 = (*pTmpVert.offset(l as isize)).index; let index: i32 = *piTriList_in_and_out.offset(i as isize); let vP = get_position(geometry, index as usize); let vN = get_normal(geometry, index as usize); let vT = get_tex_coord(geometry, index as usize); let mut bNotFound: bool = true; let mut l2: i32 = iL_in; let mut i2rec: i32 = -1i32; while l2 < l && bNotFound { let i2: i32 = (*pTmpVert.offset(l2 as isize)).index; let index2: i32 = *piTriList_in_and_out.offset(i2 as isize); let vP2 = get_position(geometry, index2 as usize); let vN2 = get_normal(geometry, index2 as usize); let vT2 = get_tex_coord(geometry, index2 as usize); i2rec = i2; if vP.x == vP2.x && vP.y == vP2.y && vP.z == vP2.z && vN.x == vN2.x && vN.y == vN2.y && vN.z == vN2.z && vT.x == vT2.x && vT.y == vT2.y && vT.z == vT2.z { bNotFound = false } else { l2 += 1 } } if !bNotFound { *piTriList_in_and_out.offset(i as isize) = *piTriList_in_and_out.offset(i2rec as isize) } l += 1 } } else { let mut iL: i32 = iL_in; let mut iR: i32 = iR_in; while iL < iR { let mut bReadyLeftSwap: bool = false; let mut bReadyRightSwap: bool = false; while !bReadyLeftSwap && iL < iR { bReadyLeftSwap = !((*pTmpVert.offset(iL as isize)).vert[channel as usize] < fSep); if !bReadyLeftSwap { iL += 1 } } while !bReadyRightSwap && iL < iR { bReadyRightSwap = (*pTmpVert.offset(iR as isize)).vert[channel as usize] < fSep; if !bReadyRightSwap { iR -= 1 } } if bReadyLeftSwap && bReadyRightSwap { let sTmp: STmpVert = *pTmpVert.offset(iL as isize); *pTmpVert.offset(iL as isize) = *pTmpVert.offset(iR as isize); *pTmpVert.offset(iR as isize) = sTmp; iL += 1; iR -= 1 } } if iL == iR { let bReadyRightSwap_0: bool = (*pTmpVert.offset(iR as isize)).vert[channel as usize] < fSep; if bReadyRightSwap_0 { iL += 1 } else { iR -= 1 } } if iL_in < iR { MergeVertsFast(piTriList_in_and_out, pTmpVert, geometry, iL_in, iR); } if iL < iR_in { MergeVertsFast(piTriList_in_and_out, pTmpVert, geometry, iL, iR_in); } }; } const g_iCells: usize = 2048; // it is IMPORTANT that this function is called to evaluate the hash since // inlining could potentially reorder instructions and generate different // results for the same effective input value fVal. #[inline(never)] unsafe fn FindGridCell(fMin: f32, fMax: f32, fVal: f32) -> usize { let fIndex = g_iCells as f32 * ((fVal - fMin) / (fMax - fMin)); let iIndex = fIndex as isize; return if iIndex < g_iCells as isize { if iIndex >= 0 { iIndex as usize } else { 0 } } else { g_iCells - 1 }; } unsafe fn GenerateInitialVerticesIndexList( pTriInfos: &mut [STriInfo], piTriList_out: &mut [i32], geometry: &mut I, iNrTrianglesIn: usize, ) -> usize { let mut iTSpacesOffs: usize = 0; let mut f = 0; let mut t: usize = 0; let mut iDstTriIndex = 0; f = 0; while f < geometry.num_faces() { let verts = geometry.num_vertices_of_face(f); if !(verts != 3 && verts != 4) { pTriInfos[iDstTriIndex].iOrgFaceNumber = f as i32; pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs as i32; if verts == 3 { let mut pVerts = &mut pTriInfos[iDstTriIndex].vert_num; pVerts[0] = 0; pVerts[1] = 1; pVerts[2] = 2; piTriList_out[iDstTriIndex * 3 + 0] = face_vert_to_index(f, 0) as i32; piTriList_out[iDstTriIndex * 3 + 1] = face_vert_to_index(f, 1) as i32; piTriList_out[iDstTriIndex * 3 + 2] = face_vert_to_index(f, 2) as i32; iDstTriIndex += 1 } else { pTriInfos[iDstTriIndex + 1].iOrgFaceNumber = f as i32; pTriInfos[iDstTriIndex + 1].iTSpacesOffs = iTSpacesOffs as i32; let i0 = face_vert_to_index(f, 0); let i1 = face_vert_to_index(f, 1); let i2 = face_vert_to_index(f, 2); let i3 = face_vert_to_index(f, 3); let T0 = get_tex_coord(geometry, i0); let T1 = get_tex_coord(geometry, i1); let T2 = get_tex_coord(geometry, i2); let T3 = get_tex_coord(geometry, i3); let distSQ_02: f32 = (T2 - T0).length_squared(); let distSQ_13: f32 = (T3 - T1).length_squared(); let mut bQuadDiagIs_02: bool = false; if distSQ_02 < distSQ_13 { bQuadDiagIs_02 = true } else if distSQ_13 < distSQ_02 { bQuadDiagIs_02 = false } else { let P0 = get_position(geometry, i0); let P1 = get_position(geometry, i1); let P2 = get_position(geometry, i2); let P3 = get_position(geometry, i3); let distSQ_02_0: f32 = (P2 - P0).length_squared(); let distSQ_13_0: f32 = (P3 - P1).length_squared(); bQuadDiagIs_02 = if distSQ_13_0 < distSQ_02_0 { false } else { true } } if bQuadDiagIs_02 { let mut pVerts_A = &mut pTriInfos[iDstTriIndex].vert_num; pVerts_A[0] = 0; pVerts_A[1] = 1; pVerts_A[2] = 2; piTriList_out[iDstTriIndex * 3 + 0] = i0 as i32; piTriList_out[iDstTriIndex * 3 + 1] = i1 as i32; piTriList_out[iDstTriIndex * 3 + 2] = i2 as i32; iDstTriIndex += 1; let mut pVerts_B = &mut pTriInfos[iDstTriIndex].vert_num; pVerts_B[0] = 0; pVerts_B[1] = 2; pVerts_B[2] = 3; piTriList_out[iDstTriIndex * 3 + 0] = i0 as i32; piTriList_out[iDstTriIndex * 3 + 1] = i2 as i32; piTriList_out[iDstTriIndex * 3 + 2] = i3 as i32; iDstTriIndex += 1 } else { let mut pVerts_A_0 = &mut pTriInfos[iDstTriIndex].vert_num; pVerts_A_0[0] = 0; pVerts_A_0[1] = 1; pVerts_A_0[2] = 3; piTriList_out[iDstTriIndex * 3 + 0] = i0 as i32; piTriList_out[iDstTriIndex * 3 + 1] = i1 as i32; piTriList_out[iDstTriIndex * 3 + 2] = i3 as i32; iDstTriIndex += 1; let mut pVerts_B_0 = &mut pTriInfos[iDstTriIndex].vert_num; pVerts_B_0[0] = 1; pVerts_B_0[1] = 2; pVerts_B_0[2] = 3; piTriList_out[iDstTriIndex * 3 + 0] = i1 as i32; piTriList_out[iDstTriIndex * 3 + 1] = i2 as i32; piTriList_out[iDstTriIndex * 3 + 2] = i3 as i32; iDstTriIndex += 1 } } iTSpacesOffs += verts } f += 1 } t = 0; while t < iNrTrianglesIn { pTriInfos[t].iFlag = 0; t += 1 } return iTSpacesOffs; } fn cos(value: f32) -> f32 { #[cfg(feature = "std")] { value.cos() } #[cfg(all(not(feature = "std"), feature = "libm"))] { libm::cosf(value) } #[cfg(all(not(feature = "std"), not(feature = "libm")))] { compile_error!("Require either 'libm' or 'std' for `cos`") } } fn acos(value: f32) -> f32 { #[cfg(feature = "std")] { value.acos() } #[cfg(all(not(feature = "std"), feature = "libm"))] { libm::acosf(value) } #[cfg(all(not(feature = "std"), not(feature = "libm")))] { compile_error!("Require either 'libm' or 'std' for `acos`") } } fn abs(value: f32) -> f32 { #[cfg(feature = "std")] { value.abs() } #[cfg(all(not(feature = "std"), feature = "libm"))] { libm::fabsf(value) } #[cfg(all(not(feature = "std"), not(feature = "libm")))] { compile_error!("Require either 'libm' or 'std' for `abs`") } } fn acosf64(value: f64) -> f64 { #[cfg(feature = "std")] { value.acos() } #[cfg(all(not(feature = "std"), feature = "libm"))] { libm::acos(value) } #[cfg(all(not(feature = "std"), not(feature = "libm")))] { compile_error!("Require either 'libm' or 'std' for `acos`") } }