Changes to protect against use of normalize3fast on degenerate vectors
parent
48324a9383
commit
d2b253f1f6
|
|
@ -568,6 +568,12 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
|
|||
|
||||
F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
|
||||
|
||||
LLVector4a default_norm;
|
||||
LLVector4a default_binorm;
|
||||
|
||||
default_norm.set(0,1,0,1);
|
||||
default_binorm.set(1,0,0,1);
|
||||
|
||||
for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
|
||||
{
|
||||
S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];
|
||||
|
|
@ -597,19 +603,31 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
|
|||
norm.mul(delta_weight*maskWeight*NORMAL_SOFTEN_FACTOR);
|
||||
scaled_normals[vert_index_mesh].add(norm);
|
||||
norm = scaled_normals[vert_index_mesh];
|
||||
norm.normalize3fast();
|
||||
|
||||
// guard against degenerate input data before we create NaNs below!
|
||||
//
|
||||
norm.normalize3fast_checked(&default_norm);
|
||||
normals[vert_index_mesh] = norm;
|
||||
|
||||
// calculate new binormals
|
||||
LLVector4a binorm = mMorphData->mBinormals[vert_index_morph];
|
||||
|
||||
// guard against degenerate input data before we create NaNs below!
|
||||
//
|
||||
if (!binorm.isFinite3() || (binorm.dot3(binorm).getF32() <= F_APPROXIMATELY_ZERO))
|
||||
{
|
||||
binorm.set(1,0,0,1);
|
||||
}
|
||||
|
||||
binorm.mul(delta_weight*maskWeight*NORMAL_SOFTEN_FACTOR);
|
||||
scaled_binormals[vert_index_mesh].add(binorm);
|
||||
LLVector4a tangent;
|
||||
tangent.setCross3(scaled_binormals[vert_index_mesh], norm);
|
||||
LLVector4a& normalized_binormal = binormals[vert_index_mesh];
|
||||
normalized_binormal.setCross3(norm, tangent);
|
||||
normalized_binormal.normalize3fast();
|
||||
|
||||
|
||||
normalized_binormal.setCross3(norm, tangent);
|
||||
normalized_binormal.normalize3fast_checked(&default_binorm);
|
||||
|
||||
tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,11 @@ public:
|
|||
// Note that this does not consider zero length vectors!
|
||||
inline void normalize3fast();
|
||||
|
||||
// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
|
||||
// Same as above except substitutes default vector contents if the vector is non-finite or degenerate due to zero length.
|
||||
//
|
||||
inline void normalize3fast_checked(LLVector4a* default = NULL);
|
||||
|
||||
// Return true if this vector is normalized with respect to x,y,z up to tolerance
|
||||
inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -410,8 +410,26 @@ inline LLSimdScalar LLVector4a::normalize3withLength()
|
|||
// Note that this does not consider zero length vectors!
|
||||
inline void LLVector4a::normalize3fast()
|
||||
{
|
||||
// find out about bad math before it takes two man-days to track down
|
||||
llassert(isFinite3() && !equals3(getZero()));
|
||||
LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
|
||||
const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
|
||||
mQ = _mm_mul_ps( mQ, approxRsqrt );
|
||||
}
|
||||
|
||||
// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
|
||||
// Note that this does not consider zero length vectors!
|
||||
inline void LLVector4a::normalize3fast_checked(LLVector4a* default)
|
||||
{
|
||||
// handle bogus inputs before NaNs are generated below
|
||||
//
|
||||
if (!isFinite3() || (dot3(*this).getF32() < F_APPROXIMATELY_ZERO))
|
||||
{
|
||||
if (default)
|
||||
*this = *default;
|
||||
else
|
||||
set(0,1,0,1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this );
|
||||
const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ);
|
||||
|
|
|
|||
|
|
@ -4472,6 +4472,9 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
|
|||
continue; //skip degenerate face
|
||||
}
|
||||
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
|
||||
//for each edge
|
||||
for (S32 k = 0; k < 3; k++) {
|
||||
S32 index = face.mEdge[j*3+k];
|
||||
|
|
@ -4493,14 +4496,14 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
|
|||
|
||||
norm_mat.rotate(n[v1], t);
|
||||
|
||||
t.normalize3fast();
|
||||
t.normalize3fast_checked(&default_norm);
|
||||
normals.push_back(LLVector3(t[0], t[1], t[2]));
|
||||
|
||||
mat.affineTransform(v[v2], t);
|
||||
vertices.push_back(LLVector3(t[0], t[1], t[2]));
|
||||
|
||||
norm_mat.rotate(n[v2], t);
|
||||
t.normalize3fast();
|
||||
t.normalize3fast_checked(&default_norm);
|
||||
normals.push_back(LLVector3(t[0], t[1], t[2]));
|
||||
}
|
||||
}
|
||||
|
|
@ -6096,6 +6099,9 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
|
|||
{
|
||||
VertexData corners[4];
|
||||
VertexData baseVert;
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
|
||||
for(S32 t = 0; t < 4; t++)
|
||||
{
|
||||
corners[t].getPosition().load3( mesh[offset + (grid_size*t)].mPos.mV);
|
||||
|
|
@ -6108,8 +6114,8 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
|
|||
lhs.setSub(corners[1].getPosition(), corners[0].getPosition());
|
||||
LLVector4a rhs;
|
||||
rhs.setSub(corners[2].getPosition(), corners[1].getPosition());
|
||||
baseVert.getNormal().setCross3(lhs, rhs);
|
||||
baseVert.getNormal().normalize3fast();
|
||||
baseVert.getNormal().setCross3(lhs, rhs);
|
||||
baseVert.getNormal().normalize3fast_checked(&default_norm);
|
||||
}
|
||||
|
||||
if(!(mTypeMask & TOP_MASK))
|
||||
|
|
@ -6559,17 +6565,12 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
|
|||
d1.setSub(mPositions[mIndices[2]], mPositions[mIndices[0]]);
|
||||
|
||||
LLVector4a normal;
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
|
||||
normal.setCross3(d0,d1);
|
||||
|
||||
if (normal.dot3(normal).getF32() > F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
normal.normalize3fast();
|
||||
}
|
||||
else
|
||||
{ //degenerate, make up a value
|
||||
normal.set(0,0,1);
|
||||
}
|
||||
|
||||
normal.normalize3fast_checked(&default_norm);
|
||||
|
||||
llassert(llfinite(normal.getF32ptr()[0]));
|
||||
llassert(llfinite(normal.getF32ptr()[1]));
|
||||
llassert(llfinite(normal.getF32ptr()[2]));
|
||||
|
|
@ -6611,11 +6612,13 @@ void LLVolumeFace::createTangents()
|
|||
CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices/3, mIndices, mTangents);
|
||||
|
||||
//normalize tangents
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
for (U32 i = 0; i < mNumVertices; i++)
|
||||
{
|
||||
//binorm[i].normalize3fast();
|
||||
//bump map/planar projection code requires normals to be normalized
|
||||
mNormals[i].normalize3fast();
|
||||
mNormals[i].normalize3fast_checked(&default_norm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6793,6 +6796,9 @@ void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMat
|
|||
mat.loadu(mat_in);
|
||||
norm_mat.loadu(norm_mat_in);
|
||||
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
|
||||
for (U32 i = 0; i < face.mNumVertices; ++i)
|
||||
{
|
||||
//transform appended face position and store
|
||||
|
|
@ -6800,7 +6806,7 @@ void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMat
|
|||
|
||||
//transform appended face normal and store
|
||||
norm_mat.rotate(src_norm[i], dst_norm[i]);
|
||||
dst_norm[i].normalize3fast();
|
||||
dst_norm[i].normalize3fast_checked(&default_norm);
|
||||
|
||||
//copy appended face texture coordinate
|
||||
dst_tc[i] = src_tc[i];
|
||||
|
|
@ -7209,7 +7215,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
#define TANGENTIAL_PARANOIA_ASSERTS 1
|
||||
#define TANGENTIAL_PARANOIA_ASSERTS 0
|
||||
|
||||
#if TANGENTIAL_PARANOIA_ASSERTS
|
||||
#define tangential_paranoia(a) llassert(a)
|
||||
|
|
@ -7289,47 +7295,28 @@ void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVe
|
|||
|
||||
// These appear to come out of the summing above distinctly non-unit-length
|
||||
//
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
|
||||
for (U32 a = 0; a < vertexCount; a++)
|
||||
{
|
||||
// Conditioning required by assets which don't necessarily reference every vert index
|
||||
// (i.e. some of the tangents can end up uninitialized and therefore indeterminate/INF)
|
||||
// and protection against zero length vectors which are not handled by normalize3fast.
|
||||
//
|
||||
if (!tan1[a].isFinite3() || tan1[a].equals3(LLVector4a::getZero()))
|
||||
{
|
||||
tan1[a].set(0,0,1,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
tan1[a].normalize3fast();
|
||||
}
|
||||
tan1[a].normalize3fast_checked(&default_norm);
|
||||
tan2[a].normalize3fast_checked(&default_norm);
|
||||
|
||||
if (!tan2[a].isFinite3() || tan2[a].equals3(LLVector4a::getZero()))
|
||||
{
|
||||
tan2[a].set(0,0,1,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
tan2[a].normalize3fast();
|
||||
}
|
||||
|
||||
const F32 cefgw = 0.03f;
|
||||
tangential_paranoia(tan1[a].isFinite3());
|
||||
tangential_paranoia(tan2[a].isFinite3());
|
||||
tangential_paranoia(tan1[a].isNormalized3(cefgw));
|
||||
tangential_paranoia(tan2[a].isNormalized3(cefgw));
|
||||
tangential_paranoia(tan1[a].isNormalized3(0.03f));
|
||||
tangential_paranoia(tan2[a].isNormalized3(0.03f));
|
||||
}
|
||||
|
||||
LLVector4a default_tangent;
|
||||
default_tangent.set(0,0,1,1);
|
||||
|
||||
for (U32 a = 0; a < vertexCount; a++)
|
||||
{
|
||||
LLVector4a n = normal[a];
|
||||
|
||||
if (!n.isFinite3() || n.equals3(LLVector4a::getZero()))
|
||||
{
|
||||
n.set(0,1,0,1);
|
||||
}
|
||||
|
||||
n.normalize3fast();
|
||||
|
||||
n.normalize3fast_checked(&default_norm);
|
||||
|
||||
const LLVector4a& t = tan1[a];
|
||||
|
||||
|
|
@ -7353,34 +7340,27 @@ void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVe
|
|||
|
||||
tangential_paranoia(tsubn.isFinite3());
|
||||
|
||||
if (tsubn.dot3(tsubn).getF32() > F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
tsubn.normalize3fast();
|
||||
tsubn.normalize3fast_checked(&default_tangent);
|
||||
|
||||
// Calculate handedness
|
||||
F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f;
|
||||
// Calculate handedness
|
||||
F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f;
|
||||
|
||||
tsubn.getF32ptr()[3] = handedness;
|
||||
tsubn.getF32ptr()[3] = handedness;
|
||||
|
||||
tangent[a] = tsubn;
|
||||
tangent[a] = tsubn;
|
||||
|
||||
tangential_paranoia(tangent[a].isNormalized3(0.1f));
|
||||
tangential_paranoia(tangent[a].isNormalized3(0.1f));
|
||||
|
||||
llassert(llfinite(tangent[a].getF32ptr()[0]));
|
||||
llassert(llfinite(tangent[a].getF32ptr()[1]));
|
||||
llassert(llfinite(tangent[a].getF32ptr()[2]));
|
||||
llassert(llfinite(tangent[a].getF32ptr()[0]));
|
||||
llassert(llfinite(tangent[a].getF32ptr()[1]));
|
||||
llassert(llfinite(tangent[a].getF32ptr()[2]));
|
||||
|
||||
llassert(!llisnan(tangent[a].getF32ptr()[0]));
|
||||
llassert(!llisnan(tangent[a].getF32ptr()[1]));
|
||||
llassert(!llisnan(tangent[a].getF32ptr()[2]));
|
||||
}
|
||||
else
|
||||
{ //degenerate, make up a value
|
||||
tangent[a].set(0,0,1,1);
|
||||
}
|
||||
llassert(!llisnan(tangent[a].getF32ptr()[0]));
|
||||
llassert(!llisnan(tangent[a].getF32ptr()[1]));
|
||||
llassert(!llisnan(tangent[a].getF32ptr()[2]));
|
||||
}
|
||||
|
||||
ll_aligned_free_16(tan1);
|
||||
ll_aligned_free_16(tan1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -817,6 +817,12 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
|
|||
size.mul(scale);
|
||||
}
|
||||
|
||||
// Catch potential badness from normalization before it happens
|
||||
//
|
||||
llassert(mat_normal.mMatrix[0].isFinite3() && (mat_normal.mMatrix[0].dot3(mat_normal.mMatrix[0]).getF32() > F_APPROXIMATELY_ZERO));
|
||||
llassert(mat_normal.mMatrix[1].isFinite3() && (mat_normal.mMatrix[1].dot3(mat_normal.mMatrix[1]).getF32() > F_APPROXIMATELY_ZERO));
|
||||
llassert(mat_normal.mMatrix[2].isFinite3() && (mat_normal.mMatrix[2].dot3(mat_normal.mMatrix[2]).getF32() > F_APPROXIMATELY_ZERO));
|
||||
|
||||
mat_normal.mMatrix[0].normalize3fast();
|
||||
mat_normal.mMatrix[1].normalize3fast();
|
||||
mat_normal.mMatrix[2].normalize3fast();
|
||||
|
|
@ -936,7 +942,9 @@ LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, const LLVector4a& po
|
|||
LLVector4a volume_normal;
|
||||
LLVector3 v_normal(normal.getF32ptr());
|
||||
volume_normal.load3(mDrawablep->getVOVolume()->agentDirectionToVolume(v_normal).mV);
|
||||
volume_normal.normalize3fast();
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
volume_normal.normalize3fast_checked(&default_norm);
|
||||
|
||||
if (texgen == LLTextureEntry::TEX_GEN_PLANAR)
|
||||
{
|
||||
|
|
@ -1909,7 +1917,10 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
|
|||
binormal.load3(t.mV);
|
||||
}
|
||||
|
||||
binormal.normalize3fast();
|
||||
LLVector4a default_binorm;
|
||||
default_binorm.set(1,0,0,1);
|
||||
binormal.normalize3fast_checked(&default_binorm);
|
||||
|
||||
LLVector2 tc = bump_tc[i];
|
||||
tc += LLVector2( bump_s_primary_light_ray.dot3(tangent).getF32(), bump_t_primary_light_ray.dot3(binormal).getF32() );
|
||||
|
||||
|
|
@ -1996,12 +2007,13 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
|
|||
LLFastTimer t(FTM_FACE_GEOM_NORMAL);
|
||||
mVertexBuffer->getNormalStrider(norm, mGeomIndex, mGeomCount, map_range);
|
||||
F32* normals = (F32*) norm.get();
|
||||
|
||||
LLVector4a default_norm;
|
||||
default_norm.set(0,1,0,1);
|
||||
for (S32 i = 0; i < num_vertices; i++)
|
||||
{
|
||||
LLVector4a normal;
|
||||
mat_normal.rotate(vf.mNormals[i], normal);
|
||||
normal.normalize3fast();
|
||||
normal.normalize3fast_checked(&default_norm);
|
||||
normal.store4a(normals);
|
||||
normals += 4;
|
||||
}
|
||||
|
|
@ -2024,12 +2036,14 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
|
|||
mask.clear();
|
||||
mask.setElement<3>();
|
||||
|
||||
LLVector4a default_tangent;
|
||||
default_tangent.set(0,0,1,1);
|
||||
|
||||
for (S32 i = 0; i < num_vertices; i++)
|
||||
{
|
||||
LLVector4a tangent_out;
|
||||
mat_normal.rotate(vf.mTangents[i], tangent_out);
|
||||
tangent_out.normalize3fast();
|
||||
|
||||
tangent_out.normalize3fast_checked(&default_tangent);
|
||||
tangent_out.setSelectWithMask(mask, vf.mTangents[i], tangent_out);
|
||||
tangent_out.store4a(tangents);
|
||||
|
||||
|
|
@ -2244,7 +2258,10 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
|
|||
dist *= 16.f;
|
||||
}
|
||||
|
||||
lookAt.normalize3fast() ;
|
||||
LLVector4a default_lookat;
|
||||
default_lookat.set(0,0,1,1);
|
||||
|
||||
lookAt.normalize3fast_checked(&default_lookat);
|
||||
|
||||
//get area of circle around node
|
||||
F32 app_angle = atanf((F32) sqrt(size_squared) / dist);
|
||||
|
|
|
|||
|
|
@ -1259,12 +1259,15 @@ F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
|
|||
|
||||
F32 dist = 0.f;
|
||||
|
||||
LLVector4a default_eyevec;
|
||||
default_eyevec.set(0,0,1,1);
|
||||
|
||||
if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
|
||||
{
|
||||
LLVector4a v = eye;
|
||||
|
||||
dist = eye.getLength3().getF32();
|
||||
eye.normalize3fast();
|
||||
eye.normalize3fast_checked(&default_eyevec);
|
||||
|
||||
if (!group->isState(LLSpatialGroup::ALPHA_DIRTY))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -411,14 +411,21 @@ void LLVOPartGroup::getGeometry(S32 idx,
|
|||
LLVector4a right;
|
||||
|
||||
right.setCross3(at, up);
|
||||
// guard against NaNs in normalize below
|
||||
llassert(right.dot3(right).getF32() > F_APPROXIMATELY_ZERO);
|
||||
right.normalize3fast();
|
||||
|
||||
up.setCross3(right, at);
|
||||
// guard against NaNs in normalize below
|
||||
llassert(up.dot3(up).getF32() > F_APPROXIMATELY_ZERO);
|
||||
up.normalize3fast();
|
||||
|
||||
if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK)
|
||||
{
|
||||
LLVector4a normvel;
|
||||
normvel.load3(part.mVelocity.mV);
|
||||
// guard against NaNs in normalize below
|
||||
llassert(normvel.dot3(normvel).getF32() > F_APPROXIMATELY_ZERO);
|
||||
normvel.normalize3fast();
|
||||
LLVector2 up_fracs;
|
||||
up_fracs.mV[0] = normvel.dot3(right).getF32();
|
||||
|
|
@ -443,6 +450,9 @@ void LLVOPartGroup::getGeometry(S32 idx,
|
|||
|
||||
up = new_up;
|
||||
right = t;
|
||||
// guard against NaNs in normalize below
|
||||
llassert(up.dot3(up).getF32() > F_APPROXIMATELY_ZERO);
|
||||
llassert(right.dot3(right).getF32() > F_APPROXIMATELY_ZERO);
|
||||
up.normalize3fast();
|
||||
right.normalize3fast();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3751,7 +3751,8 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
|
|||
{
|
||||
*normal = n;
|
||||
}
|
||||
|
||||
// guard against NaNs in normalize below
|
||||
llassert(normal->dot3(*normal).getF32() > F_APPROXIMATELY_ZERO);
|
||||
(*normal).normalize3fast();
|
||||
}
|
||||
|
||||
|
|
@ -3774,6 +3775,8 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
|
|||
{
|
||||
*tangent = tn;
|
||||
}
|
||||
// guard against NaNs in normalize below
|
||||
llassert(tangent->dot3(*tangent).getF32() > F_APPROXIMATELY_ZERO);
|
||||
(*tangent).normalize3fast();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10569,11 +10569,13 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
|
|||
LLVector4a left;
|
||||
left.load3(camera.getLeftAxis().mV);
|
||||
left.mul(left);
|
||||
llassert(left.dot3(left).getF32() > F_APPROXIMATELY_ZERO);
|
||||
left.normalize3fast();
|
||||
|
||||
LLVector4a up;
|
||||
up.load3(camera.getUpAxis().mV);
|
||||
up.mul(up);
|
||||
llassert(up.dot3(up).getF32() > F_APPROXIMATELY_ZERO);
|
||||
up.normalize3fast();
|
||||
|
||||
tdim.mV[0] = fabsf(half_height.dot3(left).getF32());
|
||||
|
|
|
|||
Loading…
Reference in New Issue