#include "stdafx.h" #ifndef _ITF_ANIMTEXPACKER_PATCH_H_ #include "AnimTexPacker/Patch.h" #endif #ifndef _ITF_GFX_ADAPTER_H_ #include "engine/AdaptersInterfaces/GFXAdapter.h" #endif //_ITF_GFX_ADAPTER_H_ #ifndef _ITF_ANIMATIONSKELETON_H_ #include "engine/animation/AnimationSkeleton.h" #endif // _ITF_ANIMATIONSKELETON_H_ #ifndef _ITF_ANIMATIONPATCHBANK_H_ #include "engine/animation/AnimationPatchBank.h" #endif // _ITF_ANIMATIONPATCHBANK_H_ #ifndef _ITF_ANIMATIONDRAW_H_ #include "engine/animation/AnimationDraw.h" #endif // _ITF_ANIMATIONDRAW_H_ #ifndef _ITF_STATSMANAGER_H_ #include "engine/stats/statsManager.h" #endif //_ITF_STATSMANAGER_H_ namespace ITF { namespace AnimTexPacker { u32 PatchList::CURRENT_SAMPLING = 16; /////////////////////////////////////////////////////////////////////////////////////////// class SimplePatch32Data { public: static const int maxVertexCount=MAX_VERTEX_BY_MESH; SimplePatch32Data() {vertexCount=0;indexCount=0;} VertexPT vertexListPT[maxVertexCount]; u32 vertexCount; u32 indexCount; }; Path Patch::m_saveInFile; u32 Patch::s_patchHdiv = PATH_HDIV_DEF; Patch::~Patch() { for (u32 i=0; im_finalPos; m_uvlocalToRotatedLocalTransform = m_sourceOfAlias->m_uvlocalToRotatedLocalTransform; } /////////////////////////////////////////////////////////////////////////////////////////// int Patch::computePlacementHeuristic(PackedTextureArea *_area, BoxI &b) { int x,y; int count=0; //upper edge y=b.miny-1; for (x = b.minx - 1; x <= b.maxx + 1; x++) if (_area->testBorder(x, y)) count++; //lower edge y=b.maxy+1; for (x = b.minx - 1; x <= b.maxx + 1; x++) if (_area->testBorder(x, y)) count++; //left edge x = b.minx - 1; for (y = b.miny; y <= b.maxy; y++) if (_area->testBorder(x, y)) count++; //right edge x = b.maxx + 1; for (y = b.miny; y <= b.maxy; y++) if (_area->testBorder(x, y)) count++; return count; } /////////////////////////////////////////////////////////////////////////////////////////// bbool Patch::Place(PackedTextureArea *_area, BoxI &b, bbool &_rotated, Transform2d &_subImageRotation) { b = BoxI(0, 0, m_UVBox.maxx-m_UVBox.minx, m_UVBox.maxy-m_UVBox.miny); int bestHeuristic = -1; int bestx=0, besty=0; int bestPhase = 0; int w = getSubImageWidth(); int h = getSubImageHeight(); ITF_SET placeableX, placeableY; ITF_SET::const_iterator xiter, yiter; ITF_ASSERT(!getSourceOfAlias()); //regular orientation _area->computeRemarkablePlacesForBox(placeableX, placeableY, w,h); for (yiter=placeableY.begin(); yiter!=placeableY.end(); yiter++) for (xiter = placeableX.begin(); xiter!=placeableX.end();xiter++) { int x = *xiter; int y = *yiter; b.minx = x; b.miny = y; b.maxx = x + w - 1; b.maxy = y + h - 1; int heur = computePlacementHeuristic(_area, b); if (heur > bestHeuristic) { if (_area->isAreaFree(x, y, w, h)) { bestHeuristic = heur; bestx = x; besty = y; bestPhase = 0; } } } #if 0 //rotated orientation _area->computeRemarkablePlacesForBox(placeableX, placeableY, h,w); for (yiter=placeableY.begin(); yiter!=placeableY.end(); yiter++) for (xiter = placeableX.begin(); xiter!=placeableX.end();xiter++) { int x = *xiter; int y = *yiter; b.minx = x; b.miny = y; b.maxx = x + h - 1; b.maxy = y + w - 1; int heur = computePlacementHeuristic(_area, b); if (heur > bestHeuristic) { if (_area->isAreaFree(x, y, h, w)) { bestHeuristic = heur; bestx = x; besty = y; bestPhase = 1; } } } #endif // if (bestPhase == 0) { b.minx = bestx; b.miny = besty; b.maxx = bestx + w - 1; b.maxy = besty + h - 1; _rotated = bfalse; _subImageRotation.setIdentity(); } else if (bestPhase == 1) { b.minx = bestx; b.miny = besty; b.maxx = bestx + h - 1; b.maxy = besty + w - 1; _rotated = btrue; Vec2d nextZero(0,(f32)m_subImageHeight-1); _subImageRotation.m_rot.set(0.f,1.f,-1.f,0.f); _subImageRotation.m_pos = -mulMatrix2d(_subImageRotation.m_rot, nextZero); } else { ITF_ASSERT(bfalse); } if (bestHeuristic < 0) return bfalse; _area->fillBox(b); return btrue; } /////////////////////////////////////////////////////////////////////////////////////////// bbool Patch::isEqual(const Patch *_other) { if (m_diffuseImagePath!=_other->m_diffuseImagePath) return bfalse; if (m_UVBox != _other->m_UVBox) return bfalse; return btrue; } /////////////////////////////////////////////////////////////////////////////////////////// void Patch::initialize(const StringID &_bankID, const StringID &_templateID, AnimTemplate *_animTemplate, ChainedBones &_chainedBones, ITF_VECTOR &_imageTab, const Path& _diffuseImagePath, AnimSkeleton *_skeleton) { m_bankID = _bankID; m_diffuseImagePath = _diffuseImagePath; m_templateID = _templateID; m_boneIndices.resize(0); nv::Image & firstImage = _imageTab[0].m_image; m_initialImageWidth = (int)firstImage.width(); m_initialImageHeight = (int)firstImage.height(); m_animTemplate = _animTemplate; m_skeleton = _skeleton; computeUVAABB(_animTemplate, _chainedBones, firstImage); u32 animBoneCount=_chainedBones.m_AnimBones.size(); m_boneIDs.resize(0); m_boneIDs.reserve(animBoneCount); m_boneIndices.reserve(animBoneCount); for (u32 i=0; igetBoneIndex(bone->m_Name); m_boneIndices.push_back(boneIndex); m_boneIDs.push_back(bone->m_Name); } for (u32 i=0; i<_imageTab.size(); i++) extractPixels(_imageTab[i].m_image, _imageTab[i].m_mode); } Path Patch::getPixelsTabFileName(u32 _idx) { Path filename(m_saveInFile); String8 name; name.setTextFormat(m_saveInFile.getBasename(), m_serialNumber, _idx); filename.changeBasename(name); return filename; } nv::Color32 * Patch::savePixelsTab(nv::Color32 * _pixelTab, u32 _idx) { if (m_saveInFile.isEmpty()) return _pixelTab; hwFile* file = newAlloc(mId_Temporary, hwFile()); if ( !file->open( getPixelsTabFileName(_idx), ITF_FILE_ATTR_CREATE_NEW | ITF_FILE_ATTR_WRITE ) ) { SF_DEL( file ); return _pixelTab; } file->write( _pixelTab, m_subImageHeight*m_subImageWidth*sizeof(nv::Color32)); file->close(); SF_DEL_ARRAY(_pixelTab); SF_DEL( file ); return NULL; } nv::Color32 * Patch::loadPixelsTab(nv::Color32 * _pixelTab, u32 _idx) { if (_pixelTab) return _pixelTab; if (m_saveInFile.isEmpty()) return _pixelTab; hwFile* file = newAlloc(mId_Temporary, hwFile()); if ( !file->open( getPixelsTabFileName(_idx), ITF_FILE_ATTR_READ ) ) { SF_DEL( file ); return _pixelTab; } _pixelTab = newAlloc(mId_Temporary,nv::Color32[m_subImageHeight*m_subImageWidth]); file->read( _pixelTab, m_subImageHeight*m_subImageWidth*sizeof(nv::Color32)); file->close(); SF_DEL( file ); return _pixelTab; } void Patch::extractPixels(nv::Image &_image, u32 _idx) { m_subImageWidth = getUVBoundingWidth(); m_subImageHeight = getUVBoundingHeight(); bbool useAlphaZero = (_image.format() == nv::Image::Format_RGB) && _idx > 0; if (_idx >= m_pixelsTab.size()) { u32 prevSize = m_pixelsTab.size(); m_pixelsTab.resize(_idx+1); for (u32 i=prevSize; i=0 && y=0 && xa = 0; dest++; } else *dest++ = emptyColor; } } ITF_ASSERT(dest==m_pixelsTab[_idx]+m_subImageWidth*m_subImageHeight); m_pixelsTab[_idx] = savePixelsTab(m_pixelsTab[_idx], _idx); } /////////////////////////////////////////////////////////////////////////////////////////// void Patch::blitToFinalPos(nv::Image &_destImage, i32 xOffset, i32 yOffset, u32 _idx) { ITF_ASSERT(!getSourceOfAlias()); if (!m_sampling.size()) return; if (_idx >= m_pixelsTab.size()) return; u32 finalW = m_widthSampling *PatchList::CURRENT_SAMPLING; u32 finalH = m_heightSampling*PatchList::CURRENT_SAMPLING; i32 destWidth = _destImage.width(); i32 destHeight = _destImage.height(); if ((m_finalPos.minx-xOffset) > destWidth || (m_finalPos.miny-yOffset) > destHeight || (m_finalPos.minx + finalW - xOffset) < 0 || (m_finalPos.maxy + finalH - yOffset) < 0) return; m_pixelsTab[_idx] = loadPixelsTab(m_pixelsTab[_idx], _idx); if (!m_pixelsTab[_idx]) return; nv::Color32 *dest = _destImage.pixels(); matrix2d invRotation; invRotation.set(m_uvlocalToRotatedLocalTransform.m_rot.m_col1.x(), m_uvlocalToRotatedLocalTransform.m_rot.m_col2.x(), m_uvlocalToRotatedLocalTransform.m_rot.m_col1.y(), m_uvlocalToRotatedLocalTransform.m_rot.m_col2.y()); Color dstColor; for (u32 y=0; y= 0 && posUV.x() < m_subImageWidth && posUV.y() >= 0 && posUV.y() < m_subImageHeight) { int x0 = (int)posUV.x(); int x1 = x0 + 1; int y0 = (int)posUV.y(); int y1 = y0 + 1; Color c00(getPixel_WithBorder(x0,y0,_idx)); Color c10(getPixel_WithBorder(x1,y0,_idx)); Color c01(getPixel_WithBorder(x0,y1,_idx)); Color c11(getPixel_WithBorder(x1,y1,_idx)); int offsetX = x + m_finalPos.minx - xOffset; int offsetY = y + m_finalPos.miny - yOffset; int offset = offsetX + offsetY*destWidth; if (offsetX >= 0 && offsetX < destWidth && offsetY >= 0 && offsetY < destHeight) { f32 xratio = posUV.x() - x0; f32 yratio = posUV.y() - y0; dstColor = c00*(1-xratio)*(1-yratio) + c10*xratio*(1-yratio) + c01*(1-xratio)*yratio + c11*xratio*yratio; nv::Color32 drawColor = nv::Color32(dstColor.getAsU32()); if (dest[offset].a < drawColor.a) dest[offset].a = drawColor.a; if (dest[offset].r < drawColor.r) dest[offset].r = drawColor.r; if (dest[offset].g < drawColor.g) dest[offset].g = drawColor.g; if (dest[offset].b < drawColor.b) dest[offset].b = drawColor.b; } } } } if (!m_saveInFile.isEmpty()) { SF_DEL_ARRAY(m_pixelsTab[_idx]); } } /////////////////////////////////////////////////////////////////////////////////////////// void Patch::growPatchMaxSize(const BoneMaxSize& _toAdd ) { m_boneMaxSize.grow(_toAdd.m_worldSize.getMax(), _toAdd.m_uvSize.getMax()); } const BoneMaxSize &Patch::getPatchMaxSize() const { ITF_ASSERT(!getSourceOfAlias()); return m_boneMaxSize; } /////////////////////////////////////////////////////////////////////////////////////////// //void Patch::resizeTexture(int _newSize) //{ // ITF_ASSERT(!getSourceOfAlias()); // int w = getSubImageWidth(); // int h = getSubImageHeight(); // if (w>=h) // { // float ratio = h/(float)w; // w = _newSize; // h = (int) (_newSize*ratio); // if (h<1) // h = 1; // } // else // { // float ratio = w/(float)h; // h = _newSize; // w = (int) (_newSize*ratio); // if (w<1) // w = 1; // } // int srcWidth = getSubImageWidth(); // int srcHeight = getSubImageHeight(); // m_imageStretchTransform.m_rot.set(w/(f32)srcWidth, 0,0, h/(f32)srcHeight); // m_imageStretchTransform.m_pos.set(0,0); // ITF_ASSERT(w &_alreadyProcessedPoints) { if (m_uvModified) return; m_uvModified = btrue; //Compute, for UV space : OriginalUV->truncatedInteger->rotatedInteger->placedInteger->final Transform2d originalToTruncated, truncatedRotatedToStretched, truncatedToRotated, rotatedToPlaced, placedToFinal; originalToTruncated.m_rot.set((f32)m_initialImageWidth, 0, 0, (f32)m_initialImageWidth); originalToTruncated.m_pos = - Vec2d((f32)m_UVBox.minx, (f32)m_UVBox.miny); Patch *transformSource = getSourceOfAlias(); if (!transformSource) transformSource = this; truncatedToRotated = transformSource->m_uvlocalToRotatedLocalTransform; truncatedRotatedToStretched = transformSource->m_imageStretchTransform; rotatedToPlaced.m_rot.setIdentity(); rotatedToPlaced.m_pos = Vec2d((f32)m_finalPos.minx+0.5f, (f32)m_finalPos.miny+0.5f); placedToFinal.m_rot.set(1.f/mapWidth, 0, 0, 1.f/mapWidth); placedToFinal.m_pos.set(0,0); Transform2d uvFinalTransform = placedToFinal*rotatedToPlaced*truncatedRotatedToStretched*truncatedToRotated*originalToTruncated; //we compute an uniform tranform, which doesnt take whole texture shapes, because rectangular textures can make UV squashed Transform2d uvVectorTranformUniform = rotatedToPlaced*truncatedRotatedToStretched*truncatedToRotated; //We modify Pos, PosEnd for each bone seed (hierarchy start). Seed is at index 0 for m_boneIndices for (u32 boneIndexInList=0; boneIndexInListm_NameToBone.find(m_boneIDs[boneIndexInList]); i32 boneIndex = -1; if (boneIDIndex>=0) boneIndex = m_animTemplate->m_NameToBone.getAt(boneIDIndex); ITF_ASSERT(boneIndex>=0 && boneIndex<(i32)m_animTemplate->m_BonesCurFrameDyn.size()); AnimBoneDyn *seedBone = &m_animTemplate->m_BonesCurFrameDyn[boneIndex]; ITF_ASSERT(seedBone); //f32 localAngleRad = seedBone->m_AngleLocal; //f32 localAngleRad = seedBone->m_Angle; if (boneIndexInList==0) { seedBone->m_Pos = mulTransform2d(uvFinalTransform, seedBone->m_Pos); seedBone->m_Angle += transformSource->m_angle; } //the following code may have problems with non-square textures or rotation. we should use X ratio instead Vec2d boneLenVec(seedBone->m_Scale.x(), 0); boneLenVec = mulMatrix2d(uvFinalTransform.m_rot, boneLenVec); seedBone->m_Scale.x() = boneLenVec.norm(); } //each AnimTemplate is broken in several Patch, //so we need to iterate each AnimPatch in it to check it's connected to a bone related to the Patch //If so, we can modify the points in the AnimPatch for (u32 patchIndex=0; patchIndexm_Patch.size(); patchIndex++) { AnimPatch *patch = &m_animTemplate->m_Patch[patchIndex]; if (patch->m_nbPatchPoints!=0) { AnimBone *boneToFind = patch->m_AnimPatchPoints[0]->m_mainRefs.m_BonePtr; u32 boneIndex=0; ITF_ASSERT(boneToFind); StringID idToFind = boneToFind->m_Name; //In the bone in current patch list? bbool isFound = bfalse; for (boneIndex=0; boneIndexm_nbPatchPoints; pointIndex++) { AnimPatchPoint *p = patch->m_AnimPatchPoints[pointIndex]; if (find(_alreadyProcessedPoints.begin(), _alreadyProcessedPoints.end(), p)!=_alreadyProcessedPoints.end()) continue; _alreadyProcessedPoints.insert(p); p->m_PosUV = mulTransform2d(uvFinalTransform, p->m_PosUV); p->m_NormaleUV = mulMatrix2d(uvVectorTranformUniform.m_rot, p->m_NormaleUV); p->m_NormaleUV.normalize(); //the following code may have problems with non-square textures or rotation. we should use Y ratio instead Vec2d boneLenVec(0, f32_Abs(p->m_mainRefs.m_Pos.y())); boneLenVec = mulMatrix2d(uvFinalTransform.m_rot, boneLenVec); p->m_mainRefs.m_Pos.y() = boneLenVec.norm()*getSign(p->m_mainRefs.m_Pos.y()); //p->m_mainRefs.m_Pos.y() *= initialToFinalHeight; //p->m_mainRefs.m_Normale = mulMatrix2d(uvVectorTranformUniform.m_rot, p->m_mainRefs.m_Normale); //p->m_mainRefs.m_Normale.normalize(); } } } } } /////////////////////////////////////////////////////////////////////////////////////////// bbool Patch::computePatchUVAABB(AnimPatch *_patch, AABB &UVs, nv::Image &_image, Vec2d *_ptList) { struct TexDesc { u32 m_sizeX, m_sizeY; f32 getRatioXY() {return (f32) m_sizeX/(f32) m_sizeY;} } textureDesc; UVs = AABB(Vec2d(0,0)); bbool isBoxInit=bfalse; TexDesc *texture = &textureDesc; SimplePatch32Data data; SimplePatch32Data *_pData = &data; Vec2d PointsNS[16]; const AnimPatchPointDyn *ppDynTab[4]; const AnimPatchPoint *ppTab[4]; AnimPatchPointDyn dummyDyn[4]; const u32 _hdiv = s_patchHdiv; const u32 _vdiv = 1; const f32 _z = 0; textureDesc.m_sizeX = _image.width(); textureDesc.m_sizeY = _image.height(); ITF_MemSet(dummyDyn, 0, sizeof(dummyDyn)); if (_patch->m_nbPatchPoints == 4) { ppTab[0] = _patch->m_AnimPatchPoints[1]; ppTab[1] = _patch->m_AnimPatchPoints[3]; ppTab[2] = _patch->m_AnimPatchPoints[0]; ppTab[3] = _patch->m_AnimPatchPoints[2]; } else { ITF_ASSERT(_patch->m_nbPatchPoints == 2); ppTab[0] = _patch->m_AnimPatchPoints[0]; ppTab[1] = _patch->m_AnimPatchPoints[1]; ppTab[2] = _patch->m_AnimPatchPoints[1]; ppTab[3] = _patch->m_AnimPatchPoints[0]; } ppDynTab[0] = &dummyDyn[ppTab[0]->m_Index]; ppDynTab[1] = &dummyDyn[ppTab[1]->m_Index]; ppDynTab[2] = &dummyDyn[ppTab[2]->m_Index]; ppDynTab[3] = &dummyDyn[ppTab[3]->m_Index]; AnimDraw::computePatchControlPoints(&PointsNS[0],ppDynTab, ppTab, _hdiv, _vdiv, bfalse); #undef PATCH32_USE_PCT #undef PATCH32_USE_COLOR #define PATCH32_DO_NOT_RETURN #define PATCH32_IGNORE_MESHELEMENTS #include "engine/AdaptersInterfaces/GFXAdapter_DrawPatch.h" VertexPT * vertices = _pData->vertexListPT; for (u32 vertIndex=0; vertIndex<_pData->vertexCount; vertIndex++) { _ptList[vertIndex] = vertices[vertIndex].m_uv; if (!isBoxInit) { UVs = AABB(_ptList[vertIndex]); isBoxInit = btrue; } else { UVs.grow(_ptList[vertIndex]); } } ITF_ASSERT(_pData->vertexCount pointListTmp1; SafeArray pointListTmp2; for (int boneIndex=0; boneIndex<(int)_chainedBones.m_AnimBones.size(); boneIndex++) { AnimBone *bone = _chainedBones.m_AnimBones[boneIndex]; //for each patch linked to this bone for (u32 patchIndex=0; patchIndex<_template->m_Patch.size(); patchIndex++) { AnimPatch *patch = &_template->m_Patch[patchIndex]; if (patch->m_nbPatchPoints!=0 && patch->m_AnimPatchPoints[0]->m_mainRefs.m_BonePtr == bone) { AABB patchBB; u32 begin = pointListTmp1.size(); pointListTmp1.resize(begin + pathSize); if (computePatchUVAABB(patch, patchBB, _image, &pointListTmp1[begin])) { if (isUVInit) { UVs.grow(patchBB); } else { UVs = patchBB; isUVInit = btrue; } } // } } } // now we recompute the pointList i32 nbPatchs = pointListTmp1.size()/pathSize; ITF_VECTOR segmentList; for (i32 i=0; i=spatch.m_endIndex; j--) if (!pointListTmp2.size() || !pointListTmp2[pointListTmp2.size()-1].IsEqual(pointListTmp1[j], MTH_EPSILON)) pointListTmp2.push_back(pointListTmp1[j]); } } m_processDelta = Vec2d::Zero; m_pointList.resize(pointListTmp2.size()); for (u32 i=0; i m_pointList[i].x()) m_processDelta.x() = m_pointList[i].x(); if (m_processDelta.y() > m_pointList[i].y()) m_processDelta.y() = m_pointList[i].y(); } for (u32 i=0; i & _sampling ) { m_widthSampling = _width; m_heightSampling = _height; m_sampling = _sampling; } } }