Getting all the units shader friendly

Thinking about improving the Artwork in Vega Strike, or making your own Mod? Submit your question and ideas in this forum.

Moderator: pyramid

Post Reply
safemode
Developer
Developer
Posts: 2150
Joined: Mon Apr 23, 2007 1:17 am
Location: Pennsylvania
Contact:

Getting all the units shader friendly

Post by safemode »

I'm not sure of where to post this, as there is no real thread for the work being done to shade up VS and get the units in line. I know there are packing issues being worked out still, and the shader is still undergoing serious work. So maybe it can get done in here.

Anyways, the real point of starting the thread was that I found this while looking around for gimp plugins.

http://nifelheim.dyndns.org/~cocidius/normalmap/

not sure if you guys are using it already or some other tool like blender. I figured, more people use gimp than blender, perhaps it would help get more things properly shaded by being able to do it in gimp. It looks promising.
Ed Sweetman endorses this message.
loki1950
The Shepherd
Posts: 5841
Joined: Fri May 13, 2005 8:37 pm
Location: Ottawa
Contact:

Post by loki1950 »

Usually both are used :wink: but blender's internal texture editor is getting much better but real detail work is done in the GIMP.

Enjoy the Choice :)
my box::HP Envy i5-6400 @2Q70GHzx4 8 Gb ram/1 Tb(Win10 64)/3 Tb Mint 19.2/GTX745 4Gb acer S243HL K222HQL
Q8200/Asus P5QDLX/8 Gb ram/WD 2Tb 2-500 G HD/GF GT640 2Gb Mint 17.3 64 bit Win 10 32 bit acer and Lenovo ideapad 320-15ARB Win 10/Mint 19.2
Deus Siddis
Elite
Elite
Posts: 1363
Joined: Sat Aug 04, 2007 3:42 pm

Re: Getting all the units shader friendly

Post by Deus Siddis »

safemode wrote:Anyways, the real point of starting the thread was that I found this while looking around for gimp plugins.
http://nifelheim.dyndns.org/~cocidius/normalmap/
I think Chuck_Starchaser posted a link to that plugin in one of his howtos. I tried it myself and tested the results in blender and it looked good, but I don't have alot of experience with normal mapping yet so I'm not sure what the quality difference is between this and baking the normal information from another 3d model.
not sure if you guys are using it already or some other tool like blender. I figured, more people use gimp than blender, perhaps it would help get more things properly shaded by being able to do it in gimp.
I think blender will be able to create tangent space normal maps in the next stable release (or probably the current dev release). I do know that it is a feature presently under development though.

Artists will need to put the extra work in to make a more detailed, higher budget model of the same vessel using this method vs the gimp plugin, but it does still seem to be the weapon of choice for the commercial game industry (which when it comes to graphics, is actually a good thing :)).
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Sorry, guys; I had totally missed this thread.

Yeah, a combo of normalmap generation techniques will have to be used to make good-looking ships, for different effects:

1) What Blender can do for you is produce a smoother mesh using Catmul Clark subdivision, and then bake a normalmap relative to the original mesh. This is good for getting round things shading more smoothly rounded without increasing the polygon count too much.

2) Another tool, XNormal, lets you add geometry, like little pipes and boxes and whatever you fancy, on top of a surface, and Xnormal will bake a normalmap for you to represent that geometry.

3) Finally, Gimp's Normalmap makes normalmaps from height maps, and this is essential for converting hand-drawn grooves and depressions into a normalmap.


We really need to use all three tools, and then combine the results. But all three are easy to learn. Combining results may be a bit trickier, since normalmaps don't blend properly; and what one is looking for is angular addition of the vectors represented by the texels of the normalmaps being added. IOW, the normalmaps need to be converted to angles, the angles added, then converted back to dU dV. I wrote a Blender noodle to do this. There's no software out there, that I know of, that can add normalmaps. I'll find that noodle and post it here.


But the biggest barrier against normalmaps, right now, is the lack of Tangents. This means that all the islands in a ship's UV unwrap have to have their forward vector pointing and aligned to the UP (or North) direction in the texture. If it points to the left or right, the normalmap will shade oddly, just like the small nascelles on top of the wings of the Llama do. If it points South, grooves look like pipes, and viceversa.
But most models were not unwrapped to respect any such rules, so trying to make normalmaps for them is an exercise in futility.

(And what's the "forward" direction of a round space station, anyways?)

We absolutely need Tangents; and I already wrote the code to compute them on the fly as meshes are loaded, and sent the code to Hellcat AND Klauss for evaluation MONTHS ago, and still nothing's happening...
:(

EDIT:
Here's the Tangent's mods I wrote:
In mesh_xml.cpp, a new function:

Code: Select all

//Tangent calculation routine by chuck_starchaser; should work...
void SumTangents(
                  int                trimax,
                  int                t3vert,
		  vector<GFXVertex> &vertices,
		  vector<int> const &triind,
                  vector<Vector>    &tan1, //tangent accumulator; --should have num elements >= num vertices
                  vector<Vector>    &tan2  //bitangent accumulator; --should have num elements >= num vertices
		)
{
  glVertex v1, v2, v3;  //temporary copies of the three vertices for each triangle
  //Vector Cur, *pVert;   //Cur is like a temporary x, y, z onto which computations are made
  Vector sdir, tdir;    //tangent and "binormal" ("bitangent") vectors, I believe
  size_t i1, i2, i3;    //indexes to 3 vertices for each triangle
  size_t a = 0;         //stride index, increments by 3 at a time
  float x1, x2, y1, y2, z1, z2, s1, s2, t1, t2; //deltas
  float r;              //for inverse cross product of tex coord deltas
  //this works only for triangles for now...
  if( t3vert < 3 ) return;
  //for each polygon (triangle or quad... we split quads into two triangles)
  for( size_t i=0; i<trimax; ++i; a += t3vert )
  {
    for( size_t pass=0; pass<t3vert-2; ++pass )
    {
      //store indexes to a triangle's 3 vertices, so we're not recomputing them all over the place
      i1 = triind[a];
      i2 = triind[a+1+pass];
      i3 = triind[a+2+pass];
      //make temporary copies of the relevant vertex data, for clarity and performance and shorter expressions
      v1.x = vertices[i1].x; v1.y = vertices[i1].y; v1.z = vertices[i1].z; v1.s = vertices[i1].s; v1.t = vertices[i1].t;
      v2.x = vertices[i2].x; v2.y = vertices[i2].y; v2.z = vertices[i2].z; v2.s = vertices[i2].s; v2.t = vertices[i2].t;
      v3.x = vertices[i3].x; v3.y = vertices[i3].y; v3.z = vertices[i3].z; v3.s = vertices[i3].s; v3.t = vertices[i3].t;
      //compute deltas. I think that the fact we use (*2-*1) and (*3-*1) is arbitrary, but I could be wrong
      x1 = v2.x - v1.x;
      x2 = v3.x - v1.x;
      y1 = v2.y - v1.y;
      y2 = v3.y - v1.y;
      z1 = v2.z - v1.z;
      z2 = v3.z - v1.z;
      s1 = v2.s - v1.s;
      s2 = v3.s - v1.s;
      t1 = v2.t - v1.t;
      t2 = v3.t - v1.t;
      //we'll have to divide by the texture coord deltas' cross product, so might as well pre-compute its inverse
      float r = 1.0f / (s1*t2 - s2*t1);
      //and now a myracle happens...
      sdir = Vector( r*(t2*x1 - t1*x2), r*(t2*y1 - t1*y2), r*(t2*z1 - t1*z2) );
      tdir = Vector( r*(s1*x2 - s2*x1), r*(s1*y2 - s2*y1), r*(s1*z2 - s2*z1) );
      //and the results are output
      tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir;
      tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir;
    }
  }
}
Also in the same file, function void Mesh::PostProcessLoading(MeshXML * xml,const vector<string> &textureOverride) needs to have a block of code inserted (whole function code below, my added block clearly marked):

Code: Select all

void Mesh::PostProcessLoading(MeshXML * xml,const vector<string> &textureOverride) {
  unsigned int i; unsigned int a=0;
  unsigned int j;
  //begin vertex normal calculations if necessary
  if (!xml->usenormals/*USE_RECALC_NORM||xml->recalc_norm*/) {//fixme!
    bool *vertrw = new bool [xml->vertices.size()]; 
    for (i=0;i<xml->vertices.size();i++) {
      if (xml->vertices[i].i==0&&
	  xml->vertices[i].j==0&&
	  xml->vertices[i].k==0) {
	vertrw[i]=true;
      }else {
	vertrw[i]=USE_RECALC_NORM;
      }
    }
    SumNormals (xml->tris.size()/3,3,xml->vertices, xml->triind,xml->vertexcount, vertrw);
    SumNormals (xml->quads.size()/4,4,xml->vertices, xml->quadind,xml->vertexcount, vertrw);
    SumNormals (xml->lines.size()/2,2,xml->vertices,xml->lineind,xml->vertexcount,vertrw);
    SumNormals (xml->nrmltristrip.size()/3,3,xml->vertices, xml->nrmltristrip,xml->vertexcount, vertrw);
    SumNormals (xml->nrmltrifan.size()/3,3,xml->vertices, xml->nrmltrifan,xml->vertexcount, vertrw);
    SumNormals (xml->nrmlquadstrip.size()/4,4,xml->vertices, xml->nrmlquadstrip,xml->vertexcount, vertrw);
    SumNormals (xml->nrmllinstrip.size()/2,2,xml->vertices, xml->nrmllinstrip,xml->vertexcount, vertrw);
    delete []vertrw;
    for (i=0;i<xml->vertices.size();i++) {
      GFXVertex &v=xml->vertices[i];
      float dis = (v.i*v.i + v.j*v.j + v.k*v.k);
      if (dis==1.0f) continue;
      dis = sqrtf(dis);
      if (dis!=0) {
        v.i/=dis;//renormalize
        v.j/=dis;
        v.k/=dis;
      }else {
        v.i=v.x;
        v.j=v.y;
        v.k=v.z;
        dis = sqrtf (v.i*v.i + v.j*v.j + v.k*v.k);
        if (dis!=0) {
          v.i/=dis;//renormalize
          v.j/=dis;
          v.k/=dis;	  
        }else {
          v.i=0;
          v.j=0;
          v.k=1;	  
        }
      } 
    }
  }


  // ************ Tangent, by chuck_starchaser: ***********
  //unconditional block opening brace for the sake of avoiding namespace clashes and/or in lieu of
  //whatever boilerplate conditionals may be needed...
  {
    //tangent accumulators are declared static for performance (to avoid allocating at each call)
    static vector<Vector> tan1(32767); //initial size may need some tweaking: If we know the number of...
    static vector<Vector> tan2(32767); //vertices in the biggest mesh, might as well use that.
    Vector vnull( 0.0f, 0.0f, 0.0f ); //to simplify initialization to zeros
    size_t total_of_vertices = xml->vertices.size();
    if( tan1.size() < total_of_vertices+1 )
    {
      tan1.resize( total_of_vertices+4000 ); //plus 4000 to avoid too frequent resizings.
      tan2.resize( total_of_vertices+4000 );
    }
    //clear the accumulators, but just for the span we're about to use
    for( size_t i=0; i<total_of_vertices; ++i ){ tan1[i]=vnull; tan2[i]=vnull; }
    //We call Sum Tangents for triangles, quads, strips, fans, etc.
    SumTangents( xml->tris.size()/3,          3, xml->vertices, xml->triind,        tan1, tan2 );
    SumTangents( xml->quads.size()/4,         4, xml->vertices, xml->quadind,       tan1, tan2 );
    SumTangents( xml->nrmltristrip.size()/3,  3, xml->vertices, xml->nrmltristrip,  tan1, tan2 );
    SumTangents( xml->nrmltrifan.size()/3,    3, xml->vertices, xml->nrmltrifan,    tan1, tan2 );
    SumTangents( xml->nrmlquadstrip.size()/4, 4, xml->vertices, xml->nrmlquadstrip, tan1, tan2 );
    //And now that we got the accumulations, here comes the final computation:
    GFXVertex    *pv;
    Vector        normal, tangent;
    Vector const *ptan1;
    float         handedness;
    //For each vertex
    for( size_t i=0; i<total_of_vertices; ++i )
    {
      pv =     xml->vertices+i;
      ptan1 =  tan1+a;
      normal = Vector( pv->i, pv->j, pv->k );
      // Gram-Schmidt orthogonalize
      tangent = *ptan1 - normal * normal.Dot( *ptan1 );
      //check for null just in case... to avoid DIV0 exceptions in Normalize()...
      if( tangent.Length() < 0.0000000001f ) tangent.x = 1.0;
      tangent.Normalize();
      // Calculate handedness
      handedness =  (normal.Cross(*ptan1)).Dot(tan2[a]) ? -1.0f : +1.0f;
      // write results
      pv->tx = tangent.x;
      pv->ty = tangent.y;
      pv->tz = tangent.z;
      pv->tw = handedness;
    }
  }
  // ************ end of Tangent, by chuck_starchaser: ***********


  a=0;
  std::vector <unsigned int> ind;
  for (a=0;a<xml->tris.size();a+=3) {
    for (j=0;j<3;j++) {
	ind.push_back (xml->triind[a+j]);
	xml->tris[a+j].i = xml->vertices[xml->triind[a+j]].i;
	xml->tris[a+j].j = xml->vertices[xml->triind[a+j]].j;
	xml->tris[a+j].k = xml->vertices[xml->triind[a+j]].k;
    }
  }
  a=0;
  for (a=0;a<xml->quads.size();a+=4) {
    for (j=0;j<4;j++) {
	ind.push_back (xml->quadind[a+j]);
	xml->quads[a+j].i=xml->vertices[xml->quadind[a+j]].i;
	xml->quads[a+j].j=xml->vertices[xml->quadind[a+j]].j;
	xml->quads[a+j].k=xml->vertices[xml->quadind[a+j]].k;
    }
  }
  a=0;
  for (a=0;a<xml->lines.size();a+=2) {
    for (j=0;j<2;j++) {
	ind.push_back (xml->lineind[a+j]);
	xml->lines[a+j].i=xml->vertices[xml->lineind[a+j]].i;
	xml->lines[a+j].j=xml->vertices[xml->lineind[a+j]].j;
	xml->lines[a+j].k=xml->vertices[xml->lineind[a+j]].k;
    }
  }
  a=0;
  unsigned int k=0;
  unsigned int l=0;
  for (l=a=0;a<xml->tristrips.size();a++) {
    for (k=0;k<xml->tristrips[a].size();k++,l++) {
	ind.push_back (xml->tristripind[l]);
	xml->tristrips[a][k].i = xml->vertices[xml->tristripind[l]].i;
	xml->tristrips[a][k].j = xml->vertices[xml->tristripind[l]].j;
	xml->tristrips[a][k].k = xml->vertices[xml->tristripind[l]].k;
    }
  }
  for (l=a=0;a<xml->trifans.size();a++) {
    for (k=0;k<xml->trifans[a].size();k++,l++) {
	ind.push_back (xml->trifanind[l]);
	xml->trifans[a][k].i = xml->vertices[xml->trifanind[l]].i;
	xml->trifans[a][k].j = xml->vertices[xml->trifanind[l]].j;
	xml->trifans[a][k].k = xml->vertices[xml->trifanind[l]].k;
    }
  }
  for (l=a=0;a<xml->quadstrips.size();a++) {
    for (k=0;k<xml->quadstrips[a].size();k++,l++) {
	ind.push_back (xml->quadstripind[l]);
	xml->quadstrips[a][k].i = xml->vertices[xml->quadstripind[l]].i;
	xml->quadstrips[a][k].j = xml->vertices[xml->quadstripind[l]].j;
	xml->quadstrips[a][k].k = xml->vertices[xml->quadstripind[l]].k;
    }
  }
  for (l=a=0;a<xml->linestrips.size();a++) {
    for (k=0;k<xml->linestrips[a].size();k++,l++) {
	ind.push_back (xml->linestripind[l]);
	xml->linestrips[a][k].i = xml->vertices[xml->linestripind[l]].i;
	xml->linestrips[a][k].j = xml->vertices[xml->linestripind[l]].j;
	xml->linestrips[a][k].k = xml->vertices[xml->linestripind[l]].k;
    }
  }  
  // TODO: add alpha handling
  //check for per-polygon flat shading
  unsigned int trimax = xml->tris.size()/3;
  a=0;
  i=0;
  j=0;
  if (!xml->usenormals) {
      for (i=0;i<trimax;i++,a+=3) {
          if (FLAT_SHADE||xml->trishade[i]==1) {
              for (j=0;j<3;j++) {
                  Vector Cur (xml->vertices[xml->triind[a+j]].x,
                              xml->vertices[xml->triind[a+j]].y,
                              xml->vertices[xml->triind[a+j]].z);
                  Cur = (Vector (xml->vertices[xml->triind[a+((j+2)%3)]].x,
                                 xml->vertices[xml->triind[a+((j+2)%3)]].y,
                                 xml->vertices[xml->triind[a+((j+2)%3)]].z)-Cur)
                      .Cross(Vector (xml->vertices[xml->triind[a+((j+1)%3)]].x,
                                     xml->vertices[xml->triind[a+((j+1)%3)]].y,
                                     xml->vertices[xml->triind[a+((j+1)%3)]].z)-Cur);
                  Normalize(Cur);
                  //Cur = Cur*(1.00F/xml->vertexcount[a+j]);
                  xml->tris[a+j].i=Cur.i;
                  xml->tris[a+j].j=Cur.j;
                  xml->tris[a+j].k=Cur.k;
#if 0
                  xml->tris[a+j].i=Cur.i/xml->vertexcount[xml->triind[a+j]];
                  xml->tris[a+j].j=Cur.j/xml->vertexcount[xml->triind[a+j]];
                  xml->tris[a+j].k=Cur.k/xml->vertexcount[xml->triind[a+j]];
#endif
              }
          }
      }
      a=0;
      trimax = xml->quads.size()/4;
      for (i=0;i<trimax;i++,a+=4) {
          if (xml->quadshade[i]==1||(FLAT_SHADE)) {
              for (j=0;j<4;j++) {
                  Vector Cur (xml->vertices[xml->quadind[a+j]].x,
                              xml->vertices[xml->quadind[a+j]].y,
                              xml->vertices[xml->quadind[a+j]].z);
                  Cur = (Vector (xml->vertices[xml->quadind[a+((j+2)%4)]].x,
                                 xml->vertices[xml->quadind[a+((j+2)%4)]].y,
                                 xml->vertices[xml->quadind[a+((j+2)%4)]].z)-Cur)
                      .Cross(Vector (xml->vertices[xml->quadind[a+((j+1)%4)]].x,
                                     xml->vertices[xml->quadind[a+((j+1)%4)]].y,
                                     xml->vertices[xml->quadind[a+((j+1)%4)]].z)-Cur);
                  Normalize(Cur);
                  //Cur = Cur*(1.00F/xml->vertexcount[a+j]);
                  xml->quads[a+j].i=Cur.i;//xml->vertexcount[xml->quadind[a+j]];
                  xml->quads[a+j].j=Cur.j;//xml->vertexcount[xml->quadind[a+j]];
                  xml->quads[a+j].k=Cur.k;//xml->vertexcount[xml->quadind[a+j]];
                  
#if 0
                  xml->quads[a+j].i=Cur.i/xml->vertexcount[xml->quadind[a+j]];
                  xml->quads[a+j].j=Cur.j/xml->vertexcount[xml->quadind[a+j]];
                  xml->quads[a+j].k=Cur.k/xml->vertexcount[xml->quadind[a+j]];
#endif
              }
          }
      }
      
  }
  string factionname = FactionUtil::GetFaction(xml->faction);
  for (int LC=0;LC<textureOverride.size();++LC) {
    if (textureOverride[LC]!="") {
      while (xml->decals.size()<=LC) {
        MeshXML::ZeTexture z;
        xml->decals.push_back(z);
      }
      if (textureOverride[LC].find(".ani")!=string::npos) {
        xml->decals[LC].decal_name="";
        xml->decals[LC].animated_name=textureOverride[LC];
        xml->decals[LC].alpha_name="";
      }else {
        xml->decals[LC].animated_name="";
        xml->decals[LC].alpha_name="";
        xml->decals[LC].decal_name=textureOverride[LC];
      }
    }
  }
  while (Decal.size()<xml->decals.size())
      Decal.push_back(NULL);
  Decal[0]=(TempGetTexture(xml, 0,factionname));
  {for (unsigned int i=1;i<xml->decals.size();i++) {
      Decal[i]=(TempGetTexture(xml, i,factionname));
  }}
  while (Decal.back()==NULL&&Decal.size()>1) {
      Decal.pop_back();
  }
  unsigned int index = 0;
  unsigned int totalvertexsize = xml->tris.size()+xml->quads.size()+xml->lines.size();
  for (index=0;index<xml->tristrips.size();index++) {
    totalvertexsize += xml->tristrips[index].size();
  }
  for (index=0;index<xml->trifans.size();index++) {
    totalvertexsize += xml->trifans[index].size();
  }
  for (index=0;index<xml->quadstrips.size();index++) {
    totalvertexsize += xml->quadstrips[index].size();
  }
  for (index=0;index<xml->linestrips.size();index++) {
    totalvertexsize += xml->linestrips[index].size();
  }
  index =0;
  GFXVertex *vertexlist = new GFXVertex[totalvertexsize];
  mn = Vector (FLT_MAX,FLT_MAX,FLT_MAX);
  mx = Vector (-FLT_MAX,-FLT_MAX,-FLT_MAX);
  radialSize = 0;
  vector <enum POLYTYPE> polytypes;
  polytypes.insert(polytypes.begin(),totalvertexsize,GFXTRI);
  //  enum POLYTYPE * polytypes= new enum POLYTYPE[totalvertexsize];//overkill but what the hell
  vector <int> poly_offsets;
  poly_offsets.insert (poly_offsets.begin(),totalvertexsize,0);
  int o_index=0;
  if (xml->tris.size()) {
    polytypes[o_index]= GFXTRI;
    poly_offsets[o_index]=xml->tris.size();
    o_index++;
  }
  if (xml->quads.size()) {
    polytypes[o_index]=GFXQUAD;
    poly_offsets[o_index]=xml->quads.size();
    o_index++;
  }
  if (xml->lines.size()) {
    polytypes[o_index]=GFXLINE;
    poly_offsets[o_index]=xml->lines.size();
    o_index++;
  }
  /*
  if (xml->lines.size())
    polytypes[o_index]=GFXLINE;
    poly_offsets[o_index]=xml->lines.size()*2;  
    o_index++;
  */
  for(a=0; a<xml->tris.size(); a++, index++) {
    vertexlist[index] = xml->tris[a];		
    updateMax (mn,mx,vertexlist[index]);
  }
  for(a=0; a<xml->quads.size(); a++, index++) {
    vertexlist[index] = xml->quads[a];
    updateMax (mn,mx,vertexlist[index]);
  }
  for(a=0; a<xml->lines.size(); a++, index++) {
    vertexlist[index] = xml->lines[a];		
    updateMax (mn,mx,vertexlist[index]);
  }

  for (a=0;a<xml->tristrips.size();a++) {

    for (unsigned int m=0;m<xml->tristrips[a].size();m++,index++) {
      vertexlist[index] = xml->tristrips[a][m];
    updateMax (mn,mx,vertexlist[index]);
    }
    polytypes[o_index]= GFXTRISTRIP;
    poly_offsets[o_index]=xml->tristrips[a].size();
    o_index++;
  }
  for (a=0;a<xml->trifans.size();a++) {
    for (unsigned int m=0;m<xml->trifans[a].size();m++,index++) {
      vertexlist[index] = xml->trifans[a][m];
    updateMax (mn,mx,vertexlist[index]);
    }
    polytypes[o_index]= GFXTRIFAN;
    poly_offsets[o_index]=xml->trifans[a].size();

    o_index++;
  }
  for (a=0;a<xml->quadstrips.size();a++) {
    for (unsigned int m=0;m<xml->quadstrips[a].size();m++,index++) {
      vertexlist[index] = xml->quadstrips[a][m];
    updateMax (mn,mx,vertexlist[index]);
    }
    polytypes[o_index]= GFXQUADSTRIP;
    poly_offsets[o_index]=xml->quadstrips[a].size();
    o_index++;
  }
  for (a=0;a<xml->linestrips.size();a++) {

    for (unsigned int m=0;m<xml->linestrips[a].size();m++,index++) {
      vertexlist[index] = xml->linestrips[a][m];
      updateMax (mn,mx,vertexlist[index]);
    }
    polytypes[o_index]= GFXLINESTRIP;
    poly_offsets[o_index]=xml->linestrips[a].size();
    o_index++;
  }
  if (mn.i==FLT_MAX&&mn.j==FLT_MAX&&mn.k==FLT_MAX) {
	  mx.i=mx.j=mx.k=mn.i=mn.j=mn.k=0;
  }
  mn.i *=xml->scale.i;
  mn.j *=xml->scale.j;
  mn.k *=xml->scale.k;
  mx.i *=xml->scale.i;
  mx.j *=xml->scale.j;
  mx.k *=xml->scale.k;  
  float x_center = (mn.i + mx.i)/2.0,
    y_center = (mn.j + mx.j)/2.0,
    z_center = (mn.k + mx.k)/2.0;
  local_pos = Vector (x_center, y_center, z_center);
  for(a=0; a<totalvertexsize; a++) {
    vertexlist[a].x*=xml->scale.i;//FIXME
    vertexlist[a].y*=xml->scale.j;
    vertexlist[a].z*=xml->scale.k;
    vertexlist[a].i*=-1;
    vertexlist[a].j*=-1;
    vertexlist[a].k*=-1;

    /*
    vertexlist[a].x -= x_center;
    vertexlist[a].y -= y_center;
    vertexlist[a].z -= z_center;
    */
  }
  for (a=0;a<xml->vertices.size();a++) {
    xml->vertices[a].x*=xml->scale.i;//FIXME
    xml->vertices[a].y*=xml->scale.j;
    xml->vertices[a].z*=xml->scale.k;
    xml->vertices[a].i*=-1;    xml->vertices[a].k*=-1;    xml->vertices[a].j*=-1;
    /*
    xml->vertices[a].x -= x_center;
    xml->vertices[a].y -= y_center;
    xml->vertices[a].z -= z_center; //BSP generation and logos require the vertices NOT be centered!
    */
  }
  /*** NOW MIN/MAX size should NOT be centered for fast bounding queries
  minSizeX -= x_center;
  maxSizeX -= x_center;
  minSizeY -= y_center;
  maxSizeY -= y_center;
  minSizeZ -= z_center;
  maxSizeZ -= z_center;
  ***/
  if( o_index || index)
 	 radialSize = .5*(mx-mn).Magnitude();

  if (xml->sharevert) {
	  vlist = new GFXVertexList (
		  (polytypes.size()?&polytypes[0]:0), 
		  xml->vertices.size(),
		  (xml->vertices.size()?&xml->vertices[0]:0), o_index,
		  (poly_offsets.size()?&poly_offsets[0]:0), false, 
		  (ind.size()?&ind[0]:0)  );
  }else {
    static bool usopttmp=(XMLSupport::parse_bool (vs_config->getVariable ("graphics","OptimizeVertexArrays","false")));
    static float optvertexlimit= (XMLSupport::parse_float (vs_config->getVariable ("graphics", "OptimizeVertexCondition","1.0")));
    bool cachunk=false;
    if (usopttmp) {
      int numopt =totalvertexsize;      
      GFXVertex * newv;
      unsigned int * ind;
      GFXOptimizeList (vertexlist,totalvertexsize,&newv,&numopt,&ind);
      if (numopt < totalvertexsize*optvertexlimit) {
	vlist = new GFXVertexList (
		(polytypes.size()?&polytypes[0]:0), 
		numopt,newv,o_index,
		(poly_offsets.size()?&poly_offsets[0]:0), false, 
		ind  );
	cachunk = true;
      }
      free (ind);
      free (newv);
    }
    if (!cachunk) {
      vlist= new GFXVertexList(
		  (polytypes.size()?&polytypes[0]:0), 
		  totalvertexsize,vertexlist,o_index,
		  (poly_offsets.size()?&poly_offsets[0]:0)  ); 
    }
  }
  /*
  vlist[GFXQUAD]= new GFXVertexList(GFXQUAD,xml->quads.size(),vertexlist+xml->tris.size());
  index = xml->tris.size()+xml->quads.size();
  numQuadstrips = xml->tristrips.size()+xml->trifans.size()+xml->quadstrips.size();
  quadstrips = new GFXVertexList* [numQuadstrips];
  unsigned int tmpind =0;
  for (a=0;a<xml->tristrips.size();a++,tmpind++) {
    quadstrips[tmpind]= new GFXVertexList (GFXTRISTRIP,xml->tristrips[a].size(),vertexlist+index);
    index+= xml->tristrips[a].size();
  }
  for (a=0;a<xml->trifans.size();a++,tmpind++) {
    quadstrips[tmpind]= new GFXVertexList (GFXTRIFAN,xml->trifans[a].size(),vertexlist+index);
    index+= xml->trifans[a].size();
  }
  for (a=0;a<xml->quadstrips.size();a++,tmpind++) {
    quadstrips[tmpind]= new GFXVertexList (GFXQUADSTRIP,xml->quadstrips[a].size(),vertexlist+index);
    index+= xml->quadstrips[a].size();
  }
  */
  CreateLogos(xml,xml->faction,xml->fg);
  // Calculate bounding sphere
  if (mn.i==FLT_MAX) {
    mn=Vector (0,0,0);
    mx=Vector (0,0,0);
  }
  GFXSetMaterial (myMatNum,xml->material);
  delete [] vertexlist;
}
Then, at the top of gfxlib_struct.h, we need to declare a static dummy, and modify GFXVertex, as follows:

Code: Select all

QVector dummyTangent;

/// Vertex, Normal, Texture, and (deprecated) Environment Mapping T2F_N3F_V3F format
struct GFXVertex 
{
  float s;
  float t;
  float i;
  float j;
  float k;
  float x;
  float y;
  float z;
  
  //Tangent, by chuck_starchaser:
  float tx;
  float ty;
  float tz;
  float tw; //this is either -1.0f or +1.0f, for "handedness", whatever that means

  GFXVertex(){}
/* ...commented out by chuck_starchaser; see new versions at the bottom
  GFXVertex(const QVector & vert, const Vector & norm, float s, float t) {
    SetVertex(vert.Cast());
    SetNormal(norm);
    SetTexCoord(s, t);    
  }
  GFXVertex(const Vector &vert, const Vector &norm, float s, float t){
    SetVertex(vert);
    SetNormal(norm);
    SetTexCoord(s, t);
  }
  GFXVertex (float x, float y, float z, float i, float j, float k, float s, float t) {this->x=x;this->y=y;this->z=z;this->i=i;this->j=j;this->k=k;this->s=s;this->t=t;}
*/
  GFXVertex &SetTexCoord(float s, float t) {this->s = s; this->t = t; return *this;}
  GFXVertex &SetNormal(const Vector &norm) {i = norm.i; j = norm.j; k = norm.k; return *this;}
  GFXVertex &SetVertex(const Vector &vert) {x = vert.i; y = vert.j; z = vert.k; return *this;}
  Vector GetVertex() const {return Vector (x,y,z);}
  const Vector & GetConstVertex () const {return (*((Vector *)&x));}
  Vector GetNormal() const {return Vector (i,j,k);}

  //Tangent, by chuck_starchaser:
  GFXVertex &SetTangent(const Vector &tan) {tx = tan.i; ty = tan.j; tz = tan.k; return *this;}
  GFXVertex &SetHandedness(const float &hand) {tw = hand; return *this;}
  Vector GetTangent() const {return Vector (tx,ty,tz);}
  float GetHandedness() const {return tw;}
  //constructors:
  GFXVertex
  (
    float x, float y, float z, float i, float j, float k, float s, float t, float tx=1.0f, float ty=0.0f, float tz=0.0f, float tw=1.0f   )
  : s(s)
  , t(t)
  , i(i)
  , j(j)
  , k(k)
  , x(x)
  , y(y)
  , z(z)
  , tx(tx)
  , ty(ty)
  , tz(tz)
  , tw(tw)
  {
  }
  GFXVertex( const QVector & vert, const Vector & norm, float s, float t, const Vector &tangent = dummyTangent, float handedness=1.0f )
  {
    SetVertex(vert.Cast());
    SetNormal(norm);
    SetTexCoord(s, t);
    SetTangent( tangent );
    SetHandedness( handedness );
  }
  GFXVertex( const Vector &vert, const Vector &norm, float s, float t, const QVector &tangent = dummyTangent, float handedness=1.0f )
  {
    SetVertex(vert);
    SetNormal(norm);
    SetTexCoord(s, t);
    SetTangent( tangent );
    SetHandedness( handedness );
  }
};
And finally, we make a static dummy tangent instance in gfxlib_struct.cpp, right at the top of the file, or anywhere:

Code: Select all

Vector dummyTangent( 1.0f, 0.0f, 0.0f ); //added by chuck_starchaser, for GFXVertex default tangent argument
That's all for computing the tangents. What I don't know how to do is pass them to the shaders. Quite possibly no code is needed for that, since the code above adds the tangents to the vertex structure; but probably the shader needs to be informed about the vertex structure.
Haven't tested the code, as I'm compilerless, but it should work out of the box.
Deus Siddis
Elite
Elite
Posts: 1363
Joined: Sat Aug 04, 2007 3:42 pm

Post by Deus Siddis »

chuck_starchaser wrote: 1) What Blender can do for you is produce a smoother mesh using Catmul Clark subdivision, and then bake a normalmap relative to the original mesh. This is good for getting round things shading more smoothly rounded without increasing the polygon count too much.

2) Another tool, XNormal, lets you add geometry, like little pipes and boxes and whatever you fancy, on top of a surface, and Xnormal will bake a normalmap for you to represent that geometry.
One thing to note though is the current stable release of blender only bakes "camera space" normal maps out of the box. I don't know enough about normal maps to be sure if you can produce an object space map by simply keeping the mesh and camera in the same orientation, but either way the gimp plugin creates tangent space maps, so there might be some issues if you don't use the current release candidate or wait for the next release. Maybe there is something I have missed though.

Also, I have a question regarding the blender vs xnormal normal baking. Is the dev blender really just limited to sub surface normal baking or will it be able to replace the need for xnormal?

I'm mostly drawing my understanding/misunderstanding from this feature blog on the blender site:

http://www.blender.org/development/curr ... er-baking/
Information from other object can now be baked onto the active object, with the "Selected to Active" option in the Bake panel. The "Distance" parameter controls how far a point on another object can be away from the point on the active object. The "Bias" parameter can be used to bake not the closest point on another surface, but rather a mesh that is further away, for example baking an ID badge onto a shirt.
A typical use case is to make a detailed, high poly object, and then bake it's normals onto an object with a low polygon count. The resulting normal map can then be applied to make the low poly object look more detailed.
The example image they give is a couple of subsurf models, but the description never mentions that the feature is limited to like subsurf models and it seems like the Distance and Bias parameters shouldn't be at all necessary or useful if you were already using subsurf versions of the same model.
3) Finally, Gimp's Normalmap makes normalmaps from height maps, and this is essential for converting hand-drawn grooves and depressions into a normalmap.
Isn't this essentially just translating one channel's worth of data into a normal map though, so that the information can be read by the normal shader but the resulting effect isn't anymore 3D-ish than what you'd get from an old grayscale bumpmap?

Not saying this would be a problem, just trying to get a better idea when and where this plugin should be used.
IOW, the normalmaps need to be converted to angles, the angles added, then converted back to dU dV. I wrote a Blender noodle to do this. There's no software out there, that I know of, that can add normalmaps. I'll find that noodle and post it here.
Would you mind at all if folks used this tool for projects beyond the scope of VS mods, opensource/content or maybe even for commercial projects?
But the biggest barrier against normalmaps, right now, is the lack of Tangents. This means that all the islands in a ship's UV unwrap have to have their forward vector pointing and aligned to the UP (or North) direction in the texture. If it points to the left or right, the normalmap will shade oddly, just like the small nascelles on top of the wings of the Llama do. If it points South, grooves look like pipes, and viceversa.
But most models were not unwrapped to respect any such rules, so trying to make normalmaps for them is an exercise in futility.
Wouldn't this also be useful for flexing, animated meshes, like possibly the partially living Rlaan vessels (or player avatars, if the project ever goes in that direction)?
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Deus Siddis wrote:One thing to note though is the current stable release of blender only bakes "camera space" normal maps out of the box. I don't know enough about normal maps to be sure if you can produce an object space map by simply keeping the mesh and camera in the same orientation, but either way the gimp plugin creates tangent space maps, so there might be some issues if you don't use the current release candidate or wait for the next release. Maybe there is something I have missed though.
Yes, the fact that you can download the release candidate ;-)
http://download.blender.org/release/Blender2.46rc/
That's the one I'm using.
Also, I have a question regarding the blender vs xnormal normal baking.
Looks like you may be right; I didn't notice the stuff you point out. I honestly doubt that Blender will be on par with XNormal any time soon for that, though; XNormal has been doing that for like 2 years and it keeps getting better.
3) Finally, Gimp's Normalmap makes normalmaps from height maps, and this is essential for converting hand-drawn grooves and depressions into a normalmap...
Isn't this essentially just translating one channel's worth of data into a normal map though, so that the information can be read by the normal shader but the resulting effect isn't anymore 3D-ish than what you'd get from an old grayscale bumpmap?
Not at all. I'm not sure I understand what you're saying, but it seems to me you're confusing the Gimp's Normalmap plugin with its Bumpmap plugin. Totally different animals. The bumpmap plugin bakes a hight-map to bump shading from a fixed, arbitrary light source. The Normalmap plugin outputs a (blue) normal-map.
Would you mind at all if folks used this tool for projects beyond the scope of VS mods, opensource/content or maybe even for commercial projects?
Oh, I very much mind commercial projects using my, or anybody's, open source stuff; I'm a big fan of CopyLeft and pure, fully restricted to open source, licensing; but my normalmap adding noodle is too simple to put a patent, copyright or lincense on. I just have to find it...
But the biggest barrier against normalmaps, right now, is the lack of Tangents. This means that all the islands in a ship's UV unwrap have to have their forward vector pointing and aligned to the UP (or North) direction in the texture. If it points to the left or right, the normalmap will shade oddly, just like the small nascelles on top of the wings of the Llama do. If it points South, grooves look like pipes, and viceversa.
But most models were not unwrapped to respect any such rules, so trying to make normalmaps for them is an exercise in futility.
Wouldn't this also be useful for flexing, animated meshes, like possibly the partially living Rlaan vessels (or player avatars, if the project ever goes in that direction)?
Absolutely; right now a dynamically moving part in a model would lose its bumpmap synchronization as it moves.
The way the VS shaders currently assume the tangent to point to the right on the texture kind of defeats the purpose of using tangent-space. It's something in-between model-space and tangent-space. We need tangents yesterday. Normalmapping of VS and VS mods' models is just not going to happen until somebody checks in the code from my previous post.
Deus Siddis
Elite
Elite
Posts: 1363
Joined: Sat Aug 04, 2007 3:42 pm

Post by Deus Siddis »

chuck_starchaser wrote:Yes, the fact that you can download the release candidate ;-)
http://download.blender.org/release/Blender2.46rc/
That's the one I'm using.
Aye, just noting to folks that they'll need to go dev, the stable won't have new features like this.
Looks like you may be right; I didn't notice the stuff you point out. I honestly doubt that Blender will be on par with XNormal any time soon for that, though; XNormal has been doing that for like 2 years and it keeps getting better.
True, but it might mean less exporting to various formats, to do all the work in blender and gimp. That's mostly just personal preference though, I hate converting to different file types, it always seems to end up being such a buggy process, I like to limit it as much as possible.
Oh, I very much mind commercial projects using my, or anybody's, open source stuff; I'm a big fan of CopyLeft and pure, fully restricted to open source, licensing; but my normalmap adding noodle is too simple to put a patent, copyright or lincense on. I just have to find it...
That's alright then. Just to be clear though, you mean the content you create with the tool as well, not just the script/code? So it should not be used in the production of indy games or movies for example.
Absolutely; right now a dynamically moving part in a model would lose its bumpmap synchronization as it moves.
The way the VS shaders currently assume the tangent to point to the right on the texture kind of defeats the purpose of using tangent-space. It's something in-between model-space and tangent-space. We need tangents yesterday. Normalmapping of VS and VS mods' models is just not going to happen until somebody checks in the code from my previous post.
Okay I see now, wasn't completely sure before that we were talking about the same thing. So then I completely agree, this is a very important feature to be supported by VS asap.
klauss
Elite
Elite
Posts: 7243
Joined: Mon Apr 18, 2005 2:40 pm
Location: LS87, Buenos Aires, República Argentina

Post by klauss »

chuck_starchaser wrote:We absolutely need Tangents; and I already wrote the code to compute them on the fly as meshes are loaded, and sent the code to Hellcat AND Klauss for evaluation MONTHS ago, and still nothing's happening...
:(
Sorry Chuck if it seems as if I had ignored you, I've been dividing my spare time across several tasks. I haven't forgotten, I just didn't get to it. In fact, I had planned to take a peek at it yesterday, but something came up and I ended up not doing it. Perhaps during the week - that and texture packings.
Oíd mortales, el grito sagrado...
Call me "Menes, lord of Cats"
Wing Commander Universe
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

klauss wrote:
chuck_starchaser wrote:We absolutely need Tangents; and I already wrote the code to compute them on the fly as meshes are loaded, and sent the code to Hellcat AND Klauss for evaluation MONTHS ago, and still nothing's happening...
:(
Sorry Chuck if it seems as if I had ignored you, I've been dividing my spare time across several tasks. I haven't forgotten, I just didn't get to it. In fact, I had planned to take a peek at it yesterday, but something came up and I ended up not doing it. Perhaps during the week - that and texture packings.
That'll be wonderful, Klauss; can't wait. Thanks in advance. We're ready to get started on a big modeling and texturing revolution, at PU. The LaGrande noodle is just about finalized, by now. We got models in the pipeline that I can't even tell you about. But we can't really finish LaGrande until we got new shaders working with the new texture packing, like the dielectric bit. It would be too much wasted work to target the current shaders. So, this is all pretty much hanging on you. Not that I'm trying to put pressure... :D
Then again, it would be absolutely wonderful if Tangents and TexturePackingVersion were in 0.5, which is supposed to be out ... this week sometime?

EDIT:
By the way, I heard from Loki that Ace just committed a new mesher.exe, --couple of days ago. Just so you know to update your svn before you work on it.
Deus Siddis
Elite
Elite
Posts: 1363
Joined: Sat Aug 04, 2007 3:42 pm

Post by Deus Siddis »

So chuck did you ever find that blender script of yours for combining normal maps? I'd like to try and composite my map baked in blender for the new derivative model with extra normal detail drawn in gimp.
Post Reply