/* Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "animcomp.h" void *SafeMalloc( size_t n, char *desc ); float *matrix; float *delta; float *best; float *comp; float *tcomp; float *bestcomp; float *frames; float *base; int MatWidth; int MatHeight; int CFrameSize; int nFrames; void AnimCompressInit( int nframes,int nVerts,int CompressedFrameSize ){ nFrames = nframes; MatWidth = nVerts * 3; MatHeight = CompressedFrameSize; CFrameSize = CompressedFrameSize; matrix = (float *)SafeMalloc( MatWidth * MatHeight * sizeof( float ), "AnimCompressInit" ); best = (float *)SafeMalloc( MatWidth * MatHeight * sizeof( float ), "AnimCompressInit" ); delta = (float *)SafeMalloc( MatWidth * MatHeight * sizeof( float ), "AnimCompressInit" ); comp = (float *)SafeMalloc( CFrameSize * nFrames * sizeof( float ), "AnimCompressInit" ); tcomp = (float *)SafeMalloc( CFrameSize * nFrames * sizeof( float ), "AnimCompressInit" ); bestcomp = (float *)SafeMalloc( CFrameSize * nFrames * sizeof( float ), "AnimCompressInit" ); base = (float *)SafeMalloc( MatWidth * sizeof( float ), "AnimCompressInit" ); frames = (float *)SafeMalloc( MatWidth * nFrames * sizeof( float ), "AnimCompressInit" ); } void AnimSetFrame( int frame,int index,float x,float y,float z ){ frames[frame * MatWidth + index * 3] = x; frames[frame * MatWidth + index * 3 + 1] = y; frames[frame * MatWidth + index * 3 + 2] = z; } typedef struct { int index; float val; } SORTP; #define F_RANDOM ( ( (float)rand() ) / (float)RAND_MAX ) extern void DOsvd( float *a,float *res,float *comp,float *values,int nframes,int framesize,int compressedsize ); void AnimCompressDoit(){ float compression; float *rescale; float *ans; float maxdev; float avedev; float tmp; int j,k,l,numave; for ( k = 0; k < MatWidth; k++ ) base[k] = 0.0f; for ( j = 0; j < nFrames; j++ ) for ( k = 0; k < MatWidth; k++ ) base[k] += frames[j * MatWidth + k]; tmp = 1.0f / (float)nFrames; for ( k = 0; k < MatWidth; k++ ) base[k] *= tmp; for ( j = 0; j < nFrames; j++ ) for ( k = 0; k < MatWidth; k++ ) frames[j * MatWidth + k] -= base[k]; ans = (float *)SafeMalloc( sizeof( float ) * MatWidth, "AnimCompressDoit" ); rescale = (float *)SafeMalloc( sizeof( float ) * CFrameSize, "AnimCompressDoit" ); DOsvd( frames,best,bestcomp,rescale,nFrames,MatWidth,MatHeight ); avedev = 0.0; for ( l = 0; l < CFrameSize; l++ ) avedev += rescale[l]; for ( l = 0; l < CFrameSize; l++ ) printf( "%3.1f ",100.0f * rescale[l] / avedev ); printf( "\n" ); for ( j = 0; j < nFrames; j++ ) { for ( l = 0; l < CFrameSize; l++ ) { bestcomp[j * CFrameSize + l] = 0.0; for ( k = 0; k < MatWidth; k++ ) bestcomp[j * CFrameSize + l] += best[l * MatWidth + k] * frames[j * MatWidth + k]; } } numave = 0; avedev = 0.0; maxdev = 0.0; for ( j = 0; j < nFrames; j++ ) { for ( k = 0; k < MatWidth; k++ ) { ans[k] = 0.0; for ( l = 0; l < CFrameSize; l++ ) ans[k] += best[l * MatWidth + k] * bestcomp[j * CFrameSize + l]; ans[k] -= frames[j * MatWidth + k]; tmp = (float)fabs( ans[k] ); if ( tmp > maxdev ) { maxdev = tmp; } avedev += tmp; numave++; } } avedev /= (float)numave; printf( "%f Max Deviation (inches) %f Ave Dev. (inches)\n",maxdev,avedev ); printf( "%d bytes original size\n",MatWidth * nFrames ); printf( "%d bytes of overhead\n",MatWidth * MatHeight ); printf( "%d bytes/frame * %d frames = %d bytes\n",CFrameSize,nFrames,CFrameSize * nFrames ); compression = (float)( MatWidth * MatHeight + CFrameSize * nFrames + MatWidth ); compression /= (float)( MatWidth * nFrames ); printf( "Overall compression = %f %%\n",100.0f - 100.0f * compression ); compression = (float)( CFrameSize ); compression /= (float)( MatWidth ); printf( "frame size compression = %f %%\n",100.0f - 100.0f * compression ); free( rescale ); free( ans ); } void AnimCompressToBytes( float *trans,float *scale,char *mat,char *ccomp,unsigned char *cbase,float *cscale,float *coffset,float *bmin,float *bmax ){ int k,l,nv,j; float maxdev; float avedev; float tmp; int numave; float t,mx; float *ans; nv = MatWidth / 3; trans[0] = 1E30f; scale[0] = -1E30f; trans[1] = 1E30f; scale[1] = -1E30f; trans[2] = 1E30f; scale[2] = -1E30f; for ( k = 0; k < MatWidth; k += 3 ) { if ( base[k] > scale[0] ) { scale[0] = base[k]; } if ( base[k] < trans[0] ) { trans[0] = base[k]; } if ( base[k + 1] > scale[1] ) { scale[1] = base[k + 1]; } if ( base[k + 1] < trans[1] ) { trans[1] = base[k + 1]; } if ( base[k + 2] > scale[2] ) { scale[2] = base[k + 2]; } if ( base[k + 2] < trans[2] ) { trans[2] = base[k + 2]; } } scale[0] -= trans[0]; scale[1] -= trans[1]; scale[2] -= trans[2]; scale[0] /= 255.0f; scale[1] /= 255.0f; scale[2] /= 255.0f; for ( k = 0; k < MatWidth; k += 3 ) { t = ( base[k] - trans[0] ) / scale[0]; if ( t < 0.0f ) { t = 0.0f; } if ( t > 255.0f ) { t = 255.0f; } cbase[k] = (unsigned char)t; t = ( base[k + 1] - trans[1] ) / scale[1]; if ( t < 0.0f ) { t = 0.0f; } if ( t > 255.0f ) { t = 255.0f; } cbase[k + 1] = (unsigned char)t; t = ( base[k + 2] - trans[2] ) / scale[2]; if ( t < 0.0f ) { t = 0.0f; } if ( t > 255.0f ) { t = 255.0f; } cbase[k + 2] = (unsigned char)t; } for ( l = 0; l < MatHeight; l++ ) { mx = 0.0; for ( k = 0; k < MatWidth; k++ ) { if ( fabs( best[l * MatWidth + k] ) > mx ) { mx = (float)fabs( best[l * MatWidth + k] ); } } if ( mx > 1E-8 ) { mx /= 127.0f; coffset[l] = 1E30f; cscale[l] = -1E30f; for ( j = 0; j < nFrames; j++ ) { bestcomp[j * MatHeight + l] *= mx; if ( bestcomp[j * MatHeight + l] > cscale[l] ) { cscale[l] = bestcomp[j * MatHeight + l]; } if ( bestcomp[j * MatHeight + l] < coffset[l] ) { coffset[l] = bestcomp[j * MatHeight + l]; } } cscale[l] -= coffset[l]; if ( cscale[l] > 1E-10 ) { for ( j = 0; j < nFrames; j++ ) { tmp = 254.0f * ( bestcomp[j * MatHeight + l] - coffset[l] ) / cscale[l] - 127.0f; if ( tmp > 127.0f ) { tmp = 127.0f; } if ( tmp < -127.0f ) { tmp = -127.0f; } ccomp[j * MatHeight + l] = (char)floor( tmp + 0.5 ); } coffset[l] += cscale[l] * 127.0f / 254.0f; cscale[l] /= 254.0f; } else { cscale[l] = 1.0f; coffset[l] = 0.0f; for ( j = 0; j < nFrames; j++ ) ccomp[j * MatHeight + l] = 0; } mx = 1.0f / mx; for ( k = 0; k < MatWidth; k++ ) { tmp = best[l * MatWidth + k] * mx; if ( tmp > 127.0f ) { tmp = 127.0f; } if ( tmp < -127.0f ) { tmp = -127.0f; } mat[k * MatHeight + l] = (char)floor( tmp + 0.5 ); } } else { cscale[l] = 1.0f; coffset[l] = 0.0f; for ( j = 0; j < nFrames; j++ ) ccomp[j * MatHeight + l] = 0; for ( k = 0; k < MatWidth; k++ ) mat[k * MatHeight + l] = 0; } } bmin[0] = 1E30f; bmin[1] = 1E30f; bmin[2] = 1E30f; bmax[0] = -1E30f; bmax[1] = -1E30f; bmax[2] = -1E30f; numave = 0; avedev = 0.0; maxdev = 0.0; ans = (float *)SafeMalloc( sizeof( float ) * MatWidth, "AnimCompressToBytes" ); for ( j = 0; j < nFrames; j++ ) { for ( k = 0; k < MatWidth; k++ ) { ans[k] = 0.0; for ( l = 0; l < CFrameSize; l++ ) ans[k] += (float)( mat[l + k * MatHeight] ) * ( (float)( ccomp[j * CFrameSize + l] ) * cscale[l] + coffset[l] ); ans[k] += (float)( cbase[k] ) * scale[k % 3] + trans[k % 3]; tmp = (float)fabs( ans[k] - frames[j * MatWidth + k] - base[k] ); if ( tmp > maxdev ) { maxdev = tmp; } avedev += tmp; numave++; if ( bmin[k % 3] > ans[k] ) { bmin[k % 3] = ans[k]; } if ( bmax[k % 3] < ans[k] ) { bmax[k % 3] = ans[k]; } } } avedev /= (float)numave; printf( "%f Max Deviation (inches) %f Ave Dev. (inches)\n",maxdev,avedev ); free( ans ); } void AnimCompressGetMatrix( float *mat ){ int k,l; for ( k = 0; k < MatWidth; k++ ) for ( l = 0; l < MatHeight; l++ ) mat[k * MatHeight + l] = best[l * MatWidth + k]; } void AnimCompressGetFrames( float *mat ){ memcpy( mat,bestcomp,CFrameSize * nFrames * sizeof( float ) ); } void AnimCompressGetBase( int i,float *x,float *y,float *z ){ *x = base[i * 3]; *y = base[i * 3 + 1]; *z = base[i * 3 + 2]; } void AnimCompressEnd(){ free( matrix ); free( best ); free( delta ); free( comp ); free( tcomp ); free( bestcomp ); free( base ); free( frames ); }