max-mapper / minecraft-skin

load minecraft skins as meshes in three.js applications
http://maxogden.github.com/minecraft-skin/
58 stars 19 forks source link

UV Mapping fails in modern versions of Three.js #5

Open bnolan opened 10 years ago

bnolan commented 10 years ago

I've just tried this out with a modern version of Three.js and got an error around line 142...

uvs[ (3 + rotateBy) % 4 ].x = (tileU * tileUvWidth + w * tileUvWidth)

I'm guessing the mesh building code has changed in three.js and it breaks the UVs somehow - but I don't really know what I'm doing so I couldn't work out how to fix it. :( It still works if you disable uv mapping, but then the man obviously looks retarded.

deathcap commented 10 years ago

@bnolan what version of three.js, can you narrow down when it first broke? Works with at least 0.58.9 and 0.56.0 (version used by voxel-engine 0.20.1)

deathcap commented 10 years ago

Tested myself, works on 0.58.10, broken on 0.66.2 (there is a gap between these versions for three.js on NPM, didn't test in between), I suspect this is related to this change:

https://github.com/mrdoob/three.js/wiki/Migration

r59 → r60 Face4 removed. Use 2 Face3 to emulate it.

which also broke https://github.com/maxogden/voxel-mesh/issues/13 .. but minecraft-skin does not explicitly use Face4 (quads). Found this hint on http://solutiondesign.com/webgl-and-three-js-texture-mapping/:

edA-qa mort-ora-y says: December 17, 2013 at 1:45 pm I think it should be noted that the coordinates used for your UV are tightly coupled to the vertex arrangement created by the CubeGeometry. This may be a problem since the Cube could in theory change it’s vertex structure and break your UVs.

I also tried the code with a PlaneGeometry and sure enough it has a different face arrangement, yielding an upside-down texture. I could have figured out that exact arrangement, but to be safe I created a new geometry instead with my explicit ordering of the vertices/faces.

I'd guess CubeGeometry changed from using Face4 (quads) to two Face3 (triangles).. yep, changed in: https://github.com/mrdoob/three.js/commit/44b1b3ab11723d94f9bdbdfd933c1096e0cce30e. Then in three.js r66, CubeGeometry was renamed to BoxGeometry.

lancewellspring commented 5 years ago

I came across this issue as well. Here is my working update to a single function to make it work again:

Skin.prototype.UVMap = function(mesh, face, x, y, w, h, rotateBy) {  
  if (!rotateBy) rotateBy = 0;  
  var geo = (mesh.geometry) ? mesh.geometry: mesh;  
  var uvs1, uvs2, vCount;  
  if(geo.faceVertexUvs[0][0].length == 3){  
      //newer versions of Three use triangles for faces  
      uvs1 = geo.faceVertexUvs[0][face*2];  
      uvs2 = geo.faceVertexUvs[0][face*2+1];  
      vCount = 3;  
  }  
  else if(geo.faceVertexUvs[0][0].length >=4) {  
      //compatibility for older versions of Three  
      uvs1 = geo.faceVertexUvs[0][face];  
      uvs2 = [{x:0,y:0},{x:0,y:0},{x:0,y:0},{x:0,y:0}];  
      vCount = 4;  
  }  
  else{  
      //might want to handle exception  
  }  
  var tileU = x;  
  var tileV = y;  
  var tileUvWidth = 1/64;  
  var tileUvHeight = 1/32;  
  var xtopleft, ytopleft, xbotleft, ybotleft, xbotright, ybotright, xtopright, ytopright;  
  xtopleft = (tileU * tileUvWidth)  
  ytopleft = 1 - (tileV * tileUvHeight)  
  xbotleft = (tileU * tileUvWidth)  
  ybotleft = 1 - (tileV * tileUvHeight + h * tileUvHeight)  
  xbotright = (tileU * tileUvWidth + w * tileUvWidth)  
  ybotright = 1 - (tileV * tileUvHeight + h * tileUvHeight)  
  xtopright = (tileU * tileUvWidth + w * tileUvWidth)  
  ytopright = 1 - (tileV * tileUvHeight)  
  uvs1[ (0 + rotateBy) % vCount ].x = xtopleft  
  uvs1[ (0 + rotateBy) % vCount ].y = ytopleft  
  uvs1[ (1 + rotateBy) % vCount ].x = uvs2[ (0 + rotateBy) % vCount ].x = xbotleft  
  uvs1[ (1 + rotateBy) % vCount ].y = uvs2[ (0 + rotateBy) % vCount ].y = ybotleft  
  uvs2[ (1 + rotateBy) % vCount ].x = xbotright  
  uvs2[ (1 + rotateBy) % vCount ].y = ybotright  
  uvs1[ (2 + rotateBy) % vCount ].x = uvs2[ (2 + rotateBy) % vCount ].x = xtopright  
  uvs1[ (2 + rotateBy) % vCount ].y = uvs2[ (2 + rotateBy) % vCount ].y = ytopright  
  if(vCount >=4){  
      uvs1[ (2 + rotateBy) % vCount ].x = xbotright  
      uvs1[ (2 + rotateBy) % vCount ].y = ybotright  
      uvs1[ (3 + rotateBy) % vCount ].x = xtopright  
      uvs1[ (3 + rotateBy) % vCount ].y = ytopright  
  }  
  geo.uvsNeedUpdate = true;  
}  

Untested on older versions of Three