// // Lol Engine // // Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net> // (c) 2009-2013 Cédric Lecacheur <jordx@free.fr> // (c) 2009-2013 Benjamin "Touky" Huet <huet.benjamin@gmail.com> // This program is free software; you can redistribute it and/or // modify it under the terms of the Do What The Fuck You Want To // Public License, Version 2, as published by Sam Hocevar. See // http://www.wtfpl.net/ for more details. // // // The EasyMesh class // ------------------ // #if !defined __EASYMESH_EASYMESH_H__ #define __EASYMESH_EASYMESH_H__ namespace lol { //Utility struct to convert command code to pseudo-bytecode struct CommandStack { private: Array<int, int, int> m_commands; Array<float> m_floats; Array<int> m_ints; int m_f_cur; int m_i_cur; public: //cmd storage void AddCmd(int cmd) { m_commands.Push(cmd, m_floats.Count(), m_ints.Count()); } int GetCmdNb() { return m_commands.Count(); } int GetCmd(int i) { ASSERT(0 <= i && i < m_commands.Count()); m_f_cur = m_commands[i].m2; m_i_cur = m_commands[i].m3; return m_commands[i].m1; } //GETTER inline float F() { return m_floats[m_f_cur++]; } inline int I() { return m_ints[m_i_cur++]; } inline int E() { return I(); } inline bool B() { return !!I(); } inline vec2 V2() { vec2 v(F()); v.y = F(); return v; } inline vec3 V3() { vec3 v(V2(), 0.f); v.z = F(); return v; } inline vec4 V4() { vec4 v(V3(), 0.f); v.w = F(); return v; } inline ivec2 IV2() { ivec2 v(I()); v.y = I(); return v; } inline ivec3 IV3() { ivec3 v(IV2(), 0); v.z = I(); return v; } inline ivec4 IV4() { ivec4 v(IV3(), 0); v.w = I(); return v; } //Alternate getters inline void GetValue(float &f) { f = F(); } inline void GetValue(int &i) { i = I(); } inline void GetValue(bool &b) { b = B(); } inline void GetValue(vec2 &v2) { v2 = V2(); } inline void GetValue(vec3 &v3) { v3 = V3(); } inline void GetValue(vec4 &v4) { v4 = V4(); } inline void GetValue(ivec2 &iv2) { iv2 = IV2(); } inline void GetValue(ivec3 &iv3) { iv3 = IV3(); } inline void GetValue(ivec4 &iv4) { iv4 = IV4(); } //For Safe Enum template< class T > inline void GetValue(T &i) { i = T((typename T::Value)I()); } //SETTER CommandStack &operator<<(int i) { m_ints << i; return *this; } CommandStack &operator<<(float f) { m_floats << f; return *this; } CommandStack &operator<<(bool b) { return (*this << (int)b); } CommandStack &operator<<(vec2 v) { return (*this << v.x << v.y); } CommandStack &operator<<(vec3 v) { return (*this << v.xy << v.z); } CommandStack &operator<<(vec4 v) { return (*this << v.xyz << v.w); } CommandStack &operator<<(ivec2 iv) { return (*this << iv.x << iv.y); } CommandStack &operator<<(ivec3 iv) { return (*this << iv.xy << iv.z); } CommandStack &operator<<(ivec4 iv) { return (*this << iv.xyz << iv.w); } }; //Utility enum for renderers struct MeshRender { enum Value { NeedData, NeedConvert, CanRender, IgnoreRender, Max } m_value; inline MeshRender(Value v) : m_value(v) {} inline MeshRender() : m_value(NeedData) {} inline operator Value() { return m_value; } }; //Vertex datas for easymesh vertex list. //TODO : <COORD, NORM, COLOR, UV> struct VertexData { vec3 m_coord; vec3 m_normal; vec4 m_color; vec4 m_texcoord; ivec4 m_bone_id; vec4 m_bone_weight; VertexData(vec3 new_coord = vec3(0.f), vec3 new_normal = vec3(0.f, 1.f, 0.f), vec4 new_color = vec4(0.f), vec4 new_texcoord = vec4(0.f), ivec4 new_bone_id = ivec4(0), vec4 new_bone_weight= vec4(0.f)) { m_coord = new_coord; m_normal = new_normal; m_color = new_color; m_texcoord = new_texcoord; m_bone_id = new_bone_id; m_bone_weight = new_bone_weight; } }; //Base class to declare shader datas class GpuShaderData { friend class GpuEasyMeshData; protected: GpuShaderData(); public: //-- GpuShaderData(uint16_t vert_decl_flags, Shader* shader, DebugRenderMode render_mode); virtual ~GpuShaderData(); //-- void AddUniform(const lol::String &new_uniform); void AddAttribute(VertexUsage usage, int index); ShaderUniform const *GetUniform(const lol::String &uniform); ShaderAttrib const *GetAttribute(VertexUsage usage, int index); //-- virtual void SetupShaderDatas(mat4 const &model) { UNUSED(model); } //-- protected: uint16_t m_vert_decl_flags; Shader* m_shader; DebugRenderMode m_render_mode; Array<lol::String, ShaderUniform> m_shader_uniform; Array<ShaderAttrib> m_shader_attrib; }; class DefaultShaderData : public GpuShaderData { public: //--- DefaultShaderData(DebugRenderMode render_mode); DefaultShaderData(uint16_t vert_decl_flags, Shader* shader, bool with_UV); virtual ~DefaultShaderData() {} void StoreUniformNames(); //--- void SetupDefaultData(bool with_UV); virtual void SetupShaderDatas(mat4 const &model); //-- Array<String> m_uniform_names; }; class GpuEasyMeshData { friend class EasyMesh; public: //--- GpuEasyMeshData(); ~GpuEasyMeshData(); //--- void AddGpuData(GpuShaderData* gpudata, class EasyMesh* src_mesh); void RenderMeshData(mat4 const &model); private: void SetupVertexData(uint16_t vdecl_flags, EasyMesh* src_mesh); Array<GpuShaderData*> m_gpudatas; //uint16_t are the vdecl/vbo flags to avoid copy same vdecl several times. Array<uint16_t, VertexDeclaration*, VertexBuffer*> m_vdatas; int m_vertexcount; //We only need only one ibo for the whole mesh IndexBuffer * m_ibo; int m_indexcount; }; struct MeshBuildOperation { enum Value { //When this flag is up, negative scaling will not invert faces. ScaleWinding = (1 << 0), CommandRecording = (1 << 1), CommandExecution = (1 << 2), QuadWeighting = (1 << 3), IgnoreQuadWeighting = (1 << 4), All = 0xffffffff } m_value; inline MeshBuildOperation(Value v) : m_value(v) {} inline MeshBuildOperation(uint64_t i) : m_value((Value)i) {} inline operator Value() { return m_value; } }; struct EasyMeshCmdType { enum Value { MeshCsg = 0, LoopStart, LoopEnd, OpenBrace, CloseBrace, ScaleWinding, QuadWeighting, SetColorA, SetColorB, SetVertColor, Translate, Rotate, RadialJitter, MeshTranform, Scale, DupAndScale, Chamfer, SplitTriangles, SmoothMesh, AppendCylinder, AppendCapsule, AppendTorus, AppendBox, AppendStar, AppendExpandedStar, AppendDisc, AppendSimpleTriangle, AppendSimpleQuad, AppendCog, Max } m_value; inline EasyMeshCmdType(Value v) : m_value(v) {} inline operator Value() { return m_value; } inline int Value() { return m_value; } }; struct MeshType { enum Value { Triangle = 0, Quad, Box, Sphere, Capsule, Torus, Cylinder, Disc, Star, ExpandedStar, Cog, Max } m_value; inline MeshType(Value v) : m_value(v) {} inline operator Value() { return m_value; } }; //TODO : Add other Build type struct TexCoordBuildType { enum Value { TriangleDefault = 0, QuadDefault = 0, BoxDefault = 0, SphereDefault = 0, CapsuleDefault = 0, TorusDefault = 0, CylinderDefault = 0, DiscDefault = 0, StarDefault = 0, ExpandedStarDefault = 0, CogDefault = 0, //NEVER FORGET TO INCREMENT THIS WHEN ADDING A VALUE Max = 1 } m_value; inline TexCoordBuildType() : m_value(TriangleDefault) {} inline TexCoordBuildType(Value v) : m_value(v) {} inline TexCoordBuildType(int v) : m_value((Value)v) {} inline operator Value() { return m_value; } }; struct MeshFaceType { enum Value { BoxFront = 0, BoxLeft = 1, BoxBack = 2, BoxRight = 3, BoxTop = 4, BoxBottom = 5, QuadDefault = 0, //NEVER FORGET TO INCREMENT THIS WHEN ADDING A VALUE Max = 6 } m_value; inline MeshFaceType(Value v) : m_value(v) {} inline operator Value() { return m_value; } }; struct TexCoordPos { enum Value { BL, //BottomLeft BR, //BottomRight TL, //TopLeft TR //TopRight } m_value; inline TexCoordPos(Value v) : m_value(v) {} inline operator Value() { return m_value; } }; class EasyMeshBuildData { public: EasyMeshBuildData() { m_color_a = vec4(0.f, 0.f, 0.f, 1.f); m_color_b = vec4(0.f, 0.f, 0.f, 1.f); m_texcoord_offset = vec2(0.f); m_texcoord_offset2 = vec2(0.f); m_texcoord_scale = vec2(1.f); m_texcoord_scale2 = vec2(1.f); m_build_flags = 0; for (int i = 0; i < MeshType::Max; ++i) { m_texcoord_build_type[i] = TexCoordBuildType::TriangleDefault; m_texcoord_build_type2[i] = TexCoordBuildType::TriangleDefault; } } inline CommandStack &CmdStack() { return m_stack; } inline int &Cmdi() { return m_cmd_i; } inline Array<int, int> &LoopStack(){ return m_loop_stack; } inline vec4 &ColorA() { return m_color_a; } inline vec4 &ColorB() { return m_color_b; } inline vec2 &TexCoordOffset() { return m_texcoord_offset; } inline vec2 &TexCoordScale() { return m_texcoord_scale; } inline vec2 &TexCoordOffset2() { return m_texcoord_offset2; } inline vec2 &TexCoordScale2() { return m_texcoord_scale2; } //UV1 void SetTexCoordBuildType(MeshType mt, TexCoordBuildType tcbt) { m_texcoord_build_type[mt] = (1 << (tcbt + 1)) | (m_texcoord_build_type[mt] & 1); } TexCoordBuildType GetTexCoordBuildType(MeshType mt) { uint32_t flag = (uint32_t)((m_texcoord_build_type[mt] & ~(1)) >> 1); int i = 0; while (flag >>= 1) i++; return TexCoordBuildType(i); } void SetTexCoordCustomBuild(MeshType mt, MeshFaceType face, vec2 BL, vec2 TR) { if (face >= m_texcoord_custom_build[mt].Count()) m_texcoord_custom_build[mt].Resize(face + 1); m_texcoord_custom_build[mt][face].m1 = BL; m_texcoord_custom_build[mt][face].m2 = TR; m_texcoord_build_type[mt] |= 1; } void ClearTexCoordCustomBuild(MeshType mt) { m_texcoord_build_type[mt] &= ~1; } /* FIXME : Do something better ? */ vec2 TexCoord(MeshType mt, TexCoordPos tcp, MeshFaceType face) { vec2 BL = vec2(0.f); vec2 TR = vec2(0.f); if (m_texcoord_build_type[mt] & 1 && face < m_texcoord_custom_build[mt].Count()) { BL = m_texcoord_custom_build[mt][face].m1; TR = m_texcoord_custom_build[mt][face].m2; } else { /* unused for now, but will be if new BuildType are added. */ TexCoordBuildType tcbt = GetTexCoordBuildType(mt); UNUSED(tcbt); if (mt == MeshType::Triangle) mt = mt; else if (mt == MeshType::Quad) { //There's nothin' else than QuadDefault BL = vec2(0.f); TR = vec2(1.f); } else if (mt == MeshType::Box) { vec2 data[][2] = { //TexCoordBuildType::BoxDefault { vec2(0.f), vec2(.5f) }, { vec2(.5f, 0.f), vec2(1.f, .5f) }, { vec2(0.f), vec2(.5f) }, { vec2(.5f, 0.f), vec2(1.f, .5f) }, { vec2(0.f, .5f), vec2(.5f, 1.f) }, { vec2(.5f, .5f), vec2(1.f, 1.f) } }; BL = data[face][0]; //[tcbt] TR = data[face][1]; //[tcbt] } else if (mt == MeshType::Sphere) mt = mt; else if (mt == MeshType::Capsule) mt = mt; else if (mt == MeshType::Torus) mt = mt; else if (mt == MeshType::Cylinder) mt = mt; else if (mt == MeshType::Disc) mt = mt; else if (mt == MeshType::Star) mt = mt; else if (mt == MeshType::ExpandedStar) mt = mt; else if (mt == MeshType::Cog) mt = mt; } vec2 res = vec2(.0f); if (tcp == TexCoordPos::BL) res = BL; else if (tcp == TexCoordPos::BR) res = vec2(TR.x, BL.y); else if (tcp == TexCoordPos::TL) res = vec2(BL.x, TR.y); else if (tcp == TexCoordPos::TR) res = TR; return res * m_texcoord_scale + m_texcoord_offset2; } //UV2 void SetTexCoordBuildType2(MeshType mt, TexCoordBuildType tcbt) { m_texcoord_build_type2[mt] = (1 << (tcbt + 1)) | (m_texcoord_build_type2[mt] & 1); } TexCoordBuildType GetTexCoordBuildType2(MeshType mt) { uint32_t flag = ((m_texcoord_build_type2[mt] & ~(1)) >> 1); int i = 0; while (flag >>= 1) i++; return TexCoordBuildType(i); } void SetTexCoordCustomBuild2(MeshType mt, MeshFaceType face, vec2 BL, vec2 TR) { if (face >= m_texcoord_custom_build2[mt].Count()) m_texcoord_custom_build2[mt].Resize(face + 1); m_texcoord_custom_build2[mt][face].m1 = BL; m_texcoord_custom_build2[mt][face].m2 = TR; m_texcoord_build_type2[mt] |= 1; } void ClearTexCoordCustomBuild2(MeshType mt) { m_texcoord_build_type2[mt] &= ~1; } vec2 TexCoord2(MeshType mt, TexCoordPos tcp, MeshFaceType face) { vec2 BL = vec2(0.f); vec2 TR = vec2(0.f); if (m_texcoord_build_type2[mt] & 1 && face < m_texcoord_custom_build2[mt].Count()) { BL = m_texcoord_custom_build2[mt][face].m1; TR = m_texcoord_custom_build2[mt][face].m2; } else { TexCoordBuildType tcbt = GetTexCoordBuildType2(mt); UNUSED(tcbt); if (mt == MeshType::Triangle) mt = mt; else if (mt == MeshType::Quad) { //There's nothin' else than QuadDefault BL = vec2(0.f); TR = vec2(1.f); } else if (mt == MeshType::Box) { vec2 data[][2] = { //TexCoordBuildType::BoxDefault { vec2(0.f), vec2(.5f) }, { vec2(.5f, 0.f), vec2(1.f, .5f) }, { vec2(0.f), vec2(.5f) }, { vec2(.5f, 0.f), vec2(1.f, .5f) }, { vec2(0.f, .5f), vec2(.5f, 1.f) }, { vec2(.5f, .5f), vec2(1.f, 1.f) } }; BL = data[face][0]; //[tcbt] TR = data[face][1]; //[tcbt] } else if (mt == MeshType::Sphere) mt = mt; else if (mt == MeshType::Capsule) mt = mt; else if (mt == MeshType::Torus) mt = mt; else if (mt == MeshType::Cylinder) mt = mt; else if (mt == MeshType::Disc) mt = mt; else if (mt == MeshType::Star) mt = mt; else if (mt == MeshType::ExpandedStar) mt = mt; else if (mt == MeshType::Cog) mt = mt; } vec2 res = vec2(.0f); if (tcp == TexCoordPos::BL) res = BL; else if (tcp == TexCoordPos::BR) res = vec2(TR.x, BL.y); else if (tcp == TexCoordPos::TL) res = vec2(BL.x, TR.y); else if (tcp == TexCoordPos::TR) res = TR; return res * m_texcoord_scale + m_texcoord_offset2; } inline bool IsEnabled(MeshBuildOperation mbo) { return (m_build_flags & mbo) != 0; } inline void Enable(MeshBuildOperation mbo) { m_build_flags |= mbo; } inline void Disable(MeshBuildOperation mbo) { m_build_flags &= ~mbo; } inline void Toggle(MeshBuildOperation mbo) { m_build_flags ^= mbo; } inline void Set(MeshBuildOperation mbo, bool value) { if (value) Enable(mbo); else Disable(mbo); } public: CommandStack m_stack; int m_cmd_i; Array<int, int> m_loop_stack; vec4 m_color_a; vec4 m_color_b; vec2 m_texcoord_offset; vec2 m_texcoord_offset2; vec2 m_texcoord_scale; vec2 m_texcoord_scale2; Array<vec2, vec2> m_texcoord_custom_build[MeshType::Max]; Array<vec2, vec2> m_texcoord_custom_build2[MeshType::Max]; uint32_t m_texcoord_build_type[MeshType::Max]; uint32_t m_texcoord_build_type2[MeshType::Max]; uint32_t m_build_flags; }; /* A safe enum for MeshCSG operations. */ struct CSGUsage { enum Value { Union = 0, Substract, SubstractLoss, //will remove B from A, but not add inverted B And, Xor, Max } m_value; inline CSGUsage() : m_value(Union) {} inline CSGUsage(Value v) : m_value(v) {} inline CSGUsage(int v) : m_value((Value)v) {} inline operator Value() { return m_value; } }; /* A safe enum for VertexDictionnary operations. */ struct VDictType { enum Value { DoesNotExist=-3, Alone=-2, Master=-1 } m_value; inline VDictType(Value v) : m_value(v) {} inline operator Value() { return m_value; } }; /* TODO : replace VDict by a proper Half-edge system */ //a class whose goal is to keep a list of the adjacent vertices for mesh operations purposes class VertexDictionnary { public: int FindVertexMaster(const int search_idx); bool FindMatchingVertices(const int search_idx, Array<int> &matching_ids); bool FindConnectedVertices(const int search_idx, const Array<uint16_t> &tri_list, const int tri0, Array<int> &connected_vert, Array<int> const *ignored_tri = nullptr); bool FindConnectedTriangles(const int search_idx, const Array<uint16_t> &tri_list, const int tri0, Array<int> &connected_tri, Array<int> const *ignored_tri = nullptr); bool FindConnectedTriangles(const ivec2 &search_idx, const Array<uint16_t> &tri_list, const int tri0, Array<int> &connected_tri, Array<int> const *ignored_tri = nullptr); bool FindConnectedTriangles(const ivec3 &search_idx, const Array<uint16_t> &tri_list, const int tri0, Array<int> &connected_tri, Array<int> const *ignored_tri = nullptr); void AddVertex(int vert_id, vec3 vert_coord); bool GetMasterList(Array<int> &ret_master_list) { ret_master_list = master_list; return ret_master_list.Count() > 0; } void Clear() { vertex_list.Empty(); } private: //<VertexId, VertexLocation, VertexMasterId> Array<int, vec3, int> vertex_list; //List of the master_ vertices Array<int> master_list; }; struct Axis { enum Value { X, Y, Z } m_value; inline Axis() : m_value(X) {} inline Axis(Value v) : m_value(v) {} inline operator Value() { return m_value; } }; class EasyMesh { friend class EasyMeshParser; friend class GpuEasyMeshData; public: EasyMesh(); EasyMesh(const EasyMesh& em); bool Compile(char const *command); void ExecuteCmdStack(); void MeshConvert(GpuShaderData* new_gpu_sdata); void MeshConvert(Shader* ProvidedShader = nullptr); bool Render(mat4 const &model); MeshRender GetMeshState() { return m_state; } bool SetRender(bool should_render); private: void UpdateVertexDict(Array< int, int > &vertex_dict); //------------------------------------------------------------------------- //Mesh CSG operations //------------------------------------------------------------------------- private: void MeshCsg(CSGUsage csg_operation); public: /* [cmd:csgu] Performs a Union operation as (mesh0_Outside + mesh1_Outside) */ void CsgUnion() { MeshCsg(CSGUsage::Union); } /* [cmd:csgs] Performs a Substract operation as (mesh0_Outside + mesh1_Inside-inverted) */ void CsgSub() { MeshCsg(CSGUsage::Substract); } /* [cmd:csgsl] Performs a Substract operation without keeping the mesh1 part */ void CsgSubL() { MeshCsg(CSGUsage::SubstractLoss); } /* [cmd:csga] Performs an And operation as (mesh0_Inside + mesh1_Inside) */ void CsgAnd() { MeshCsg(CSGUsage::And); } /* [cmd:csgx] Performs a Xor operation as (m0_Outside/m0_Inside-inverted + m1_Outside/m1_Inside-inverted) */ void CsgXor() { MeshCsg(CSGUsage::Xor); } //------------------------------------------------------------------------- //Mesh Base operations //------------------------------------------------------------------------- public: /* [cmd:lp[ ]] will perform a loop of loopnb */ void LoopStart(int loopnb); /* No cmd, implicit ] */ void LoopEnd(); /* [cmd:[] from this point onward, any operation will not be performed on previous vertices */ void OpenBrace(); /* [cmd:]] Merge current vertices with previous context */ void CloseBrace(); /* [cmd:tsw] When active, on negative-scaling, normal-vector correction will not occur */ void ToggleScaleWinding(); /* [cmd:tqw] When active, quad will have a fifth center vertex */ void ToggleQuadWeighting(); /* [cmd:sc] Set both color */ void SetCurColor(vec4 const &color); /* [cmd:sca] Set base color A */ void SetCurColorA(vec4 const &color); /* [cmd:scb] Set base color B */ void SetCurColorB(vec4 const &color); /* [cmd:scv] Sets all vertices in this scope color. */ void SetVertColor(vec4 const &color); //------------------------------------------------------------------------- //Internal : Basic triangle/vertex operations //------------------------------------------------------------------------- private: void AddVertex(vec3 const &coord); void AddDuplicateVertex(int i); void AddLerpVertex(int i, int j, float alpha); void AddLerpVertex(VertexData const &vi, VertexData const &vj, float alpha); VertexData GetLerpVertex(int i, int j, float alpha); VertexData GetLerpVertex(VertexData const &vi, VertexData const &vj, float alpha); void AppendQuad(int i1, int i2, int i3, int i4, int base); void AppendQuadDuplicateVerts(int i1, int i2, int i3, int i4, int base); void AppendTriangle(int i1, int i2, int i3, int base); void AppendTriangleDuplicateVerts(int i1, int i2, int i3, int base); void ComputeNormals(int start, int vcount); public: //DEBUG void ComputeTexCoord(float uv_scale, int uv_offset); //------------------------------------------------------------------------- //Internal : Vertices operations //------------------------------------------------------------------------- private: void SetTexCoordData(vec2 const &new_offset, vec2 const &new_scale); void SetTexCoordData2(vec2 const &new_offset, vec2 const &new_scale); void SetCurVertNormal(vec3 const &normal); void SetCurVertColor(vec4 const &color); void SetCurVertTexCoord(vec2 const &texcoord); void SetCurVertTexCoord2(vec2 const &texcoord); public: //------------------------------------------------------------------------- //Mesh transform operations //------------------------------------------------------------------------- /* [cmd:t/tx/ty/tz] Translate vertices - v : Translation quantity. */ void Translate(vec3 const &v); /* See Rotate */ void RotateX(float angle); /* See Rotate */ void RotateY(float angle); /* See Rotate */ void RotateZ(float angle); /* [cmd:r/rx/ry/rz] Rotate vertices - angle : rotation quantity. - axis : rotation axis. */ void Rotate(float angle, vec3 const &axis); /* [cmd:rj] Randomly move vertices along Origin-to-vertex as f(vtx) = vtx + o2v * (1.0 + rand(r)) - r : jitter maximum value. */ void RadialJitter(float r); /* [cmd:tax] multiply axis y&z by x as f(y) = y * (1.0 + (ny * x + xoff)) - ny : value of n for y. - nz : value of n for z. - xoff : value of xoff. - absolute (def:true) : if (true) Multiply will use an absolute x. */ void TaperX(float ny, float nz, float xoff=0.f, bool absolute=true); /* [cmd:tay] Same as TaperX, with Y */ void TaperY(float nx, float nz, float yoff=0.f, bool absolute=true); /* [cmd:taz] Same as TaperX, with Z */ void TaperZ(float nx, float ny, float zoff=0.f, bool absolute=true); /* [cmd:twx] Twist vertices around x axis with x as rotation value as f(p) = (RotateX(x * t + toff) * p) - t : Angle multiplier. - toff : Applied offset. */ void TwistX(float t, float toff=0.f); /* [cmd:twy] Same as TwistX, with Y */ void TwistY(float t, float toff=0.f); /* [cmd:twz] Same as TwistX, with Z */ void TwistZ(float t, float toff=0.f); /* [cmd:shx] Shear vertices using x value as shear quantity as f(y) = y + (ny * x + xoff) - ny : Value of n for y. - nz : Value of n for z. - xoff : Value of xoff. - absolute (def:true) : if (true) Multiply will use an absolute x. */ void ShearX(float ny, float nz, float xoff=0.f, bool absolute=true); /* [cmd:shy] Same as ShearX, with Y */ void ShearY(float nx, float nz, float yoff=0.f, bool absolute=true); /* [cmd:shz] Same as ShearX, with Z */ void ShearZ(float nx, float ny, float zoff=0.f, bool absolute=true); /* [cmd:stx] Stretch vertices using x value as stretch quantity as f(y) = y + (pow(x, ny) + xoff) - ny : Value of n for y. - nz : Value of n for z. - xoff : Value of xoff. */ void StretchX(float ny, float nz, float xoff=0.f); /* [cmd:sty] Same as StretchX, with Y */ void StretchY(float nx, float nz, float yoff=0.f); /* [cmd:stz] Same as StretchX, with Z */ void StretchZ(float nx, float ny, float zoff=0.f); /* [cmd:bdxy] Bend vertices using x as bend quantity along y axis using f(p) = (RotateY(x * t + toff) * p) - t : Angle multiplier. - xoff : Applied offset. */ void BendXY(float t, float toff=0.f); /* [cmd:bdxz] Same as BendXY, with X & Z */ void BendXZ(float t, float toff=0.f); /* [cmd:bdyx] Same as BendXY, with Y & X */ void BendYX(float t, float toff=0.f); /* [cmd:bdyz] Same as BendXY, with Y & Z */ void BendYZ(float t, float toff=0.f); /* [cmd:bdzx] Same as BendXY, with Z & X */ void BendZX(float t, float toff=0.f); /* [cmd:bdzy] Same as BendXY, with Z & Y */ void BendZY(float t, float toff=0.f); private: struct MeshTransform { enum Value { Taper, Twist, Bend, Stretch, Shear } m_value; inline MeshTransform() : m_value(Taper) {} inline MeshTransform(Value v) : m_value(v) {} inline operator Value() { return m_value; } }; void DoMeshTransform(MeshTransform ct, Axis axis0, Axis axis1, float n0, float n1, float noff, bool absolute=false); public: /* [cmd:s/sx/sy/sz] Scale vertices - s : scale quantity. */ void Scale(vec3 const &s); void Scale(float s) { Scale(vec3(s)); } /* [cmd:mx] Mirror vertices through X-plane Acts as an OpenBrace */ void MirrorX(); /* [cmd:my] Mirror vertices through Y-plane Acts as an OpenBrace */ void MirrorY(); /* [cmd:mz] Mirror vertices through Z-plane Acts as an OpenBrace */ void MirrorZ(); /* [no-cmd] Duplicates vertices and scale duplicate Acts as an OpenBrace */ void DupAndScale(vec3 const &s, bool open_brace=false); /* [cmd:ch] Performs a chamfer operation //TODO : Make it work - f : Chamfer quantity. */ void Chamfer(float f); /* [cmd:splt] split triangles in 4 smaller ones - pass : Number of pass applied. */ void SplitTriangles(int pass); private: void SplitTriangles(int pass, VertexDictionnary *vert_dict); public: /* [cmd:smth] Smooth the mesh by subdivising it. - pass : a pass is made of (n0 split then n1 smooth) repeat. - split_per_pass : n0 value in above explanation. - smooth_per_pass : n1 value in above explanation. */ void SmoothMesh(int pass, int split_per_pass, int smooth_per_pass); //------------------------------------------------------------------------- //Mesh shape operations //------------------------------------------------------------------------- /* [cmd:ac] Cylinder centered on (0,0,0) with BBox [-.5*max(d1, d2), -.5*h, -.5*max(d1, d2)] - nbsides : Number of sides. [+.5*max(d1, d2), +.5*h, +.5*max(d1, d2)] - h : Height of the cylinder. - d1 : Lower diameter. - d2 : Upper diameter. - dualside : if (true) will also create inner sides : TOOD:TOREMOVE?? : needed ? - smooth : if (true) will smooth normals : TOOD:TOREMOVE : smooth should be handled elsewhere - close : if (true) will add discs to close the cylinder */ void AppendCylinder(int nsides, float h, float d1, float d2, bool dualside=false, bool smooth=false, bool close=false); /* [cmd:asph] Sphere centered on (0,0,0) with BBox [-.5*d][.5*d] - ndivisions : number of subdivisions each Sphere triangle will sustain. - d : Diameter. */ void AppendSphere(int ndivisions, float d); /* [cmd:acap] Capsule centered on (0,0,0) with BBox [-.5*d, -(.5*d+h), -.5*d][.5*d, (.5*d+h), .5*d] - ndivisions : number of subdivisions each Sphere triangle will sustain. - h : Inner height. - d : Diameter. */ void AppendCapsule(int ndivisions, float h, float d); /* [cmd:ato] Torus centered on (0,0,0) with BBox [-.5*d2][.5*d2] - ndivisions : number of subdivisions of the torus. - d1 : Inner diameter. - d2 : Outer diameter. */ void AppendTorus(int ndivisions, float d1, float d2); /* [cmd:ab] Box centered on (0,0,0) with BBox [-.5 * size][.5 * size] - size : size of the box. - chamf : size of the chamfer. */ void AppendBox(vec3 const &size, float chamf=0.f); //Same as AppendBox void AppendSmoothChamfBox(vec3 const &size, float chamf); //Same as AppendBox void AppendFlatChamfBox(vec3 const &size, float chamf); //Same as AppendBox void AppendBox(vec3 const &size, float chamf, bool smooth); /* [cmd:as] Append a Star centered on (0,0,0) contained within a disc of "max(d1, d2)" diameter. - nbranches : Number of branches. - d1 : double Length of the branches. - d2 : double Length of the "branch" located between d1-branches. - fade : if (true) in-between branches use ColorB. - fade2 : if (true) Star branches use ColorB. */ void AppendStar(int nbranches, float d1, float d2, bool fade=false, bool fade2=false); /* [cmd:aes] Star centered on (0,0,0) contained within a disc of "max(max(d1, d2), max(d1 + extrad, d2 + extrad))" diameter. Expanded star branches use ColorB. - nbranches : Number of branches. - d1 : Double Length of the branches. - d2 : Double Length of the "branch" located between r1-branches. - extrad : Extra length added to expand all branches. */ void AppendExpandedStar(int nbranches, float d1, float d2, float extrad=0.f); /* [cmd:ad] Disc centered on (0,0,0) with d diameter. - nbsides : Number of sides. - d : Diameter. - fade : if (true) Outer vertices will use ColorB */ void AppendDisc(int nsides, float d, bool fade=false); /* [cmd:at] Triangle centered on (0,0,0) contained within a disc of "d" diameter. - d : diameter of the containing disc.. - fade : if (true) 2nd & 3rd Vertices will use ColorB */ void AppendSimpleTriangle(float d, bool fade=false); /* [cmd:aq] Quad centered on (0,0,0) contained within BBox [-size*.5f, 0, -size*.5f][size*.5f, 0, size*.5f] - size : Size of quad. - fade : if (true) 3rd & 4th Vertices will use ColorB */ void AppendSimpleQuad(float size, bool fade=false); private: //complex version of above one void AppendSimpleQuad(vec2 p1, vec2 p2, float z=0.f, bool fade=false); public: /* [cmd:acg] Gear centered on (0,0,0) contained within BBox [-.5*max(d1,d2), -.5*h, -.5*max(d1, d2)] - h : Height of the Gear. [+.5*max(d1,d2), +.5*h, +.5*max(d1, d2)] - d10 : Upper Inner diameter. - d20 : Lower Inner diameter. - d1 : Upper Outer diameter. - d2 : Lower Outer diameter. - d12 : Upper Cog diameter. - d22 : Lower Cog diameter. - sidemul : multiplier for the size of the cogs. - offset : useless */ void AppendCog(int nbsides, float h, float d10, float d20, float d11, float d21, float d12, float d22, float sidemul=0.f, bool offset=false); //------------------------------------------------------------------------- //TODO : Mesh Bone operations //------------------------------------------------------------------------- //void AddBone(int parent_id) {} //Convenience functions public: int GetVertexCount() { return m_vert.Count(); } vec3 const &GetVertexLocation(int i) { return m_vert[i].m_coord; } private: Array<uint16_t> m_indices; Array<VertexData> m_vert; //<vert count, indices count> Array<int, int> m_cursors; MeshRender m_state; GpuEasyMeshData m_gpu_data; public: inline EasyMeshBuildData* BD() { if (!m_build_data) m_build_data = new EasyMeshBuildData(); return m_build_data; }; private: class EasyMeshBuildData* m_build_data; }; } /* namespace lol */ #endif /* __EASYMESH_EASYMESH_H__ */