/*************************************************************************/ /* surface_tool.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* 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. */ /*************************************************************************/ #include "surface_tool.h" #include "method_bind_ext.inc" #define _VERTEX_SNAP 0.0001 #define EQ_VERTEX_DIST 0.00001 bool SurfaceTool::Vertex::operator==(const Vertex& p_b) const { if (vertex!=p_b.vertex) return false; if (uv!=p_b.uv) return false; if (uv2!=p_b.uv2) return false; if (normal!=p_b.normal) return false; if (binormal!=p_b.binormal) return false; if (color!=p_b.color) return false; if (bones.size()!=p_b.bones.size()) return false; for(int i=0;i& p_bones) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(p_bones.size()!=4); ERR_FAIL_COND( !first && !(format&Mesh::ARRAY_FORMAT_BONES)); format|=Mesh::ARRAY_FORMAT_BONES; last_bones=p_bones; } void SurfaceTool::add_weights( const Vector& p_weights) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(p_weights.size()!=4); ERR_FAIL_COND( !first && !(format&Mesh::ARRAY_FORMAT_WEIGHTS)); format|=Mesh::ARRAY_FORMAT_WEIGHTS; last_weights=p_weights; } void SurfaceTool::add_smooth_group(bool p_smooth) { ERR_FAIL_COND(!begun); if (index_array.size()) { smooth_groups[index_array.size()]=p_smooth; } else { smooth_groups[vertex_array.size()]=p_smooth; } } void SurfaceTool::add_triangle_fan(const Vector& p_vertexes, const Vector& p_uvs, const Vector& p_colors,const Vector& p_uv2s, const Vector& p_normals, const Vector& p_tangents) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(primitive!=Mesh::PRIMITIVE_TRIANGLES); ERR_FAIL_COND(p_vertexes.size()<3); #define ADD_POINT(n)\ {\ if(p_colors.size() > n)\ add_color(p_colors[n]);\ if(p_uvs.size() > n)\ add_uv(p_uvs[n]);\ if(p_uv2s.size() > n)\ add_uv2(p_uv2s[n]);\ if(p_normals.size() > n)\ add_normal(p_normals[n]);\ if(p_tangents.size() > n)\ add_tangent(p_tangents[n]);\ add_vertex(p_vertexes[n]);\ } for(int i=0;i SurfaceTool::commit(const Ref& p_existing) { Ref mesh; if (p_existing.is_valid()) mesh=p_existing; else mesh= Ref( memnew( Mesh ) ); int varr_len=vertex_array.size(); if (varr_len==0) return mesh; int surface = mesh->get_surface_count(); Array a; a.resize(Mesh::ARRAY_MAX); for (int i=0;i array; array.resize(varr_len); PoolVector::Write w = array.write(); int idx=0; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next(),idx++) { const Vertex &v=E->get(); switch(i) { case Mesh::ARRAY_VERTEX: { w[idx]=v.vertex; } break; case Mesh::ARRAY_NORMAL: { w[idx]=v.normal; } break; } } w=PoolVector::Write(); a[i]=array; } break; case Mesh::ARRAY_FORMAT_TEX_UV: case Mesh::ARRAY_FORMAT_TEX_UV2: { PoolVector array; array.resize(varr_len); PoolVector::Write w = array.write(); int idx=0; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next(),idx++) { const Vertex &v=E->get(); switch(i) { case Mesh::ARRAY_TEX_UV: { w[idx]=v.uv; } break; case Mesh::ARRAY_TEX_UV2: { w[idx]=v.uv2; } break; } } w=PoolVector::Write(); a[i]=array; } break; case Mesh::ARRAY_FORMAT_TANGENT: { PoolVector array; array.resize(varr_len*4); PoolVector::Write w = array.write(); int idx=0; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next(),idx+=4) { const Vertex &v=E->get(); w[idx+0]=v.tangent.x; w[idx+1]=v.tangent.y; w[idx+2]=v.tangent.z; //float d = v.tangent.dot(v.binormal,v.normal); float d = v.binormal.dot( v.normal.cross(v.tangent)); w[idx+3]=d<0 ? -1 : 1; } w=PoolVector::Write(); a[i]=array; } break; case Mesh::ARRAY_FORMAT_COLOR: { PoolVector array; array.resize(varr_len); PoolVector::Write w = array.write(); int idx=0; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next(),idx++) { const Vertex &v=E->get(); w[idx]=v.color; } w=PoolVector::Write(); a[i]=array; } break; case Mesh::ARRAY_FORMAT_BONES: { PoolVector array; array.resize(varr_len*4); PoolVector::Write w = array.write(); int idx=0; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next(),idx+=4) { const Vertex &v=E->get(); ERR_CONTINUE( v.bones.size()!=4 ); for(int j=0;j<4;j++) { w[idx+j]=v.bones[j]; } } w=PoolVector::Write(); a[i]=array; } break; case Mesh::ARRAY_FORMAT_WEIGHTS: { PoolVector array; array.resize(varr_len*4); PoolVector::Write w = array.write(); int idx=0; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next(),idx+=4) { const Vertex &v=E->get(); ERR_CONTINUE( v.weights.size()!=4 ); for(int j=0;j<4;j++) { w[idx+j]=v.weights[j]; } } w=PoolVector::Write(); a[i]=array; } break; case Mesh::ARRAY_FORMAT_INDEX: { ERR_CONTINUE( index_array.size() ==0 ); PoolVector array; array.resize(index_array.size()); PoolVector::Write w = array.write(); int idx=0; for(List< int>::Element *E=index_array.front();E;E=E->next(),idx++) { w[idx]=E->get(); } w=PoolVector::Write(); a[i]=array; } break; default: {} } } mesh->add_surface_from_arrays(primitive,a); if (material.is_valid()) mesh->surface_set_material(surface,material); return mesh; } void SurfaceTool::index() { if (index_array.size()) return; //already indexed HashMap indices; List new_vertices; for(List< Vertex >::Element *E=vertex_array.front();E;E=E->next()) { int *idxptr=indices.getptr(E->get()); int idx; if (!idxptr) { idx=indices.size(); new_vertices.push_back(E->get()); indices[E->get()]=idx; } else { idx=*idxptr; } index_array.push_back(idx); } vertex_array.clear(); vertex_array=new_vertices; format|=Mesh::ARRAY_FORMAT_INDEX; } void SurfaceTool::deindex() { if (index_array.size()==0) return; //nothing to deindex Vector< Vertex > varr; varr.resize(vertex_array.size()); int idx=0; for (List< Vertex >::Element *E=vertex_array.front();E;E=E->next()) { varr[idx++]=E->get(); } vertex_array.clear(); for (List::Element *E=index_array.front();E;E=E->next()) { ERR_FAIL_INDEX(E->get(),varr.size()); vertex_array.push_back(varr[E->get()]); } format&=~Mesh::ARRAY_FORMAT_INDEX; } void SurfaceTool::_create_list(const Ref& p_existing, int p_surface, List *r_vertex, List *r_index, int& lformat) { Array arr = p_existing->surface_get_arrays(p_surface); ERR_FAIL_COND( arr.size() !=VS::ARRAY_MAX ); PoolVector varr = arr[VS::ARRAY_VERTEX]; PoolVector narr = arr[VS::ARRAY_NORMAL]; PoolVector tarr = arr[VS::ARRAY_TANGENT]; PoolVector carr = arr[VS::ARRAY_COLOR]; PoolVector uvarr = arr[VS::ARRAY_TEX_UV]; PoolVector uv2arr = arr[VS::ARRAY_TEX_UV2]; PoolVector barr = arr[VS::ARRAY_BONES]; PoolVector warr = arr[VS::ARRAY_WEIGHTS]; int vc = varr.size(); if (vc==0) return; lformat=0; PoolVector::Read rv; if (varr.size()) { lformat|=VS::ARRAY_FORMAT_VERTEX; rv=varr.read(); } PoolVector::Read rn; if (narr.size()) { lformat|=VS::ARRAY_FORMAT_NORMAL; rn=narr.read(); } PoolVector::Read rt; if (tarr.size()) { lformat|=VS::ARRAY_FORMAT_TANGENT; rt=tarr.read(); } PoolVector::Read rc; if (carr.size()) { lformat|=VS::ARRAY_FORMAT_COLOR; rc=carr.read(); } PoolVector::Read ruv; if (uvarr.size()) { lformat|=VS::ARRAY_FORMAT_TEX_UV; ruv=uvarr.read(); } PoolVector::Read ruv2; if (uv2arr.size()) { lformat|=VS::ARRAY_FORMAT_TEX_UV2; ruv2=uv2arr.read(); } PoolVector::Read rb; if (barr.size()) { lformat|=VS::ARRAY_FORMAT_BONES; rb=barr.read(); } PoolVector::Read rw; if (warr.size()) { lformat|=VS::ARRAY_FORMAT_WEIGHTS; rw=warr.read(); } for(int i=0;i b; b.resize(4); b[0]=barr[i*4+0]; b[1]=barr[i*4+1]; b[2]=barr[i*4+2]; b[3]=barr[i*4+3]; v.bones=b; } if (lformat&VS::ARRAY_FORMAT_WEIGHTS) { Vector w; w.resize(4); w[0]=warr[i*4+0]; w[1]=warr[i*4+1]; w[2]=warr[i*4+2]; w[3]=warr[i*4+3]; v.weights=w; } r_vertex->push_back(v); } //indices PoolVector idx= arr[VS::ARRAY_INDEX]; int is = idx.size(); if (is) { lformat|=VS::ARRAY_FORMAT_INDEX; PoolVector::Read iarr=idx.read(); for(int i=0;ipush_back(iarr[i]); } } } void SurfaceTool::create_from(const Ref& p_existing, int p_surface) { clear(); primitive=p_existing->surface_get_primitive_type(p_surface); _create_list(p_existing,p_surface,&vertex_array,&index_array,format); material=p_existing->surface_get_material(p_surface); } void SurfaceTool::append_from(const Ref& p_existing, int p_surface,const Transform& p_xform) { if (vertex_array.size()==0) { primitive=p_existing->surface_get_primitive_type(p_surface); format=0; } int nformat; List nvertices; List nindices; _create_list(p_existing,p_surface,&nvertices,&nindices,nformat); format|=nformat; int vfrom = vertex_array.size(); for(List::Element *E=nvertices.front();E;E=E->next()) { Vertex v=E->get(); v.vertex=p_xform.xform(v.vertex); if (nformat&VS::ARRAY_FORMAT_NORMAL) { v.normal=p_xform.basis.xform(v.normal); } if (nformat&VS::ARRAY_FORMAT_TANGENT) { v.tangent=p_xform.basis.xform(v.tangent); v.binormal=p_xform.basis.xform(v.binormal); } vertex_array.push_back(v); } for(List::Element *E=nindices.front();E;E=E->next()) { int dst_index = E->get()+vfrom; /* if (dst_index <0 || dst_index>=vertex_array.size()) { print_line("invalid index!"); } */ index_array.push_back(dst_index); } if (index_array.size()%3) print_line("IA not div of 3?"); } //mikktspace callbacks int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext * pContext) { Vector::Element*> &varr = *((Vector::Element*>*)pContext->m_pUserData); return varr.size()/3; } int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext * pContext, const int iFace){ return 3; //always 3 } void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert){ Vector::Element*> &varr = *((Vector::Element*>*)pContext->m_pUserData); Vector3 v = varr[iFace*3+iVert]->get().vertex; fvPosOut[0]=v.x; fvPosOut[1]=v.y; fvPosOut[2]=v.z; } void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert){ Vector::Element*> &varr = *((Vector::Element*>*)pContext->m_pUserData); Vector3 v = varr[iFace*3+iVert]->get().normal; fvNormOut[0]=v.x; fvNormOut[1]=v.y; fvNormOut[2]=v.z; } void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert){ Vector::Element*> &varr = *((Vector::Element*>*)pContext->m_pUserData); Vector2 v = varr[iFace*3+iVert]->get().uv; fvTexcOut[0]=v.x; fvTexcOut[1]=v.y; //fvTexcOut[1]=1.0-v.y; } void SurfaceTool::mikktSetTSpaceBasic(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert){ Vector::Element*> &varr = *((Vector::Element*>*)pContext->m_pUserData); Vertex &vtx = varr[iFace*3+iVert]->get(); vtx.tangent = Vector3(fvTangent[0],fvTangent[1],fvTangent[2]); vtx.binormal = vtx.normal.cross(vtx.tangent) * fSign; } void SurfaceTool::generate_tangents() { ERR_FAIL_COND(!(format&Mesh::ARRAY_FORMAT_TEX_UV)); ERR_FAIL_COND(!(format&Mesh::ARRAY_FORMAT_NORMAL)); bool indexed = index_array.size()>0; if (indexed) deindex(); SMikkTSpaceInterface mkif; mkif.m_getNormal=mikktGetNormal; mkif.m_getNumFaces=mikktGetNumFaces; mkif.m_getNumVerticesOfFace=mikktGetNumVerticesOfFace; mkif.m_getPosition=mikktGetPosition; mkif.m_getTexCoord=mikktGetTexCoord; mkif.m_setTSpaceBasic=mikktSetTSpaceBasic; mkif.m_setTSpace=NULL; SMikkTSpaceContext msc; msc.m_pInterface=&mkif; Vector::Element*> vtx; vtx.resize(vertex_array.size()); int idx=0; for (List::Element *E=vertex_array.front();E;E=E->next()) { vtx[idx++]=E; E->get().binormal=Vector3(); E->get().tangent=Vector3(); } msc.m_pUserData=&vtx; bool res = genTangSpaceDefault(&msc); ERR_FAIL_COND(!res); format|=Mesh::ARRAY_FORMAT_TANGENT; if (indexed) index(); } void SurfaceTool::generate_normals() { ERR_FAIL_COND(primitive!=Mesh::PRIMITIVE_TRIANGLES); bool was_indexed=index_array.size(); deindex(); HashMap vertex_hash; int count=0; bool smooth=false; if (smooth_groups.has(0)) smooth=smooth_groups[0]; List< Vertex >::Element *B=vertex_array.front(); for(List< Vertex >::Element *E=B;E;) { List< Vertex >::Element *v[3]; v[0]=E; v[1]=v[0]->next(); ERR_FAIL_COND(!v[1]); v[2]=v[1]->next(); ERR_FAIL_COND(!v[2]); E=v[2]->next(); Vector3 normal = Plane(v[0]->get().vertex,v[1]->get().vertex,v[2]->get().vertex).normal; if (smooth) { for(int i=0;i<3;i++) { Vector3 *lv=vertex_hash.getptr(v[i]->get()); if (!lv) { vertex_hash.set(v[i]->get(),normal); } else { (*lv)+=normal; } } } else { for(int i=0;i<3;i++) { v[i]->get().normal=normal; } } count+=3; if (smooth_groups.has(count) || !E) { if (vertex_hash.size()) { while (B!=E) { Vector3* lv=vertex_hash.getptr(B->get()); if (lv) { B->get().normal=lv->normalized(); } B=B->next(); } } else { B=E; } vertex_hash.clear(); if (E) { smooth=smooth_groups[count]; print_line("SMOOTH AT "+itos(count)+": "+itos(smooth)); } } } format|=Mesh::ARRAY_FORMAT_NORMAL; if (was_indexed) { index(); smooth_groups.clear(); } } void SurfaceTool::set_material(const Ref& p_material) { material=p_material; } void SurfaceTool::clear() { begun=false; primitive=Mesh::PRIMITIVE_LINES; format=0; last_bones.clear(); last_weights.clear(); index_array.clear(); vertex_array.clear(); smooth_groups.clear(); } void SurfaceTool::_bind_methods() { ClassDB::bind_method(_MD("begin","primitive"),&SurfaceTool::begin); ClassDB::bind_method(_MD("add_vertex","vertex"),&SurfaceTool::add_vertex); ClassDB::bind_method(_MD("add_color","color"),&SurfaceTool::add_color); ClassDB::bind_method(_MD("add_normal","normal"),&SurfaceTool::add_normal); ClassDB::bind_method(_MD("add_tangent","tangent"),&SurfaceTool::add_tangent); ClassDB::bind_method(_MD("add_uv","uv"),&SurfaceTool::add_uv); ClassDB::bind_method(_MD("add_uv2","uv2"),&SurfaceTool::add_uv2); ClassDB::bind_method(_MD("add_bones","bones"),&SurfaceTool::add_bones); ClassDB::bind_method(_MD("add_weights","weights"),&SurfaceTool::add_weights); ClassDB::bind_method(_MD("add_smooth_group","smooth"),&SurfaceTool::add_smooth_group); ClassDB::bind_method(_MD("add_triangle_fan", "vertexes", "uvs", "colors", "uv2s", "normals", "tangents"),&SurfaceTool::add_triangle_fan, DEFVAL(Vector()), DEFVAL(Vector()), DEFVAL(Vector()),DEFVAL(Vector()), DEFVAL(Vector())); ClassDB::bind_method(_MD("set_material","material:Material"),&SurfaceTool::set_material); ClassDB::bind_method(_MD("index"),&SurfaceTool::index); ClassDB::bind_method(_MD("deindex"),&SurfaceTool::deindex); ///ClassDB::bind_method(_MD("generate_flat_normals"),&SurfaceTool::generate_flat_normals); ClassDB::bind_method(_MD("generate_normals"),&SurfaceTool::generate_normals); ClassDB::bind_method(_MD("add_index", "index"), &SurfaceTool::add_index); ClassDB::bind_method(_MD("commit:Mesh","existing:Mesh"),&SurfaceTool::commit,DEFVAL(Variant())); ClassDB::bind_method(_MD("clear"),&SurfaceTool::clear); } SurfaceTool::SurfaceTool() { first=false; begun=false; primitive=Mesh::PRIMITIVE_LINES; format=0; }