1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
29 using namespace NLMISC
;
39 // ***************************************************************************
40 // ***************************************************************************
42 // ***************************************************************************
43 // ***************************************************************************
46 // ***************************************************************************
47 const CLodCharacterShapeBuild::CPixelInfo
CLodCharacterShapeBuild::CPixelInfo::EmptyPixel(
48 CVector::Null
, CVector(1000,0,0));
51 // ***************************************************************************
52 CLodCharacterShapeBuild::CLodCharacterShapeBuild()
59 // ***************************************************************************
60 void CLodCharacterShapeBuild::compile(const std::vector
<bool> &triangleSelection
, uint textureOverSample
)
62 nlassert(UVs
.size()==Vertices
.size());
63 nlassert(Normals
.size()==Vertices
.size());
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
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])
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
109 for(uint corner
=0; corner
<3; corner
++)
111 pInf
[corner
].Pos
= Vertices
[idx
[corner
]];
112 pInf
[corner
].Normal
= Normals
[idx
[corner
]];
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
;
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
;
142 CPolygon2D::TRasterVect rasters
;
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
;
150 // get the center coord of this texel
153 for(sint x
=x0
;x
<=x1
;x
++)
155 // get the center coord of this texel
157 // Compute Pos/Normal on this texel.
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
;
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.
179 if(textureOverSample
==1)
181 _TextureInfo
= overTextureInfo
;
185 // eg: 3 gives 1. delta ranges from -1 to 1.
186 float middle
= (textureOverSample
-1)/2.f
;
189 for(y
=0;y
<(sint
)_Height
;y
++)
191 for(x
=0;x
<(sint
)_Width
;x
++)
193 // for all samples, take the best one.
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.
211 bestPixel
= overPixel
;
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
++)
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.
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
;
266 // ***************************************************************************
267 void CLodCharacterShapeBuild::serial(NLMISC::IStream
&f
)
270 f
.serialCheck(NELID("_LEN"));
271 f
.serialCheck(NELID("DOLC"));
272 f
.serialCheck(NELID("DLUB"));
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
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
];
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
);
303 f
.serialCont(TriangleIndices
);
308 f
.serialCont(Normals
);
309 f
.serial(_Width
, _Height
);
310 f
.serialCont(_TextureInfo
);
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())
332 return &_TextureInfo
[0];
336 // ***************************************************************************
337 // ***************************************************************************
338 // CLodCharacterShape
339 // ***************************************************************************
340 // ***************************************************************************
343 // ***************************************************************************
344 CLodCharacterShape::CLodCharacterShape()
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
);
371 contReset(_TriangleIndices
);
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
];
387 _TriangleIndices
= triangleIndices
;
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
422 vInf
.Influence
= skinWeights
[i
].Weights
[j
];
423 // Insert this vertex influence in the bone.
424 _Bones
[boneId
].InfVertices
.push_back(vInf
);
427 // stop for this vertex.
433 // ***************************************************************************
434 bool CLodCharacterShape::addAnim(const CAnimBuild
&animBuild
)
436 // first, verify don't exist.
437 if(getAnimIdByName(animBuild
.Name
)!=-1)
440 // build basics of the animation
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());
453 dstAnim
.Keys
.resize(animBuild
.Keys
.size());
456 // Pack animation. 1st pass: compute max size over the animation vertices
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
];
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));
503 // ***************************************************************************
504 void CLodCharacterShape::CAnim::serial(NLMISC::IStream
&f
)
506 (void)f
.serialVersion(0);
510 f
.serial(AnimLength
);
511 f
.serial(OOAnimLength
);
512 f
.serial(UnPackScaleFactor
);
517 // ***************************************************************************
518 void CLodCharacterShape::CBoneInfluence::serial(NLMISC::IStream
&f
)
520 (void)f
.serialVersion(0);
523 f
.serialCont(InfVertices
);
527 // ***************************************************************************
528 void CLodCharacterShape::serial(NLMISC::IStream
&f
)
531 f
.serialCheck(NELID("_LEN"));
532 f
.serialCheck(NELID("DOLC"));
533 f
.serialCheck(NELID("PAHS"));
539 sint ver
= f
.serialVersion(1);
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
);
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
];
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
);
572 f
.serialCont(_Anims
);
573 f
.serialCont(_AnimMap
);
578 f
.serialCont(_Normals
);
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())
599 // ***************************************************************************
600 sint
CLodCharacterShape::getBoneIdByName(const std::string
&name
) const
602 CstItStrIdMap it
= _BoneMap
.find(name
);
603 if(it
== _BoneMap
.end())
610 // ***************************************************************************
611 const TLodCharacterIndexType
*CLodCharacterShape::getTriangleArray() const
614 return &_TriangleIndices
[0];
619 // ***************************************************************************
620 const CLodCharacterShape::CVector3s
*CLodCharacterShape::getAnimKey(uint animId
, TGlobalAnimationTime time
, bool wrapMode
, CVector
&unPackScaleFactor
) const
622 H_AUTO( NL3D_LodCharacterShape_getAnimKey
)
626 if(animId
>=_Anims
.size())
630 const CAnim
&anim
= _Anims
[animId
];
633 unPackScaleFactor
= anim
.UnPackScaleFactor
;
637 localTime
= (float)fmod((float)time
, (float)anim
.AnimLength
);
639 localTime
= (float)time
;
641 // Clamp to the range.
642 clamp(localTime
, 0, anim
.AnimLength
);
645 sint keyId
= (sint
)floor( (localTime
*anim
.OOAnimLength
) * anim
.NumKeys
);
646 clamp(keyId
, 0, sint(anim
.NumKeys
-1));
649 return &anim
.Keys
[keyId
* _NumVertices
];
652 // ***************************************************************************
653 const CUV
*CLodCharacterShape::getUVs() const
661 // ***************************************************************************
662 const CVector
*CLodCharacterShape::getNormals() const
672 // ***************************************************************************
673 // ***************************************************************************
674 // Bone Alpha Testing
675 // ***************************************************************************
676 // ***************************************************************************
679 // ***************************************************************************
680 void CLodCharacterShape::startBoneAlpha(std::vector
<uint8
> &tmpAlphas
) const
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())
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;