Show bonus/malus timer text if available
[ryzomcore.git] / nel / src / 3d / lod_character_shape.cpp
blob21e1cce7f0106fc98855f65bf15242914ad688fa
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
19 #include "nel/3d/lod_character_shape.h"
20 #include "nel/misc/vectord.h"
21 #include "nel/misc/fast_floor.h"
22 #include "nel/3d/lod_character_texture.h"
23 #include "nel/misc/triangle.h"
24 #include "nel/misc/polygon.h"
25 #include "nel/misc/hierarchical_timer.h"
28 using namespace std;
29 using namespace NLMISC;
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 namespace NL3D
39 // ***************************************************************************
40 // ***************************************************************************
41 // CLodCharacterShape
42 // ***************************************************************************
43 // ***************************************************************************
46 // ***************************************************************************
47 const CLodCharacterShapeBuild::CPixelInfo CLodCharacterShapeBuild::CPixelInfo::EmptyPixel(
48 CVector::Null, CVector(1000,0,0));
51 // ***************************************************************************
52 CLodCharacterShapeBuild::CLodCharacterShapeBuild()
54 _Width= 0;
55 _Height= 0;
59 // ***************************************************************************
60 void CLodCharacterShapeBuild::compile(const std::vector<bool> &triangleSelection, uint textureOverSample)
62 nlassert(UVs.size()==Vertices.size());
63 nlassert(Normals.size()==Vertices.size());
65 // take the sqrtf.
66 textureOverSample= (uint)sqrtf((float)textureOverSample);
67 textureOverSample= max(textureOverSample, 1U);
69 _Width= NL3D_CLOD_TEXT_WIDTH;
70 _Height= NL3D_CLOD_TEXT_HEIGHT;
72 uint wOver= _Width*textureOverSample;
73 uint hOver= _Height*textureOverSample;
74 std::vector<CPixelInfo> overTextureInfo;
76 // do some tri selection?
77 bool triSelectOk= triangleSelection.size()==TriangleIndices.size()/3;
80 // **** reset the texture and init with "empty" pixel (ie normal.x==1000, which is not possible because normal.norm()==1)
81 _TextureInfo.resize(_Width*_Height);
82 fill(_TextureInfo.begin(), _TextureInfo.end(), CPixelInfo::EmptyPixel);
83 // do it to the oversampled texture
84 overTextureInfo.resize(wOver*hOver, CPixelInfo::EmptyPixel);
87 // **** For each triangle in the shape, polyfill this triangle from a texture view, in the overSampledTexture
88 CPolygon2D poly;
89 poly.Vertices.resize(3);
90 for(uint i=0; i<TriangleIndices.size();i+=3)
92 // if selection OK, and the tri is not selected, skip
93 if(triSelectOk && !triangleSelection[i/3])
94 continue;
96 // Setup triangle.
97 // -----------
98 uint idx[3];
99 idx[0]= TriangleIndices[i+0];
100 idx[1]= TriangleIndices[i+1];
101 idx[2]= TriangleIndices[i+2];
102 // compute corners UVs, in texel size
103 CTriangle triangleUV;
104 triangleUV.V0= CVector(UVs[idx[0]].U*wOver, UVs[idx[0]].V*hOver, 0);
105 triangleUV.V1= CVector(UVs[idx[1]].U*wOver, UVs[idx[1]].V*hOver, 0);
106 triangleUV.V2= CVector(UVs[idx[2]].U*wOver, UVs[idx[2]].V*hOver, 0);
107 // compute corners pixelInfos
108 CPixelInfo pInf[3];
109 for(uint corner=0; corner<3; corner++)
111 pInf[corner].Pos= Vertices[idx[corner]];
112 pInf[corner].Normal= Normals[idx[corner]];
114 // Compute Gradients
115 CVector GradPosX, GradPosY, GradPosZ;
116 CVector GradNormalX, GradNormalY, GradNormalZ;
117 triangleUV.computeGradient(pInf[0].Pos.x, pInf[1].Pos.x, pInf[2].Pos.x, GradPosX);
118 triangleUV.computeGradient(pInf[0].Pos.y, pInf[1].Pos.y, pInf[2].Pos.y, GradPosY);
119 triangleUV.computeGradient(pInf[0].Pos.z, pInf[1].Pos.z, pInf[2].Pos.z, GradPosZ);
120 triangleUV.computeGradient(pInf[0].Normal.x, pInf[1].Normal.x, pInf[2].Normal.x, GradNormalX);
121 triangleUV.computeGradient(pInf[0].Normal.y, pInf[1].Normal.y, pInf[2].Normal.y, GradNormalY);
122 triangleUV.computeGradient(pInf[0].Normal.z, pInf[1].Normal.z, pInf[2].Normal.z, GradNormalZ);
123 // Compute Gradients offset
124 float OffPosX, OffPosY, OffPosZ;
125 float OffNormalX, OffNormalY, OffNormalZ;
126 OffPosX= pInf[0].Pos.x - GradPosX*triangleUV.V0;
127 OffPosY= pInf[0].Pos.y - GradPosY*triangleUV.V0;
128 OffPosZ= pInf[0].Pos.z - GradPosZ*triangleUV.V0;
129 OffNormalX= pInf[0].Normal.x - GradNormalX*triangleUV.V0;
130 OffNormalY= pInf[0].Normal.y - GradNormalY*triangleUV.V0;
131 OffNormalZ= pInf[0].Normal.z - GradNormalZ*triangleUV.V0;
134 // PolyFiller
135 // -----------
136 CVector2f dCenter(0.5f, 0.5f);
137 // select texels if their centers is in the triangle
138 poly.Vertices[0]= triangleUV.V0-dCenter;
139 poly.Vertices[1]= triangleUV.V1-dCenter;
140 poly.Vertices[2]= triangleUV.V2-dCenter;
141 // polyFiller
142 CPolygon2D::TRasterVect rasters;
143 sint minY;
144 poly.computeBorders(rasters, minY);
145 for(sint y=0;y<(sint)rasters.size();y++)
147 sint x0= rasters[y].first;
148 sint x1= rasters[y].second;
149 CVector v;
150 // get the center coord of this texel
151 v.y= y+minY+0.5f;
152 v.z= 0;
153 for(sint x=x0;x<=x1;x++)
155 // get the center coord of this texel
156 v.x= x+0.5f;
157 // Compute Pos/Normal on this texel.
158 CPixelInfo texelInf;
159 texelInf.Pos.x= OffPosX + GradPosX * v;
160 texelInf.Pos.y= OffPosY + GradPosY * v;
161 texelInf.Pos.z= OffPosZ + GradPosZ * v;
162 texelInf.Normal.x= OffNormalX + GradNormalX * v;
163 texelInf.Normal.y= OffNormalY + GradNormalY * v;
164 texelInf.Normal.z= OffNormalZ + GradNormalZ * v;
165 // normalize.
166 texelInf.Normal.normalize();
167 // Store (NB: overwrite any pixel)
168 sint texX= ((uint)x)%wOver;
169 sint texY= ((uint)y+minY)%hOver;
170 overTextureInfo[texY*wOver+texX]= texelInf;
177 // **** Down sample the overSampled texture.
178 sint x,y;
179 if(textureOverSample==1)
181 _TextureInfo= overTextureInfo;
183 else
185 // eg: 3 gives 1. delta ranges from -1 to 1.
186 float middle= (textureOverSample-1)/2.f;
188 // for all pixels.
189 for(y=0;y<(sint)_Height;y++)
191 for(x=0;x<(sint)_Width;x++)
193 // for all samples, take the best one.
194 sint xo, yo;
195 float bestDist= FLT_MAX;
196 CPixelInfo bestPixel= CPixelInfo::EmptyPixel;
197 for(yo=0;yo<(sint)textureOverSample;yo++)
199 for(xo=0;xo<(sint)textureOverSample;xo++)
201 // compute distance to the pixel center.
202 float dist= sqr(yo-middle)+sqr(xo-middle);
203 const CPixelInfo &overPixel= overTextureInfo[(y*textureOverSample+yo)*wOver+(x*textureOverSample+xo)];
204 // if the overPixel is not empty.
205 if( !(overPixel==CPixelInfo::EmptyPixel) )
207 // take it if the best: nearest to the pixel center.
208 if(dist<bestDist)
210 bestDist= dist;
211 bestPixel= overPixel;
217 // fill textureInfo
218 _TextureInfo[y*_Width+x]= bestPixel;
224 // **** Dilate texture 1 time: each empty pixel info get neighbor full pixel (8box)
225 // copy the non dilated texture
226 std::vector<CPixelInfo> tmpTextureInfo= _TextureInfo;
227 // process all pixels
228 for(y=0;y<(sint)_Height;y++)
230 sint y0=y-1, y1=y+1;
231 y0= max(y0, 0);
232 y1= min(y1, (sint)_Height-1);
233 for(x=0;x<(sint)_Width;x++)
235 CPixelInfo &texelInf= _TextureInfo[y*_Width+x];
236 // if an empty pixel.
237 if(texelInf==CPixelInfo::EmptyPixel)
239 // dilate: look around for non empty pixel.
240 sint x0=x-1, x1=x+1;
241 x0= max(x0, 0);
242 x1= min(x1, (sint)_Width-1);
243 // For the 8 possible pixels (nb: look us too, but doesn't matter since we are an empty pixel)
244 for(sint yb= y0; yb<=y1;yb++)
246 for(sint xb= x0; xb<=x1;xb++)
248 // if the neighbor is not an empty pixel. NB: avoid override problems using not Dilated texture
249 CPixelInfo &nbTexelInf= tmpTextureInfo[yb*_Width+xb];
250 if( !(nbTexelInf==CPixelInfo::EmptyPixel) )
252 // write it in the center pixel, and skip the search
253 texelInf= nbTexelInf;
254 yb= y1+1;
255 break;
266 // ***************************************************************************
267 void CLodCharacterShapeBuild::serial(NLMISC::IStream &f)
269 // NEL_CLODBULD
270 f.serialCheck(NELID("_LEN"));
271 f.serialCheck(NELID("DOLC"));
272 f.serialCheck(NELID("DLUB"));
275 Version 1:
276 - UVs and Normals + texture info
278 sint ver= f.serialVersion(1);
280 f.serialCont(Vertices);
281 f.serialCont(SkinWeights);
282 f.serialCont(BonesNames);
283 #ifdef NL_LOD_CHARACTER_INDEX16
284 // must serial 16 bits index as 32 bits
285 if (f.isReading())
287 std::vector<uint32> readVect;
288 f.serialCont(readVect);
289 TriangleIndices.resize(readVect.size());
290 for(uint k = 0; k < readVect.size(); ++k)
292 nlassert(readVect[k] <= 0xffff);
293 TriangleIndices[k] = (uint16) readVect[k];
296 else
298 std::vector<uint32> saveVect(TriangleIndices.size());
299 std::copy(TriangleIndices.begin(), TriangleIndices.end(), saveVect.begin()); // copy will do the job
300 f.serialCont(saveVect);
302 #else
303 f.serialCont(TriangleIndices);
304 #endif
305 if(ver>=1)
307 f.serialCont(UVs);
308 f.serialCont(Normals);
309 f.serial(_Width, _Height);
310 f.serialCont(_TextureInfo);
312 else
314 // Must init dummy UVs/normals
315 UVs.resize(Vertices.size(), CUV(0,0));
316 Normals.resize(Vertices.size(), CVector::K);
317 // Must init dummy texture
318 _Width= NL3D_CLOD_TEXT_WIDTH;
319 _Height= NL3D_CLOD_TEXT_HEIGHT;
320 _TextureInfo.resize(_Width*_Height, CPixelInfo::EmptyPixel);
326 // ***************************************************************************
327 const CLodCharacterShapeBuild::CPixelInfo *CLodCharacterShapeBuild::getTextureInfoPtr()
329 if(_TextureInfo.empty())
330 return NULL;
331 else
332 return &_TextureInfo[0];
336 // ***************************************************************************
337 // ***************************************************************************
338 // CLodCharacterShape
339 // ***************************************************************************
340 // ***************************************************************************
343 // ***************************************************************************
344 CLodCharacterShape::CLodCharacterShape()
346 _NumVertices= 0;
347 _NumTriangles= 0;
350 // ***************************************************************************
351 void CLodCharacterShape::buildMesh(const std::string &name, const CLodCharacterShapeBuild &lodBuild)
353 uint numVertices= (uint)lodBuild.Vertices.size();
354 const vector<uint32> &triangleIndices= lodBuild.TriangleIndices;
355 const vector<CMesh::CSkinWeight> &skinWeights= lodBuild.SkinWeights;
356 const vector<CUV> &uvs= lodBuild.UVs;
357 const vector<CVector> &normals= lodBuild.Normals;
359 nlassert(numVertices>0);
360 nlassert(!triangleIndices.empty());
361 nlassert((triangleIndices.size()%3)==0);
362 nlassert(skinWeights.size() == numVertices);
363 nlassert(uvs.size() == numVertices);
364 nlassert(normals.size() == numVertices);
366 // reset data
367 contReset(_Anims);
368 contReset(_AnimMap);
369 contReset(_Bones);
370 contReset(_BoneMap);
371 contReset(_TriangleIndices);
372 contReset(_UVs);
373 contReset(_Normals);
375 // Copy data.
376 _Name= name;
377 _NumVertices= numVertices;
378 _NumTriangles= (uint32)triangleIndices.size()/3;
379 #ifdef NL_LOD_CHARACTER_INDEX16
380 _TriangleIndices.resize(triangleIndices.size());
381 for(uint k = 0; k < triangleIndices.size(); ++k)
383 nlassert(triangleIndices[k] <= 0xffff);
384 _TriangleIndices[k] = (uint16) triangleIndices[k];
386 #else
387 _TriangleIndices= triangleIndices;
388 #endif
389 _UVs= uvs;
390 _Normals= normals;
392 // check indices.
393 uint i;
394 for(i=0;i<triangleIndices.size();i++)
396 nlassert(triangleIndices[i]<_NumVertices);
399 // Copy bone names, and compute bone Map
400 _Bones.resize(lodBuild.BonesNames.size());
401 for(i=0; i<_Bones.size(); i++)
403 _Bones[i].Name= lodBuild.BonesNames[i];
404 _BoneMap.insert( make_pair(_Bones[i].Name, i) );
407 // "Normalize" SkinWeights for CLodCharacterShape
408 for(i=0;i<skinWeights.size();i++)
410 nlassert(skinWeights[i].Weights[0]>0);
411 // for all slots not 0
412 for(uint j=0;j<NL3D_MESH_SKINNING_MAX_MATRIX;j++)
414 // if this this slot is used.
415 if(skinWeights[i].Weights[j]>0)
417 uint boneId= skinWeights[i].MatrixId[j];
418 nlassert(boneId < _Bones.size());
419 // init the vInf data
420 CVertexInf vInf;
421 vInf.VertexId= i;
422 vInf.Influence= skinWeights[i].Weights[j];
423 // Insert this vertex influence in the bone.
424 _Bones[boneId].InfVertices.push_back(vInf);
426 else
427 // stop for this vertex.
428 break;
433 // ***************************************************************************
434 bool CLodCharacterShape::addAnim(const CAnimBuild &animBuild)
436 // first, verify don't exist.
437 if(getAnimIdByName(animBuild.Name)!=-1)
438 return false;
440 // build basics of the animation
441 CAnim dstAnim;
442 dstAnim.Name= animBuild.Name;
443 dstAnim.AnimLength= animBuild.AnimLength;
444 // Possible to have an Anim with just one key. setup an epsilon for animLength if 0.
445 if(dstAnim.AnimLength<=0)
446 dstAnim.AnimLength= 0.001f;
447 dstAnim.OOAnimLength= 1.0f / animBuild.AnimLength;
448 dstAnim.NumKeys= animBuild.NumKeys;
449 // verify size of the array
450 nlassert(dstAnim.NumKeys>0);
451 nlassert(dstAnim.NumKeys * _NumVertices == animBuild.Keys.size());
452 // resize dest array
453 dstAnim.Keys.resize(animBuild.Keys.size());
456 // Pack animation. 1st pass: compute max size over the animation vertices
457 uint i;
458 // minimum shape size is , say, 1 cm :)
459 CVector maxSize(0.01f, 0.01f, 0.01f);
460 for(i=0;i<animBuild.Keys.size();i++)
462 // take the maxSize of the abs values
463 maxSize.maxof(maxSize, -animBuild.Keys[i]);
464 maxSize.maxof(maxSize, animBuild.Keys[i]);
467 // compute the UnPackScaleFactor ie maxSize, to be multiplied by max Abs value of a sint16
468 dstAnim.UnPackScaleFactor= maxSize * (1.0f/32767);
470 // Pack animation. 2st pass: pack.
471 CVectorD packScaleFactor;
472 packScaleFactor.x= 1.0 / dstAnim.UnPackScaleFactor.x;
473 packScaleFactor.y= 1.0 / dstAnim.UnPackScaleFactor.y;
474 packScaleFactor.z= 1.0 / dstAnim.UnPackScaleFactor.z;
475 // For all key vertices
476 for(i=0;i<animBuild.Keys.size();i++)
478 CVector v= animBuild.Keys[i];
479 CVector3s &dstV= dstAnim.Keys[i];
481 // compress
482 v.x= float(v.x*packScaleFactor.x);
483 v.y= float(v.y*packScaleFactor.y);
484 v.z= float(v.z*packScaleFactor.z);
485 // clamp to sint16 limits (for float precision problems).
486 clamp(v.x, -32767, 32767);
487 clamp(v.y, -32767, 32767);
488 clamp(v.z, -32767, 32767);
489 // get into the vector3s
490 dstV.x= (sint16)floor(v.x);
491 dstV.y= (sint16)floor(v.y);
492 dstV.z= (sint16)floor(v.z);
496 // Add the anim to the array, and add an entry to the map
497 _Anims.push_back(dstAnim);
498 _AnimMap.insert(make_pair(dstAnim.Name, (uint32)_Anims.size()-1));
500 return true;
503 // ***************************************************************************
504 void CLodCharacterShape::CAnim::serial(NLMISC::IStream &f)
506 (void)f.serialVersion(0);
508 f.serial(Name);
509 f.serial(NumKeys);
510 f.serial(AnimLength);
511 f.serial(OOAnimLength);
512 f.serial(UnPackScaleFactor);
513 f.serialCont(Keys);
517 // ***************************************************************************
518 void CLodCharacterShape::CBoneInfluence::serial(NLMISC::IStream &f)
520 (void)f.serialVersion(0);
522 f.serial(Name);
523 f.serialCont(InfVertices);
527 // ***************************************************************************
528 void CLodCharacterShape::serial(NLMISC::IStream &f)
530 // NEL_CLODSHAP
531 f.serialCheck(NELID("_LEN"));
532 f.serialCheck(NELID("DOLC"));
533 f.serialCheck(NELID("PAHS"));
536 Version 1:
537 - UVs and Normals.
539 sint ver= f.serialVersion(1);
541 f.serial(_Name);
542 f.serial(_NumVertices);
543 f.serial(_NumTriangles);
544 f.serialCont(_Bones);
545 f.serialCont(_BoneMap);
546 // nb : indices are always saved in 32 bits for compatibility
547 #ifndef NL_LOD_CHARACTER_INDEX16
548 f.serialCont(_TriangleIndices);
549 #else
550 if (f.isReading())
552 std::vector<uint32> savedIndices;
553 f.serialCont(savedIndices);
554 _TriangleIndices.resize(savedIndices.size());
555 for(uint k = 0; k < savedIndices.size(); ++k)
557 nlassert(savedIndices[k] <= 0xffff);
558 _TriangleIndices[k] = (uint16) savedIndices[k];
561 else
563 std::vector<uint32> savedIndices;
564 savedIndices.resize(_TriangleIndices.size());
565 for(uint k = 0; k < savedIndices.size(); ++k)
567 savedIndices[k] = _TriangleIndices[k];
569 f.serialCont(savedIndices);
571 #endif
572 f.serialCont(_Anims);
573 f.serialCont(_AnimMap);
575 if(ver>=1)
577 f.serialCont(_UVs);
578 f.serialCont(_Normals);
580 else
582 // Must init dummy UVs/normals
583 _UVs.resize(_NumVertices, CUV(0,0));
584 _Normals.resize(_NumVertices, CVector::K);
588 // ***************************************************************************
589 sint CLodCharacterShape::getAnimIdByName(const std::string &name) const
591 CstItStrIdMap it= _AnimMap.find(name);
592 if(it == _AnimMap.end())
593 return -1;
594 else
595 return it->second;
599 // ***************************************************************************
600 sint CLodCharacterShape::getBoneIdByName(const std::string &name) const
602 CstItStrIdMap it= _BoneMap.find(name);
603 if(it == _BoneMap.end())
604 return -1;
605 else
606 return it->second;
610 // ***************************************************************************
611 const TLodCharacterIndexType *CLodCharacterShape::getTriangleArray() const
613 if(_NumTriangles)
614 return &_TriangleIndices[0];
615 else
616 return NULL;
619 // ***************************************************************************
620 const CLodCharacterShape::CVector3s *CLodCharacterShape::getAnimKey(uint animId, TGlobalAnimationTime time, bool wrapMode, CVector &unPackScaleFactor) const
622 H_AUTO( NL3D_LodCharacterShape_getAnimKey )
624 float localTime;
626 if(animId>=_Anims.size())
627 return NULL;
629 // get the anim.
630 const CAnim &anim= _Anims[animId];
632 // scale info
633 unPackScaleFactor= anim.UnPackScaleFactor;
635 // Loop mgt.
636 if(wrapMode)
637 localTime= (float)fmod((float)time, (float)anim.AnimLength);
638 else
639 localTime= (float)time;
641 // Clamp to the range.
642 clamp(localTime, 0, anim.AnimLength);
644 // get the key.
645 sint keyId= (sint)floor( (localTime*anim.OOAnimLength) * anim.NumKeys );
646 clamp(keyId, 0, sint(anim.NumKeys-1));
648 // return the key.
649 return &anim.Keys[keyId * _NumVertices];
652 // ***************************************************************************
653 const CUV *CLodCharacterShape::getUVs() const
655 if(_NumVertices==0)
656 return NULL;
658 return &_UVs[0];
661 // ***************************************************************************
662 const CVector *CLodCharacterShape::getNormals() const
664 if(_NumVertices==0)
665 return NULL;
667 return &_Normals[0];
672 // ***************************************************************************
673 // ***************************************************************************
674 // Bone Alpha Testing
675 // ***************************************************************************
676 // ***************************************************************************
679 // ***************************************************************************
680 void CLodCharacterShape::startBoneAlpha(std::vector<uint8> &tmpAlphas) const
682 // clear
683 tmpAlphas.clear();
684 // alocate, and fill
685 tmpAlphas.resize(getNumVertices(), 0);
688 // ***************************************************************************
689 void CLodCharacterShape::addBoneAlpha(uint boneId, std::vector<uint8> &tmpAlphas) const
691 // Yoyo: This is an error to not have the same skeleton that the one stored in the lod shape. But must not crash
692 if(boneId>=_Bones.size())
693 return;
694 const CBoneInfluence &bone= _Bones[boneId];
696 // for all vertices influenced by this bone, must set the alpha to full
697 for(uint i=0; i<bone.InfVertices.size(); i++)
699 const CVertexInf &vInf= bone.InfVertices[i];
700 tmpAlphas[vInf.VertexId]= 255;
705 } // NL3D