/* optimize.cpp - studio model optimization (generate triangle strips as possible) Copyright (C) 2017 Uncle Mike This program 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 3 of the License, or (at your option) any later version. This program 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. */ #include "cmdlib.h" #include "mathlib.h" #include "stringlib.h" #include "studio.h" #include "studiomdl.h" int used[MAXSTUDIOTRIANGLES]; // the command list holds counts and s/t values that are valid for // every frame short commands[MAXSTUDIOTRIANGLES * 13]; int numcommands; // all frames will have their vertexes rearranged and expanded // so they are in the order expected by the command list int allverts, alltris, stripcount; int stripverts[MAXSTUDIOTRIANGLES+2]; int striptris[MAXSTUDIOTRIANGLES+2]; int neighbortri[MAXSTUDIOTRIANGLES][3]; int neighboredge[MAXSTUDIOTRIANGLES][3]; s_trianglevert_t (*triangles)[3]; s_mesh_t *pmesh; void FindNeighbor (int starttri, int startv) { s_trianglevert_t m1, m2; int j; s_trianglevert_t *last, *check; int k; // used[starttri] |= (1 << startv); last = &triangles[starttri][0]; m1 = last[(startv+1)%3]; m2 = last[(startv+0)%3]; for (j=starttri+1, check=&triangles[starttri+1][0] ; jnumtris ; j++, check += 3) { if (used[j] == 7) continue; for (k=0 ; k<3 ; k++) { if (memcmp(&check[k],&m1,sizeof(m1))) continue; if (memcmp(&check[ (k+1)%3 ],&m2,sizeof(m2))) continue; neighbortri[starttri][startv] = j; neighboredge[starttri][startv] = k; neighbortri[j][k] = starttri; neighboredge[j][k] = startv; used[starttri] |= (1 << startv); used[j] |= (1 << k); return; } } } int StripLength (int starttri, int startv) { int j; int k; used[starttri] = 2; stripverts[0] = (startv)%3; stripverts[1] = (startv+1)%3; stripverts[2] = (startv+2)%3; striptris[0] = starttri; striptris[1] = starttri; striptris[2] = starttri; stripcount = 3; while( 1 ) { if (stripcount & 1) { j = neighbortri[starttri][(startv+1)%3]; k = neighboredge[starttri][(startv+1)%3]; } else { j = neighbortri[starttri][(startv+2)%3]; k = neighboredge[starttri][(startv+2)%3]; } if (j == -1 || used[j]) goto done; stripverts[stripcount] = (k+2)%3; striptris[stripcount] = j; stripcount++; used[j] = 2; starttri = j; startv = k; } done: // clear the temp used flags for (j=0 ; jnumtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } int FanLength (int starttri, int startv) { int j; int k; used[starttri] = 2; stripverts[0] = (startv)%3; stripverts[1] = (startv+1)%3; stripverts[2] = (startv+2)%3; striptris[0] = starttri; striptris[1] = starttri; striptris[2] = starttri; stripcount = 3; while( 1 ) { j = neighbortri[starttri][(startv+2)%3]; k = neighboredge[starttri][(startv+2)%3]; if (j == -1 || used[j]) goto done; stripverts[stripcount] = (k+2)%3; striptris[stripcount] = j; stripcount++; used[j] = 2; starttri = j; startv = k; } done: // clear the temp used flags for (j=0 ; jnumtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } //Generate a list of trifans or strips for the model, which holds for all frames int numcommandnodes; int BuildTris (s_trianglevert_t (*x)[3], s_mesh_t *y, byte **ppdata ) { int i, j, k, m; int startv; int len, bestlen, besttype; static int bestverts[MAXSTUDIOTRIANGLES]; static int besttris[MAXSTUDIOTRIANGLES]; static int peak[MAXSTUDIOTRIANGLES]; int type; int total = 0; double t; int maxlen; triangles = x; pmesh = y; t = I_FloatTime(); for (i=0 ; inumtris ; i++) { neighbortri[i][0] = neighbortri[i][1] = neighbortri[i][2] = -1; used[i] = 0; peak[i] = pmesh->numtris; } // printf("finding neighbors\n"); for (i=0 ; inumtris; i++) { for (k = 0; k < 3; k++) { if (used[i] & (1 << k)) continue; FindNeighbor( i, k ); } // printf("%d", used[i] ); } // printf("\n"); // // build tristrips // numcommandnodes = 0; numcommands = 0; memset (used, 0, sizeof(used)); for (i=0 ; inumtris ;) { // pick an unused triangle and start the trifan if (used[i]) { i++; continue; } maxlen = 9999; bestlen = 0; m = 0; for (k = i; k < pmesh->numtris && bestlen < 127; k++) { int localpeak = 0; if (used[k]) continue; if (peak[k] <= bestlen) continue; m++; for (type = 0 ; type < 2 ; type++) { for (startv =0 ; startv < 3 ; startv++) { if (type == 1) len = FanLength (k, startv); else len = StripLength (k, startv); if (len > 127) { // skip these, they are too long to encode } else if (len > bestlen) { besttype = type; bestlen = len; for (j=0 ; j localpeak) localpeak = len; } } peak[k] = localpeak; if (localpeak == maxlen) break; } total += (bestlen - 2); // printf("%d (%d) %d\n", bestlen, pmesh->numtris - total, i ); maxlen = bestlen; // mark the tris on the best strip as used for (j=0 ; jvertindex; commands[numcommands++] = tri->normindex; if(( g_texture[pmesh->skinref].flags & STUDIO_NF_CHROME ) || !store_uv_coords ) { commands[numcommands++] = tri->s; commands[numcommands++] = tri->t; } else { #if 0 float error_u = fabs( tri->u - HalfToFloat( FloatToHalf( tri->u ))); float error_v = fabs( tri->v - HalfToFloat( FloatToHalf( tri->v ))); Msg( "store coord: ( %.7f %.7f ), error: ( %.7f %.7f )\n", tri->u, tri->v, error_u, error_v ); #endif commands[numcommands++] = FloatToHalf( tri->u ); commands[numcommands++] = FloatToHalf( tri->v ); } } // printf("%d ", bestlen - 2 ); numcommandnodes++; if( t != I_FloatTime()) { // don't write progress to log printf( "%2d%%\r", (total * 100) / pmesh->numtris ); t = I_FloatTime(); } } commands[numcommands++] = 0; // end of list marker *ppdata = (byte *)commands; // printf("%d %d %d\n", numcommandnodes, numcommands, pmesh->numtris ); return numcommands * sizeof( short ); }