From ede9aaf0c6a9e4e91f086859349b08d497bba42b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 1 Nov 2018 00:04:28 -0400 Subject: [PATCH] Code and test fixes --- lib/loadObj.js | 56 ++++++++------ .../data/box-complex-material-alpha/alpha.png | Bin 0 -> 2255 bytes .../box-complex-material-alpha/ambient.gif | Bin 0 -> 4040 bytes .../box-complex-material-alpha.mtl | 20 +++++ .../box-complex-material-alpha.obj | 46 ++++++++++++ .../data/box-complex-material-alpha/bump.png | Bin 0 -> 4998 bytes .../box-complex-material-alpha/diffuse.png | Bin 0 -> 8889 bytes .../box-complex-material-alpha/emission.jpg | Bin 0 -> 7092 bytes .../box-complex-material-alpha/shininess.png | Bin 0 -> 4094 bytes .../box-complex-material-alpha/specular.jpeg | Bin 0 -> 5618 bytes .../box-complex-material.mtl | 1 - .../box-objects-groups-materials.obj | 6 +- specs/data/box/box.mtl | 4 +- specs/lib/createGltfSpec.js | 71 +----------------- specs/lib/loadMtlSpec.js | 50 ++++++------ specs/lib/loadObjSpec.js | 35 ++++++--- 16 files changed, 154 insertions(+), 135 deletions(-) create mode 100644 specs/data/box-complex-material-alpha/alpha.png create mode 100644 specs/data/box-complex-material-alpha/ambient.gif create mode 100644 specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl create mode 100644 specs/data/box-complex-material-alpha/box-complex-material-alpha.obj create mode 100644 specs/data/box-complex-material-alpha/bump.png create mode 100644 specs/data/box-complex-material-alpha/diffuse.png create mode 100644 specs/data/box-complex-material-alpha/emission.jpg create mode 100644 specs/data/box-complex-material-alpha/shininess.png create mode 100644 specs/data/box-complex-material-alpha/specular.jpeg diff --git a/lib/loadObj.js b/lib/loadObj.js index 937d83f..c84b61b 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -479,7 +479,7 @@ function loadMaterials(mtlPaths, objPath, options) { return Promise.map(mtlPaths, function(mtlPath) { mtlPath = normalizeMtlPath(mtlPath, objDirectory); var shallowPath = path.join(objDirectory, path.basename(mtlPath)); - if (options.secure && outsideDirectory(mtlPath, objPath)) { + if (options.secure && outsideDirectory(mtlPath, objDirectory)) { // Try looking for the .mtl in the same directory as the obj options.logger('The material file is outside of the obj directory and the secure flag is true. Attempting to read the material file from within the obj directory instead.'); return loadMtl(shallowPath) @@ -511,34 +511,40 @@ function loadMaterials(mtlPaths, objPath, options) { }); } +function loadImagePath(imagePath, objPath, options) { + var objDirectory = path.dirname(objPath); + var shallowPath = path.join(objDirectory, path.basename(imagePath)); + if (options.secure && outsideDirectory(imagePath, objDirectory)) { + // Try looking for the image in the same directory as the obj + options.logger('Image file is outside of the obj directory and the secure flag is true. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored'); + }); + } + return loadImage(imagePath, options) + .catch(function(error) { + // Try looking for the image in the same directory as the obj + options.logger(error.message); + options.logger('Could not read image file at ' + imagePath + '. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored.'); + }); +} + function loadImages(imagePaths, objPath, options) { var images = {}; - var objDirectory = path.dirname(objPath); return Promise.map(imagePaths, function(imagePath) { - var shallowPath = path.join(objDirectory, path.basename(imagePath)); - if (options.secure && outsideDirectory(imagePath, objDirectory)) { - // Try looking for the image in the same directory as the obj - options.logger('Image file is outside of the obj directory and the secure flag is true. Attempting to read the image file from within the obj directory instead.'); - return loadImage(shallowPath, options) - .catch(function(error) { - options.logger(error.message); - options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored'); - }); - } else { - return loadImage(imagePath, options) - .catch(function(error) { - // Try looking for the image in the same directory as the obj - options.logger(error.message); - options.logger('Could not read image file at ' + imagePath + '. Attempting to read the image file from within the obj directory instead.'); - return loadImage(shallowPath, options); - }) - .catch(function(error) { - options.logger(error.message); - options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored.'); - }); - } + return loadImagePath(imagePath, objPath, options) + .then(function(image) { + images[imagePath] = image; + }); }, {concurrency : 10}) - .then(function(images) { + .then(function() { return images; }); } diff --git a/specs/data/box-complex-material-alpha/alpha.png b/specs/data/box-complex-material-alpha/alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..d04eb82f52c40bc1af9a3a8f516c0de93da592d7 GIT binary patch literal 2255 zcmV;=2r&1FP)FfK>5mnqYvqeW=j14KyJn zTA*voO{Lw0+M=4J2`y=E&Bb<#ofyaW*sbk%ACRVTF7~l~F7Jc<|JKM({mXMM?|XdS z=KvH^NFjw3qCyz?esyuF*=#l$OY}O1B;v7XI1~y+;;0~1jV;ZFk}^wWWo3Dp>HD9B zgMOdS7YN3dXQo{~`XR>EeXXWyTkAE#FY#P*dZQC(Ct~zMx&}6s+B!{QUK$#8hV3UW zQV7v+y0gaGX3pjR$iIho_-xn*-D1Ly* zk-Li&W^4-+d9t)wui!4;{wa|!M^-6S73&{}ym`Dvk$rr?L*&lfrjl$wR@Mja)la-> zlKqcmxl1#*B$5GFGJa;aBPv+7WAaBqo>T`sLRjAqpmWs9Pq-t)oQQ zoZBV`L;Hw>q!e|e$fZ5fJ4#+zQrJOAig=&bN$e>9eQR{IM(~5g4Ca{P;(mUS+^(+q+!$ys>Zu^K?;a<(gZ5U+u_Y0QhljR{Yy2T zc7MJQ^Gp>D7o8j((ey9SWSR^PQ{e#HX3nkyP!v+#@$aQvAZ0K7j3x&#&}cY$H3!I^y+uG- z*36FNu!GDK`ZCv-bwzA1brXiRijZ>a@kn+jME$P^fI0^-u`H7r$xgw%rW0X4im5{HE191r(>wBeo)jhiG=dpiX2(m~|smL!fO+ zTt$liOt%AG){3jhmVZ$Wfxfe1Ao`JZ+94y?irKVoq22*K+eB1EGuc8t1TJ=FuE?zj z9MG{nb49*hhX6wCeVHn<8G{3u^&2w{8vowJppewRlYY}`9@hhk1iD;lD{`Pqg9D?hm47uHe z2x9rUAf(9xm{L_Q1le7O3evPo2yz>;itG@8Sd4%xW-~wJMk_K%Q!hVcrwtjTZGazw z%>mHJ3#qlBgH%@YLDtu!gRET32kBpl4$}S;K1hQG9YnK&2ck0qzLaXY5Nj1eNNo)l z(y{^}#Af3{I+_te+E#HP4Jh}KIxfTj7-QfDi&ehLl-QLdr|o5DQL7 zB^OeT5>jFQkyL~WF`IGjBPKSav=rx2%gBb5AU%&5*pOnt89f_PgcPDauq2g!G^?P}AtpA6cnMDT~`wt&v*o6)<`wbst5#MOb=i`IG z96{zF4&a3x!*)uX_?#bd+KugWXy=E7BG^V+5q=1Kh-X6NbR6crKiJ5B%VttTk2|H=Z?CKU~4esz~Y> zmPyH#k0D(}pkW%xx1xbHo^%xf*9ja$;N%HU`ij8DGbkWe`_4hyiolmcC}tvdmV@W( z^=B|Zm}faZ;l5^UHT{tDoG#PcijYAB72$qHp2h3BA?nxWe|0@%t_UO^puQmYgBS>$ zJ4(6Z5Kl`W-o6MOqjW=>c`o+*g2Cq z^rFCZUBMQhA;;DV>dL`PJU$IsLckHdiMpGb{rf(HtRXPg+Cv-UFR$fvZKU-qRV{K; zcRqon1!y*E!t@eCDBMy5J49C$M>8;Q4jDR4M?GMzv&uYU=4s4sDKM>NS_P_ey%#zWHEW zmGj{8J0d%IihV-mmBYGDqta7G!gyYOLh>&)HTO*^{yfCd-^ua^8W>V|yE=WNOr`F6 zH>F@X5^u@(jZ@_#i;78H_sjWcHeLTW3OVe(GOr%4e@=-RO6+O*$K1ue@;|(1YJ(yZ zQ_EhN9SGEB0x(}X}s3D7_lh-x< z-Imt%qQ)2g;pxEeX$ddRNIeTTv|QU%TG!H^rr-JHv1w0uW@JX%PAsxqPAIoF)*FoF z71h<%mF3?Th=lw;uh+ZeUtV@O-7>Ugkny^RzQklQ84blnT81RA#$wTEB(i)33Mr(J dLJ9$Z{{r)zV|143F1i2!002ovPDHLkV1iPOEQ|mE literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/ambient.gif b/specs/data/box-complex-material-alpha/ambient.gif new file mode 100644 index 0000000000000000000000000000000000000000..1276823d350c3a7939ce8021a937381ee08564ae GIT binary patch literal 4040 zcmV;(4>#~fNk%w1VbcK90O$Vzj;w2st!$94ZIQ2TlCW=+v2c>Icbc(rm9ue^vv`)Y za+I`rnzMPEw0fMid!4p?p0|CUxPG9yf1$d7pSy;px`&{=hoQZQsJw}yzKN;5ile`Z zrNE1-zl*EDjH$qqs=<@3!HuiJl&->#tizSB!;h`RmaoN^u*R6S#+jMN=(5O~xW}5B z$mzDop}EMLo5||4%A2>zqPoePoXYFC%A=jj?4Hc+y33`b%;%uZ?!e2Yq|NBN&8DHw z@4w8grOxTR&Zn%*>cGve!_BCs&+EO9W%A!qly{(&@(2v#izdv(xam)9S?3y2sSCvDNFi z)a%97yU5kGwbk>+*1WmZ?a9`*%+CV@`yx8!-*y_*N zz`fb=&Dy)c+3U{P$I;orzS{D_+U(ET$iUn4!`to9+sVh<=g-}}&E3P++R4P+?#SHe z!QJ%N+se`2zR}&v#oh4F+v?Tb#mU|2&)>z^+{@D5%gWyC+1D%Jh%;fgj;_BSv*xcge&E@yt;@{)q+1=&M+T-ip=$(;ne8$-Raih=IP|-eu4u>gDI;;Op4t z=;hYy_vGp9=jz($>E_q$_}=RH*zNh~?Aze%`r7XL>FwO=?CIg{{N(NR=kDX^@8sp~ z_v`NJ>G0*}@A&QS>*(GAsP^XKaG`|b4U@$>NO^!)Di>hkpQ_4MoP_5JVm>+|*U z_Vw)T_W$?x?ezEa^7r}k`1|(w^z`}s_xbkq`u+I&_xAh#`TO|#{Q39&|NH&=`2PR- z|Ns2|`~Ls@A^s6Va%Ew3Wn>_CX>@2HM@dak04x9i007ef(*OVn{s5B+97wRB!Gj1B zDqP60p~Hs|BTAe|v7*I`7&B_z$g!ixk03*e97(dI$&)Bks$9vkrOTHvW6GRKv!>0P zICJXUsW6zTOMddW33CMq5FR{eD7}FcNR}>P>I|Y(su$0zIeT#$#0H8D6=1`P9ZQz1 zjU!?5Sh9+>ZAxRR2897qwyxc~V|9e#qspz{kGd9x`HHu&;kz)p40`pqF-28%=pLR- z*)EVes*E|_oKRI9$(2Kk9%~~`U(TF^4FWy7wQChX_~=6IS5+7quXE>a5#}^)otmWR z9zGn%Bh9~Q8e2`g`S2Tjf+df+h==p*!)*+bK7HjQ*4V@TyS8z>`^i#J#H$~E#J72n zw!*BRKRnFr_Z^MpfkD52ZWwuRhaX0&9O2)AxdGwcfD#!KM+*o>_*x#wF$mEs;7O=q zWk=9r;X{7#x8aDQwSY*4A0E_87bLbgniTh?Sdc0ix@cozIkX}pL5qlZrCmCB*xf5Pd}(G~H;DP0DukWs zrdT$p$r+n()+rVY3;so?oqVc*=Vq_8*{7W_==hf{Wez$h4RT)9OO=Ycd4nx&86#Yz zkscx7PV!-j=@lsA6o;pz!m*Pjq^7z_POspWs;B-iG%8atkiKds5Sz->sjZ}jQIjUT z+PcV0wg?(*f_rq*$sXxN*n+GpS$eE)Cc^X2wb;gkiI4q35tAR&CYTPkH)`s_*{3{tMtq`RW&Fwea6>Qn1=SG1G}zpYIJFu>m+_{d7ZB#Lll zR4lx(z4PI#QXLbEwu{98KZ76}DcQnp$A?)9vb_342yICFqI_5j{H#2$#ND2xip-VW zVDr2KV+V>!S@2953_r`OLxezt`cWxHS5{BbMLI@OA^L|4Zq9`Lx zH{D#b3o#fOPa+hssJyT~&cM;V-a2$5&cgCwgtLobeB<>zlGvTmpx_LfU1VoBf&w_w zhOK$*A!;{JM$)Qbkv!O_1+ZYCyt=V8l*zieGPbJt$%z4Z)y?T(lTJI_Ig65pf?AF~EP5ONrP(nm_T4p01-BO$jJ5->6%TKdmI>PK^8{Xlg^O$u52|rv z0Hf7FXT!r>=R()G@N9$EJWC>LPz@gvpe=A8(pTqP1GC}9E<6KJ+N9mX2P5i)*Ir;; z*FGex&XF(GW}se!Hb5R!6Nhf+vxRW3j1kziuS4)l94>eb!TmKT3A}a-32BjP*|;uk z8T$}EIOoDi%W#AtdIqm~@jzgtnmr<*UKT*a!_7G_K3P0sg9ZSdNfX$CP~o)|^f#>! zc|>zW(c>C#Gy%{2SveSooiG>}WPgb8RUZPo+<@W97K^fxHyQ%3?IMd)OByx=5MiP| zByYL_@&3=oyk(Mp!E19^PYe8)G#CL#5J$DnvI=`~b8w~(0bG*9Iu)=F7s4*lW z;!Vo%m}-dzG)WV{4yvVt$+-!G*XUqmghHgybs+)uz5F7QFVr=Ct61Kcj~Oj2r=pz_>(o4)(BfeN$dgO~=H^Ak|E;We`XN z6s%Z>R&H(!l>_^rnlg0bgH|Vv8Ok&=8X7cU zFxmUo`F8h9pTO(av;d9bYyhAuF%eL!;NVn~hr$KE)LHzqn3TDN*F+#`UrdC%laY4P z{zyRasusW;mY(5Z&_=a0NcsU35%Ofl-SU!qH3RWZ8Z<=H$fOk`;6_FPMQAaZp8pIP zK)3lzjgZdDOp$8tNPwxwKoJ=l=IKd$ht!qs)nF!#A0RJGimCQcQ-`7=BqYn&lU8=F zn|vHzgRKe%nzS$EngAA&++A!S;v(y~ z7C+)qj?Mq9_Noso0m7lddGvnvAe#cW9e@#Uq*3|7m+S-t@5q(q!rd~+2p6yZAN|qR zp!y(F$2GYfQ;Y!khT`u&Z8HIWfbC-g?k<2voR7kMU_kw}9Y8kPFR!<{+XWkOLq3Lo zwB-Pm#(&ph2o!e+8+tEMSF6vk#0wV@ihv zApwHevJbgX10yJgZ59HI;Dif94O8HGN+$rg5E9jZ4Z!dTQ!oNqNQeI1762L01BSp0 z(GU*VfDEZ{1}HFbl($zQ;R|<$h@2>J0U(1Rp$45eip;hGC836-n2IA70IpCHcc6-} z7-0Zt68aR2wkTNwPzxtv2e;UZUKIo>@d3U#jFZNNC$S1JNQ}zJOiYLpBejgs2mlwr z5}Fu|$`}A=#}ayAjl{SEFF|eGc#9BVj4Xi*3J{LA$bc}>2j~ckB?uEZ(2k@y0p$1+ zzK~w>NQVPp3N;Z5$(WBf_ysrN1pqmP9Ci~jP>?D}k2(Q-3aNet(26>N3(NM9!50AP zxD%l;hZf0o&A1bHAcY;tZA9P~XfTpo=VE`M1t?i^B(NDs(EgI{<^lES7x5^QriKAE z`498MFK~;8EPPsN_kT%i63|Hk5Rc)PVga+V2M|G zQv&b?G9nB2*Of1o0FkgG$>0k;NtOnc0iK4I$>0T08I_m_mjlrVo5+^2v<7x*5We67 zdI4p9absf+|*1hB2+PM+FfCe7G zoHl0xQ9zjES&m|00@X=*0}ujQ5Sr~76O~W}Ai$Bi762H~1AQQ#_o))TPzYmC0~f%b z>Jj01yxW6cW?-yAPXTXqBxqPI=Z7g+M_=Dqd*#@LOP^GTBIlu5CA*Uq8VHO literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl new file mode 100644 index 0000000..5c598ff --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl @@ -0,0 +1,20 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.200000 0.200000 0.200000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.100000 0.100000 0.100000 +Ni 1.000000 +d 0.900000 +Tr 0.100000 +map_Ka ambient.gif +map_Ke emission.jpg +map_Kd diffuse.png +map_Ks specular.jpeg +map_Ns shininess.png +map_Bump bump.png +map_d alpha.png +illum 2 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj new file mode 100644 index 0000000..15d7f4a --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-complex-material-alpha.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl Material +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-complex-material-alpha/bump.png b/specs/data/box-complex-material-alpha/bump.png new file mode 100644 index 0000000000000000000000000000000000000000..16ec3a936601922682f2efadd6f19a39d80c7150 GIT binary patch literal 4998 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/diffuse.png b/specs/data/box-complex-material-alpha/diffuse.png new file mode 100644 index 0000000000000000000000000000000000000000..8704966c032736dd3b504891393c5a69e90f0a7b GIT binary patch literal 8889 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 Hu0mjfJUwN5 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/emission.jpg b/specs/data/box-complex-material-alpha/emission.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e5c85a943f6c5c95ca46b1bfd5fc9e8d7f1c4b77 GIT binary patch literal 7092 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/shininess.png b/specs/data/box-complex-material-alpha/shininess.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcf1a2c625c38a8da19b7c7390102dde2b3f90a GIT binary patch literal 4094 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/specular.jpeg b/specs/data/box-complex-material-alpha/specular.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..661bf98060289f196f149b5c0b6b05ff737f9ff8 GIT binary patch literal 5618 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 IJS9*353{Fhng9R* literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material/box-complex-material.mtl b/specs/data/box-complex-material/box-complex-material.mtl index 5c598ff..1ab3601 100644 --- a/specs/data/box-complex-material/box-complex-material.mtl +++ b/specs/data/box-complex-material/box-complex-material.mtl @@ -16,5 +16,4 @@ map_Kd diffuse.png map_Ks specular.jpeg map_Ns shininess.png map_Bump bump.png -map_d alpha.png illum 2 diff --git a/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj b/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj index 1bb7698..b2fb821 100644 --- a/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj +++ b/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj @@ -36,7 +36,7 @@ vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 1.0000 0.0000 -g CubeBlue_CubeBlue_Blue +g Blue usemtl Blue f 1/1/1 2/2/1 4/3/1 3/4/1 f 3/5/2 4/6/2 8/7/2 7/8/2 @@ -79,7 +79,7 @@ vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 1.0000 0.0000 -g CubeGreen_CubeGreen_Green +g Green usemtl Green f 9/21/7 10/22/7 12/23/7 11/24/7 f 11/25/8 12/26/8 16/27/8 15/28/8 @@ -122,7 +122,7 @@ vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 1.0000 0.0000 -g CubeRed_CubeRed_Red +g Red usemtl Red f 17/41/13 18/42/13 20/43/13 19/44/13 f 19/45/14 20/46/14 24/47/14 23/48/14 diff --git a/specs/data/box/box.mtl b/specs/data/box/box.mtl index a304b43..4f8d129 100644 --- a/specs/data/box/box.mtl +++ b/specs/data/box/box.mtl @@ -3,10 +3,10 @@ newmtl Material Ns 96.078431 -Ka 0.000000 0.000000 0.000000 +Ka 0.100000 0.000000 0.000000 Kd 0.640000 0.640000 0.640000 Ks 0.500000 0.500000 0.500000 -Ke 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.100000 Ni 1.000000 d 1.000000 illum 2 diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index fd51b20..9097848 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -17,7 +17,6 @@ var diffuseTextureUrl = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png'; var defaultOptions = obj2gltf.defaults; -var defined = Cesium.defined; var checkTransparencyOptions = clone(defaultOptions); checkTransparencyOptions.checkTransparency = true; @@ -25,7 +24,6 @@ describe('createGltf', function() { var boxObjData; var duplicateBoxObjData; var groupObjData; - var mixedAttributesObjData; var diffuseTexture; var transparentDiffuseTexture; @@ -50,12 +48,7 @@ describe('createGltf', function() { loadImage(transparentDiffuseTextureUrl, checkTransparencyOptions) .then(function(image) { transparentDiffuseTexture = image; - }), - loadObj(mixedAttributesObjUrl, defaultOptions) - .then(function(data) { - mixedAttributesObjData = data; }) - ]).then(done); }); @@ -266,8 +259,8 @@ describe('createGltf', function() { boxObjData.nodes[1].meshes[0].primitives[0].normals.length = 0; var gltf = createGltf(boxObjData, defaultOptions); - var kmc1 = gltf.materials.Material.extensions.KHR_materials_common; - var kmc2 = gltf.materials.Material_constant.extensions.KHR_materials_common; + var kmc1 = gltf.materials['Material'].extensions.KHR_materials_common; + var kmc2 = gltf.materials['Material-2'].extensions.KHR_materials_common; expect(kmc1.technique).toBe('PHONG'); expect(kmc2.technique).toBe('CONSTANT'); @@ -279,8 +272,8 @@ describe('createGltf', function() { boxObjData.nodes[0].meshes[0].primitives[0].normals.length = 0; var gltf = createGltf(boxObjData, defaultOptions); - var kmc1 = gltf.materials.Material.extensions.KHR_materials_common; - var kmc2 = gltf.materials.Material_shaded.extensions.KHR_materials_common; + var kmc1 = gltf.materials['Material'].extensions.KHR_materials_common; + var kmc2 = gltf.materials['Material-2'].extensions.KHR_materials_common; expect(kmc1.technique).toBe('CONSTANT'); expect(kmc2.technique).toBe('PHONG'); @@ -317,62 +310,6 @@ describe('createGltf', function() { expect(attributes.TEXCOORD_0).toBeUndefined(); }); - function getDiffuse(material) { - return material.extensions.KHR_materials_common.values.diffuse; - } - - fit('splits incompatible materials', function() { - var gltf = createGltf(mixedAttributesObjData, defaultOptions); - var materials = gltf.materials; - var materialNames = Object.keys(materials).sort(); - - // Expect three copies of each material for - // * positions/normals/uvs - // * positions/normals - // * positions/uvs - expect(materialNames).toEqual([ - 'Material', - 'Material-2', - 'Material-3', - 'Missing', - 'Missing-2', - 'Missing-3', - 'default', - 'default-2', - 'default-3' - ]); - console.log(materials['Material']); - console.log(materials['Material-2']); - console.log(materials['Material-3']); - - - expect(getDiffuse(materials['Material'])).toBe('texture_cesium'); - expect(getDiffuse(materials['Material-2'])).toEqual('texture_cesium'); - //expect(getDiffuse(materials['Material-3'])).toBe('texture_cesium'); - // expect(getDiffuse(materials['Missing'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['Missing-2'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['Missing-3'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['default'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['default-2'])).toEqual([0.0, 0.0, 0.0, 1.0]); - // expect(getDiffuse(materials['default-3'])).toEqual([0.0, 0.0, 0.0, 1.0]); - - // // Test that primitives without uvs reference materials without textures - // for (var meshName in meshes) { - // if (meshes.hasOwnProperty(meshName)) { - // var mesh = meshes[meshName]; - // var primitives = mesh.primitives; - // var primitivesLength = primitives.length; - // for (var i = 0; i < primitivesLength; ++i) { - // var primitive = primitives[i]; - // var material = materials[primitive.material]; - // if (!defined(primitive.attributes.TEXCOORD_0)) { - // expect(material.extensions.KHR_materials_common.diffuse).toEqual([ 0.5, 0.5, 0.5, 1 ]); - // } - // } - // } - // } - }); - function expandObjData(objData, duplicatesLength) { var primitive = objData.nodes[0].meshes[0].primitives[0]; var indices = primitive.indices; diff --git a/specs/lib/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index 208fe4d..ae579d1 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -2,7 +2,7 @@ var path = require('path'); var loadMtl = require('../../lib/loadMtl'); -var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl'; +var complexMaterialAlphaUrl = 'specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl'; var diffuseAmbientSameMaterialUrl = 'specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl'; var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; var texturedWithOptionsMaterialUrl = 'specs/data/box-texture-options/box-texture-options.mtl'; @@ -14,7 +14,7 @@ function getImagePath(objPath, relativePath) { describe('loadMtl', function() { it('loads complex material', function(done) { - expect(loadMtl(complexMaterialUrl) + expect(loadMtl(complexMaterialAlphaUrl) .then(function(materials) { var material = materials.Material; expect(material).toBeDefined(); @@ -24,13 +24,13 @@ describe('loadMtl', function() { expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]); expect(material.specularShininess).toEqual(96.078431); expect(material.alpha).toEqual(0.9); - expect(material.ambientTexture).toEqual(getImagePath(complexMaterialUrl, 'ambient.gif')); - expect(material.emissionTexture).toEqual(getImagePath(complexMaterialUrl, 'emission.jpg')); - expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialUrl, 'diffuse.png')); - expect(material.specularTexture).toEqual(getImagePath(complexMaterialUrl, 'specular.jpeg')); - expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialUrl, 'shininess.png')); - expect(material.normalMap).toEqual(getImagePath(complexMaterialUrl, 'bump.png')); - expect(material.alphaMap).toEqual(getImagePath(complexMaterialUrl, 'alpha.png')); + expect(material.ambientTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'ambient.gif')); + expect(material.emissionTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'emission.jpg')); + expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'diffuse.png')); + expect(material.specularTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'specular.jpeg')); + expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'shininess.png')); + expect(material.normalMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'bump.png')); + expect(material.alphaMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'alpha.png')); }), done).toResolve(); }); @@ -45,29 +45,27 @@ describe('loadMtl', function() { }); it('loads mtl with textures having options', function(done) { - options.metallicRoughness = true; expect(loadMtl(texturedWithOptionsMaterialUrl) .then(function(materials) { - expect(materials.length).toBe(1); - var material = materials[0]; - var pbr = material.pbrMetallicRoughness; - expect(pbr.baseColorTexture).toBeDefined(); - expect(pbr.metallicRoughnessTexture).toBeDefined(); - expect(pbr.baseColorFactor).toEqual([1.0, 1.0, 1.0, 0.9]); - expect(pbr.metallicFactor).toBe(1.0); - expect(pbr.roughnessFactor).toBe(1.0); - expect(material.name).toBe('Material'); - expect(material.emissiveTexture).toBeDefined(); - expect(material.normalTexture).toBeDefined(); - expect(material.occlusionTexture).toBeDefined(); - expect(material.emissiveFactor).toEqual([1.0, 1.0, 1.0]); - expect(material.alphaMode).toBe('BLEND'); - expect(material.doubleSided).toBe(true); + var material = materials.Material; + expect(material).toBeDefined(); + expect(material.ambientColor).toEqual([0.2, 0.2, 0.2, 1.0]); + expect(material.emissionColor).toEqual([0.1, 0.1, 0.1, 1.0]); + expect(material.diffuseColor).toEqual([0.64, 0.64, 0.64, 1.0]); + expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]); + expect(material.specularShininess).toEqual(96.078431); + expect(material.alpha).toEqual(0.9); + expect(material.ambientTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'ambient.gif')); + expect(material.emissionTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'emission.jpg')); + expect(material.diffuseTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'diffuse.png')); + expect(material.specularTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'specular.jpeg')); + expect(material.specularShininessMap).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'shininess.png')); + expect(material.normalMap).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'bump.png')); }), done).toResolve(); }); it('ambient texture is ignored if it is the same as the diffuse texture', function(done) { - expect(loadMtl(diffuseAmbientSameMaterialUrl, options) + expect(loadMtl(diffuseAmbientSameMaterialUrl) .then(function(materials) { expect(Object.keys(materials).length).toBe(1); var material = materials['Material']; diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index ba38277..7d3bc0d 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -122,8 +122,8 @@ describe('loadObj', function() { expect(loadObj(objUnnormalizedUrl, defaultOptions) .then(function(data) { var scratchNormal = new Cesium.Cartesian3(); - var mesh = getMeshes(data)[0]; - var normals = mesh.normals; + var primitive = getPrimitives(data)[0]; + var normals = primitive.normals; var normalsLength = normals.length / 3; for (var i = 0; i < normalsLength; ++i) { var normalX = normals.get(i * 3); @@ -365,7 +365,11 @@ describe('loadObj', function() { }); it('loads obj with missing mtllib', function(done) { - expect(loadObj(objMissingMtllibUrl, defaultOptions) + var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; + + expect(loadObj(objMissingMtllibUrl, options) .then(function(data) { expect(data.materials).toEqual({}); expect(spy.calls.argsFor(0)[0].indexOf('ENOENT') >= 0).toBe(true); @@ -378,7 +382,7 @@ describe('loadObj', function() { it('loads obj with missing usemtl', function(done) { expect(loadObj(objMissingUsemtlUrl, defaultOptions) .then(function(data) { - expect(data.materials.length).toBe(1); + expect(Object.keys(data.materials).length).toBe(1); expect(data.nodes[0].meshes[0].primitives[0].material).toBe('Material'); }), done).toResolve(); }); @@ -394,6 +398,8 @@ describe('loadObj', function() { it('does not load resources outside of the obj directory when secure is true', function(done) { var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; options.secure = true; expect(loadObj(objExternalResourcesUrl, options) @@ -409,20 +415,23 @@ describe('loadObj', function() { }); it('loads resources from root directory when the .mtl path does not exist', function(done) { - expect(loadObj(objResourcesInRootUrl, options) + expect(loadObj(objResourcesInRootUrl, defaultOptions) .then(function(data) { - expect(data.materials['Material'].diffuseTexture.source).toBeDefined(); - expect(diffuseTexture.source).toBeDefined(); + var material = data.materials['Material']; + var image = data.images[material.diffuseTexture]; + expect(image.source).toBeDefined(); }), done).toResolve(); }); it('loads resources from root directory when the .mtl path is outside of the obj directory and secure is true', function(done) { + var options = clone(defaultOptions); options.secure = true; expect(loadObj(objExternalResourcesInRootUrl, options) .then(function(data) { - var materials = data.materials; - expect(Object.keys(materials).length).toBe(2); - expect(materials['MaterialTextured'].diffuseTexture.source).toBeDefined(); + expect(Object.keys(data.materials).length).toBe(2); + var material = data.materials['MaterialTextured']; + var image = data.images[material.diffuseTexture]; + expect(image.source).toBeDefined(); }), done).toResolve(); }); @@ -436,7 +445,11 @@ describe('loadObj', function() { }); it('loads obj with missing texture', function(done) { - expect(loadObj(objMissingTextureUrl, defaultOptions) + var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; + + expect(loadObj(objMissingTextureUrl, options) .then(function(data) { var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png'); expect(data.images[imagePath]).toBeUndefined();