├── .gitignore ├── BOUNDARY_CONTOURS.png ├── README.md ├── TRIANGLES.png ├── exsample └── gdi │ ├── console.sln │ ├── console.vcxproj │ ├── console.vcxproj.user │ ├── main.cpp │ ├── window.cpp │ └── window.h └── tesselator ├── detail ├── cdt.inl ├── dict.hpp ├── geometry.hpp ├── mesh.hpp ├── mono.hpp ├── normal.inl ├── public.h ├── sweep.hpp └── sweep.inl └── tesselator.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /BOUNDARY_CONTOURS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/BOUNDARY_CONTOURS.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tesselator 2 | 3 | https://github.com/sdragonx/libtess 4 | 5 | this is refactored version of the original libtess which comes with the GLU reference implementation. 6 | this is C++ version(c++98), Using STL as memory pool. 7 | Independent version without any third-party libraries, header files only. 8 | 9 | 重构版本的 gluTesselation 库,C++98 版本,使用了 STL 容器做内存缓存。 10 | 不依赖任何三方库,独立版本,只需要引用头文件就能使用。 11 | 12 | 琢磨了几个月,这次心血来潮,重构了代码,用了十来天时间(记不清了)。 13 | 原版本是个C库,到处是跳转和 #define,要把这些分散的函数封装成类,难度颇大,改动一处错误百出。 14 | libtess2 作者对原代码重构过一次,但还是 C 版本,而且整体结构变动不大。 15 | 这次重构,看着 libtess2 的代码,又看着原版代码,为了查找错误,前后对照了几遍!吐血! 16 | 这次重构成 C++ 版本,是第一步,之后会进行后续优化。 17 | 18 | 也很纳闷,这么多年了,没有人重做一个OpenGL的三角形分解库,一直是 SGI 的这个 libtess。 19 | GPC 简单易用,但是开源协议不友好;poly2tri 比 libtess快,但效果不如 libtess 好,有时候很不稳定。 20 | 希望我这个代码做个好的开头,有人能构建出更好更快的库,同时开源协议要友好。 21 | 22 | For OpenGL, For Open source !!! 23 | 24 | # main class: 25 |

26 | class Tesselator  
27 | {  
28 |   int init();  
29 |   void dispose();  
30 |   int add_contour( int size, const void* pointer, int stride, int count );  
31 |   int tesselate( TessWindingRule windingRule, TessElementType elementType, int polySize = 3);  
32 | };
33 | 
34 | # Exsample: 35 |

36 | 
37 | #define LIBTESS_USE_VEC2
38 | 
39 | #include 
40 | #include 
41 | 
42 | struct vec2f  
43 | {  
44 |     float x, y;  
45 | };  
46 |   
47 | //tesselate polygons
48 | libtess::Tesselator tess;  
49 | tess.init();  
50 | 
51 | std::vector points;  
52 | //points.push_back(...)//add some points  
53 | tess.add_contour( 2, &points[0], sizeof(Vec2), points.size() );  
54 | //tess.AddContour( ... );  
55 | tess.Tesselate( libtess::TESS_WINDING_ODD, libtess::TESS_TRIANGLES );  
56 |   
57 | //OpenGL drawing:
58 | void draw_elements(int shape, const Vec2* vs, const int* indices, int size)
59 | {
60 |     #ifdef LIBTESS_HIGH_PRECISION
61 |     glVertexPointer(2, GL_DOUBLE, sizeof(Vec2), vs);
62 |     #else
63 |     glVertexPointer(2, GL_FLOAT, sizeof(Vec2), vs);
64 |     #endif
65 |     glEnableClientState(GL_VERTEX_ARRAY);
66 |     glDrawElements(shape, size, GL_UNSIGNED_INT, indices);
67 |     glDisableClientState(GL_VERTEX_ARRAY);
68 | }
69 | 
70 | draw_elements(CGL_TRIANGLES, &tess.vertices[0], &tess.elements[0], tess.elements.size());
71 | 
72 | 73 | TESS_TRIANGLES执行结果: 74 | ![TESS_TRIANGLES执行结果](https://github.com/sdragonx/libtess/blob/master/TRIANGLES.png) 75 | TESS_BOUNDARY_CONTOURS执行结果: 76 | ![TESS_BOUNDARY_CONTOURS执行结果](https://github.com/sdragonx/libtess/blob/master/BOUNDARY_CONTOURS.png) 77 | 78 | -------------------------------------------------------------------------------- /TRIANGLES.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/TRIANGLES.png -------------------------------------------------------------------------------- /exsample/gdi/console.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console", "console.vcxproj", "{ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Debug|x64.ActiveCfg = Debug|x64 17 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Debug|x64.Build.0 = Debug|x64 18 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Debug|x86.ActiveCfg = Debug|Win32 19 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Debug|x86.Build.0 = Debug|Win32 20 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Release|x64.ActiveCfg = Release|x64 21 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Release|x64.Build.0 = Release|x64 22 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Release|x86.ActiveCfg = Release|Win32 23 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /exsample/gdi/console.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {ED3F6BC6-BDE5-47C7-9B13-239EBB5B6D4A} 24 | Win32Proj 25 | ConsoleApplication1 26 | 10.0.14393.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | bin\$(Platform)\ 76 | $(ExecutablePath) 77 | include;$(IncludePath) 78 | lib;$(LibraryPath) 79 | obj\$(Platform)\ 80 | 81 | 82 | true 83 | bin\$(Platform)\ 84 | $(ExecutablePath) 85 | include;$(IncludePath) 86 | lib;$(LibraryPath) 87 | obj\$(Platform)\ 88 | 89 | 90 | false 91 | $(ExecutablePath) 92 | include;$(IncludePath) 93 | lib;$(LibraryPath) 94 | bin\$(Platform)\ 95 | obj\$(Platform)\ 96 | 97 | 98 | false 99 | $(ExecutablePath) 100 | include;$(IncludePath) 101 | lib;$(LibraryPath) 102 | bin\$(Platform)\ 103 | obj\$(Platform)\ 104 | 105 | 106 | 107 | NotUsing 108 | Level3 109 | Disabled 110 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE 111 | 112 | 113 | Console 114 | 115 | 116 | 117 | 118 | NotUsing 119 | Level3 120 | Disabled 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE 122 | 123 | 124 | Console 125 | 126 | 127 | 128 | 129 | Level3 130 | NotUsing 131 | MaxSpeed 132 | true 133 | true 134 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE 135 | 136 | 137 | Console 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | Level3 145 | NotUsing 146 | MaxSpeed 147 | true 148 | true 149 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE 150 | 151 | 152 | Console 153 | true 154 | true 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /exsample/gdi/console.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(TargetDir) 5 | WindowsLocalDebugger 6 | 7 | 8 | $(TargetDir) 9 | WindowsLocalDebugger 10 | 11 | 12 | $(TargetDir) 13 | WindowsLocalDebugger 14 | 15 | 16 | $(TargetDir) 17 | WindowsLocalDebugger 18 | 19 | -------------------------------------------------------------------------------- /exsample/gdi/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/exsample/gdi/main.cpp -------------------------------------------------------------------------------- /exsample/gdi/window.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #include "Window.h" 4 | 5 | static HWND hWindow; // 主窗口 6 | static UINT uTimerID = 0; // 计时器 ID 7 | static HDC hMemDC; // 绘图设备 8 | static HBITMAP hBackBuffer; // 背景缓冲图片 9 | static HFONT hFont; // 字体 10 | 11 | // 主消息处理函数 12 | LRESULT CALLBACK WindowProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam); 13 | 14 | // 初始化函数 15 | void OnWMCreate(HWND hWnd); 16 | 17 | // 释放函数 18 | void OnWMDestroy(HWND hWnd); 19 | 20 | // 窗口大小改变 21 | void OnWMResize(HWND hWnd, int width, int height); 22 | 23 | // 内部绘制函数 24 | void OnWMPaint(HWND hWnd); 25 | 26 | // 返回窗口句柄 27 | HWND Window() 28 | { 29 | return hWindow; 30 | } 31 | 32 | // 窗口创建函数 33 | HWND InitWindow(LPCTSTR title, int width, int height, bool scalable) 34 | { 35 | HWND hWnd; 36 | HINSTANCE hInstance = GetModuleHandle(NULL); 37 | 38 | WNDCLASSEX wc = { 0 }; 39 | wc.cbSize = sizeof(WNDCLASSEX); 40 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 41 | wc.lpfnWndProc = WindowProc; 42 | wc.hInstance = hInstance; 43 | wc.hIcon = LoadIconW(hInstance, L"ICO_MAIN"); 44 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 45 | wc.hbrBackground = (HBRUSH) (COLOR_WINDOW); 46 | wc.lpszMenuName = NULL; 47 | wc.lpszClassName = CLASSNAME; 48 | wc.hIconSm = LoadIconW(hInstance, L"ICO_MAIN"); 49 | 50 | if (!RegisterClassEx(&wc)) { 51 | return NULL; 52 | } 53 | 54 | DWORD style; 55 | if (scalable) { 56 | // 窗口可以缩放 57 | style = WS_OVERLAPPEDWINDOW; 58 | } 59 | else { 60 | // 固定大小窗口 61 | style = WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION; 62 | } 63 | 64 | RECT rect = { 0, 0, static_cast(width), static_cast(height) }; 65 | AdjustWindowRectEx(&rect, style, FALSE, WS_EX_CLIENTEDGE); 66 | width = static_cast(rect.right - rect.left); 67 | height = static_cast(rect.bottom - rect.top); 68 | 69 | hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, CLASSNAME, title, style, 70 | CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL); 71 | 72 | if (hWnd) { 73 | ShowWindow(hWnd, SW_SHOW); 74 | } 75 | 76 | return hWnd; 77 | } 78 | 79 | // 主消息处理函数 80 | LRESULT CALLBACK WindowProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) 81 | { 82 | switch (Message) { 83 | case WM_CREATE: 84 | OnWMCreate(hWnd); 85 | break; 86 | case WM_DESTROY: 87 | OnWMDestroy(hWnd); 88 | break; 89 | case WM_CLOSE: 90 | //OutputDebugStringA("WM_CLOSE\n"); 91 | PostQuitMessage(0); 92 | break; 93 | case WM_QUIT: 94 | //OutputDebugStringA("WM_QUIT\n"); 95 | break; 96 | case WM_ERASEBKGND:// 不擦除背景 97 | return TRUE; 98 | case WM_SIZE:// 窗口大小改变 99 | OnWMResize(hWnd, LOWORD(lParam), HIWORD(lParam)); 100 | break; 101 | case WM_PAINT:// 窗口绘制事件 102 | OnWMPaint(hWnd); 103 | break; 104 | case WM_TIMER:// 计时器事件 105 | OnTimer(); 106 | Repaint(hWnd); 107 | break; 108 | 109 | case WM_KEYDOWN: 110 | if (wParam == VK_ESCAPE)PostQuitMessage(0); 111 | if (OnKeyDown)OnKeyDown(int(wParam)); 112 | break; 113 | case WM_KEYUP: 114 | if (OnKeyUp)OnKeyUp(int(wParam)); 115 | break; 116 | 117 | case WM_MOUSEMOVE: 118 | OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 119 | break; 120 | case WM_LBUTTONDOWN: 121 | OnMouseDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), VK_LBUTTON); 122 | break; 123 | case WM_LBUTTONUP: 124 | OnMouseUp(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), VK_LBUTTON); 125 | break; 126 | case WM_RBUTTONDOWN: 127 | OnMouseDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), VK_RBUTTON); 128 | break; 129 | case WM_RBUTTONUP: 130 | OnMouseUp(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), VK_RBUTTON); 131 | break; 132 | 133 | default: 134 | break; 135 | } 136 | return DefWindowProc(hWnd, Message, wParam, lParam); 137 | } 138 | 139 | // 重绘窗口 140 | void Repaint(HWND hwnd) 141 | { 142 | RECT rect; 143 | GetClientRect(hwnd, &rect); 144 | RedrawWindow(hwnd, &rect, 0, RDW_INVALIDATE | RDW_NOERASE | RDW_UPDATENOW); 145 | } 146 | 147 | // 执行消息循环 148 | void DoEvents() 149 | { 150 | MSG msg; 151 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 152 | if (!TranslateAccelerator(msg.hwnd, NULL, &msg)) { 153 | TranslateMessage(&msg); 154 | DispatchMessage(&msg); 155 | } 156 | } 157 | } 158 | 159 | // 判断程序是否运行 160 | bool Running() 161 | { 162 | return hWindow != NULL; 163 | } 164 | 165 | // 运行 APP 166 | int RunApp() 167 | { 168 | MSG msg; 169 | 170 | // 主消息循环: 171 | while (GetMessage(&msg, NULL, 0, 0)) { 172 | if (!TranslateAccelerator(msg.hwnd, NULL, &msg)) { 173 | TranslateMessage(&msg); 174 | DispatchMessage(&msg); 175 | } 176 | } 177 | return 0; 178 | } 179 | 180 | // 初始化函数 181 | void OnWMCreate(HWND hWnd) 182 | { 183 | hWindow = hWnd; 184 | 185 | // 获取窗口客户区大小 186 | RECT rect; 187 | GetClientRect(hWnd, &rect); 188 | 189 | // 创建内存 DC 和背景缓冲位图 190 | hMemDC = CreateCompatibleDC(NULL); 191 | hBackBuffer = CreateBitmap(rect.right - rect.left, rect.bottom - rect.top, 1, 32, NULL); 192 | SelectObject(hMemDC, hBackBuffer); 193 | 194 | // 创建字体 195 | hFont = CreateFont(32, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, GB2312_CHARSET, 0, 0, 0, DEFAULT_PITCH, TEXT("msyh")); 196 | 197 | // 选择字体 198 | SelectObject(hMemDC, hFont); 199 | 200 | // 字体透明 201 | SetBkMode(hMemDC, TRANSPARENT); 202 | 203 | // 设置计时器 204 | uTimerID = SetTimer(hWnd, 0, 40, NULL); 205 | } 206 | 207 | // 释放函数 208 | void OnWMDestroy(HWND hWnd) 209 | { 210 | if (uTimerID) { 211 | KillTimer(hWnd, uTimerID); 212 | uTimerID = 0; 213 | } 214 | 215 | // 删除双缓冲 216 | DeleteObject(hBackBuffer); 217 | DeleteDC(hMemDC); 218 | // 删除字体 219 | DeleteObject(hFont); 220 | 221 | // 退出消息 222 | //PostQuitMessage(0); 223 | 224 | hWindow = NULL; 225 | } 226 | 227 | // 窗口大小改变 228 | void OnWMResize(HWND hWnd, int width, int height) 229 | { 230 | // 重建绘图缓冲区 231 | RECT rect; 232 | GetClientRect(hWnd, &rect); 233 | 234 | // 创建内存 DC 和背景缓冲位图 235 | if (hBackBuffer) { 236 | DeleteObject(hBackBuffer); 237 | } 238 | hBackBuffer = CreateBitmap(rect.right - rect.left, rect.bottom - rect.top, 1, 32, NULL); 239 | SelectObject(hMemDC, hBackBuffer); 240 | } 241 | 242 | // 内部绘制函数 243 | void OnWMPaint(HWND hWnd) 244 | { 245 | PAINTSTRUCT ps; 246 | HDC hdc = BeginPaint(hWnd, &ps); 247 | RECT rect; 248 | 249 | // 获取窗口内部区域大小 250 | GetClientRect(hWnd, &rect); 251 | 252 | // 创建背景画刷 253 | HBRUSH brush = CreateSolidBrush(0xFF8000); 254 | 255 | // 填充背景 256 | FillRect(hMemDC, &rect, brush); 257 | 258 | // 删除画刷 259 | DeleteObject(brush); 260 | 261 | // 绘制游戏,用背景缓冲的 HDC 262 | OnPaint(hMemDC, rect); 263 | 264 | // 背景缓冲绘制到窗口 dc 265 | BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hMemDC, 0, 0, SRCCOPY); 266 | 267 | // 释放窗口 dc 268 | ReleaseDC(hWnd, hdc); 269 | 270 | EndPaint(hWnd, &ps); 271 | } 272 | 273 | // 显示消息对话框 274 | void msgbox(PCTSTR param, ...) 275 | { 276 | TCHAR buf[1024] = {}; 277 | va_list body; 278 | va_start(body, param); 279 | _vsntprintf(buf, 1024, param, body); 280 | va_end(body); 281 | 282 | MessageBox(hWindow, buf, TEXT("消息"), MB_OK); 283 | } 284 | -------------------------------------------------------------------------------- /exsample/gdi/window.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #ifndef WIN32_WINDOW_H 4 | #define WIN32_WINDOW_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define CLASSNAME TEXT("win32.window") 12 | 13 | // 返回窗口句柄 14 | HWND Window(); 15 | 16 | // 窗口创建函数 17 | HWND InitWindow(LPCTSTR title, int width, int height, bool scalable = true); 18 | 19 | // 重绘窗口 20 | void Repaint(HWND hWnd); 21 | 22 | // 判断程序是否运行 23 | bool Running(); 24 | 25 | // 执行消息循环 26 | void DoEvents(); 27 | 28 | // 运行 APP 29 | int RunApp(); 30 | 31 | // 32 | // 窗口事件 33 | // 34 | 35 | // 窗口大小改变 36 | void OnSize(int width, int height); 37 | 38 | // 按键按下 39 | void OnKeyDown(int key); 40 | 41 | // 按键弹起 42 | void OnKeyUp(int key); 43 | 44 | // 鼠标按下 45 | void OnMouseDown(int x, int y, int button); 46 | 47 | // 鼠标弹起 48 | void OnMouseUp(int x, int y, int button); 49 | 50 | // 鼠标移动 51 | void OnMouseMove(int x, int y); 52 | 53 | // 计时器事件 54 | void OnTimer(); 55 | 56 | // 窗口绘制事件 57 | void OnPaint(HDC hdc, const RECT& rect); 58 | 59 | // 60 | // 辅助函数 61 | // 62 | 63 | // 显示消息对话框 64 | void msgbox(PCTSTR param, ...); 65 | 66 | //--------------------------------------------------------------------------- 67 | #endif //WIN32_WINDOW_H 68 | -------------------------------------------------------------------------------- /tesselator/detail/cdt.inl: -------------------------------------------------------------------------------- 1 | #ifndef LIBTESS_CDT_HPP 2 | #define LIBTESS_CDT_HPP 3 | 4 | #include "mesh.hpp" 5 | 6 | namespace libtess { 7 | 8 | /* Starting with a valid triangulation, uses the Edge Flip algorithm to 9 | * refine the triangulation into a Constrained Delaunay Triangulation. 10 | */ 11 | LIBTESS_INLINE void Tesselator::MeshRefineDelaunay(Mesh *mesh) 12 | { 13 | /* At this point, we have a valid, but not optimal, triangulation. 14 | * We refine the triangulation using the Edge Flip algorithm 15 | * 16 | * 1) Find all internal edges 17 | * 2) Mark all dual edges 18 | * 3) insert all dual edges into a queue 19 | */ 20 | 21 | Face *f; 22 | std::stack stack; 23 | HalfEdge *e; 24 | int maxFaces = 0, maxIter = 0, iter = 0; 25 | 26 | for (f = mesh->m_faceHead.next; f != &mesh->m_faceHead; f = f->next) { 27 | if (f->inside) { 28 | e = f->edge; 29 | do { 30 | e->mark = EdgeIsInternal(e); // Mark internal edges 31 | if (e->mark && !e->mirror->mark) stack.push(e); // Insert into queue 32 | e = e->Lnext; 33 | } while (e != f->edge); 34 | maxFaces++; 35 | } 36 | } 37 | 38 | /* The algorithm should converge on O(n^2), since the predicate is not robust, 39 | * we'll save guard against infinite loop. 40 | */ 41 | maxIter = maxFaces * maxFaces; 42 | 43 | /* Pop stack until we find a reversed edge 44 | * Flip the reversed edge, and insert any of the four opposite edges 45 | * which are internal and not already in the stack (!marked) 46 | */ 47 | while (!stack.empty() && iter < maxIter) { 48 | e = stack.top(); 49 | stack.pop(); 50 | e->mark = e->mirror->mark = 0; 51 | if (!EdgeIsLocallyDelaunay(e)) { 52 | HalfEdge *edges[4]; 53 | int i; 54 | mesh->FlipEdge(e); 55 | // for each opposite edge 56 | edges[0] = e->Lnext; 57 | edges[1] = e->Onext->mirror; 58 | edges[2] = e->mirror->Lnext; 59 | edges[3] = e->mirror->Onext->mirror; 60 | for (i = 0; i < 4; i++) { 61 | if (!edges[i]->mark && EdgeIsInternal(edges[i])) { 62 | edges[i]->mark = edges[i]->mirror->mark = 1; 63 | stack.push(edges[i]); 64 | } 65 | } 66 | } 67 | iter++; 68 | } 69 | } 70 | 71 | #if 0 72 | 73 | Index Tesselator::GetNeighbourFace(HalfEdge* edge) 74 | { 75 | if (!edge->mirror->Lface) 76 | return INVALID_INDEX; 77 | if (!edge->mirror->Lface->inside) 78 | return INVALID_INDEX; 79 | return edge->mirror->Lface->n; 80 | } 81 | 82 | int Tesselator::OutputPolymesh(int elementType, int polySize) 83 | { 84 | Vertex* v = 0; 85 | Face* f = 0; 86 | HalfEdge* edge = 0; 87 | int maxFaceCount = 0; 88 | int maxVertexCount = 0; 89 | int faceVerts, i; 90 | Index *elements = 0; 91 | 92 | /* Assume that the input data is triangles now. 93 | * Try to merge as many polygons as possible 94 | */ 95 | if (polySize > 3) { 96 | if (!mesh.MergeConvexFaces(polySize)) { 97 | return LIBTESS_ERROR; 98 | } 99 | } 100 | 101 | // Mark unused 102 | for (v = mesh.m_vtxHead.next; v != &mesh.m_vtxHead; v = v->next) 103 | v->n = INVALID_INDEX; 104 | 105 | // Create unique IDs for all vertices and faces. 106 | for (f = mesh.m_faceHead.next; f != &mesh.m_faceHead; f = f->next) { 107 | f->n = INVALID_INDEX; 108 | if (!f->inside) continue; 109 | 110 | edge = f->edge; 111 | faceVerts = 0; 112 | do { 113 | v = edge->vertex; 114 | if (v->n == INVALID_INDEX) { 115 | v->n = maxVertexCount; 116 | maxVertexCount++; 117 | } 118 | faceVerts++; 119 | edge = edge->Lnext; 120 | } while (edge != f->edge); 121 | 122 | assert(faceVerts <= polySize); 123 | 124 | f->n = maxFaceCount; 125 | ++maxFaceCount; 126 | } 127 | 128 | //int elementCount = maxFaceCount; 129 | if (elementType == TESS_CONNECTED_POLYGONS) 130 | maxFaceCount *= 2; 131 | //tess->elements = (Index*)tess->alloc.memalloc( tess->alloc.userData,sizeof(Index) * maxFaceCount * polySize ); 132 | this->elements.resize(maxFaceCount * polySize); 133 | 134 | 135 | int vertexCount = maxVertexCount; 136 | //tess->vertices = (TESSreal*)tess->alloc.memalloc( tess->alloc.userData, sizeof(TESSreal) * tess->vertexCount * vertexSize ); 137 | this->vertices.resize(vertexCount); 138 | 139 | //tess->vertexIndices = (Index*)tess->alloc.memalloc( tess->alloc.userData, sizeof(Index) * tess->vertexCount ); 140 | this->indices.resize(vertexCount); 141 | 142 | // Output vertices. 143 | for (v = mesh.m_vtxHead.next; v != &mesh.m_vtxHead; v = v->next) { 144 | if (v->n != INVALID_INDEX) { 145 | // Store coordinate 146 | #ifdef LIBTESS_USE_VEC3 147 | this->vertices[v->n].x = v->coords.x; 148 | this->vertices[v->n].y = v->coords.y; 149 | this->vertices[v->n].z = v->coords.z; 150 | #else 151 | this->vertices[v->n].x = v->coords.x; 152 | this->vertices[v->n].y = v->coords.y; 153 | #endif 154 | 155 | // Store vertex index. 156 | this->indices[v->n] = v->idx; 157 | } 158 | } 159 | 160 | // Output indices. 161 | elements = &this->elements[0]; 162 | for (f = mesh.m_faceHead.next; f != &mesh.m_faceHead; f = f->next) { 163 | if (!f->inside) continue; 164 | 165 | // Store polygon 166 | edge = f->edge; 167 | faceVerts = 0; 168 | do { 169 | v = edge->vertex; 170 | *elements++ = v->n; 171 | faceVerts++; 172 | edge = edge->Lnext; 173 | } while (edge != f->edge); 174 | 175 | // Fill unused. 176 | for (i = faceVerts; i < polySize; ++i) { 177 | *elements++ = INVALID_INDEX; 178 | } 179 | 180 | // Store polygon connectivity 181 | if (elementType == TESS_CONNECTED_POLYGONS) { 182 | edge = f->edge; 183 | do { 184 | *elements++ = GetNeighbourFace(edge); 185 | edge = edge->Lnext; 186 | } while (edge != f->edge); 187 | // Fill unused. 188 | for (i = faceVerts; i < polySize; ++i) { 189 | *elements++ = INVALID_INDEX; 190 | } 191 | } 192 | } 193 | 194 | return LIBTESS_OK; 195 | } 196 | 197 | #endif 198 | 199 | }// end namespace libtess 200 | 201 | #endif //LIBTESS_CDT_HPP 202 | -------------------------------------------------------------------------------- /tesselator/detail/dict.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) 3 | * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice including the dates of first publication and 13 | * either this permission notice or a reference to 14 | * http://oss.sgi.com/projects/FreeB/ 15 | * shall be included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 22 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * Except as contained in this notice, the name of Silicon Graphics, Inc. 26 | * shall not be used in advertising or otherwise to promote the sale, use or 27 | * other dealings in this Software without prior written authorization from 28 | * Silicon Graphics, Inc. 29 | */ 30 | /* 31 | ** Author: Eric Veach, July 1994. 32 | ** 33 | */ 34 | 35 | #ifndef LIBTESS_DICT_HPP 36 | #define LIBTESS_DICT_HPP 37 | 38 | #include "public.h" 39 | 40 | namespace libtess { 41 | 42 | // 43 | // Dist 44 | // 45 | 46 | typedef void *DictKey; 47 | 48 | typedef int (*PFN_DICTKEY_COMPARE)(void *frame, DictKey key1, DictKey key2); 49 | 50 | struct DictNode 51 | { 52 | DictKey key; 53 | DictNode *next; 54 | DictNode *prev; 55 | 56 | DictNode() : key(), next(), prev() {} 57 | }; 58 | 59 | class Dict 60 | { 61 | protected: 62 | //head.next == first node; 63 | //head.prev == last node 64 | DictNode head; 65 | void *frame; 66 | PFN_DICTKEY_COMPARE comp; 67 | 68 | pool poolbuf; 69 | 70 | public: 71 | Dict(); 72 | ~Dict(); 73 | 74 | void init(void *_frame, PFN_DICTKEY_COMPARE pfn); 75 | void dispose(); 76 | 77 | DictNode* insert(DictNode *node, DictKey key); 78 | DictNode* insert(DictKey key) { return this->insert(&head, key); } 79 | void erase(DictNode *node); 80 | DictNode* find(DictKey key); 81 | DictNode* min(); 82 | DictNode* max(); 83 | 84 | protected: 85 | DictNode* allocate() 86 | { 87 | #ifdef LIBTESS_USE_POOL 88 | return poolbuf.allocate(); 89 | #else 90 | return new DictNode; 91 | #endif 92 | } 93 | 94 | void deallocate(DictNode* n) 95 | { 96 | #ifdef LIBTESS_USE_POOL 97 | poolbuf.deallocate(n); 98 | #else 99 | delete n; 100 | #endif 101 | } 102 | }; 103 | 104 | // 105 | // source 106 | // 107 | 108 | // DictKey 109 | 110 | LIBTESS_INLINE DictKey dictKey(DictNode* n) 111 | { 112 | return n->key; 113 | } 114 | 115 | // Dict 116 | 117 | LIBTESS_INLINE Dict::Dict() : head(), frame(), comp() 118 | { 119 | 120 | } 121 | 122 | LIBTESS_INLINE Dict::~Dict() 123 | { 124 | this->dispose(); 125 | } 126 | 127 | LIBTESS_INLINE void Dict::init(void *_frame, PFN_DICTKEY_COMPARE pfn) 128 | { 129 | head.key = NULL; 130 | head.next = &head; 131 | head.prev = &head; 132 | 133 | frame = _frame; 134 | comp = pfn; 135 | } 136 | 137 | LIBTESS_INLINE void Dict::dispose() 138 | { 139 | #ifdef LIBTESS_USE_POOL 140 | poolbuf.dispose(); 141 | #else 142 | DictNode* node = head.next; 143 | DictNode* next; 144 | while (node && node != &head) { 145 | next = node->next; 146 | delete node; 147 | node = next; 148 | } 149 | #endif 150 | } 151 | 152 | LIBTESS_INLINE DictNode * Dict::insert(DictNode *node, DictKey key) 153 | { 154 | DictNode *newNode; 155 | 156 | do { 157 | node = node->prev; 158 | } while (node->key != NULL && !(*comp)(frame, node->key, key)); 159 | 160 | newNode = this->allocate(); 161 | if (newNode == NULL) return NULL; 162 | 163 | newNode->key = key; 164 | newNode->next = node->next; 165 | node->next->prev = newNode; 166 | newNode->prev = node; 167 | node->next = newNode; 168 | 169 | return newNode; 170 | } 171 | 172 | LIBTESS_INLINE void Dict::erase(DictNode *node) 173 | { 174 | node->next->prev = node->prev; 175 | node->prev->next = node->next; 176 | this->deallocate(node); 177 | } 178 | 179 | LIBTESS_INLINE DictNode * Dict::find(DictKey key) 180 | { 181 | DictNode *node = &head; 182 | 183 | do { 184 | node = node->next; 185 | } while (node->key != NULL && !(*comp)(frame, key, node->key)); 186 | 187 | return node; 188 | } 189 | 190 | LIBTESS_INLINE DictNode* Dict::min() 191 | { 192 | return head.next; 193 | } 194 | 195 | LIBTESS_INLINE DictNode* Dict::max() 196 | { 197 | return head.prev; 198 | } 199 | 200 | }// end namespace libtess 201 | 202 | #endif// LIBTESS_DICT_HPP 203 | -------------------------------------------------------------------------------- /tesselator/detail/geometry.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/geometry.hpp -------------------------------------------------------------------------------- /tesselator/detail/mesh.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/mesh.hpp -------------------------------------------------------------------------------- /tesselator/detail/mono.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/mono.hpp -------------------------------------------------------------------------------- /tesselator/detail/normal.inl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/normal.inl -------------------------------------------------------------------------------- /tesselator/detail/public.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/public.h -------------------------------------------------------------------------------- /tesselator/detail/sweep.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/sweep.hpp -------------------------------------------------------------------------------- /tesselator/detail/sweep.inl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/detail/sweep.inl -------------------------------------------------------------------------------- /tesselator/tesselator.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdragonx/tesselator/a3f856ee70ada515dd621c59614478d63f3886c6/tesselator/tesselator.hpp --------------------------------------------------------------------------------