diff --git a/src/art/webgl/art.js b/src/art/webgl/art.js index d84de114fb5d7aa8a99d37bdf2cb214a4b6f891b..fe83336aefa8f3e7774b8a48e24bfca3c33d5a69 100644 --- a/src/art/webgl/art.js +++ b/src/art/webgl/art.js @@ -636,7 +636,6 @@ App.Art.applyFigures = function(slave, scene, p) { p.hideDick = true; } - /* switch (slave.vaginalAccessory) { case "dildo": figures.push("Dildo 1"); @@ -663,7 +662,6 @@ App.Art.applyFigures = function(slave, scene, p) { figures.push("Anal Plug 3"); break; } - */ if (slave.chastityAnus || slave.chastityVagina || slave.chastityPenis) { figures.push("Chastity Belt Base"); @@ -680,12 +678,28 @@ App.Art.applyFigures = function(slave, scene, p) { } } + if (slave.piercing.ear.weight === 1) { figures.push("Light Piercing Left Ear", "Light Piercing Left Ear"); } + if (slave.piercing.nose.weight === 1) { figures.push("Light Piercing Left Nose"); } + if (slave.piercing.nose.weight === 2) { figures.push("Heavy Piercing Nose"); } + if (slave.piercing.eyebrow.weight === 1) { figures.push("Light Piercing Left Eyebrow"); } + if (slave.piercing.lips.weight === 1) { figures.push("Light Piercing Right Lips"); } + if (slave.piercing.navel.weight === 1) { figures.push("Light Piercing Belly"); } + if (slave.piercing.areola.weight === 1) { figures.push("Light Piercing Left Areola"); } + if (slave.piercing.nipple.weight === 1) { figures.push("Light Piercing Left Nipple", "Light Piercing Right Nipple"); } + if (slave.piercing.nipple.weight === 2) { figures.push("Heavy Piercing Left Nipple", "Heavy Piercing Right Nipple"); } + switch (slave.bellyAccessory) { // case "a corset": figures.push("Corset A_80240"); break; // case "an extreme corset": figures.push("corset1_112951"); break; default: break; } + switch (slave.legAccessory) { + case "long stockings": + figures.push("Long Stockings"); + break; + } + switch (slave.faceAccessory) { case "porcelain mask": figures.push("Porcelain Mask"); @@ -733,43 +747,84 @@ App.Art.applyFigures = function(slave, scene, p) { } if (!p.hideHair) { - switch (slave.hStyle) { - case "afro": figures.push("Yara Hair"); break; - case "cornrows": figures.push("HR TIGER BRAIDS G2F"); break; - case "bun": figures.push("Adia Hair"); break; - case "neat": figures.push("Samira Hair"); break; - case "strip": figures.push("Rebel Hair"); break; - case "tails": figures.push("Kinley Hair G8"); break; - case "up": figures.push("Pina Hair G8F"); break; - case "ponytail": figures.push("Ponytail"); break; - case "braided": figures.push("LLF-MishkaGeBase1"); break; - case "dreadlocks": figures.push("Dreads"); break; - case "permed": figures.push("IchigoHair"); break; - case "curled": figures.push("Havana Hair"); break; - case "luxurious": figures.push("BaronessHR"); break; - case "messy bun": figures.push("Krayon Hair"); break; - case "messy": figures.push("MessyHairG3"); break; - case "eary": figures.push("GeorginaHair"); break; - case "undercut": figures.push("Edit Female Hair"); break; - case "bangs": figures.push("Neko Hair Genesis 8 Female"); break; - case "hime": figures.push("Nyohair"); break; - case "drills": figures.push("LLF-BunnyCurls-G3"); break; - case "bald": break; - case "shaved": break; - case "buzzcut": break; - case "trimmed": break; - - // not implemented - case "double buns": figures.push("Krayon Hair"); break; - case "chignon": figures.push("BaronessHR"); break; - case "french twist": figures.push("HR TIGER BRAIDS G2F"); break; - case "crown braid": figures.push("HR TIGER BRAIDS G2F"); break; - case "dutch braid": figures.push("HR TIGER BRAIDS G2F"); break; - case "double dutch braid": figures.push("HR TIGER BRAIDS G2F"); break; - case "pixie cut": break; - case "bob cut": break; - - default: break; + if (slave.hLength < 50) { + switch (slave.hStyle) { + case "afro": figures.push("Yara Hair"); break; + case "cornrows": figures.push("Lush"); break; + case "bun": figures.push("Adia Hair"); break; + case "neat": figures.push("Samira Hair"); break; + case "strip": figures.push("Rebel Hair"); break; + case "tails": figures.push("Kinley Hair G8"); break; + case "up": figures.push("Pina Hair G8F"); break; + case "ponytail": figures.push("Ponytail"); break; + case "braided": figures.push("LLF-MishkaGeBase1"); break; + case "dreadlocks": figures.push("Dreads"); break; + case "permed": figures.push("IchigoHair"); break; + case "curled": figures.push("Havana Hair"); break; + case "luxurious": figures.push("Rose59"); break; + case "messy bun": figures.push("Krayon Hair"); break; + case "messy": figures.push("MessyHairG3"); break; + case "eary": figures.push("GeorginaHair"); break; + case "undercut": figures.push("Edit Female Hair"); break; + case "bangs": figures.push("Neko Hair Genesis 8 Female"); break; + case "hime": figures.push("Nyohair"); break; + case "drills": figures.push("LLF-BunnyCurls-G3"); break; + case "bald": break; + case "shaved": break; + case "buzzcut": break; + case "trimmed": break; + + // not implemented + case "double buns": figures.push("Gaze"); break; + case "chignon": figures.push("BaronessHR"); break; + case "french twist": figures.push("HR TIGER BRAIDS G2F"); break; + case "crown braid": figures.push("Sky293"); break; + case "dutch braid": figures.push("Carrousel"); break; + case "double dutch braid": figures.push("HR TIGER BRAIDS G2F"); break; + case "pixie cut": break; + case "bob cut": break; + + default: break; + } + } else { + switch (slave.hStyle) { + case "afro": figures.push("Yara Hair"); break; + case "cornrows": figures.push("Lush"); break; + case "bun": figures.push("Sky158"); break; + case "neat": figures.push("Samira Hair"); break; + case "strip": figures.push("Wanda"); break; + case "tails": figures.push("Suckerpunch"); break; + case "up": figures.push("Pina Hair G8F"); break; + case "ponytail": figures.push("Paraguay"); break; + case "braided": figures.push("Butterfly160"); break; + case "dreadlocks": figures.push("Sparks"); break; + case "permed": figures.push("Nightwish"); break; + case "curled": figures.push("Eyesonme"); break; + case "luxurious": figures.push("Rose59"); break; + case "messy bun": figures.push("Alice"); break; + case "messy": figures.push("MessyHairG3"); break; + case "eary": figures.push("GeorginaHair"); break; + case "undercut": figures.push("Roulette"); break; + case "bangs": figures.push("Neko Hair Genesis 8 Female"); break; + case "hime": figures.push("Peak"); break; + case "drills": figures.push("Didar"); break; + case "bald": break; + case "shaved": break; + case "buzzcut": break; + case "trimmed": break; + + // not implemented + case "double buns": figures.push("Gaze"); break; + case "chignon": figures.push("BaronessHR"); break; + case "french twist": figures.push("HR TIGER BRAIDS G2F"); break; + case "crown braid": figures.push("Sky293"); break; + case "dutch braid": figures.push("Carrousel"); break; + case "double dutch braid": figures.push("HR TIGER BRAIDS G2F"); break; + case "pixie cut": break; + case "bob cut": break; + + default: break; + } } } @@ -1300,7 +1355,7 @@ App.Art.applyMaterials = function(slave, scene, p) { let makeupColor; let makeupOpacity; - let lipsGloss = 1.49; + let lipsGloss = 2.5; let lipsRough = 0.45; let lipsMetal = 0; @@ -1359,9 +1414,6 @@ App.Art.applyMaterials = function(slave, scene, p) { } makeupColor = App.Art.hexToRgb(makeupColor); - lipsColor[0] = makeupColor[0] * makeupOpacity + lipsColor[0] * (1 - makeupOpacity); - lipsColor[1] = makeupColor[1] * makeupOpacity + lipsColor[1] * (1 - makeupOpacity); - lipsColor[2] = makeupColor[2] * makeupOpacity + lipsColor[2] * (1 - makeupOpacity); let nailColor; switch (slave.nails) { @@ -1400,112 +1452,231 @@ App.Art.applyMaterials = function(slave, scene, p) { nailColor = App.Art.hexToRgb(nailColor); - switch (slave.hStyle) { - case "afro": - materials.push(["yara_scalp", "Ka", hairColor]); - materials.push(["yara_hair", "Ka", hairColor]); - break; - case "cornrows": - materials.push(["tiger_scalp", "Ka", hairColor]); - materials.push(["tiger_hair", "Ka", hairColor]); - break; - case "bun": - materials.push(["adia_scalp", "Ka", hairColor]); - materials.push(["adia_hair", "Ka", hairColor]); - break; - case "neat": - materials.push(["samira_scalp", "Ka", hairColor]); - materials.push(["samira_hair", "Ka", hairColor]); - break; - case "strip": - materials.push(["rebel_scalp", "Ka", hairColor]); - materials.push(["rebel_hair", "Ka", hairColor]); - break; - case "tails": - materials.push(["kinley_scalp", "Ka", hairColor]); - materials.push(["kinley_hair_thin_strands", "Ka", hairColor]); - materials.push(["kinley_hair_long", "Ka", hairColor]); - materials.push(["kinley_hair_strands", "Ka", hairColor]); - materials.push(["kinley_hair_base", "Ka", hairColor]); - materials.push(["kinley_hair_tie", "Ka", hairColor]); - break; - case "up": - materials.push(["pina_scalp", "Ka", hairColor]); - materials.push(["pina_hair1", "Ka", hairColor]); - materials.push(["pina_hair2", "Ka", hairColor]); - break; - case "ponytail": - materials.push(["ponytail_scalp", "Ka", hairColor]); - materials.push(["ponytail_hair1", "Ka", hairColor]); - materials.push(["ponytail_hair2", "Ka", hairColor]); - materials.push(["ponytail_hair3", "Ka", hairColor]); - materials.push(["ponytail_holder", "Ka", hairColor]); - break; - case "braided": - materials.push(["mishka_scalp", "Ka", hairColor]); - materials.push(["mishka_hair1", "Ka", hairColor]); - materials.push(["mishka_hair2", "Ka", hairColor]); - materials.push(["mishka_hair3", "Ka", hairColor]); - break; - case "dreadlocks": - materials.push(["dreads_scalp", "Ka", hairColor]); - materials.push(["dreads_hair", "Ka", hairColor]); - break; - case "permed": - materials.push(["ichigo_scalp", "Ka", hairColor]); - materials.push(["ichigo_hair1", "Ka", hairColor]); - materials.push(["ichigo_hair2", "Ka", hairColor]); - break; - case "curled": - materials.push(["havana_hair", "Ka", hairColor]); - break; - case "luxurious": - materials.push(["baroness_scalp", "Ka", hairColor]); - materials.push(["baroness_hair", "Ka", hairColor]); - break; - case "messy bun": - materials.push(["krayon_scalp", "Ka", hairColor]); - materials.push(["krayon_hair1", "Ka", hairColor]); - materials.push(["krayon_hair2", "Ka", hairColor]); - materials.push(["krayon_hair3", "Ka", hairColor]); - materials.push(["krayon_hair4", "Ka", hairColor]); - break; - case "messy": - materials.push(["messy_scalp", "Ka", hairColor]); - materials.push(["messy_hair", "Ka", hairColor]); - break; - case "eary": - materials.push(["georgina_scalp", "Ka", hairColor]); - materials.push(["georgina_hair1", "Ka", hairColor]); - materials.push(["georgina_hair2", "Ka", hairColor]); - break; - case "undercut": - materials.push(["edit_scalp", "Ka", hairColor]); - materials.push(["edit_hair", "Ka", hairColor]); - break; - case "bangs": - materials.push(["neko_scalp", "Ka", hairColor]); - materials.push(["neko_hair", "Ka", hairColor]); - break; - case "hime": - materials.push(["nyo_scalp", "Ka", hairColor]); - materials.push(["nyo_hair", "Ka", hairColor]); - break; - case "drills": - materials.push(["bunny_scalp", "Ka", hairColor]); - materials.push(["bunny_hair1", "Ka", hairColor]); - materials.push(["bunny_hair2", "Ka", hairColor]); - materials.push(["bunny_hair3", "Ka", hairColor]); - materials.push(["bunny_hair4", "Ka", hairColor]); - break; - case "buzzcut": - case "trimmed": - materials.push(["shaved_face", "Ka", hairColor]); - materials.push(["shaved_torso", "Ka", hairColor]); - break; - case "bald": - case "shaved": - default: break; + if (slave.hLength < 50) { + switch (slave.hStyle) { + case "afro": + materials.push(["yara_scalp", "Ka", hairColor]); + materials.push(["yara_hair", "Ka", hairColor]); + break; + case "cornrows": + materials.push(["lush_s4studio_mesh_4", "Ka", hairColor]); + break; + case "bun": + materials.push(["adia_scalp", "Ka", hairColor]); + materials.push(["adia_hair", "Ka", hairColor]); + break; + case "neat": + materials.push(["samira_scalp", "Ka", hairColor]); + materials.push(["samira_hair", "Ka", hairColor]); + break; + case "strip": + materials.push(["rebel_scalp", "Ka", hairColor]); + materials.push(["rebel_hair", "Ka", hairColor]); + break; + case "tails": + materials.push(["kinley_scalp", "Ka", hairColor]); + materials.push(["kinley_hair_thin_strands", "Ka", hairColor]); + materials.push(["kinley_hair_long", "Ka", hairColor]); + materials.push(["kinley_hair_strands", "Ka", hairColor]); + materials.push(["kinley_hair_base", "Ka", hairColor]); + materials.push(["kinley_hair_tie", "Ka", hairColor]); + break; + case "up": + materials.push(["pina_scalp", "Ka", hairColor]); + materials.push(["pina_hair1", "Ka", hairColor]); + materials.push(["pina_hair2", "Ka", hairColor]); + break; + case "ponytail": + materials.push(["ponytail_scalp", "Ka", hairColor]); + materials.push(["ponytail_hair1", "Ka", hairColor]); + materials.push(["ponytail_hair2", "Ka", hairColor]); + materials.push(["ponytail_hair3", "Ka", hairColor]); + materials.push(["ponytail_holder", "Ka", hairColor]); + break; + case "braided": + materials.push(["mishka_scalp", "Ka", hairColor]); + materials.push(["mishka_hair1", "Ka", hairColor]); + materials.push(["mishka_hair2", "Ka", hairColor]); + materials.push(["mishka_hair3", "Ka", hairColor]); + break; + case "dreadlocks": + materials.push(["dreads_scalp", "Ka", hairColor]); + materials.push(["dreads_hair", "Ka", hairColor]); + break; + case "permed": + materials.push(["ichigo_scalp", "Ka", hairColor]); + materials.push(["ichigo_hair1", "Ka", hairColor]); + materials.push(["ichigo_hair2", "Ka", hairColor]); + break; + case "curled": + materials.push(["havana_hair", "Ka", hairColor]); + break; + case "luxurious": + materials.push(["rose59_geom_00", "Ka", hairColor]); + break; + case "messy bun": + materials.push(["krayon_scalp", "Ka", hairColor]); + materials.push(["krayon_hair1", "Ka", hairColor]); + materials.push(["krayon_hair2", "Ka", hairColor]); + materials.push(["krayon_hair3", "Ka", hairColor]); + materials.push(["krayon_hair4", "Ka", hairColor]); + break; + case "messy": + materials.push(["messy_scalp", "Ka", hairColor]); + materials.push(["messy_hair", "Ka", hairColor]); + break; + case "eary": + materials.push(["georgina_scalp", "Ka", hairColor]); + materials.push(["georgina_hair1", "Ka", hairColor]); + materials.push(["georgina_hair2", "Ka", hairColor]); + break; + case "undercut": + materials.push(["edit_scalp", "Ka", hairColor]); + materials.push(["edit_hair", "Ka", hairColor]); + break; + case "bangs": + materials.push(["neko_scalp", "Ka", hairColor]); + materials.push(["neko_hair", "Ka", hairColor]); + break; + case "hime": + materials.push(["nyo_scalp", "Ka", hairColor]); + materials.push(["nyo_hair", "Ka", hairColor]); + break; + case "drills": + materials.push(["bunny_scalp", "Ka", hairColor]); + materials.push(["bunny_hair1", "Ka", hairColor]); + materials.push(["bunny_hair2", "Ka", hairColor]); + materials.push(["bunny_hair3", "Ka", hairColor]); + materials.push(["bunny_hair4", "Ka", hairColor]); + break; + case "french twist": + materials.push(["tiger_scalp", "Ka", hairColor]); + materials.push(["tiger_hair", "Ka", hairColor]); + break; + case "chignon": + materials.push(["baroness_scalp", "Ka", hairColor]); + materials.push(["baroness_hair", "Ka", hairColor]); + break; + case "crown braid": + materials.push(["sky293_s4studio_mesh_2", "Ka", hairColor]); + break; + case "double dutch braid": + materials.push(["tiger_scalp", "Ka", hairColor]); + materials.push(["tiger_hair", "Ka", hairColor]); + break; + case "double buns": + materials.push(["gaze_s4studio_mesh_3", "Ka", hairColor]); + break; + case "dutch braid": + materials.push(["carrousel_dicksquad", "Ka", hairColor]); + break; + case "buzzcut": + case "trimmed": + materials.push(["shaved_face", "Ka", hairColor]); + materials.push(["shaved_torso", "Ka", hairColor]); + break; + case "bald": + case "shaved": + default: break; + } + } else { + switch (slave.hStyle) { + case "afro": + materials.push(["yara_scalp", "Ka", hairColor]); + materials.push(["yara_hair", "Ka", hairColor]); + break; + case "cornrows": + materials.push(["lush_s4studio_mesh_4", "Ka", hairColor]); + break; + case "bun": + materials.push(["sky158_groupname", "Ka", hairColor]); + break; + case "neat": + materials.push(["samira_scalp", "Ka", hairColor]); + materials.push(["samira_hair", "Ka", hairColor]); + break; + case "strip": + materials.push(["wanda_groupname", "Ka", hairColor]); + break; + case "tails": + materials.push(["suckerpunch_hair", "Ka", hairColor]); + break; + case "up": + materials.push(["inkstone_groupname", "Ka", hairColor]); + break; + case "ponytail": + materials.push(["paraguay_group_1", "Ka", hairColor]); + break; + case "braided": + materials.push(["butterfly160_s4studio_mesh_1", "Ka", hairColor]); + break; + case "dreadlocks": + materials.push(["sparks_s4studio_mesh_2", "Ka", hairColor]); + break; + case "permed": + materials.push(["nightwish_group_1", "Ka", hairColor]); + break; + case "curled": + materials.push(["eyesonme_hair", "Ka", hairColor]); + break; + case "luxurious": + materials.push(["rose59_geom_00", "Ka", hairColor]); + break; + case "messy bun": + materials.push(["alice_group_1", "Ka", hairColor]); + break; + case "messy": + materials.push(["messy_scalp", "Ka", hairColor]); + materials.push(["messy_hair", "Ka", hairColor]); + break; + case "eary": + materials.push(["georgina_scalp", "Ka", hairColor]); + materials.push(["georgina_hair1", "Ka", hairColor]); + materials.push(["georgina_hair2", "Ka", hairColor]); + break; + case "undercut": + materials.push(["roulette_roulette", "Ka", hairColor]); + break; + case "bangs": + materials.push(["neko_scalp", "Ka", hairColor]); + materials.push(["neko_hair", "Ka", hairColor]); + break; + case "hime": + materials.push(["peak_group_1", "Ka", hairColor]); + break; + case "drills": + materials.push(["didar_didar", "Ka", hairColor]); + break; + case "french twist": + materials.push(["tiger_scalp", "Ka", hairColor]); + materials.push(["tiger_hair", "Ka", hairColor]); + break; + case "chignon": + materials.push(["baroness_scalp", "Ka", hairColor]); + materials.push(["baroness_hair", "Ka", hairColor]); + break; + case "crown braid": + materials.push(["sky293_s4studio_mesh_2", "Ka", hairColor]); + break; + case "double dutch braid": + materials.push(["tiger_scalp", "Ka", hairColor]); + materials.push(["tiger_hair", "Ka", hairColor]); + break; + case "double buns": + materials.push(["gaze_s4studio_mesh_3", "Ka", hairColor]); + break; + case "dutch braid": + materials.push(["carrousel_dicksquad", "Ka", hairColor]); + break; + case "buzzcut": + case "trimmed": + materials.push(["shaved_face", "Ka", hairColor]); + materials.push(["shaved_torso", "Ka", hairColor]); + break; + case "bald": + case "shaved": + default: break; + } } let eyebrowColor = App.Art.hexToRgb(extractColor(slave.eyebrowHColor)); @@ -1537,29 +1708,19 @@ App.Art.applyMaterials = function(slave, scene, p) { break; } - if (slave.face < -66) { - materials.push(["Eyelashes", "map_D", "base2/eyelash/EyeLash_0.jpg"]); - } else if (slave.face < -33) { - materials.push(["Eyelashes", "map_D", "base2/eyelash/EyeLash_1.jpg"]); - } else if (slave.face < 0) { - materials.push(["Eyelashes", "map_D", "base/G8FBaseEyelashes_1006.jpg"]); - } else if (slave.face < 33) { - materials.push(["Eyelashes", "map_D", "base2/eyelash/EyeLash_2.jpg"]); - } else if (slave.face < 66) { - materials.push(["Eyelashes", "map_D", "base2/eyelash/EyeLash_3.jpg"]); - } else { - materials.push(["Eyelashes", "map_D", "base2/eyelash/EyeLash_4.jpg"]); - } + let eyelashes = ["base2/eyelash/EyeLash_2.jpg", "base2/eyelash/EyeLash_5.jpg", "base/G8FBaseEyelashes_1006.jpg"]; + let eyelash = Math.floor(App.Art.random() * eyelashes.length); + materials.push(["Eyelashes", "map_D", eyelashes[eyelash]]); let irisColorLeft = App.Art.hexToRgb(extractColor(slave.eye.left ? extractColor(slave.eye.left.iris) : extractColor("black"))); let irisColorRight = App.Art.hexToRgb(extractColor(slave.eye.right ? extractColor(slave.eye.right.iris) : extractColor("black"))); let scleraColorLeft = App.Art.hexToRgb(extractColor(slave.eye.left ? extractColor(slave.eye.left.sclera) : extractColor("black"))); let scleraColorRight = App.Art.hexToRgb(extractColor(slave.eye.right ? extractColor(slave.eye.right.sclera) : extractColor("black"))); - materials.push(["Iris_Left", "Ka", [irisColorLeft[0]*1.4*2.5, irisColorLeft[1]*1.4*2.5, irisColorLeft[2]*1.4*2.5]]); - materials.push(["Iris_Right", "Ka", [irisColorRight[0]*1.4*2.5, irisColorRight[1]*1.4*2.5, irisColorRight[2]*1.4*2.5]]); - materials.push(["Sclera_Left", "Ka", [scleraColorLeft[0]*0.8*1.8, scleraColorLeft[1]*0.8*2, scleraColorLeft[2]*0.8*2]]); - materials.push(["Sclera_Right", "Ka", [scleraColorRight[0]*0.8*1.8, scleraColorRight[1]*0.8*2, scleraColorRight[2]*0.8*2]]); + materials.push(["Iris_Left", "Ka", [irisColorLeft[0]*2.2, irisColorLeft[1]*2.2, irisColorLeft[2]*2.2]]); + materials.push(["Iris_Right", "Ka", [irisColorRight[0]*2.2, irisColorRight[1]*2.2, irisColorRight[2]*2.2]]); + materials.push(["Sclera_Left", "Ka", [scleraColorLeft[0]*1.4, scleraColorLeft[1]*1.4, scleraColorLeft[2]*1.4]]); + materials.push(["Sclera_Right", "Ka", [scleraColorRight[0]*1.4, scleraColorRight[1]*1.4, scleraColorRight[2]*1.4]]); // expected skin color let O = App.Art.hexToRgb(skinColorCatcher(slave).skinColor); @@ -1569,6 +1730,10 @@ App.Art.applyMaterials = function(slave, scene, p) { lipsColor = [O[0]*0.76*lbrf, O[1]*0.55*lbrf, O[2]*0.6*lbrf]; areolaColor = lipsColor; + lipsColor[0] = makeupColor[0] * makeupOpacity + lipsColor[0] * (1 - makeupOpacity); + lipsColor[1] = makeupColor[1] * makeupOpacity + lipsColor[1] * (1 - makeupOpacity); + lipsColor[2] = makeupColor[2] * makeupOpacity + lipsColor[2] * (1 - makeupOpacity); + // average skin texture color let oSkinColors = { "Tara": [194/255, 148/255, 124/255], diff --git a/src/art/webgl/engine.js b/src/art/webgl/engine.js index a89db2b1a5df24998f3057ee8e93f5e4f8ddae34..68315d1b2645db9f37c5a17e33f3ad24d9ec22b4 100644 --- a/src/art/webgl/engine.js +++ b/src/art/webgl/engine.js @@ -36,19 +36,24 @@ App.Art.Engine = class { layout(location = 1) out vec3 gNormal; uniform sampler2D alpha; + uniform float d; in vec2 textureCoord; in vec3 pos; in vec3 normal; void main() { - float map_d = texture(alpha, textureCoord).r; + float map_d = d * texture(alpha, textureCoord).r; if (map_d < 0.85) discard; gPosition = pos; - gNormal = normalize(normal); + + if (!gl_FrontFacing) + gNormal = -normalize(normal); + else + gNormal = normalize(normal); }`; } @@ -73,11 +78,12 @@ App.Art.Engine = class { precision highp float; uniform sampler2D alpha; + uniform float d; in vec2 textureCoord; out float gShadowDepth; void main() { - float map_d = texture(alpha, textureCoord).r; + float map_d = d * texture(alpha, textureCoord).r; if (map_d < 0.85) discard; @@ -268,65 +274,66 @@ App.Art.Engine = class { getFsSourceForwardPass(dl, pl) { return `#version 300 es - precision highp float; + precision highp float; - uniform float lightInt[${dl}]; - uniform vec3 lightAmb[${dl}]; - uniform vec3 lightColor[${dl}]; - uniform vec3 lightVect[${dl}]; - - uniform float pointLightInt[${pl}]; - uniform vec3 pointLightAmb[${pl}]; - uniform vec3 pointLightColor[${pl}]; - uniform vec3 pointLightPos[${pl}]; - - uniform vec3 shadowDir; - uniform float shadowBiasMin; - uniform float shadowBiasMax; - uniform float shadowIntensity; - - uniform float sssPower; - uniform float sssDistortion; - uniform float sssIntensity; - uniform float sssAmbient; - - uniform float whiteM; - uniform float gammaY; - uniform float uchimuraP; - uniform float uchimuraA; - uniform float ssaoInt; - - uniform float d; - uniform vec3 Ka; - uniform vec3 Ks; - uniform vec3 Ke; - uniform float Ni; - uniform float r; - uniform float m; - uniform float sss; - - uniform vec2 offset; - uniform float angle; - uniform float scale; - - uniform float sNormals; - uniform float sSSAO; - uniform float sAO; - uniform float sIAO; - uniform float sAmbient; - uniform float sAlbedo; - uniform float sSpecular; - uniform float sEmission; - uniform float sAlpha; - uniform float sGamma; - uniform float sReinhard; - uniform float sUchimura; - uniform float sNormal; - uniform float sHDR; - uniform float sShadows; - uniform float sSSS; - - uniform float overlay; + uniform float lightInt[${dl}]; + uniform vec3 lightAmb[${dl}]; + uniform vec3 lightColor[${dl}]; + uniform vec3 lightVect[${dl}]; + + uniform float pointLightInt[${pl}]; + uniform vec3 pointLightAmb[${pl}]; + uniform vec3 pointLightColor[${pl}]; + uniform vec3 pointLightPos[${pl}]; + + uniform vec3 shadowDir; + uniform float shadowBiasMin; + uniform float shadowBiasMax; + uniform float shadowIntensity; + + uniform float sssPower; + uniform float sssDistortion; + uniform float sssIntensity; + uniform float sssAmbient; + + uniform float whiteM; + uniform float gammaY; + uniform float uchimuraP; + uniform float uchimuraA; + uniform float ssaoInt; + uniform bool opaque; + + uniform float d; + uniform vec3 Ka; + uniform vec3 Ks; + uniform vec3 Ke; + uniform float Ni; + uniform float r; + uniform float m; + uniform bool sss; + + uniform vec2 offset; + uniform float angle; + uniform float scale; + + uniform bool sNormals; + uniform bool sSSAO; + uniform bool sAO; + uniform bool sIAO; + uniform bool sAmbient; + uniform bool sAlbedo; + uniform bool sSpecular; + uniform bool sEmission; + uniform bool sAlpha; + uniform bool sGamma; + uniform bool sReinhard; + uniform bool sUchimura; + uniform bool sNormal; + uniform bool sHDR; + uniform bool sShadows; + uniform bool sSSS; + + uniform bool overlay; uniform vec3 cameraPos; @@ -341,25 +348,6 @@ App.Art.Engine = class { const float PI = 3.14159265359; - vec2 poissonDisk[16] = vec2[]( - vec2( -0.94201624, -0.39906216 ), - vec2( 0.94558609, -0.76890725 ), - vec2( -0.094184101, -0.92938870 ), - vec2( 0.34495938, 0.29387760 ), - vec2( -0.91588581, 0.45771432 ), - vec2( -0.81544232, -0.87912464 ), - vec2( -0.38277543, 0.27676845 ), - vec2( 0.97484398, 0.75648379 ), - vec2( 0.44323325, -0.97511554 ), - vec2( 0.53742981, -0.47373420 ), - vec2( -0.26496911, -0.41893023 ), - vec2( 0.79197514, 0.19090188 ), - vec2( -0.24188840, 0.99706507 ), - vec2( -0.81409955, 0.91437590 ), - vec2( 0.19984126, 0.78641367 ), - vec2( 0.14383161, -0.14100790 ) - ); - float distributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness*roughness; @@ -441,19 +429,13 @@ App.Art.Engine = class { return T * w0 + L * w1 + S * w2; } - float random(vec3 seed, int i){ - vec4 seed4 = vec4(seed,i); - float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); - return fract(sin(dot_product) * 43758.5453); - } - void main() { - vec2 resolution = vec2(textureSize(textSampler[0], 0)); - mat3 TBN = createTBNMatrix(pos, textureCoord, normal); - vec3 new_normal = normal; - vec3 map_Ka = vec3(0.0,0.0,0.0); - vec3 map_Ks = vec3(0.0,0.0,0.0); + if (!gl_FrontFacing) + new_normal = -normal; + + vec3 map_Ka = vec3(0.0,0.0,0.0); + vec3 map_Ks = vec3(0.0,0.0,0.0); vec3 map_Ke = vec3(0.0,0.0,0.0); float map_d = 1.0; float ao = 1.0; @@ -462,56 +444,68 @@ App.Art.Engine = class { float roughness = 0.0; float metallic = 0.0; + vec2 resolution = vec2(textureSize(textSampler[0], 0)); vec2 coord = textureCoord - vec2(0.5, 0.5) - offset; coord = vec2(coord.x * cos(angle) - coord.y * sin(angle), coord.x * sin(angle) + coord.y * cos(angle)); coord = coord / scale; coord = coord + vec2(0.5, 0.5); - if (sAlpha == 1.0) - map_d = d * texture(textSampler[1], coord).r; - - if (map_d < 0.05) - discard; + if (sAlpha) + map_d = d * texture(textSampler[1], coord).r; + + if (!opaque) { + if (map_d < 0.01 || map_d == 1.0) + discard; + } else { + if (map_d < 1.0) + discard; + } - if (sNormal == 1.0) { + if (sNormal) { vec3 map_Kn = texture(textSampler[5], coord).rgb *2.0-1.0; - if (map_Kn != vec3(-1.0,-1.0,-1.0)) + if (map_Kn != vec3(-1.0,-1.0,-1.0)) { + mat3 TBN = createTBNMatrix(pos, textureCoord, new_normal); new_normal = normalize(TBN * map_Kn); - } + } + } - if (sSSAO == 1.0) + if (sSSAO) ao = texture(textSampler[0], vec2(gl_FragCoord.x, gl_FragCoord.y) / resolution).r; - if (sSSS == 1.0) + if (sSSS) iao = texture(textSampler[9], vec2(gl_FragCoord.x, gl_FragCoord.y) / resolution).r; - if (sAlbedo == 1.0) + if (sAlbedo) map_Ka = Ka * texture(textSampler[2], coord).rgb; + map_Ka += 0.0001; - if (sSpecular == 1.0) { + if (sSpecular) { map_Ks = Ks * texture(textSampler[3], coord).rgb; roughness = r * texture(textSampler[6], coord).r; metallic = m * texture(textSampler[7], coord).r; } - if (sEmission == 1.0) + if (sEmission) map_Ke = Ke * texture(textSampler[4], coord).rgb; - if (sShadows == 1.0) { + if (sShadows) { vec3 projCoords = shadowMap.xyz/shadowMap.w * 0.5 + 0.5; float currentDepth = projCoords.z; float bias = max(shadowBiasMax * (1.0 - dot(new_normal, shadowDir)), shadowBiasMin); vec2 texelSize = 1.0 / vec2(textureSize(textSampler[8], 0)); - for (int i=0;i<16;i++){ - int index = int(16.0*random(floor(pos.xyz*1000.0), i))%16; - float pcfDepth = texture(textSampler[8], projCoords.xy + poissonDisk[index]*texelSize).r; - shadow += currentDepth - bias < pcfDepth ? 1.0 : 0.0; + for(int x = -1; x <= 1; ++x) + { + for(int y = -1; y <= 1; ++y) + { + float pcfDepth = texture(textSampler[8], projCoords.xy + vec2(x, y) * texelSize).r; + shadow += currentDepth - bias < pcfDepth ? 1.0 : 0.0; + } } shadow /= 16.0; shadow = min(shadow + (1.0-shadowIntensity), 1.0); } - + // material parameters vec3 albedo = pow(map_Ka, vec3(2.2)); ao = pow(ao, ssaoInt); @@ -523,7 +517,7 @@ App.Art.Engine = class { // reflectance equation vec3 Lo = vec3(0.0); vec3 Le = map_Ke; - for (int i = 0; i < ${dl}; i++) { + for (int i = 0; i < ${dl}; i++) { if (lightInt[i] <= 0.0) continue; @@ -535,7 +529,7 @@ App.Art.Engine = class { // cook-torrance brdf float NDF = distributionGGX(N, H, roughness); float G = geometrySmith(N, V, L, roughness); - vec3 F = fresnelSchlickRoughness(max(dot(H, V), 0.0), F0, roughness); + vec3 F = fresnelSchlickRoughness(max(dot(H, V), 0.0), F0, roughness); vec3 numerator = NDF * G * F; float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.00001; @@ -547,7 +541,7 @@ App.Art.Engine = class { // add to outgoing radiance Lo float NdotL = max(dot(N, L), 0.0); - if (map_d >= 0.85 || overlay == 1.0) { + if (map_d >= 0.85 || overlay) { Lo += (kD * albedo / PI + specular * map_Ks) * radiance * NdotL * shadow; } else { @@ -557,9 +551,9 @@ App.Art.Engine = class { // ambient lighting vec3 La = kD * albedo * lightAmb[i]; - if (map_d >= 0.85 || overlay == 1.0) { + if (map_d >= 0.85 || overlay) { // sss - if (sSSS == 1.0 && sss == 1.0) { + if (sSSS && sss) { vec3 H_d = normalize(L + N * sssDistortion); float VdotH = pow(clamp(dot(V, -H_d), 0.0, 1.0), sssPower); Lo += (kD * albedo / PI + specular * map_Ks) * (VdotH+sssAmbient) * iao * sssIntensity * NdotL * shadow; @@ -584,7 +578,7 @@ App.Art.Engine = class { // cook-torrance brdf float NDF = distributionGGX(N, H, roughness); float G = geometrySmith(N, V, L, roughness); - vec3 F = fresnelSchlickRoughness(max(dot(H, V), 0.0), F0, roughness); + vec3 F = fresnelSchlickRoughness(max(dot(H, V), 0.0), F0, roughness); vec3 numerator = NDF * G * F; float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.00001; @@ -592,11 +586,11 @@ App.Art.Engine = class { // kS is equal to Fresnel vec3 kD = vec3(1.0) - F; - kD *= 1.0 - metallic; - + kD *= 1.0 - metallic; + // add to outgoing radiance Lo float NdotL = max(dot(N, L), 0.0); - if (map_d >= 0.85 || overlay == 1.0) { + if (map_d >= 0.85 || overlay) { Lo += (kD * albedo / PI + specular * map_Ks) * radiance * NdotL * shadow; } else { @@ -606,9 +600,9 @@ App.Art.Engine = class { // ambient lighting vec3 La = kD * albedo * pointLightAmb[i]; - if (map_d >= 0.85 || overlay == 1.0) { + if (map_d >= 0.85 || overlay) { // sss - if (sSSS == 1.0 && sss == 1.0) { + if (sSSS && sss) { vec3 H_d = normalize(L + N * sssDistortion); float VdotH = pow(clamp(dot(V, -H_d), 0.0, 1.0), sssPower); Lo += (kD * albedo / PI + specular * map_Ks) * (VdotH+sssAmbient) * iao * sssIntensity * NdotL * shadow; @@ -619,44 +613,45 @@ App.Art.Engine = class { } else { Lo += La; } - } + } vec3 c = (Lo + Le); - if (sReinhard == 1.0) { + if (sReinhard) { float l_old = 0.2126*c.r+0.7152*c.g+0.0722*c.b; float numerator = l_old * (1.0 + (l_old / (whiteM*whiteM))); float l_new = numerator / (1.0 + l_old); c = c * (l_new / l_old); } - if (sUchimura == 1.0) { + if (sUchimura) { c = tonemapUchimura(c, uchimuraP, uchimuraA); } - if (sGamma == 1.0) - c = pow(c, vec3(1.0/gammaY)); + if (sGamma) + c = pow(c, vec3(1.0/gammaY)); - if (sNormals == 1.0) - c = new_normal*0.5+0.5; + if (sNormals) + c = new_normal*0.5+0.5; - if (sAO == 1.0) + if (sAO) c = vec3(ao); - if (sIAO == 1.0) + if (sIAO) c = vec3(iao); outputColor = vec4(c*map_d, map_d); }`; } - initBuffers(sceneData, dir) { + initBuffers(sceneData, sceneParams, dir) { // init buffer containers this.buffers = new class {}; this.buffers.models = []; for (let m=0; m < sceneData.models.length; m++) { this.buffers.models[m] = new class {}; + this.buffers.models[m].tree = this.buildModelTree(sceneParams.models[m]); } // init quad buffers @@ -688,15 +683,15 @@ App.Art.Engine = class { modelBuffers.indexSizes = []; modelBuffers.figureIndices = []; modelBuffers.figureSeemMaps = []; + modelBuffers.figureTargets = []; + modelBuffers.figureWeights = []; + modelBuffers.figureTarget = []; + for (let i=0, count=0; i < modelData.figures.length; i++) { modelBuffers.verticesPositionBuffer[i] = this.gl.createBuffer(); - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, modelBuffers.verticesPositionBuffer[i]); - this.gl.bufferData(this.gl.ARRAY_BUFFER, this.base64ToFloat(modelData.figures[i].verts), this.gl.STATIC_DRAW); modelBuffers.vertexPositionBuffer[i] = this.base64ToFloat(modelData.figures[i].verts); modelBuffers.verticesNormalBuffer[i] = this.gl.createBuffer(); - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, modelBuffers.verticesNormalBuffer[i]); - this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(modelBuffers.vertexPositionBuffer[i].length), this.gl.STATIC_DRAW); modelBuffers.verticesTextureCoordBuffer[i] = this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, modelBuffers.verticesTextureCoordBuffer[i]); @@ -716,20 +711,21 @@ App.Art.Engine = class { let seemMap = new Int32Array(seems.length*2); let indices = modelBuffers.figureIndices[i]; for (let j=0, h=0; j <= seems.length-3; j+=3, h+=6) { - let pos = seems[j+0]; - let oldInd = seems[j+1]; - let newInd = seems[j+2]; - indices[pos] = newInd/3; - seemMap[h] = oldInd; - seemMap[h+1] = newInd; - seemMap[h+2] = oldInd+1; - seemMap[h+3] = newInd+1; - seemMap[h+4] = oldInd+2; - seemMap[h+5] = newInd+2; + let idx = seems[j+1]; + let value = seems[j+2]; + indices[seems[j]] = value/3; + seemMap[h] = idx; + seemMap[h+1] = value; + seemMap[h+2] = idx+1; + seemMap[h+3] = value+1; + seemMap[h+4] = idx+2; + seemMap[h+5] = value+2; } modelBuffers.figureSeemMaps[i] = seemMap; + modelBuffers.figureTargets[i] = this.base64ToInt(modelData.figures[i].targets); + modelBuffers.figureWeights[i] = this.base64ToFloat(modelData.figures[i].weights); + modelBuffers.figureTarget[i] = parseInt(modelData.figures[i].target); } - this.initMorphs(modelBuffers, modelData, dir); } } @@ -762,6 +758,12 @@ App.Art.Engine = class { this.gl.bindTexture(this.gl.TEXTURE_2D, this.buffers.sssColorBufferBlur); this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.R16F, screenWidth, screenHeight, 0, this.gl.RED, this.gl.FLOAT, null); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.buffers.guassianColorBuffer); + this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA32F, screenWidth, screenHeight, 0, this.gl.RGBA, this.gl.FLOAT, null); + + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.buffers.forwardFBO); + this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT32F, screenWidth, screenHeight); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); } @@ -868,6 +870,25 @@ App.Art.Engine = class { this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, buffers.sssColorBufferBlur, 0); this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); + // forward color buffer + buffers.forwardBuffer = this.gl.createFramebuffer(); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, buffers.forwardBuffer); + + buffers.guassianColorBuffer = this.gl.createTexture(); + this.gl.bindTexture(this.gl.TEXTURE_2D, buffers.guassianColorBuffer); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); + this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA32F, screenWidth, screenHeight, 0, this.gl.RGBA, this.gl.FLOAT, null); + this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, buffers.guassianColorBuffer, 0); + + this.gl.drawBuffers([this.gl.COLOR_ATTACHMENT0]); + + buffers.forwardFBO = this.gl.createRenderbuffer(); + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, buffers.forwardFBO); + this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT32F, screenWidth, screenHeight); + this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, buffers.forwardFBO); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); + const random = function(seed) { let x = Math.sin(seed+1) * 10000; return x - Math.floor(x); @@ -1155,7 +1176,7 @@ App.Art.Engine = class { this.gl.depthFunc(this.gl.LEQUAL); this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA); - this.initBuffers(sceneData, dir); + this.initBuffers(sceneData, sceneParams, dir); this.initTextures(sceneData, dir); this.initShaders(sceneParams); } @@ -1164,16 +1185,10 @@ App.Art.Engine = class { // update logic this.update(sceneParams); - // set culling - if (V.setFaceCulling) { - this.gl.enable(this.gl.CULL_FACE); - this.gl.cullFace(this.gl.BACK); - } else { - this.gl.disable(this.gl.CULL_FACE); - } this.gl.clearColor(0, 0, 0, 0); // draw scene + this.gl.enable(this.gl.CULL_FACE); this.gl.disable(this.gl.BLEND); this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffers.gBuffer); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); @@ -1183,6 +1198,7 @@ App.Art.Engine = class { this.gl.enable(this.gl.BLEND); if (V.setShadowMapping) { + this.gl.disable(this.gl.CULL_FACE); this.gl.disable(this.gl.BLEND); this.gl.cullFace(this.gl.FRONT); this.gl.viewport(0, 0, sceneParams.settings.rwidth*4, sceneParams.settings.rheight*4); @@ -1197,6 +1213,7 @@ App.Art.Engine = class { } if (V.setSSAO) { + this.gl.enable(this.gl.CULL_FACE); this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffers.ssaoFBO); this.gl.clear(this.gl.COLOR_BUFFER_BIT); this.gl.useProgram(this.shaderProgramSSAO); @@ -1211,6 +1228,7 @@ App.Art.Engine = class { } if (V.setSSS) { + this.gl.enable(this.gl.CULL_FACE); this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffers.sssFBO); this.gl.clear(this.gl.COLOR_BUFFER_BIT); this.gl.useProgram(this.shaderProgramSSS); @@ -1224,9 +1242,19 @@ App.Art.Engine = class { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); } + // this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffers.forwardBuffer); + if (V.setFaceCulling) { + this.gl.enable(this.gl.CULL_FACE); + } else { + this.gl.disable(this.gl.CULL_FACE); + } this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); this.gl.useProgram(this.shaderProgramForwardPass); - this.drawForwardPass(sceneParams); + this.drawForwardPass(sceneParams, true); + + this.gl.disable(this.gl.CULL_FACE); + this.gl.useProgram(this.shaderProgramForwardPass); + this.drawForwardPass(sceneParams, false); this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); // clone from offscreen to real canvas @@ -1283,7 +1311,7 @@ App.Art.Engine = class { } // create model transforms - this.applyMorphs(modelParams, modelBuffers); + this.applyMorphs(sceneParams, modelParams, modelBuffers); let matRot = this.matrixMakeRotation(this.degreeToRad(modelParams.transform.xr), this.degreeToRad(modelParams.transform.yr), this.degreeToRad(modelParams.transform.zr)); let matTrans = this.matrixMakeTranslation(modelParams.transform.x, modelParams.transform.y, modelParams.transform.z); @@ -1352,6 +1380,7 @@ App.Art.Engine = class { this.gl.activeTexture(this.gl.TEXTURE0); this.gl.bindTexture(this.gl.TEXTURE_2D, this.textures[mat.map_D]); this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramGeometry, "alpha"), 0); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramGeometry, "d"), mat.d); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, modelBuffers.verticesIndexBuffer[count]); this.gl.drawElements(this.gl.TRIANGLES, modelBuffers.indexSizes[count], this.gl.UNSIGNED_INT, 0); @@ -1404,6 +1433,7 @@ App.Art.Engine = class { this.gl.activeTexture(this.gl.TEXTURE0); this.gl.bindTexture(this.gl.TEXTURE_2D, this.textures[mat.map_D]); this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramShadow, "alpha"), 0); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramShadow, "d"), mat.d); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, modelBuffers.verticesIndexBuffer[count]); this.gl.drawElements(this.gl.TRIANGLES, modelBuffers.indexSizes[count], this.gl.UNSIGNED_INT, 0); @@ -1511,21 +1541,23 @@ App.Art.Engine = class { this.gl.drawElements(this.gl.TRIANGLES, 6, this.gl.UNSIGNED_SHORT, 0); } - drawForwardPass(sceneParams) { + drawForwardPass(sceneParams, opaque) { // set scene uniforms - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sNormals"), sceneParams.settings.normals); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sAO"), sceneParams.settings.ao); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sIAO"), sceneParams.settings.iao); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sSSAO"), V.setSSAO); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sSSS"), sceneParams.settings.sss); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sShadows"), V.setShadowMapping); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sAlbedo"), sceneParams.settings.albedo); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sSpecular"), sceneParams.settings.specular); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sEmission"), sceneParams.settings.emission); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sNormal"), sceneParams.settings.normal); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sAlpha"), sceneParams.settings.alpha); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sReinhard"), sceneParams.settings.reinhard); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sGamma"), sceneParams.settings.gamma); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "opaque"), opaque); + + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sNormals"), sceneParams.settings.normals); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sAO"), sceneParams.settings.ao); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sIAO"), sceneParams.settings.iao); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sSSAO"), V.setSSAO); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sSSS"), sceneParams.settings.sss); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sShadows"), V.setShadowMapping); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sAlbedo"), sceneParams.settings.albedo); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sSpecular"), sceneParams.settings.specular); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sEmission"), sceneParams.settings.emission); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sNormal"), sceneParams.settings.normal); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sAlpha"), sceneParams.settings.alpha); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sReinhard"), sceneParams.settings.reinhard); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sGamma"), sceneParams.settings.gamma); this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "whiteM"), sceneParams.settings.whiteM); this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "gammaY"), sceneParams.settings.gammaY); this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sUchimura"), sceneParams.settings.uchimura); @@ -1609,9 +1641,9 @@ App.Art.Engine = class { if (matId !== "ao_surface") { if (h > 0) { - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "overlay"), 1.0); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "overlay"), 1); } else { - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "overlay"), 0.0); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "overlay"), 0); } this.gl.activeTexture(this.gl.TEXTURE0); @@ -1666,7 +1698,7 @@ App.Art.Engine = class { this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "angle"), mat.transform[2]); this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "scale"), mat.transform[3]); - this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sss"), mat.sss); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramForwardPass, "sss"), mat.sss); // draw materials this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, modelBuffers.verticesIndexBuffer[count]); @@ -1678,18 +1710,76 @@ App.Art.Engine = class { } } - applyMorphs(modelParams, modelBuffers) { - if (modelBuffers.oldMorphValues !== JSON.stringify(modelParams.morphs) + JSON.stringify(modelParams.figures)) { - for (let f=0; f < modelParams.figures.length; f++) { - if (!modelParams.visible || !modelParams.figures[f].visible) { - continue; + traverseTree(tree, current, callback) { + current = current.toString(); + let children = tree[current]; + + let isRoot = tree.roots.includes(current); + + current = parseInt(current); + + if (children !== undefined) { + callback.call(this, current, isRoot, false); + children.forEach(child => this.traverseTree(tree, child, callback)); + } else { + callback.call(this, current, isRoot, true); + } + } + + buildModelTree(modelParams) { + let tree = {}; + tree.roots = []; + + for (let i = 0; i < modelParams.figures.length; i++) { + let parent = modelParams.figures[i].morphTarget; + let f = i.toString(); + + if (parent === '') { + tree.roots.push(f); + continue; + } + + parent = modelParams.figures.findIndex(x => x.figId === parent); + if (tree[parent] === undefined) { + tree[parent] = []; + } + tree[parent].push(f); + } + return tree; + } + + makeCheckString(sceneParams, modelParams) { + let checkString = ''; + modelParams.morphs.forEach(m => checkString += m.value + '' + m.offset); + modelParams.figures.forEach(f => checkString += f.visible); + checkString += sceneParams.settings.morphOffsetA + '' + sceneParams.settings.morphOffsetB + '' + sceneParams.settings.morphOffsetM; + return checkString; + } + + applyMorphs(sceneParams, modelParams, modelBuffers) { + if (modelParams.visible && modelBuffers.oldMorphValues !== this.makeCheckString(sceneParams, modelParams)) { + modelBuffers.tree.roots.forEach(root => this.traverseTree(modelBuffers.tree, root, function(f, isRoot, isLeaf) { + // check if children are visible + let visible = false; + this.traverseTree(modelBuffers.tree, f, function(f2, isRoot, isLeaf) { + if (modelParams.figures[f2].visible) { + visible = true; + } + }); + + if (!visible) { + return; } let vertexPosition = new Float32Array(modelBuffers.vertexPositionBuffer[f]); let vertexNormal = new Float32Array(vertexPosition.length); + // apply morph for (let m=0; m < modelParams.morphs.length; m++) { let morphValue = modelParams.morphs[m].value; + if (modelParams.figures[f].morphOffset) { + morphValue *= modelParams.morphs[m].offset; + } if (morphValue !== 0) { let vp = modelBuffers.vertexPositionMorphs[f][m]; @@ -1707,6 +1797,71 @@ App.Art.Engine = class { } } + // get morph delta + if (!isLeaf) { + let pDelta = new Float32Array(vertexPosition); + let vp = new Float32Array(modelBuffers.vertexPositionBuffer[f]); + for (let j=0; j < vp.length; j+=3) { + pDelta[j] -= vp[j]; + pDelta[j+1] -= vp[j+1]; + pDelta[j+2] -= vp[j+2]; + } + modelBuffers.posDelta = pDelta; // save morph delta + } + + if (!isRoot) { + let targets = modelBuffers.figureTargets[f]; + let weights = modelBuffers.figureWeights[f]; + let target = targets[modelBuffers.figureTarget[f]]; + let pDelta = modelBuffers.posDelta; + let nDelta = modelBuffers.normDelta; + let mA = sceneParams.settings.morphOffsetA; + let mB = sceneParams.settings.morphOffsetB; + let mM = sceneParams.settings.morphOffsetM; + + if (modelParams.figures[f].morphFollow) { + for (let j = 0; j < vertexPosition.length; j+=3) { + let t1 = targets[j]; // get closest triangle to base mesh + let t2 = targets[j+1]; + let t3 = targets[j+2]; + + let w1 = weights[j]; + let w2 = weights[j+1]; + let w3 = weights[j+2]; + + // find precomputed baricentric point + let p1 = pDelta[t1]*w1 + pDelta[t2]*w2 + pDelta[t3]*w3; + let p2 = pDelta[t1+1]*w1 + pDelta[t2+1]*w2 + pDelta[t3+1]*w3; + let p3 = pDelta[t1+2]*w1 + pDelta[t2+2]*w2 + pDelta[t3+2]*w3; + + vertexPosition[j] += p1; + vertexPosition[j+1] += p2; + vertexPosition[j+2] += p3; + + let n1 = nDelta[t1]*w1 + nDelta[t2]*w2 + nDelta[t3]*w3; + let n2 = nDelta[t1+1]*w1 + nDelta[t2+1]*w2 + nDelta[t3+1]*w3; + let n3 = nDelta[t1+2]*w1 + nDelta[t2+2]*w2 + nDelta[t3+2]*w3; + + let l = this.vectorLength([p1, p2, p3]); + if (l > 0.01) { // apply morph delta and add offset according to base mesh normals + l = Math.min(l / mA, mM) * mB; + vertexPosition[j] += l*n1; + vertexPosition[j+1] += l*n2; + vertexPosition[j+2] += l*n3; + } + } + } else { // static morph, follow one vertex + let pDeltaX = pDelta[target*3]; + let pDeltaY = pDelta[target*3+1]; + let pDeltaZ = pDelta[target*3+2]; + for (let j = 0; j < vertexPosition.length; j+=3) { + vertexPosition[j] += pDeltaX; + vertexPosition[j+1] += pDeltaY; + vertexPosition[j+2] += pDeltaZ; + } + } + } + // recalculate normals let indices = modelBuffers.figureIndices[f]; for (let j=0; j < indices.length; j+=3) { @@ -1737,19 +1892,28 @@ App.Art.Engine = class { vertexNormal[seemMap[j]] = vertexNormal[seemMap[j+1]]; } + if (!isLeaf) { + for (let j=0; j < vertexNormal.length; j+=3) { + let l = this.vectorLength([vertexNormal[j], vertexNormal[j+1], vertexNormal[j+2]]); + vertexNormal[j] /= l; + vertexNormal[j+1] /= l; + vertexNormal[j+2] /= l; + } + modelBuffers.normDelta = vertexNormal; + } + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, modelBuffers.verticesPositionBuffer[f]); this.gl.bufferData(this.gl.ARRAY_BUFFER, vertexPosition, this.gl.STATIC_DRAW); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, modelBuffers.verticesNormalBuffer[f]); this.gl.bufferData(this.gl.ARRAY_BUFFER, vertexNormal, this.gl.STATIC_DRAW); - } - - modelBuffers.oldMorphValues = JSON.stringify(modelParams.morphs) + JSON.stringify(modelParams.figures); + })); + modelBuffers.oldMorphValues = this.makeCheckString(sceneParams, modelParams); } } base64ToFloat(array) { - let b = window.atob(array); + let b = window.atob(this.gzipToBase64(array)); let fLen = b.length / (Float32Array.BYTES_PER_ELEMENT-1); let dView = new DataView(new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT)); let fAry = new Float32Array(fLen); @@ -1758,7 +1922,7 @@ App.Art.Engine = class { for (let j=0; j < fLen; j++){ p = j * 3; dView.setUint8(0, 0); // skip 1 precision byte - dView.setUint8(1, b.charCodeAt(p+0)); + dView.setUint8(1, b.charCodeAt(p)); dView.setUint8(2, b.charCodeAt(p+1)); dView.setUint8(3, b.charCodeAt(p+2)); fAry[j] = dView.getFloat32(0, true); @@ -1767,7 +1931,7 @@ App.Art.Engine = class { } base64ToInt(array) { - let b = window.atob(array); + let b = window.atob(this.gzipToBase64(array)); let fLen = b.length / Int32Array.BYTES_PER_ELEMENT; let dView = new DataView(new ArrayBuffer(Int32Array.BYTES_PER_ELEMENT)); let fAry = new Int32Array(fLen); @@ -1784,7 +1948,11 @@ App.Art.Engine = class { return fAry; } - base64ToByte(array) { + gzipToBase64(array) { + if (array === '') { + return array; + } + let b = window.atob(array); let fLen = b.length / Uint8Array.BYTES_PER_ELEMENT; let dView = new DataView(new ArrayBuffer(Uint8Array.BYTES_PER_ELEMENT)); @@ -1794,7 +1962,9 @@ App.Art.Engine = class { dView.setUint8(0, b.charCodeAt(j)); fAry[j] = dView.getUint8(0); } - return fAry; + + let data = pako.ungzip(fAry, {raw:false, "to":"string"}); + return data; } degreeToRad(d) { return d * (Math.PI / 180); } @@ -1816,6 +1986,11 @@ App.Art.Engine = class { return [v[0]/l, v[1]/l, v[2]/l]; } + vectorLength(v) { + let l = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + return l; + } + matrixMakeProjection(fov, aspect, near, far) { return [[1/(aspect*Math.tan(fov*0.5/180*Math.PI)), 0, 0, 0], [0, (1/Math.tan(fov*0.5/180*Math.PI)), 0, 0],