diff --git a/src/art/artJS.js b/src/art/artJS.js index 5e4b1fc326a96f5649212c1d4df808d490122976..da5a1fe82d2422344da962f3c80f55f5fe40462c 100644 --- a/src/art/artJS.js +++ b/src/art/artJS.js @@ -21,7 +21,9 @@ globalThis.SlaveArt = function(artSlave, artSize, UIDisplay) { return LegacyVectorArt(artSlave, UIDisplay); } else if (imageChoice === 3) { /* VECTOR ART REVAMP*/ return RevampedVectorArt(artSlave); - } else { /* RENDERED IMAGES BY SHOKUSHU */ + } else if (imageChoice === 0) { /* RENDERED IMAGES BY SHOKUSHU */ + return ArtControlRendered(artSlave, artSize); + } else if (imageChoice === 4) { /* Elohiem's Webgl */ return ArtControlRendered(artSlave, artSize); } }; @@ -141,8 +143,10 @@ App.Art.SlaveArtElement = function(artSlave, artSize, UIDisplay) { return App.Art.legacyVectorArtElement(artSlave, UIDisplay); } else if (imageChoice === 3) { /* VECTOR ART REVAMP*/ return App.Art.revampedVectorArtElement(artSlave); - } else { /* RENDERED IMAGES BY SHOKUSHU */ + } else if (imageChoice === 0) { /* RENDERED IMAGES BY SHOKUSHU */ return App.Art.renderedArtElement(artSlave, artSize); + } else if (imageChoice === 4) { /* Elohiem's Webgl */ + return App.Art.renderedArtElement2(artSlave, artSize); } }; @@ -194,6 +198,113 @@ globalThis.CustomArt = function(slave, imageSize) { return App.Art.elementToMarkup(App.Art.customArtElement(slave, imageSize)); }; +App.Art.doneOnce = false; +App.Art.engine = ""; +App.Art.defaultSceneParams = ""; +App.Art.isDraggingCanvas = false; + +/** + * @param {App.Entity.SlaveState} slave + * @param {number} artSize + * @returns {HTMLElement} + */ +App.Art.renderedArtElement2 = function(slave, artSize) { + + if (App.Art.doneOnce == false) { + let sceneData = App.Art.sceneGetData(); + let sceneParams = App.Art.sceneGetParams(); + App.Art.engine = new App.Art.Engine(); + App.Art.engine.bind(sceneData); + App.Art.defaultSceneParams = JSON.parse(JSON.stringify(sceneParams)); + App.Art.doneOnce = true; + } + + if (slave.sceneParams == "") { + slave.sceneParams = JSON.parse(JSON.stringify(App.Art.defaultSceneParams)); + } + + let cvs = document.createElement("canvas"); + cvs.setAttribute("style", "z-index: 2;"); + + cvs.onmousemove= function(e){ + if(!App.Art.isDraggingCanvas){return;} + e.preventDefault(); + e.stopPropagation(); + + slave.sceneParams.transform.y = slave.sceneParams.transform.y - e.movementY/10; + slave.sceneParams.transform.yr = slave.sceneParams.transform.yr + e.movementX*5; + App.Art.engine.render(slave.sceneParams, cvs); + } + + cvs.onmousedown= function(e){ + e.preventDefault(); + e.stopPropagation(); + App.Art.isDraggingCanvas=true; + } + + cvs.onmouseup= function(e){ + if(!App.Art.isDraggingCanvas){return;} + e.preventDefault(); + e.stopPropagation(); + App.Art.isDraggingCanvas=false; + } + + cvs.onmouseout = function(e){ + if(!App.Art.isDraggingCanvas){return;} + e.preventDefault(); + e.stopPropagation(); + App.Art.isDraggingCanvas=false; + } + + cvs.onmousewheel = function(e){ + slave.sceneParams.camera.fov = slave.sceneParams.camera.fov + e.deltaY/10; + + if (slave.sceneParams.camera.fov < 1) + slave.sceneParams.camera.fov = 1; + if (slave.sceneParams.camera.fov > 179) + slave.sceneParams.camera.fov = 179; + + + App.Art.engine.render(slave.sceneParams, cvs); + return false; + } + + if (artSize) { + let sz; + switch (artSize) { + case 3: + sz = [300, 530]; + break; + case 2: + sz = [300, 530]; + break; + case 1: + sz = [150, 150]; + break; + default: + sz = [120, 120]; + break; + } + cvs.width = sz[0]; + cvs.height = sz[1]; + } + + slave.sceneParams.transform.y = App.Art.defaultSceneParams.transform.y; + slave.sceneParams.transform.yr = App.Art.defaultSceneParams.transform.yr; + slave.sceneParams.camera.fov = App.Art.defaultSceneParams.camera.fov; + slave.sceneParams.settings.rwidth = cvs.width*2; + slave.sceneParams.settings.rheight = cvs.height*2; + + App.Art.applySurfaces(slave); + App.Art.applyMaterials(slave); + App.Art.applyMorphs(slave); + + App.Art.engine.render(slave.sceneParams, cvs); + + console.log(slave.sceneParams) + return cvs; +}; + /** * @param {App.Entity.SlaveState} slave * @param {number} artSize @@ -357,7 +468,32 @@ globalThis.extractColor = (function() { ["strawberry-blonde", "#e5a88c"], /* these are not actually FreeCities canon, but like to appear in custom descriptions */ ["brunette", "#6d4936"], - ["dark", "#463325"] + ["dark", "#463325"], + + ["aliceblue","#f0f8ff"],["antiquewhite","#faebd7"],["aqua","#00ffff"],["aquamarine","#7fffd4"],["azure","#f0ffff"], + ["beige","#f5f5dc"],["bisque","#ffe4c4"],["black","#000000"],["blanchedalmond","#ffebcd"],["blue","#0000ff"],["blueviolet","#8a2be2"],["brown","#a52a2a"],["burlywood","#deb887"], + ["cadetblue","#5f9ea0"],["chartreuse","#7fff00"],["chocolate","#d2691e"],["coral","#ff7f50"],["cornflowerblue","#6495ed"],["cornsilk","#fff8dc"],["crimson","#dc143c"],["cyan","#00ffff"], + ["darkblue","#00008b"],["darkcyan","#008b8b"],["darkgoldenrod","#b8860b"],["darkgray","#a9a9a9"],["darkgreen","#006400"],["darkkhaki","#bdb76b"],["darkmagenta","#8b008b"],["darkolivegreen","#556b2f"], + ["darkorange","#ff8c00"],["darkorchid","#9932cc"],["darkred","#8b0000"],["darksalmon","#e9967a"],["darkseagreen","#8fbc8f"],["darkslateblue","#483d8b"],["darkslategray","#2f4f4f"],["darkturquoise","#00ced1"], + ["darkviolet","#9400d3"],["deeppink","#ff1493"],["deepskyblue","#00bfff"],["dimgray","#696969"],["dodgerblue","#1e90ff"], + ["firebrick","#b22222"],["floralwhite","#fffaf0"],["forestgreen","#228b22"],["fuchsia","#ff00ff"], + ["gainsboro","#dcdcdc"],["ghostwhite","#f8f8ff"],["gold","#ffd700"],["goldenrod","#daa520"],["gray","#808080"],["green","#008000"],["greenyellow","#adff2f"], + ["honeydew","#f0fff0"],["hotpink","#ff69b4"], + ["indianred ","#cd5c5c"],["indigo","#4b0082"],["ivory","#fffff0"],["khaki","#f0e68c"], + ["lavender","#e6e6fa"],["lavenderblush","#fff0f5"],["lawngreen","#7cfc00"],["lemonchiffon","#fffacd"],["lightblue","#add8e6"],["lightcoral","#f08080"],["lightcyan","#e0ffff"],["lightgoldenrodyellow","#fafad2"], + ["lightgrey","#d3d3d3"],["lightgreen","#90ee90"],["lightpink","#ffb6c1"],["lightsalmon","#ffa07a"],["lightseagreen","#20b2aa"],["lightskyblue","#87cefa"],["lightslategray","#778899"],["lightsteelblue","#b0c4de"], + ["lightyellow","#ffffe0"],["lime","#00ff00"],["limegreen","#32cd32"],["linen","#faf0e6"], + ["magenta","#ff00ff"],["maroon","#800000"],["mediumaquamarine","#66cdaa"],["mediumblue","#0000cd"],["mediumorchid","#ba55d3"],["mediumpurple","#9370d8"],["mediumseagreen","#3cb371"],["mediumslateblue","#7b68ee"], + ["mediumspringgreen","#00fa9a"],["mediumturquoise","#48d1cc"],["mediumvioletred","#c71585"],["midnightblue","#191970"],["mintcream","#f5fffa"],["mistyrose","#ffe4e1"],["moccasin","#ffe4b5"], + ["navajowhite","#ffdead"],["navy","#000080"], + ["oldlace","#fdf5e6"],["olive","#808000"],["olivedrab","#6b8e23"],["orange","#ffa500"],["orangered","#ff4500"],["orchid","#da70d6"], + ["palegoldenrod","#eee8aa"],["palegreen","#98fb98"],["paleturquoise","#afeeee"],["palevioletred","#d87093"],["papayawhip","#ffefd5"],["peachpuff","#ffdab9"],["peru","#cd853f"],["pink","#ffc0cb"],["plum","#dda0dd"],["powderblue","#b0e0e6"],["purple","#800080"], + ["rebeccapurple","#663399"],["red","#ff0000"],["rosybrown","#bc8f8f"],["royalblue","#4169e1"], + ["saddlebrown","#8b4513"],["salmon","#fa8072"],["sandybrown","#f4a460"],["seagreen","#2e8b57"],["seashell","#fff5ee"],["sienna","#a0522d"],["silver","#c0c0c0"],["skyblue","#87ceeb"],["slateblue","#6a5acd"],["slategray","#708090"],["snow","#fffafa"],["springgreen","#00ff7f"],["steelblue","#4682b4"], + ["tan","#d2b48c"],["teal","#008080"],["thistle","#d8bfd8"],["tomato","#ff6347"],["turquoise","#40e0d0"], + ["violet","#ee82ee"], + ["wheat","#f5deb3"],["white","#ffffff"],["whitesmoke","#f5f5f5"], + ["yellow","#ffff00"],["yellowgreen","#9acd32"] ]); /* these are HTML color names supported by most browsers */ @@ -529,6 +665,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#C39696"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#E1B585"; colorSlave.areolaColor = "#C39696"; @@ -646,6 +784,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#7C594B"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#AC7C4A"; colorSlave.areolaColor = "#7C594B"; @@ -767,6 +907,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#92684C"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#B27554"; colorSlave.areolaColor = "#92684C"; @@ -888,6 +1030,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#AC8074"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#CFB48D"; colorSlave.areolaColor = "#AC8074"; @@ -1005,6 +1149,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#A7624F"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#CC8D53"; colorSlave.areolaColor = "#A7624F"; @@ -1122,6 +1268,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#AC8074"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#CFB48D"; colorSlave.areolaColor = "#AC8074"; @@ -1239,6 +1387,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#BF7577"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#DCA972"; colorSlave.areolaColor = "#BF7577"; @@ -1356,6 +1506,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#A7624F"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#CC8D53"; colorSlave.areolaColor = "#A7624F"; @@ -1473,6 +1625,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#976051"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#BA855E"; colorSlave.areolaColor = "#976051"; @@ -1590,6 +1744,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#C36E45"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#C17848"; colorSlave.areolaColor = "#C36E45"; @@ -1707,6 +1863,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#976051"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#BA855E"; colorSlave.areolaColor = "#976051"; @@ -1824,6 +1982,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#92684C"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#B27554"; colorSlave.areolaColor = "#92684C"; @@ -1945,6 +2105,8 @@ globalThis.skinColorCatcher = function(artSlave) { colorSlave.areolaColor = "#92684C"; colorSlave.labiaColor = "#F977A3"; break; + case "sun tanned": + case "spray tanned": case "tan": colorSlave.skinColor = "#B27554"; colorSlave.areolaColor = "#92684C"; diff --git a/src/art/rendered/engine.js b/src/art/rendered/engine.js new file mode 100644 index 0000000000000000000000000000000000000000..92e53e14a3959b33cb0139f8021f12ae89db6be4 --- /dev/null +++ b/src/art/rendered/engine.js @@ -0,0 +1,662 @@ +'use strict'; + +App.Art.Engine = class { + constructor() { + this.vsSourceBg = `#version 300 es + precision highp float; + + in vec2 vertexPosition; + out vec2 vertexPos; + + void main() { + vertexPos = vertexPosition; + gl_Position = vec4(vertexPosition, 0.0, 1.0); + }`; + + this.fsSourceBg = `#version 300 es + precision highp float; + precision highp sampler2D; + + uniform sampler2D textSampler; + uniform vec4 backgroundColor; + + in vec2 vertexPos; + out vec4 outputColor; + + void main() { + vec2 textureCoord = vec2(vertexPos.s, -vertexPos.t) * 0.5 + 0.5; + vec3 c = backgroundColor.rgb * texture(textSampler, textureCoord.st).rgb; + outputColor = vec4(c.rgb * backgroundColor.a, backgroundColor.a); + }`; + + this.vsSource = `#version 300 es + precision highp float; + + uniform mat4 matView; + uniform mat4 matProj; + uniform mat4 matRot; + uniform mat4 matTrans; + uniform mat4 matScale; + + in vec3 vertexNormal; + in vec3 vertexPosition; + in vec2 textureCoordinate; + in vec3 vertexTangent; + + in vec3 vertexNormalMorph; + in vec3 vertexPositionMorph; + + out vec2 textureCoord; + out vec3 normal; + out mat3 TBN; + + void main() { + gl_Position = matProj * matView * matTrans * matScale * matRot * vec4(vertexPosition + vertexPositionMorph, 1.0) + 0.01; + normal = normalize((matRot * vec4(vertexNormal + vertexNormalMorph, 1.0)).xyz); + + vec3 T = normalize(vec3(matTrans * matRot * vec4(vertexTangent, 0.0))); + vec3 N = normalize(vec3(matTrans * matRot * vec4(vertexNormal + vertexNormalMorph, 0.0))); + T = normalize(T - dot(T, N) * N); + vec3 B = cross(N, T); + TBN = mat3(T, B, N); + + textureCoord = textureCoordinate; + }`; + + this.fsSource = `#version 300 es + precision highp float; + precision highp sampler2D; + + uniform float lightInt; + uniform float lightAmb; + uniform vec3 lightColor; + uniform float whiteM; + uniform float gammaY; + + uniform vec3 Ka; + uniform vec3 Kd; + uniform vec3 Ks; + uniform float d; + uniform float Ns; + + uniform float sNormals; + uniform float sAmbient; + uniform float sDiffuse; + uniform float sSpecular; + uniform float sAlpha; + uniform float sGamma; + uniform float sReinhard; + uniform float sNormal; + + uniform vec3 lightVect; + uniform vec3 lookDir; + + uniform sampler2D textSampler[6]; + + in vec2 textureCoord; + in vec3 normal; + in mat3 TBN; + + out vec4 outputColor; + + void main() { + vec3 new_normal = normal; + vec3 map_Ka = vec3(0.0,0.0,0.0); + vec3 map_Kd = vec3(0.0,0.0,0.0); + vec3 map_Ks = vec3(0.0,0.0,0.0); + float map_Ns = 0.0; + float map_d = 1.0; + float specular = 1.0; + + if (sNormal == 1.0) { + vec3 map_Kn = texture(textSampler[5], textureCoord.st).rgb *2.0-1.0; + if (map_Kn != vec3(-1.0,-1.0,-1.0)) + new_normal = normalize(TBN * map_Kn); + } + + float angle = max(dot(-lightVect, new_normal),0.0); + vec3 reflection = reflect(-lightVect, new_normal); + + if (sAmbient == 1.0) + map_Ka = Ka * texture(textSampler[0], textureCoord.st).rgb; + + if (sDiffuse == 1.0) + map_Kd = Kd * texture(textSampler[1], textureCoord.st).rgb; + + if (sSpecular == 1.0) { + map_Ks = Ks * texture(textSampler[2], textureCoord.st).rgb; + map_Ns = Ns * texture(textSampler[3], textureCoord.st).r; + specular = pow(max(dot(reflection, lookDir),0.0), (0.0001+map_Ns)); + } + + if (sAlpha == 1.0) + map_d = d * texture(textSampler[4], textureCoord.st).r; + + vec3 Ld = map_Kd * lightInt * angle * lightColor; + vec3 Ls = map_Ks * specular * lightColor; + vec3 La = map_Ka * lightAmb * lightColor; + + vec3 vLighting = Ld + Ls + La; + vec3 c = map_Kd * vLighting; + + if (sReinhard == 1.0) { + 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 (sGamma == 1.0) { + c.r = pow(c.r, (1.0/gammaY)); + c.g = pow(c.g, (1.0/gammaY)); + c.b = pow(c.b, (1.0/gammaY)); + } + + if (sNormals == 1.0) { + c = new_normal; + } + + outputColor = vec4(c*map_d, map_d); + }`; + } + + initBuffers = function() { + this.backgroundPositionBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.backgroundPositionBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, 1, 1, -1, 1]), this.gl.STATIC_DRAW); + + this.backgroundIndexBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.backgroundIndexBuffer); + this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), this.gl.STATIC_DRAW); + + this.verticesPositionBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesPositionBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.base64ToFloat(this.sceneData.model.verts)), this.gl.STATIC_DRAW); + + this.verticesNormalBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesNormalBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.base64ToFloat(this.sceneData.model.vertsn)), this.gl.STATIC_DRAW); + + this.verticesTextureCoordBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesTextureCoordBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.base64ToFloat(this.sceneData.model.texts)), this.gl.STATIC_DRAW); + + this.verticesTangentBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesTangentBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.base64ToFloat(this.sceneData.model.tans)), this.gl.STATIC_DRAW); + + this.vertexPositionMorphs = []; + this.vertexNormalMorphs = []; + for(let i=0; i < this.sceneData.model.mverts.length; i++) { + this.vertexPositionMorphs[i] = new Float32Array(this.base64ToFloat(this.sceneData.model.mverts[i])); + this.vertexNormalMorphs[i] = new Float32Array(this.base64ToFloat(this.sceneData.model.mvertsn[i])); + } + + this.verticesMorphBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesMorphBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.sceneData.model.verts.length), this.gl.STATIC_DRAW); + + this.verticesNormalMorphBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesNormalMorphBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.sceneData.model.vertsn.length), this.gl.STATIC_DRAW); + + this.verticesIndexBuffer = []; + this.indexSizes = []; + for (let i=0, count=0; i < this.sceneData.model.figures.length; i++) { + for (let j=0; j < this.sceneData.model.figures[i].surfaces.length; j++, count++) { + this.verticesIndexBuffer[count] = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer[count]); + let intArray = this.base64ToInt(this.sceneData.model.figures[i].surfaces[j].vertsi); + this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(intArray), this.gl.STATIC_DRAW); + this.indexSizes[count] = intArray.length; + } + } + } + + loadTexture = function(gl, url) { + // return dummy texture + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255])); + + // stream real textures + let image = new Image(); + image.onload = function() { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + }; + image.src = url; + + return texture; + } + + initTextures = function() { + // load model textures + this.modelTextures = []; + for (let i=0; i < this.sceneData.textures.length; i++) { + this.modelTextures[i] = this.loadTexture(this.gl, this.sceneData.textures[i]); + } + } + + initShaders = function() { + // compile shaders + let vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER); + this.gl.shaderSource(vertexShader, this.vsSource); + this.gl.compileShader(vertexShader); + + let fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER); + this.gl.shaderSource(fragmentShader, this.fsSource); + this.gl.compileShader(fragmentShader); + + this.shaderProgram = this.gl.createProgram(); + this.gl.attachShader(this.shaderProgram, vertexShader); + this.gl.attachShader(this.shaderProgram, fragmentShader); + this.gl.linkProgram(this.shaderProgram); + //console.log(this.gl.getProgramInfoLog(this.shaderProgram)); + + let vertexShaderBg = this.gl.createShader(this.gl.VERTEX_SHADER); + this.gl.shaderSource(vertexShaderBg, this.vsSourceBg); + this.gl.compileShader(vertexShaderBg); + + let fragmentShaderBg = this.gl.createShader(this.gl.FRAGMENT_SHADER); + this.gl.shaderSource(fragmentShaderBg, this.fsSourceBg); + this.gl.compileShader(fragmentShaderBg); + + this.shaderProgramBg = this.gl.createProgram(); + this.gl.attachShader(this.shaderProgramBg, vertexShaderBg); + this.gl.attachShader(this.shaderProgramBg, fragmentShaderBg); + this.gl.linkProgram(this.shaderProgramBg); + + this.gl.useProgram(this.shaderProgram); + + // enable vertex attributes + this.backgroundPositionAttribute = this.gl.getAttribLocation(this.shaderProgramBg, "vertexPosition"); + this.gl.enableVertexAttribArray(this.backgroundPositionAttribute); + + this.vertexPositionAttribute = this.gl.getAttribLocation(this.shaderProgram, "vertexPosition"); + this.gl.enableVertexAttribArray(this.vertexPositionAttribute); + + this.textureCoordAttribute = this.gl.getAttribLocation(this.shaderProgram, "textureCoordinate"); + this.gl.enableVertexAttribArray(this.textureCoordAttribute); + + this.vertexNormalAttribute = this.gl.getAttribLocation(this.shaderProgram, "vertexNormal"); + this.gl.enableVertexAttribArray(this.vertexNormalAttribute); + + this.vertexTangentAttribute = this.gl.getAttribLocation(this.shaderProgram, "vertexTangent"); + this.gl.enableVertexAttribArray(this.vertexTangentAttribute); + + this.vertexNormalMorphAttribute = this.gl.getAttribLocation(this.shaderProgram, "vertexNormalMorph"); + this.gl.enableVertexAttribArray(this.vertexNormalMorphAttribute); + + this.vertexPositionMorphAttribute = this.gl.getAttribLocation(this.shaderProgram, "vertexPositionMorph"); + this.gl.enableVertexAttribArray(this.vertexPositionMorphAttribute); + } + + bind = function(sceneData) { + this.sceneData = sceneData; + + this.offscreenCanvas = document.createElement("canvas"); + this.gl = this.offscreenCanvas.getContext("webgl2", {alpha:true, premultipliedAlpha: true}); + + this.gl.enable(this.gl.CULL_FACE); + this.gl.cullFace(this.gl.BACK); + this.gl.enable(this.gl.DEPTH_TEST); + this.gl.depthFunc(this.gl.LEQUAL); + this.gl.enable(this.gl.BLEND); + this.gl.blendEquation( this.gl.FUNC_ADD ); + this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA); + + this.initBuffers(); + this.initTextures(); + this.initShaders(); + } + + render = function(sceneParams, canvas) { + // set render resolution + this.offscreenCanvas.width = sceneParams.settings.rwidth; + this.offscreenCanvas.height = sceneParams.settings.rheight; + this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight); + + // draw background + this.gl.clearColor(0, 0, 0, 0); + this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); + this.gl.useProgram(this.shaderProgramBg); + if (sceneParams.background.visible) + this.drawBackground(sceneParams); + + // draw model + this.gl.clear(this.gl.DEPTH_BUFFER_BIT); + this.gl.useProgram(this.shaderProgram); + this.drawModel(sceneParams); + + // clone from offscreen to real canvas + let ctx = canvas.getContext('2d', {alpha:true}); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(this.gl.canvas, 0, 0, canvas.width, canvas.height); + } + + drawBackground = function(sceneParams) { + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgramBg, "textSampler"), 0); + this.gl.uniform4fv(this.gl.getUniformLocation(this.shaderProgramBg, "backgroundColor"), sceneParams.background.color); + + this.gl.activeTexture(this.gl.TEXTURE0); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[sceneParams.background.filename]); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.backgroundPositionBuffer); + this.gl.vertexAttribPointer(this.backgroundPositionAttribute, 2, this.gl.FLOAT, false, 0, 0); + + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.backgroundIndexBuffer); + this.gl.drawElements(this.gl.TRIANGLES, 6, this.gl.UNSIGNED_SHORT, 0); + } + + drawModel = function(sceneParams) { + // create camera + let camRotX = this.degreeToRad(-sceneParams.camera.xr); + let camRotY = this.degreeToRad(-sceneParams.camera.yr); + let camRotZ = this.degreeToRad(sceneParams.camera.zr); + + let up = [Math.sin(camRotZ), Math.cos(camRotZ), Math.sin(camRotZ)]; + let camera = [sceneParams.camera.x, sceneParams.camera.y, sceneParams.camera.z]; + + let matCameraRot = this.matrixMulMatrix(this.matrixMakeRotationX(camRotX), this.matrixMakeRotationY(camRotY)); + let lookDir = this.matrixMulVector(matCameraRot, [0, 0, 1]); + let target = this.vectorAdd(lookDir, camera); + let matCamera = this.matrixPointAt(camera, target, up); + + // create transforms + this.applyMorphs(sceneParams); + let matProj = this.matrixMakeProjection(sceneParams.camera.fov, sceneParams.settings.rheight/sceneParams.settings.rwidth, sceneParams.camera.fnear, sceneParams.camera.ffar); + let matView = this.matrixInverse(matCamera); + let matRot = this.matrixMakeRotation(this.degreeToRad(sceneParams.transform.xr), this.degreeToRad(sceneParams.transform.yr), this.degreeToRad(sceneParams.transform.zr)); + let matTrans = this.matrixMakeTranslation(sceneParams.transform.x, sceneParams.transform.y, sceneParams.transform.z); + let matScale = this.matrixMakeScaling( sceneParams.transform.scale); + + let lightVect = this.polarToCart(this.degreeToRad(sceneParams.light.yr), this.degreeToRad(sceneParams.light.xr)); + let lightAmb = sceneParams.light.ambient; + let lightInt = sceneParams.light.intensity; + let lightColor = sceneParams.light.color; + let whiteM = sceneParams.settings.whiteM; + let gammaY = sceneParams.settings.gammaY; + + // set uniforms + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sNormals"), sceneParams.settings.normals); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sAmbient"), sceneParams.settings.ambient); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sDiffuse"), sceneParams.settings.diffuse); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sSpecular"), sceneParams.settings.specular); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sNormal"), sceneParams.settings.normal); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sAlpha"), sceneParams.settings.alpha); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sReinhard"), sceneParams.settings.reinhard); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "sGamma"), sceneParams.settings.gamma); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "whiteM"), whiteM); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "gammaY"), gammaY); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "lightAmb"), lightAmb); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "lightInt"), lightInt); + this.gl.uniform3fv(this.gl.getUniformLocation(this.shaderProgram, "lightColor"), lightColor); + this.gl.uniform3fv(this.gl.getUniformLocation(this.shaderProgram, "lightVect"), lightVect); + this.gl.uniform3fv(this.gl.getUniformLocation(this.shaderProgram, "lookDir"), lookDir); + + for (let i = 0; i < sceneParams.model.morphs.length; i++) + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, sceneParams.model.morphs[i].morphId), sceneParams.model.morphs[i].value); + + this.gl.uniformMatrix4fv(this.gl.getUniformLocation(this.shaderProgram, "matTrans"), false, new Float32Array(this.matrixFlatten(matTrans))); + this.gl.uniformMatrix4fv(this.gl.getUniformLocation(this.shaderProgram, "matScale"), false, new Float32Array(this.matrixFlatten(matScale))); + this.gl.uniformMatrix4fv(this.gl.getUniformLocation(this.shaderProgram, "matRot"), false, new Float32Array(this.matrixFlatten(matRot))); + this.gl.uniformMatrix4fv(this.gl.getUniformLocation(this.shaderProgram, "matProj"), false, new Float32Array(this.matrixFlatten(matProj))); + this.gl.uniformMatrix4fv(this.gl.getUniformLocation(this.shaderProgram, "matView"), false, new Float32Array(this.matrixFlatten(matView))); + + // bind vertex buffers + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesPositionBuffer); + this.gl.vertexAttribPointer(this.vertexPositionAttribute, 3, this.gl.FLOAT, false, 0, 0); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesTextureCoordBuffer); + this.gl.vertexAttribPointer(this.textureCoordAttribute, 2, this.gl.FLOAT, false, 0, 0); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesNormalBuffer); + this.gl.vertexAttribPointer(this.vertexNormalAttribute, 3, this.gl.FLOAT, false, 0, 0); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesTangentBuffer); + this.gl.vertexAttribPointer(this.vertexTangentAttribute, 3, this.gl.FLOAT, false, 0, 0); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesMorphBuffer); + this.gl.vertexAttribPointer(this.vertexPositionMorphAttribute, 3, this.gl.FLOAT, false, 0, 0); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesNormalMorphBuffer); + this.gl.vertexAttribPointer(this.vertexNormalMorphAttribute, 3, this.gl.FLOAT, false, 0, 0); + + // bind materials per surface + for (let i=0, count=0; i < this.sceneData.model.figures.length; i++) { + for (let j=0; j < this.sceneData.model.figures[i].surfaces.length; j++, count++) { + if(!sceneParams.model.figures[i].visible) + continue; + + let visible = sceneParams.model.figures[i].surfaces[j].visible; + let matId = sceneParams.model.figures[i].surfaces[j].matId; + let matIdx = sceneParams.materials.map(e => e.matId).indexOf(matId); + if (matIdx == -1) + continue; + let mat = sceneParams.materials[matIdx]; + + if (mat.d > 0 && visible) + { + this.gl.activeTexture(this.gl.TEXTURE0); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[parseInt(mat.map_Ka)]); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgram, "textSampler[0]"), 0); + + this.gl.activeTexture(this.gl.TEXTURE1); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[parseInt(mat.map_Kd)]); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgram, "textSampler[1]"), 1); + + this.gl.activeTexture(this.gl.TEXTURE2); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[parseInt(mat.map_Ks)]); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgram, "textSampler[2]"), 2); + + this.gl.activeTexture(this.gl.TEXTURE3); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[parseInt(mat.map_Ns)]); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgram, "textSampler[3]"), 3); + + this.gl.activeTexture(this.gl.TEXTURE4); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[parseInt(mat.map_D)]); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgram, "textSampler[4]"), 4); + + this.gl.activeTexture(this.gl.TEXTURE5); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.modelTextures[parseInt(mat.map_Kn)]); + this.gl.uniform1i(this.gl.getUniformLocation(this.shaderProgram, "textSampler[5]"), 5); + + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "d"), mat.d); + this.gl.uniform3fv(this.gl.getUniformLocation(this.shaderProgram, "Ka"), mat.Ka); + this.gl.uniform3fv(this.gl.getUniformLocation(this.shaderProgram, "Kd"), mat.Kd); + this.gl.uniform3fv(this.gl.getUniformLocation(this.shaderProgram, "Ks"), mat.Ks); + this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, "Ns"), mat.Ns); + + // draw materials + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer[count]); + this.gl.drawElements(this.gl.TRIANGLES, this.indexSizes[count], this.gl.UNSIGNED_INT, 0); + } + } + } + } + + applyMorphs = function(sceneParams) { + if(this.oldMorphValues != JSON.stringify(sceneParams.model.morphs)) { + let vertexPositionMorph = new Float32Array(this.sceneData.model.verts.length); + let vertexNormalMorph = new Float32Array(this.sceneData.model.vertsn.length); + + for(let i=0; i < this.vertexPositionMorphs.length; i++) { + let morphValue = sceneParams.model.morphs[i].value; + + if (morphValue != 0) { + let vp = this.vertexPositionMorphs[i]; + let vn = this.vertexNormalMorphs[i]; + + if (morphValue == 1) + for(let j=0; j < this.vertexPositionMorphs[i].length; j++) { + vertexPositionMorph[j] += vp[j]; + vertexNormalMorph[j] += vn[j]; + } + else + for(let j=0; j < this.vertexPositionMorphs[i].length; j++) { + vertexPositionMorph[j] += vp[j] * morphValue; + vertexNormalMorph[j] += vn[j] * morphValue; + } + } + } + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesMorphBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, vertexPositionMorph, this.gl.STATIC_DRAW); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.verticesNormalMorphBuffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, vertexNormalMorph, this.gl.STATIC_DRAW); + + this.oldMorphValues = JSON.stringify(sceneParams.model.morphs); + } + } + + base64ToFloat = function(array) { + let b = window.atob(array), + fLen = b.length / Float32Array.BYTES_PER_ELEMENT, + dView = new DataView(new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT)), + fAry = new Float32Array(fLen), + p = 0; + + for(let j=0; j < fLen; j++){ + p = j * 4; + dView.setUint8(0, b.charCodeAt(p)); + dView.setUint8(1, b.charCodeAt(p+1)); + dView.setUint8(2, b.charCodeAt(p+2)); + dView.setUint8(3, b.charCodeAt(p+3)); + fAry[j] = dView.getFloat32(0, true); + } + return fAry; + } + + base64ToInt = function(array) { + let b = window.atob(array), + fLen = b.length / Int32Array.BYTES_PER_ELEMENT, + dView = new DataView(new ArrayBuffer(Int32Array.BYTES_PER_ELEMENT)), + fAry = new Int32Array(fLen), + p = 0; + + for(let j=0; j < fLen; j++){ + p = j * 4; + dView.setUint8(0, b.charCodeAt(p)); + dView.setUint8(1, b.charCodeAt(p+1)); + dView.setUint8(2, b.charCodeAt(p+2)); + dView.setUint8(3, b.charCodeAt(p+3)); + fAry[j] = dView.getInt32(0, true); + } + return fAry; + } + + base64ToByte = function(array) { + let b = window.atob(array), + fLen = b.length / Uint8Array.BYTES_PER_ELEMENT, + dView = new DataView(new ArrayBuffer(Uint8Array.BYTES_PER_ELEMENT)), + fAry = new Uint8Array(fLen); + + for(let j=0; j < fLen; j++){ + dView.setUint8(0, b.charCodeAt(j)); + fAry[j] = dView.getUint8(0); + } + return fAry; + } + + degreeToRad = (d) => d * (Math.PI / 180) + + polarToCart = (y, p) => ([Math.sin(p) * Math.cos(y), Math.sin(p) * Math.sin(y), Math.cos(p)]) + + vectorAdd = (v1, v2) => ([v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]]) + + vectorSub = (v1, v2) => ([v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]]) + + vectorMul = (v, k) => ([v[0] * k, v[1] * k, v[2] * k]) + + vectorDotProduct = (v1, v2) => ([v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]]) + + vectorCrossProduct = (v1, v2) => ([v1[1] * v2[2] - v1[2] * v2[1], v1[2] * v2[0] - v1[0] * v2[2], v1[0] * v2[1] - v1[1] * v2[0]]) + + vectorNormalize = function(v) { + let l = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + return [v[0]/l, v[1]/l, v[2]/l] + } + + matrixMakeProjection = (fov, aspect, near, far) => ([[aspect * (1/Math.tan(fov*0.5/180*Math.PI)), 0, 0, 0], + [0, (1/Math.tan(fov*0.5/180*Math.PI)), 0, 0], + [0, 0, far/(far-near), 1], + [0, 0, (-far*near)/(far-near), 0]]) + + matrixPointAt = function(pos, target, up){ + let newForward = this.vectorNormalize(this.vectorSub(target, pos)); + let a = this.vectorMul(newForward, this.vectorDotProduct(up, newForward)); + let newUp = this.vectorNormalize(this.vectorSub(up, a)); + let newRight = this.vectorCrossProduct(newUp, newForward); + + return [[newRight[0], newRight[1], newRight[2], 0], + [newUp[0], newUp[1], newUp[2], 0], + [newForward[0], newForward[1], newForward[2], 0], + [pos[0], pos[1], pos[2], 1]] + } + + matrixMakeRotation = function(xr, yr, zr){ + let cosA = Math.cos(xr); + let cosB = Math.cos(yr); + let cosC = Math.cos(zr); + let sinA = Math.sin(xr); + let sinB = Math.sin(yr); + let sinC = Math.sin(zr); + + return([[cosB*cosC, -cosB*sinC, sinB, 0], + [sinA*sinB*cosC+cosA*sinC, -sinA*sinB*sinC+cosA*cosC, -sinA*cosB, 0], + [-cosA*sinB*cosC+sinA*sinC, cosA*sinB*sinC+sinA*cosC, cosA*cosB, 0], + [0, 0, 0, 1]]) + } + + matrixMakeRotationX = (r) => ([[1, 0, 0], + [0, Math.cos(r), Math.sin(r)], + [0, -Math.sin(r), Math.cos(r)]]) + + matrixMakeRotationY = (r) => ([[Math.cos(r), 0, Math.sin(r)], + [0, 1, 0], + [-Math.sin(r), 0, Math.cos(r)]]) + + matrixMakeRotationZ = (r) => ([[Math.cos(r), Math.sin(r), 0], + [-Math.sin(r), Math.cos(r), 0], + [0, 0, 1]]) + + matrixMakeTranslation = (x, y, z) => ([[1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [x, y, z, 1]]) + + matrixMakeScaling = (s) => ([[s, 0, 0, 0], + [0, s, 0, 0], + [0, 0, s, 0], + [0, 0, 0, 1]]) + + matrixMulMatrix = (m1, m2) => ([[m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0] + m1[0][2] * m2[2][0], + m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1] + m1[0][2] * m2[2][1], + m1[0][0] * m2[0][2] + m1[0][1] * m2[1][2] + m1[0][2] * m2[2][2]], + [m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0] + m1[1][2] * m2[2][0], + m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1] + m1[1][2] * m2[2][1], + m1[1][0] * m2[0][2] + m1[1][1] * m2[1][2] + m1[1][2] * m2[2][2]], + [m1[2][0] * m2[0][0] + m1[2][1] * m2[1][0] + m1[2][2] * m2[2][0], + m1[2][0] * m2[0][1] + m1[2][1] * m2[1][1] + m1[2][2] * m2[2][1], + m1[2][0] * m2[0][2] + m1[2][1] * m2[1][2] + m1[2][2] * m2[2][2]]]) + + matrixMulVector = (m, v) => ([v[0] * m[0][0] + v[1] * m[1][0] + v[2] * m[2][0], + v[0] * m[0][1] + v[1] * m[1][1] + v[2] * m[2][1], + v[0] * m[0][2] + v[1] * m[1][2] + v[2] * m[2][2]]) + + matrixInverse = (m) => ([[m[0][0], m[1][0], m[2][0], 0], + [m[0][1], m[1][1], m[2][1], 0], + [m[0][2], m[1][2], m[2][2], 0], + [-(m[3][0]*m[0][0]+m[3][1]*m[0][1]+m[3][2]*m[0][2]), -(m[3][0]*m[1][0]+m[3][1]*m[1][1]+m[3][2]*m[1][2]), -(m[3][0]*m[2][0]+m[3][1]*m[2][1]+m[3][2]*m[2][2]), 1]]) + + matrixFlatten = (m) => ([m[0][0],m[0][1],m[0][2],m[0][3], + m[1][0],m[1][1],m[1][2],m[1][3], + m[2][0],m[2][1],m[2][2],m[2][3], + m[3][0],m[3][1],m[3][2],m[3][3]]) +} diff --git a/src/art/rendered/renderedArt.js b/src/art/rendered/renderedArt.js new file mode 100644 index 0000000000000000000000000000000000000000..830dd8db12d4dd355f15dd09ef1ac8352e3a04ac --- /dev/null +++ b/src/art/rendered/renderedArt.js @@ -0,0 +1,510 @@ +App.Art.getMaterialById = function(sceneParams, id) { + for (let i =0; i < sceneParams.materials.length; i++) + if(sceneParams.materials[i].matId == id) + return sceneParams.materials[i] + + return null; +}; + +App.Art.getMorphById = function(sceneParams, id) { + for (let i =0; i < sceneParams.model.morphs.length; i++) + if(sceneParams.model.morphs[i].morphId == id) + return sceneParams.model.morphs[i] + + return null; +}; + +App.Art.getSurfaceById = function(sceneParams, id) { + for (let i=0, count=0; i < sceneParams.model.figures.length; i++) + for (let j=0; j < sceneParams.model.figures[i].surfaces.length; j++, count++) + if(sceneParams.model.figures[i].surfaces[j].surfaceId == id) + return sceneParams.model.figures[i].surfaces[j] + return null; +}; + +App.Art.resetMorphs = function(slave) { + for (let i =0; i < slave.sceneParams.model.morphs.length; i++) + slave.sceneParams.model.morphs[i].value = App.Art.defaultSceneParams.model.morphs[i].value; +}; + +App.Art.applySurfaces = function(slave) { + let surfaces = []; + + if (slave.dick != 0 || (!(slave.scrotum <= 0 || slave.balls <= 0))) { + surfaces.push(["Futalicious_Genitalia_G8F_Shaft_Futalicious_Shell", "visible", true]); + surfaces.push(["Futalicious_Genitalia_G8F_Glans_Futalicious_Shell", "visible", true]); + surfaces.push(["Futalicious_Genitalia_G8F_Testicles_Futalicious_Shell", "visible", true]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Front_Futalicious_Shell", "visible", true]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Middle_Futalicious_Shell", "visible", true]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Back_Futalicious_Shell", "visible", true]); + surfaces.push(["Futalicious_Genitalia_G8F_Rectum_Futalicious_Shell", "visible", false]); + surfaces.push(["Torso_Front", "visible", true]); + surfaces.push(["Torso_Middle", "visible", true]); + surfaces.push(["Torso_Back", "visible", true]); + + surfaces.push(["Genitalia", "visible", true]); + surfaces.push(["Anus", "visible", true]); + surfaces.push(["new_gens_V8_1840_Genitalia", "visible", true]); + surfaces.push(["new_gens_V8_1840_Anus", "visible", true]); + } + else { + surfaces.push(["Futalicious_Genitalia_G8F_Shaft_Futalicious_Shell", "visible", false]); + surfaces.push(["Futalicious_Genitalia_G8F_Glans_Futalicious_Shell", "visible", false]); + surfaces.push(["Futalicious_Genitalia_G8F_Testicles_Futalicious_Shell", "visible", false]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Front_Futalicious_Shell", "visible", false]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Middle_Futalicious_Shell", "visible", false]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Back_Futalicious_Shell", "visible", false]); + surfaces.push(["Futalicious_Genitalia_G8F_Rectum_Futalicious_Shell", "visible", false]); + surfaces.push(["Torso_Front", "visible", false]); + surfaces.push(["Torso_Middle", "visible", false]); + surfaces.push(["Torso_Back", "visible", false]); + + surfaces.push(["Genitalia", "visible", true]); + surfaces.push(["Anus", "visible", true]); + surfaces.push(["new_gens_V8_1840_Genitalia", "visible", true]); + surfaces.push(["new_gens_V8_1840_Anus", "visible", true]); + } + + surfaces.push(["Arms", "visible", hasBothArms(slave)]); + surfaces.push(["Fingernails", "visible", hasBothArms(slave)]); + surfaces.push(["Legs", "visible", hasBothLegs(slave)]); + surfaces.push(["Toenails", "visible", hasBothLegs(slave)]); + + let cockSkin; + let skin; + + switch (slave.skin) { + case "pure white": + case "ivory": + case "white": + cockSkin = "White"; + skin = "Ceridwen"; + break; + case "extremely pale": + case "very pale": + cockSkin = "White"; + skin = "Celinette"; + break; + case "pale": + case "extremely fair": + cockSkin = "White"; + skin = "Kimmy"; + break; + case "very fair": + case "fair": + cockSkin = "Light"; + skin = "Saffron"; + break; + case "light": + case "light olive": + cockSkin = "Light"; + skin = "FemaleBase"; + break; + case "sun tanned": + case "spray tanned": + case "tan": + cockSkin = "Light"; + skin = "Reagan"; + break; + case "olive": + cockSkin = "Mid"; + skin = "Kathy"; + break; + case "bronze": + cockSkin = "Mid"; + skin = "Mylou"; + break; + case "dark olive": + cockSkin = "Mid"; + skin = "Adaline"; + break; + case "dark": + cockSkin = "Mid"; + skin = "Daphne"; + break; + case "light beige": + cockSkin = "Mid"; + skin = "Minami"; + break; + case "beige": + cockSkin = "Mid"; + skin = "Tara"; + break; + case "dark beige": + case "light brown": + cockSkin = "Dark"; + skin = "Topmodel"; + break; + case "brown": + case "dark brown": + cockSkin = "Dark"; + skin = "Angelica"; + break; + case "black": + case "ebony": + case "pure black": + cockSkin = "Dark"; + skin = "DarkSkin"; + break; + } + + surfaces.push(["Futalicious_Genitalia_G8F_Glans_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Futalicious_Genitalia_G8F_Shaft_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Futalicious_Genitalia_G8F_Testicles_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Front_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Middle_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Futalicious_Genitalia_G8F_Torso_Back_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Futalicious_Genitalia_G8F_Rectum_Futalicious_Shell", "matId", cockSkin + "Futalicious_Genitalia_G8F_Glans_Futalicious_Shell"]); + surfaces.push(["Torso_Front", "matId", skin + "Torso"]); + surfaces.push(["Torso_Middle", "matId", skin + "Torso"]); + surfaces.push(["Torso_Back", "matId", skin + "Torso"]); + + surfaces.push(["Torso", "matId", skin + "Torso"]); + surfaces.push(["Face", "matId", skin + "Face"]); + surfaces.push(["Lips", "matId", skin + "Lips"]); + surfaces.push(["Ears", "matId", skin + "Ears"]); + surfaces.push(["Legs", "matId", skin + "Legs"]); + surfaces.push(["Arms", "matId", skin + "Arms"]); + surfaces.push(["EyeSocket", "matId", skin + "Face"]); + surfaces.push(["Toenails", "matId", skin + "Toenails"]); + surfaces.push(["Fingernails", "matId", skin + "Fingernails"]); + surfaces.push(["Genitalia", "matId", skin + "Genitalia"]); + surfaces.push(["Anus", "matId", skin + "Anus"]); + + let sceneParams = slave.sceneParams; + for (let i=0, count=0; i < sceneParams.model.figures.length; i++) + for (let j=0; j < sceneParams.model.figures[i].surfaces.length; j++, count++) + for (let h =0; h < surfaces.length; h++) + if (sceneParams.model.figures[i].surfaces[j].surfaceId == surfaces[h][0]) + sceneParams.model.figures[i].surfaces[j][surfaces[h][1]] = surfaces[h][2]; +}; + +App.Art.applyMaterials = function(slave) { + let materials = []; + + function hexToRgb(hex) { + hex = hex.replace('#',''); + let r = parseInt(hex.substring(0,2), 16); + let g = parseInt(hex.substring(2,4), 16); + let b = parseInt(hex.substring(4,6), 16); + return [r/255,g/255,b/255]; + } + + let hairColor = hexToRgb(extractColor(slave.hColor)); + let lipsColor = hexToRgb(skinColorCatcher(slave).lipsColor); + + let makeupColor; + let makeupOpacity; + + switch (slave.makeup) { + case 1: + // Nice + makeupColor = "#ff69b4"; + makeupOpacity = 0.5; + break; + case 2: + // Gorgeous + makeupColor = "#8b008b"; + makeupOpacity = 0.7; + break; + case 3: + // Hair coordinated + makeupColor = extractColor(slave.hColor); + makeupOpacity = 0.3; + break; + case 4: + // Slutty + makeupColor = "#ff0000"; + makeupOpacity = 1; + break; + case 5: + // Neon + makeupColor = "#DC143C"; + makeupOpacity = 1; + break; + case 6: + // Neon hair coordinated + makeupColor = extractColor(slave.hColor); + makeupOpacity = 1; + break; + case 7: + // Metallic + makeupColor = "#b22222"; + makeupOpacity = 0.7; + break; + case 8: + // Metallic hair coordinated + makeupColor = extractColor(slave.hColor); + makeupOpacity = 0.7; + break; + default: + makeupColor = "#ffffff" + makeupOpacity = 0; + break; + } + makeupColor = 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) { + case 2: + // color-coordinated with hair + nailColor = extractColor(slave.hColor); + break; + case 4: + // bright and glittery + nailColor = "#ff0000"; + break; + case 6: + // neon + nailColor = "#DC143C"; + break; + case 7: + // color-coordinated neon + nailColor = extractColor(slave.hColor); + break; + case 8: + // metallic + nailColor = "#b22222"; + break; + case 9: + // color-coordinated metallic + nailColor = extractColor(slave.hColor); + break; + default: + nailColor = "#ffffff" + break; + } + + nailColor = hexToRgb(nailColor); + + + materials.push(["HeadThin", "Kd", hairColor]); + materials.push(["Head", "Kd", hairColor]); + materials.push(["TuckedThin", "Kd", hairColor]); + materials.push(["TuckedR", "Kd", hairColor]); + materials.push(["BangsThin", "Kd", hairColor]); + materials.push(["Bangs", "Kd", hairColor]); + materials.push(["Scalp", "Kd", hairColor]); + + let irisColor; + let scleraColor; + + if (hasAnyEyes(slave)) { + irisColor = hexToRgb(extractColor(hasLeftEye(slave) ? extractColor(slave.eye.left.iris) : extractColor(slave.eye.right.iris))); + scleraColor = hexToRgb(extractColor(hasLeftEye(slave) ? extractColor(slave.eye.left.sclera) : extractColor(slave.eye.left.sclera))); + } else { + irisColor = hexToRgb(extractColor("black")); + scleraColor = hexToRgb(extractColor("black")); + } + + materials.push(["Irises", "Kd", [irisColor[0] * 1.5, irisColor[1] * 1.5, irisColor[2] * 1.5]]); + materials.push(["Sclera", "Kd", [scleraColor[0] * 1.2, scleraColor[1] * 1.2, scleraColor[2] * 1.2]]); + + switch (slave.skin) { + case "pure white": + case "ivory": + case "white": + materials.push(["CeridwenFingernails", "Kd", nailColor]); + materials.push(["CeridwenLips", "Kd", lipsColor]); + materials.push(["WhiteFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [1.05, 1, 1]]); + break; + case "extremely pale": + case "very pale": + materials.push(["CelinetteFingernails", "Kd", nailColor]); + materials.push(["CelinetteLips", "Kd", lipsColor]); + materials.push(["WhiteFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [1.05, 1, 1]]); + break; + case "pale": + case "extremely fair": + materials.push(["KimmyFingernails", "Kd", nailColor]); + materials.push(["KimmyLips", "Kd", lipsColor]); + materials.push(["WhiteFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [1, 0.95, 0.91]]); + break; + case "very fair": + case "fair": + materials.push(["SaffronFingernails", "Kd", nailColor]); + materials.push(["SaffronLips", "Kd", lipsColor]); + materials.push(["LightFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [1.1, 1.1, 1.1]]); + break; + case "light": + case "light olive": + materials.push(["FemaleBaseFingernails", "Kd", nailColor]); + materials.push(["FemaleBaseLips", "Kd", lipsColor]); + materials.push(["LightFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [1.0, 1.0, 1.0]]); + break; + case "sun tanned": + case "spray tanned": + case "tan": + materials.push(["ReaganFingernails", "Kd", nailColor]); + materials.push(["ReaganLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.97, 0.95, 0.95]]); + break; + case "olive": + materials.push(["KathyFingernails", "Kd", nailColor]); + materials.push(["KathyLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.95, 0.92, 0.92]]); + break; + case "bronze": + materials.push(["MylouFingernails", "Kd", nailColor]); + materials.push(["MylouLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.91, 0.95, 0.98]]); + break; + case "dark olive": + materials.push(["AdalineFingernails", "Kd", nailColor]); + materials.push(["AdalineLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.90, 0.90, 0.90]]); + break; + case "dark": + materials.push(["DaphneFingernails", "Kd", nailColor]); + materials.push(["DaphneLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.88, 0.93, 0.96]]); + break; + case "light beige": + materials.push(["MinamiFingernails", "Kd", nailColor]); + materials.push(["MinamiLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.68, 0.74, 0.8]]); + break; + case "beige": + materials.push(["TaraFingernails", "Kd", nailColor]); + materials.push(["TaraLips", "Kd", lipsColor]); + materials.push(["MidFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.77, 0.77, 0.77]]); + break; + case "dark beige": + case "light brown": + materials.push(["TopmodelFingernails", "Kd", nailColor]); + materials.push(["TopmodelLips", "Kd", lipsColor]); + materials.push(["DarkFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [1.7, 1.75, 1.75]]); + break; + case "brown": + case "dark brown": + materials.push(["AngelicaFingernails", "Kd", nailColor]); + materials.push(["AngelicaLips", "Kd", lipsColor]); + materials.push(["DarkFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.85, 0.85, 0.85]]); + break; + case "black": + case "ebony": + case "pure black": + materials.push(["DarkSkinFingernails", "Kd", nailColor]); + materials.push(["DarkSkinLips", "Kd", lipsColor]); + materials.push(["DarkFutalicious_Genitalia_G8F_Glans_Futalicious_Shell", "Kd", [0.7, 0.7, 0.77]]); + break; + } + + let sceneParams = slave.sceneParams; + for (let i =0; i < sceneParams.materials.length; i++) + for (let j =0; j < materials.length; j++) + if (sceneParams.materials[i].matId == materials[j][0]) + sceneParams.materials[i][materials[j][1]] = materials[j][2]; +}; + +App.Art.applyMorphs = function(slave) { + let morphs = []; + + if (slave.devotion > 50) { + morphs.push(["posesHigh", 1]); + } else if (slave.trust >= -20) { + if (slave.devotion <= 20) { + morphs.push(["posesLow", 1]); + } else { + morphs.push(["posesMid", 1]); + } + } else { + morphs.push(["posesMid", 1]); + } + + switch (slave.faceShape) { + case "normal": + morphs.push(["faceShapeNormal", 1]); break; + case "masculine": + morphs.push(["faceShapeMasculine", 1]); break; + case "androgynous": + break; + case "cute": + morphs.push(["faceShapeCute", 1]); break; + case "sensual": + morphs.push(["faceShapeSensual", 1]); break; + case "exotic": + morphs.push(["faceShapeExotic", 1]); break; + } + + switch (slave.boobShape) { + case "normal": + break; + case "perky": + morphs.push(["boobShapePerky", 1]); break; + case "saggy": + morphs.push(["boobShapeSaggy", 1]); break; + case "torpedo-shaped": + morphs.push(["boobShapeTorpedo", 1]); break; + case "downward-facing": + morphs.push(["boobShapeDownward", 1]); break; + case "wide-set": + morphs.push(["boobShapeWide", 1]); break; + } + + switch (slave.nipples) { + case "huge": + morphs.push(["nipplesHuge", 1]); break; + case "tiny": + morphs.push(["nipplesTiny", 1]); break; + case "cute": + morphs.push(["nipplesCute", 1]); break; + case "puffy": + morphs.push(["nipplesPuffy", 1]); break; + case "inverted": + morphs.push(["nipplesInverted", 1]); break; + case "partially inverted": + morphs.push(["nipplesPartiallyInverted", 1]); break; + case "fuckable": + morphs.push(["nipplesFuckable", 1]); break; + } + + if (slave.foreskin !== 0) + morphs.push(["foreskin", 1]); + + if (slave.dick == 0 && !(slave.scrotum <= 0 || slave.balls <= 0)) { + morphs.push(["dickRemove", 1]); + } + else if (slave.dick != 0) { + morphs.push(["dick", (slave.dick / 8) -1]); + } + + if (slave.vagina == -1) + morphs.push(["vaginaRemove", 1]); + + if (slave.scrotum <= 0 || slave.balls <= 0) { + morphs.push(["ballsRemove", 1]); + } + else { + morphs.push(["balls", slave.scrotum / 6 -1]); + } + + morphs.push(["areolae", slave.areolae / 2 -1]); + + morphs.push(["shoulders", slave.shoulders / -2]); + morphs.push(["lips", slave.lips / 100 - 0.5]); + slave.sceneParams.transform.scale = slave.height/175 // height by object transform + morphs.push(["muscles", (slave.muscles+30) /50]); + morphs.push(["belly", slave.belly/10000]); + morphs.push(["hips", slave.hips/2]); + morphs.push(["butt", (slave.butt-1)/31]); + morphs.push(["boobs", (slave.boobs-1)/1000]); + morphs.push(["waist", slave.waist/50]); + morphs.push(["weight", slave.weight/100]); + + if (slave.visualAge < 20) + morphs.push(["physicalAgeYoung", -(slave.visualAge-20)/15]); + //else + // morphs.push(["physicalAgeOld", (slave.visualAge-20)/50]); + + App.Art.resetMorphs(slave); + + let sceneParams = slave.sceneParams; + for (let i =0; i < sceneParams.model.morphs.length; i++) + for (let j =0; j < morphs.length; j++) + if (sceneParams.model.morphs[i].morphId == morphs[j][0]) + sceneParams.model.morphs[i].value = morphs[j][1]; +}; \ No newline at end of file diff --git a/src/art/rendered/scene1.js b/src/art/rendered/scene1.js new file mode 100644 index 0000000000000000000000000000000000000000..e375ddbb16a3fcb667256092a9dcf6e3cf14430b Binary files /dev/null and b/src/art/rendered/scene1.js differ diff --git a/tsconfig.json b/tsconfig.json index 15bd704fb06e74387a05b159108e8e79ceef1c71..a3374d53e3b53f906afeb2cefa81fdf4fc8f5c6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ "exclude": [ "src/001-lib/Jquery/**", "src/001-lib/mousetrap/**", - "src/js/displayVariables.js" + "src/js/displayVariables.js", + "src/art/rendered/scene1.js", ] }