法线贴图
NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/
#include <iostream>
#include <cmath>
// 结构体表示顶点
struct Vertex {
float position[3];
float normal[3];
};
// 函数用于将法线贴图应用到正方体的顶点上
void ApplyNormalMap(Vertex* vertices, int numVertices, float* normalMap, int width, int height) {
for (int i = 0; i < numVertices; ++i) {
float u = vertices[i].position[0]; // 顶点的x坐标作为法线贴图的u坐标
float v = vertices[i].position[1]; // 顶点的y坐标作为法线贴图的v坐标
int x = std::round(u * (width - 1)); // 根据坐标计算法线贴图中的像素位置
int y = std::round(v * (height - 1));
// 从法线贴图中获取法线向量
float nx = normalMap[(y * width + x) * 3];
float ny = normalMap[(y * width + x) * 3 + 1];
float nz = normalMap[(y * width + x) * 3 + 2];
// 归一化法线向量
float length = std::sqrt(nx * nx + ny * ny + nz * nz);
nx /= length;
ny /= length;
nz /= length;
// 更新顶点的法线属性
vertices[i].normal[0] = nx;
vertices[i].normal[1] = ny;
vertices[i].normal[2] = nz;
}
}
纹理映射
struct CreateSphere : zeno::INode {
virtual void apply() override {
auto prim = std::make_shared<zeno::PrimitiveObject>();
auto position = get_input2<zeno::vec3f>("position");
auto scale = get_input2<zeno::vec3f>("scaleSize");
auto rows = get_input2<int>("rows");
auto columns = get_input2<int>("columns");
auto radius = get_input2<float>("radius");
auto quad = get_input2<bool>("quads");
if (rows < 2) {
rows = 2;
}
if (columns < 3) {
columns = 3;
}
std::vector<zeno::vec3f> verts = {};
std::vector<zeno::vec2i> poly = {};
std::vector<int> loops = {};
std::vector<zeno::vec2f> uvs = {};
std::vector<zeno::vec3f> nors = {};
verts.push_back (vec3f(0,1,0));
for (auto row = 1; row < rows; row++) {
float v = (float)row / (float)rows;
float theta = M_PI * v;
for (auto column = 0; column < columns; column++) {
float u = (float)column / (float)columns;
float phi = M_PI * 2 * u;
float x = sin(theta) * cos(phi);
float y = cos(theta);
float z = -sin(theta) * sin(phi);
verts.push_back(vec3f(x,y,z));
}
}
verts.push_back (vec3f(0,-1,0));
{
//head
for (auto column = 0; column < columns; column++) {
if (column == columns - 1) {
loops.push_back(0);
loops.push_back(columns);
loops.push_back(1);
poly.push_back(vec2i(column * 3, 3));
} else {
loops.push_back(0);
loops.push_back(column + 1);
loops.push_back(column + 2);
poly.push_back(vec2i(column * 3, 3));
}
}
//body
for (auto row = 1; row < rows - 1; row++) {
for (auto column = 0; column < columns; column++) {
if (column == columns - 1) {
loops.push_back((row - 1) * columns + 1);
loops.push_back((row - 1) * columns + columns);
loops.push_back(row * columns + columns);
loops.push_back(row * columns + 1);
poly.push_back(vec2i(columns * 3 + (row - 1) * columns * 4 + column * 4, 4));
} else {
loops.push_back((row - 1) * columns + column + 2);
loops.push_back((row - 1) * columns + column + 1);
loops.push_back(row * columns + column + 1);
loops.push_back(row * columns + column + 2);
poly.push_back(vec2i(loops.size() - 4, 4));
}
}
}
//tail
for (auto column = 0; column < columns; column++) {
if (column == columns - 1) {
loops.push_back((rows - 2) * columns + 1);
loops.push_back((rows - 2) * columns + column + 1);
loops.push_back((rows - 1) * columns + 1);
poly.push_back(vec2i(columns * 3 + (rows - 2) * columns * 4 + column * 3, 3));
} else {
loops.push_back((rows - 2) * columns + column + 2);
loops.push_back((rows - 2) * columns + column + 1);
loops.push_back((rows - 1) * columns + 1);
poly.push_back(vec2i(loops.size() - 3, 3));
}
}
}
for(int column = 0;column < columns;column++){
uvs.push_back({(column+0.5f)/columns, 1.0f, 0.0f});
}
for(int row = 1;row < rows;row++){
for(int column = 0;column < columns+1;column++){
uvs.push_back({(column+0.0f)/columns,1.0f-(row+0.0f)/rows,0.0f});
}
}
for(int column = 0;column < columns;column++){
uvs.push_back({(column+0.5f)/columns, 0.0f, 0.0f});
}
auto& loops_uv = prim->loops.add_attr<int>("uvs");
loops_uv.resize(0);
for(int column = 0;column < columns;column++){
loops_uv.push_back(column);
loops_uv.push_back(columns+column);
loops_uv.push_back(columns+column+1);
}
for(int row = 1;row < rows-1;row++){
for(int column = 0;column < columns;column++){
loops_uv.push_back(columns+(columns+1)*(row-1)+column+1);
loops_uv.push_back(columns+(columns+1)*(row-1)+column);
loops_uv.push_back(columns+(columns+1)*row+column);
loops_uv.push_back(columns+(columns+1)*row+column+1);
}
}
for(int column = 0;column < columns;column++){
loops_uv.push_back(columns+(columns+1)*(rows-2)+column+1);
loops_uv.push_back(columns+(columns+1)*(rows-2)+column);
loops_uv.push_back(columns+(columns+1)*(rows-1)+column);
}
nors.resize(verts.size());
for(int i = 0; i < verts.size(); i++){
auto n = verts[i];
auto p = verts[i];
auto rotate = get_input2<zeno::vec3f>("rotate");
glm::mat4 transform = glm::mat4 (1.0);
transform = glm::translate(transform, glm::vec3(position[0], position[1], position[2]));
transform = glm::rotate(transform, glm::radians(rotate[0]), glm::vec3(1, 0, 0));
transform = glm::rotate(transform, glm::radians(rotate[1]), glm::vec3(0, 1, 0));
transform = glm::rotate(transform, glm::radians(rotate[2]), glm::vec3(0, 0, 1));
transform = glm::scale(transform, glm::vec3(scale[0],scale[1],scale[2]) * radius);
auto gp = transform * glm::vec4(p[0], p[1], p[2], 1);
verts[i] = zeno::vec3f(gp.x, gp.y, gp.z);
auto n_transform = glm::transpose(glm::inverse(transform));
auto gn = n_transform * glm::vec4 (n[0], n[1], n[2], 0);
nors[i] = zeno::vec3f (gn.x, gn.y, gn.z);
}
prim->verts.resize(verts.size());
for (auto i = 0; i < verts.size(); i++) {
prim->verts[i] = verts[i];
}
prim->polys.resize(poly.size());
for (auto i = 0; i < poly.size(); i++) {
prim->polys[i] = poly[i];
}
prim->loops.resize(loops.size());
for (auto i = 0; i < loops.size(); i++) {
prim->loops[i] = loops[i];
}
prim->uvs.resize(uvs.size());
for (auto i = 0; i < uvs.size(); i++) {
prim->uvs[i] = uvs[i];
}
auto &nors2 = prim->verts.add_attr<zeno::vec3f>("nrm");
for (auto i = 0; i < nors.size(); i++) {
nors2[i] = nors[i];
}
if (!get_input2<bool>("hasNormal")){
prim->verts.attrs.erase("nrm");
}
if (!get_input2<bool>("hasVertUV")){
prim->uvs.clear();
prim->loops.erase_attr("uvs");
}
if (get_input2<bool>("isFlipFace")){
for (auto i = 0; i < prim->polys.size(); i++) {
auto [base, cnt] = prim->polys[i];
for (int j = 0; j < (cnt / 2); j++) {
std::swap(prim->loops[base + j], prim->loops[base + cnt - 1 - j]);
if (prim->loops.has_attr("uvs")) {
std::swap(prim->loops.attr<int>("uvs")[base + j], prim->loops.attr<int>("uvs")[base + cnt - 1 - j]);
}
}
}
}
if(!quad){
primTriangulate(prim.get());
}
set_output("prim",std::move(prim));
}
};
ZENDEFNODE(CreateSphere, {
{
{"vec3f", "position", "0, 0, 0"},
{"vec3f", "scaleSize", "1, 1, 1"},
{"float", "radius", "1"},
ROTATE_PARM
NORMUV_PARM
{"int", "rows", "12"},
{"int", "columns", "24"},
{"bool", "quads", "0"},
},
{"prim"},
{},
{"create"},
});
struct PrimUVEdgeDuplicate : INode {
virtual void apply() override {
auto prim = get_input<PrimitiveObject>("prim");
auto writeUVToVertex = get_input2<bool>("writeUVToVertex");
bool isTris = prim->tris.size() > 0;
if (isTris) {
primPolygonate(prim.get(), true);
}
std::map<std::tuple<int, int>, int> mapping;
std::vector<std::pair<int, int>> new_vertex_index;
std::vector<int> new_loops;
auto& uvs = prim->loops.attr<int>("uvs");
for (auto i = 0; i < prim->loops.size(); i++) {
int vi = prim->loops[i];
int uvi = uvs[i];
if (mapping.count({vi, uvi}) == 0) {
int index = mapping.size();
mapping[{vi, uvi}] = index;
new_vertex_index.emplace_back(vi, uvi);
}
new_loops.push_back(mapping[{vi, uvi}]);
}
for (auto i = 0; i < prim->loops.size(); i++) {
prim->loops[i] = new_loops[i];
}
AttrVector<vec3f> new_verts;
new_verts.resize(mapping.size());
for (auto i = 0; i < mapping.size(); i++) {
int org = new_vertex_index[i].first;
new_verts[i] = prim->verts[org];
}
prim->verts.foreach_attr<AttrAcceptAll>([&] (auto const &key, auto const &arr) {
using T = std::decay_t<decltype(arr[0])>;
auto &attr = new_verts.add_attr<T>(key);
for (auto i = 0; i < attr.size(); i++) {
attr[i] = arr[new_vertex_index[i].first];
}
});
std::swap(prim->verts, new_verts);
if (writeUVToVertex) {
auto &vert_uv = prim->verts.add_attr<vec3f>("uv");
auto &loopsuv = prim->loops.attr<int>("uvs");
for (auto i = 0; i < prim->loops.size(); i++) {
auto uv = prim->uvs[loopsuv[i]];
vert_uv[prim->loops[i]] = {uv[0], uv[1], 0};
}
}
if (isTris) {
primTriangulate(prim.get(), true, false);
}
set_output("prim", std::move(prim));
}
};
ZENO_DEFNODE(PrimUVEdgeDuplicate)({
{
"prim",
{"bool", "writeUVToVertex", "1"},
},
{
"prim",
},
{},
{"primitive"},
});
void primSampleTexture(
std::shared_ptr<PrimitiveObject> prim,
const std::string &srcChannel,
const std::string &uvSource,
const std::string &dstChannel,
std::shared_ptr<PrimitiveObject> img,
const std::string &wrap,
const std::string &filter,
vec3f borderColor,
float remapMin,
float remapMax
) {
if (!img->userData().has("isImage")) throw zeno::Exception("not an image");
using ColorT = float;
const ColorT *data = (float *)img->verts.data();
auto &clr = prim->add_attr<zeno::vec3f>(dstChannel);
auto w = img->userData().get2<int>("w");
auto h = img->userData().get2<int>("h");
std::function<zeno::vec3f(vec3f, const ColorT*, int, int, int, vec3f)> queryColor;
if (filter == "nearest") {
if (wrap == "REPEAT") {
queryColor = [=](vec3f uv, const ColorT *data, int w, int h, int n, vec3f _clr) -> vec3f {
uv = (uv - remapMin) / (remapMax - remapMin);
auto iuv = uvRepeat(uv, w, h);
return queryColorInner(iuv, data, w, n);
};
} else if (wrap == "CLAMP_TO_EDGE") {
queryColor = [=](vec3f uv, const ColorT *data, int w, int h, int n, vec3f _clr) -> vec3f {
uv = (uv - remapMin) / (remapMax - remapMin);
auto iuv = uvClampToEdge(uv, w, h);
return queryColorInner(iuv, data, w, n);
};
} else if (wrap == "CLAMP_TO_BORDER") {
queryColor = [=](vec3f uv, const ColorT *data, int w, int h, int n, vec3f clr) -> vec3f {
uv = (uv - remapMin) / (remapMax - remapMin);
if (uv[0] < 0 || uv[0] > 1 || uv[1] < 0 || uv[1] > 1) {
return clr;
}
auto iuv = uvClampToEdge(uv, w, h);
return queryColorInner(iuv, data, w, n);
};
} else {
zeno::log_error("wrap type error");
throw std::runtime_error("wrap type error");
}
}
else {
queryColor = [=](vec3f uv, const ColorT *data, int w, int h, int n, vec3f _clr) -> vec3f {
uv = (uv - remapMin) / (remapMax - remapMin);
float u, v;
auto iuv = uvClampToEdge(uv, w, h, u, v);
auto c00 = queryColorInner(iuv, data, w, n, h);
auto c01 = queryColorInner(iuv + vec2i(1, 0), data, w, n, h);
auto c10 = queryColorInner(iuv + vec2i(0, 1), data, w, n, h);
auto c11 = queryColorInner(iuv + vec2i(1, 1), data, w, n, h);
auto a = zeno::mix(c00, c01, u);
auto b = zeno::mix(c10, c11, u);
auto c = zeno::mix(a, b, v);
return c;
};
}
if (uvSource == "vertex") {
auto &uv = prim->attr<zeno::vec3f>(srcChannel);
#pragma omp parallel for
for (auto i = 0; i < uv.size(); i++) {
clr[i] = queryColor(uv[i], data, w, h, 3, borderColor);
}
}
else if (uvSource == "tris") {
auto uv0 = prim->tris.attr<vec3f>("uv0");
auto uv1 = prim->tris.attr<vec3f>("uv1");
auto uv2 = prim->tris.attr<vec3f>("uv2");
#pragma omp parallel for
for (auto i = 0; i < prim->tris.size(); i++) {
auto tri = prim->tris[i];
clr[tri[0]] = queryColor(uv0[i], data, w, h, 3, borderColor);
clr[tri[1]] = queryColor(uv1[i], data, w, h, 3, borderColor);
clr[tri[2]] = queryColor(uv2[i], data, w, h, 3, borderColor);
}
}
else if (uvSource == "loopsuv") {
auto &loopsuv = prim->loops.attr<int>("uvs");
#pragma omp parallel for
for (auto i = 0; i < prim->loops.size(); i++) {
auto uv = prim->uvs[loopsuv[i]];
int index = prim->loops[i];
clr[index] = queryColor({uv[0], uv[1], 0}, data, w, h, 3, borderColor);
}
}
else {
zeno::log_error("unknown uvSource");
throw std::runtime_error("unknown uvSource");
}
}
void primSampleTexture(
std::shared_ptr<PrimitiveObject> prim,
const std::string &srcChannel,
const std::string &uvSource,
const std::string &dstChannel,
std::shared_ptr<PrimitiveObject> img,
const std::string &wrap,
vec3f borderColor,
float remapMin,
float remapMax
) {
return primSampleTexture(prim, srcChannel, uvSource, dstChannel, img, wrap, "nearest", borderColor, remapMin, remapMax);;
}
struct PrimSample2D : zeno::INode {
virtual void apply() override {
auto prim = get_input<PrimitiveObject>("prim");
auto srcChannel = get_input2<std::string>("uvChannel");
auto srcSource = get_input2<std::string>("uvSource");
auto dstChannel = get_input2<std::string>("targetChannel");
auto image = get_input2<PrimitiveObject>("image");
auto wrap = get_input2<std::string>("wrap");
auto filter = get_input2<std::string>("filter");
auto borderColor = get_input2<vec3f>("borderColor");
auto remapMin = get_input2<float>("remapMin");
auto remapMax = get_input2<float>("remapMax");
primSampleTexture(prim, srcChannel, srcSource, dstChannel, image, wrap, filter, borderColor, remapMin, remapMax);
set_output("outPrim", std::move(prim));
}
};
ZENDEFNODE(PrimSample2D, {
{
{"PrimitiveObject", "prim"},
{"PrimitiveObject", "image"},
{"string", "uvChannel", "uv"},
{"enum vertex tris loopsuv", "uvSource", "vertex"},
{"string", "targetChannel", "clr"},
{"float", "remapMin", "0"},
{"float", "remapMax", "1"},
{"enum REPEAT CLAMP_TO_EDGE CLAMP_TO_BORDER", "wrap", "REPEAT"},
{"enum nearest linear", "filter", "nearest"},
{"vec3f", "borderColor", "0,0,0"},
},
{
{"PrimitiveObject", "outPrim"}
},
{},
{"primitive"},
});
Other Reference
/*
* Author: Christian Petry
* Homepage: www.petry-christian.de
*
* License: MIT
* Copyright (c) 2014 Christian Petry
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//console.log("Warnings are disabled!");
//console.warn = function() {};
NMO_FileDrop.initHeightMap();
NMO_RenderView.initRenderer();
var NMO_Main = new function(){
this.TextureEnum = {
NORMAL : 0,
DISPLACEMENT : 1,
AMBIENT : 2,
SPECULAR : 3
}
this.auto_update = true;
this.current_texture = this.TextureEnum.NORMAL;
this.normal_map_mode = "height";
this.download_btn = document.getElementById('download');
this.download_all_btn = document.getElementById('download_all');
this.activate_height_tab = function(type){
this.normal_map_mode = type;
if (type == "height"){
document.getElementById('tab_btn_heightmap').disabled = true;
document.getElementById('tab_btn_pictures').disabled = false;
$('#pictures_map').hide("slide", {direction: "right"}, 400, function() {
NMO_RenderNormalview.reinitializeShader("height");
NMO_RenderNormalview.renderNormalview_update("height");
NMO_NormalMap.createNormalMap();
NMO_Main.setTexturePreview(NMO_NormalMap.normal_canvas, "normal_img", NMO_NormalMap.normal_canvas.width, NMO_NormalMap.normal_canvas.height);
NMO_DisplacementMap.createDisplacementMap();
NMO_AmbientOccMap.createAmbientOcclusionTexture();
NMO_SpecularMap.createSpecularTexture();
$('#height_map').show("slide", {direction: "left"}, 400);
});
}
else if (type == "pictures"){
document.getElementById('tab_btn_pictures').disabled = true;
document.getElementById('tab_btn_heightmap').disabled = false;
$('#height_map').hide("slide", {direction: "left"}, 400, function() {
NMO_RenderNormalview.reinitializeShader("pictures");
NMO_RenderNormalview.renderNormalview_update("pictures");
NMO_NormalMap.createNormalMap();
NMO_Main.setTexturePreview(NMO_NormalMap.normal_canvas, "normal_img", NMO_NormalMap.normal_canvas.width, NMO_NormalMap.normal_canvas.height);
NMO_RenderNormalview.renderNormalToHeight(); // when the last one was loaded
NMO_DisplacementMap.createDisplacementMap();
NMO_AmbientOccMap.createAmbientOcclusionTexture();
NMO_SpecularMap.createSpecularTexture();
$('#pictures_map').show("slide", {direction: "right"}, 400);
});
}
}
this.activate_texture = function(type){
if (type == "normal"){
document.getElementById('tab_btn_normal').disabled = true;
document.getElementById('tab_btn_displace').disabled = false;
document.getElementById('tab_btn_ao').disabled = false;
document.getElementById('tab_btn_specular').disabled = false;
//console.log("normal!");
document.getElementById('normal_map').style.cssText = "";
document.getElementById('normal_settings').style.cssText = "";
document.getElementById('displacement_map').style.cssText = "display: none;";
document.getElementById('displacement_settings').style.cssText = "display: none;";
document.getElementById('ao_map').style.cssText = "display: none;";
document.getElementById('ao_settings').style.cssText = "display: none;";
document.getElementById('specular_map').style.cssText = "display: none;";
document.getElementById('specular_settings').style.cssText = "display: none;";
document.getElementById('file_name').placeholder = "NormalMap";
this.current_texture = this.TextureEnum.NORMAL;
}
else if (type == "displace"){
document.getElementById('tab_btn_normal').disabled = false;
document.getElementById('tab_btn_displace').disabled = true;
document.getElementById('tab_btn_ao').disabled = false;
document.getElementById('tab_btn_specular').disabled = false;
document.getElementById('normal_map').style.cssText = "display: none;";
document.getElementById('normal_settings').style.cssText = "display: none;";
document.getElementById('displacement_map').style.cssText = "";
document.getElementById('displacement_settings').style.cssText = "";
document.getElementById('ao_map').style.cssText = "display: none;";
document.getElementById('ao_settings').style.cssText = "display: none;";
document.getElementById('specular_map').style.cssText = "display: none;";
document.getElementById('specular_settings').style.cssText = "display: none;";
document.getElementById('file_name').placeholder = "DisplacementMap";
this.current_texture = this.TextureEnum.DISPLACEMENT;
//console.log("displace!");
}
else if (type == "ao"){
document.getElementById('tab_btn_normal').disabled = false;
document.getElementById('tab_btn_displace').disabled = false;
document.getElementById('tab_btn_ao').disabled = true;
document.getElementById('tab_btn_specular').disabled = false;
document.getElementById('normal_map').style.cssText = "display: none;";
document.getElementById('normal_settings').style.cssText = "display: none;";
document.getElementById('displacement_map').style.cssText = "display: none;";
document.getElementById('displacement_settings').style.cssText = "display: none;";
document.getElementById('ao_map').style.cssText = "";
document.getElementById('ao_settings').style.cssText = "";
document.getElementById('specular_map').style.cssText = "display: none;";
document.getElementById('specular_settings').style.cssText = "display: none;";
document.getElementById('file_name').placeholder = "AmbientOcclusionMap";
this.current_texture = this.TextureEnum.AMBIENT;
//console.log("displace!");
}
else if (type == "specular"){
document.getElementById('tab_btn_normal').disabled = false;
document.getElementById('tab_btn_displace').disabled = false;
document.getElementById('tab_btn_ao').disabled = false;
document.getElementById('tab_btn_specular').disabled = true;
document.getElementById('normal_map').style.cssText = "display: none;";
document.getElementById('normal_settings').style.cssText = "display: none;";
document.getElementById('displacement_map').style.cssText = "display: none;";
document.getElementById('displacement_settings').style.cssText = "display: none;";
document.getElementById('ao_map').style.cssText = "display: none;";
document.getElementById('ao_settings').style.cssText = "display: none;";
document.getElementById('specular_map').style.cssText = "";
document.getElementById('specular_settings').style.cssText = "";
document.getElementById('file_name').placeholder = "SpecularMap";
this.current_texture = this.TextureEnum.SPECULAR;
//console.log("displace!");
}
}
this.setTexturePreview = function(canvas, img_id, width, height){
var img = document.getElementById(img_id);
//canvas.width = width;
//canvas.height = height;
//console.log(img_id + ": " + width);
img.getContext('2d').clearRect ( 0 , 0 , img.width, img.height );
var ratio = width / height;
var draw_width = ratio >= 1 ? NMO_FileDrop.container_height : (NMO_FileDrop.container_height * ratio );
var draw_height = ratio >= 1 ? (NMO_FileDrop.container_height / ratio ) : NMO_FileDrop.container_height;
var reduce_canvas = document.createElement('canvas');
var helper_canvas = document.createElement('canvas');
helper_canvas.width = width;
helper_canvas.height = height;
reduce_canvas.width = width;
reduce_canvas.height = height;
var current_width = width;
var current_height = height;
var reduce_context = reduce_canvas.getContext('2d');
var helper_context = helper_canvas.getContext('2d');
reduce_context.clearRect(0,0,reduce_context.width, reduce_context.height);
reduce_context.drawImage(canvas, 0, 0, width, height);
helper_context.clearRect(0,0,helper_canvas.width, helper_canvas.height);
helper_context.drawImage(canvas, 0, 0, width, height);
while(2*draw_width < current_width && 2*draw_height < current_height ){
//console.log("redraw!");
helper_context.clearRect(0, 0, helper_canvas.width, helper_canvas.height);
helper_context.drawImage(reduce_canvas, 0, 0, reduce_canvas.width, reduce_canvas.height);
reduce_context.clearRect(0, 0, reduce_canvas.width, reduce_canvas.height);
reduce_context.drawImage(helper_canvas, 0, 0, reduce_canvas.width * 0.5, reduce_canvas.height * 0.5);
current_width *= 0.5;
current_height *= 0.5;
}
//console.log(draw_width + ", " + draw_height)
img.height = draw_height;
img.width = draw_width;
img.getContext('2d').drawImage(reduce_canvas, 0, 0, current_width, current_height, 0,0, draw_width, draw_height);
if (canvas == NMO_NormalMap.normal_canvas)
NMO_RenderView.normal_map.needsUpdate = true;
else if (canvas == NMO_DisplacementMap.displacement_canvas)
NMO_RenderView.displacement_map.needsUpdate = true;
else if (canvas == NMO_AmbientOccMap.ao_canvas)
NMO_RenderView.ao_map.needsUpdate = true;
else if (canvas == NMO_SpecularMap.specular_canvas)
NMO_RenderView.specular_map.needsUpdate = true;
}
this.toggle_height_column = function(){
if ($("#column_height").is(":visible") == true) {
$("#column_btn_left_div").html("<<");
$("#column_height").hide("slide", {direction: "right"}, 400);
/*$(".column").each(function () {
$(this).css("width", "438px");
});
$(".preview_img").each(function () {
$(this).css("max-width", "400px");
$(this).css("max-height", "400px");
});
$(".view").each(function () {
$(this).css("max-width", "400px");
$(this).css("max-height", "400px");
});
$(".helper").each(function () {
$(this).css("max-width", "400px");
$(this).css("max-height", "400px");
});
container_height = 400;
updateCurrentTexture();
renderer.setSize( 400, 400 );*/
}
else{
$("#column_btn_left_div").html(">>");
$("#column_height").show("slide", {direction: "right"}, 400);
}
}
this.toggle_preview_column = function(){
if ($("#preview").is(":visible") == true) {
$("#column_btn_right_div").html(">>");
$("#preview").hide("slide", {direction: "left"}, 400);
}
else{
$("#column_btn_right_div").html("<<");
$("#preview").show("slide", {direction: "left"}, 400);
}
}
this.getImageType = function(){
var select_file_type = document.getElementById('file_type');
var file_type = select_file_type.options[select_file_type.selectedIndex].value;
return file_type;
};
this.switchJPGQual = function(){
if (this.getImageType() != 'jpg'){
document.getElementById('file_jpg_qual').style.cssText = "display: none;";
document.getElementById('total_transparency').style.cssText = "font-size:11px;";
}
else{
document.getElementById('total_transparency').style.cssText = "display: none;";
document.getElementById('file_jpg_qual').style.cssText = "font-size:11px;";
}
};
this.toggleAutoUpdate = function(){
this.auto_update = !this.auto_update;
if (this.auto_update)
NMO_NormalMap.createNormalMap();
NMO_DisplacementMap.createDisplacementMap();
NMO_AmbientOccMap.createAmbientOcclusionTexture();
NMO_SpecularMap.createSpecularTexture();
};
this.updateCurrentTexture = function(){
if (this.current_texture == TextureEnum.NORMAL)
NMO_NormalMap.createNormalMap();
else if (this.current_texture == TextureEnum.DISPLACEMENT)
NMO_DisplacementMap.createDisplacementMap();
else if (this.current_texture == TextureEnum.AMBIENT)
NMO_AmbientOccMap.createAmbientOcclusionTexture();
else if (this.current_texture == TextureEnum.SPECULAR)
NMO_SpecularMap.createSpecularTexture();
};
this.download_all_btn.addEventListener('click', function (e) {
NMO_Main.downloadImage("NormalMap");
NMO_Main.downloadImage("DisplacementMap");
NMO_Main.downloadImage("AmbientOcclusionMap");
NMO_Main.downloadImage("SpecularMap");
});
this.download_btn.addEventListener('click', function (e) {
if (document.getElementById('normal_map').style.cssText != "display: none;"){
NMO_Main.downloadImage("NormalMap");
}
else if (document.getElementById('displacement_map').style.cssText != "display: none;"){
NMO_Main.downloadImage("DisplacementMap");
}
else if (document.getElementById('ao_map').style.cssText != "display: none;"){
NMO_Main.downloadImage("AmbientOcclusionMap");
}
else if (document.getElementById('specular_map').style.cssText != "display: none;"){
NMO_Main.downloadImage("SpecularMap");
}
});
this.downloadImage = function(type){
console.log("Downloading image");
var qual = 0.9;
var file_name = "download";
var canvas = document.createElement("canvas");
var file_type = NMO_Main.getImageType();
var image_type = "image/png";
if (file_type == "jpg")
image_type = "image/jpeg";
if (type == "NormalMap"){
canvas.width = NMO_NormalMap.normal_canvas.width;
canvas.height = NMO_NormalMap.normal_canvas.height;
var context = canvas.getContext('2d');
if (file_type == "png")
context.globalAlpha = $('#transparency_nmb').val() / 100;
context.drawImage(NMO_NormalMap.normal_canvas,0,0);
file_name="NormalMap";
}
else if (type == "DisplacementMap"){
canvas.width = NMO_DisplacementMap.displacement_canvas.width;
canvas.height = NMO_DisplacementMap.displacement_canvas.height;
var context = canvas.getContext('2d');
if (file_type == "png")
context.globalAlpha = $('#transparency_nmb').val() / 100;
context.drawImage(NMO_DisplacementMap.displacement_canvas,0,0);
file_name="DisplacementMap";
}
else if (type == "AmbientOcclusionMap"){
canvas.width = NMO_AmbientOccMap.ao_canvas.width;
canvas.height = NMO_AmbientOccMap.ao_canvas.height;
var context = canvas.getContext('2d');
if (file_type == "png")
context.globalAlpha = $('#transparency_nmb').val() / 100;
context.drawImage(NMO_AmbientOccMap.ao_canvas,0,0);
file_name="AmbientOcclusionMap";
}
else if (type == "SpecularMap"){
canvas.width = NMO_SpecularMap.specular_canvas.width;
canvas.height = NMO_SpecularMap.specular_canvas.height;
var context = canvas.getContext('2d');
if (file_type == "png")
context.globalAlpha = $('#transparency_nmb').val() / 100;
context.drawImage(NMO_SpecularMap.specular_canvas,0,0);
file_name="SpecularMap";
}
if (document.getElementById('file_name').value != "")
file_name = document.getElementById('file_name').value;
var qual = $('#file_jpg_qual_nmb').val() / 100;
if (file_type == "tiff"){
CanvasToTIFF.toBlob(canvas, function(blob) {
saveAs(blob, file_name + ".tif");
});
}
else{
canvas.toBlob(function(blob) {
saveAs(blob, file_name + "." + file_type);
}, image_type, qual);
}
}
}
/*
* Author: Christian Petry
* Homepage: www.petry-christian.de
*
* License: MIT
* Copyright (c) 2014 Christian Petry
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
var NMO_NormalMap = new function(){
this.invert_red = false;
this.invert_green = false;
this.invert_source = false;
this.height_offset = true;
this.smoothing = 0;
this.strength = 2.5;
this.level = 7;
this.normal_type = "sobel";
this.normal_canvas = document.createElement("canvas");
this.getNextPowerOf2 = function(nmb){
i = 2;
while(i < Math.pow(2,14)){
i *= 2;
if(i >= nmb)
return i;
}
};
this.createNormalMap = function(){
NMO_RenderNormalview.renderNormalView();
NMO_Main.setTexturePreview(this.normal_canvas, "normal_img", this.normal_canvas.width, this.normal_canvas.height);
};
this.invertRed = function(){
this.invert_red = !this.invert_red;
if (this.invert_red){
NMO_RenderNormalview.normalmap_uniforms["invertR"].value = -1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["invertR"].value = -1;
}
else{
NMO_RenderNormalview.normalmap_uniforms["invertR"].value = 1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["invertR"].value = 1;
}
NMO_NormalMap.createNormalMap();
};
this.invertGreen = function(){
this.invert_green = !this.invert_green;
if (this.invert_green){
NMO_RenderNormalview.normalmap_uniforms["invertG"].value = -1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["invertG"].value = -1;
}
else{
NMO_RenderNormalview.normalmap_uniforms["invertG"].value = 1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["invertG"].value = 1;
}
NMO_NormalMap.createNormalMap();
};
this.invertSource = function(){
this.invert_source = !this.invert_source;
if (!this.invert_source){
NMO_RenderNormalview.normalmap_uniforms["invertH"].value = 1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["invertH"].value = 1;
}
else{
NMO_RenderNormalview.normalmap_uniforms["invertH"].value = -1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["invertH"].value = -1;
}
NMO_NormalMap.createNormalMap();
};
this.heightOffset = function(){
this.height_offset = !this.height_offset;
if (this.height_offset){
NMO_RenderNormalview.normalmap_uniforms["heightOffset"].value = 0;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["heightOffset"].value = 0;
}
else{
NMO_RenderNormalview.normalmap_uniforms["heightOffset"].value = 1;
NMO_RenderNormalview.normalmap_from_pictures_uniforms["heightOffset"].value = 1;
}
NMO_NormalMap.createNormalMap();
};
this.setNormalSetting = function(element, v, initial){
if (element == "blur_sharp"){
smoothing = v;
NMO_RenderNormalview.gaussian_shader_y.uniforms["v"].value = v / NMO_FileDrop.height_image.naturalWidth / 5;
NMO_RenderNormalview.gaussian_shader_x.uniforms["h"].value = v / NMO_FileDrop.height_image.naturalHeight / 5;
//NMO_RenderNormalview.gaussian_shader.uniforms["sigma"].value = v / NMO_FileDrop.height_image.naturalHeight / 5;
}
else if (element == "strength"){
strength = v;
NMO_RenderNormalview.normalmap_uniforms["dz"].value = 1.0 / v * (1.0 + Math.pow(2.0, document.getElementById('level_nmb').value));
NMO_RenderNormalview.normalmap_from_pictures_uniforms["dz"].value = 1.0 / v * (1.0 + Math.pow(2.0, document.getElementById('level_nmb').value));
}
else if (element == "level"){
level = v;
NMO_RenderNormalview.normalmap_uniforms["dz"].value = 1.0 / document.getElementById('strength_nmb').value * (1.0 + Math.pow(2.0, v));
NMO_RenderNormalview.normalmap_from_pictures_uniforms["dz"].value = 1.0 / document.getElementById('strength_nmb').value * (1.0 + Math.pow(2.0, v));
}
else if (element == "type"){
normal_type = v;
if (v == "sobel")
NMO_RenderNormalview.normalmap_uniforms["type"].value = 0;
else
NMO_RenderNormalview.normalmap_uniforms["type"].value = 1;
}
if (typeof initial === 'undefined')
NMO_NormalMap.createNormalMap();
};
}