【教程】自定义顶点网格三角剖分库,cdt2d插件安装

image
最近在做一些自定义顶点的需求,三角形组装的算法麻烦还不尽人意,干脆找了个三方库。这个效果有点像我在spine里面添加顶点时候自动组装的三角形效果了,挺不错的。

一、CDT2D 三角剖分库

1.1 功能简介

CDT2D (Constrained Delaunay Triangulation in 2D) 是一个高性能的二维三角剖分算法库。

1.2 算法原理

  • Delaunay三角剖分:保证所有三角形的外接圆内不包含其他顶点,生成质量最优的三角网格
  • 无边界模式:本项目使用无约束边界的剖分方式,适合凸多边形和简单凹多边形

1.3 安装与使用

安装依赖

npm install rollup cdt2d terser
const fs = require('fs');
const path = require('path');
const { rollup } = require('rollup');
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const { minify } = require('terser');

const outputDir = path.join(__dirname, 'assets/libs');

if (!fs.existsSync(outputDir)) {
  fs.mkdirSync(outputDir, { recursive: true });
}

const dtsContent = `declare function cdt2d(...args: any[]): any;
export = cdt2d;
`;

async function build() {
  console.log('Building cdt2d...\n');

  try {
    console.log('Step 1/3: Rollup bundling...');
    
    const bundle = await rollup({
      input: 'node_modules/cdt2d/cdt2d.js',
      plugins: [
        resolve({ preferBuiltins: false }),
        commonjs()
      ]
    });

    const { output } = await bundle.generate({
      format: 'umd',
      name: 'cdt2d',
      exports: 'default'
    });
    
    const bundledCode = output[0].code;
    const jsPath = path.join(outputDir, 'cdt2d.js');
    fs.writeFileSync(jsPath, bundledCode);
    console.log(`✓ cdt2d.js (${(bundledCode.length / 1024).toFixed(1)} KB)`);

    console.log('\nStep 2/3: Terser minifying...');
    
    const minified = await minify(bundledCode, {
      compress: {
        dead_code: true,
        drop_debugger: true,
        conditionals: true,
        evaluate: true,
        booleans: true,
        loops: true,
        unused: true,
        hoist_funs: true,
        keep_fargs: false,
        hoist_vars: false,
        if_return: true,
        join_vars: true,
        side_effects: true,
        warnings: false
      },
      mangle: {
        toplevel: false,
        reserved: ['cdt2d']
      },
      format: {
        comments: false,
        beautify: false
      }
    });

    if (minified.error) {
      throw minified.error;
    }

    const minPath = path.join(outputDir, 'cdt2d.min.js');
    fs.writeFileSync(minPath, minified.code);
    
    const minSize = minified.code.length / 1024;
    const originalSize = bundledCode.length / 1024;
    const ratio = ((1 - minSize / originalSize) * 100).toFixed(1);
    
    console.log(`✓ cdt2d.min.js (${minSize.toFixed(1)} KB, ${ratio}% compressed)`);

    console.log('\nStep 3/3: Generating d.ts...');
    
    const dtsPath = path.join(outputDir, 'cdt2d.d.ts');
    fs.writeFileSync(dtsPath, dtsContent);
    console.log(`✓ cdt2d.d.ts`);

    console.log('\n' + '='.repeat(40));
    console.log('Build completed!');
    console.log('='.repeat(40));
    console.log(`\nOutput: ${path.relative(__dirname, outputDir)}/`);

  } catch (error) {
    console.error('\nBuild failed:', error.message);
    process.exit(1);
  }
}

build();

创建一个js脚本,执行js脚本后会在assets/libs/构建js代码文件和d.ts声明文件

测试使用

import cdt2d = require("./libs/cdt2d");

const { ccclass, property } = cc._decorator;

@ccclass
export default class CDT2DExample extends cc.Component {

    onLoad() {
        this.testCDT2D();
    }

    testCDT2D() {
        console.log('=== CDT2D 测试开始 ===');

        // 示例1: 简单的正方形
        const points1 = [
            [0, 0],
            [100, 0],
            [100, 100],
            [0, 100]
        ];

        const edges1 = [
            [0, 1],
            [1, 2],
            [2, 3],
            [3, 0]
        ];

        const triangles1 = cdt2d(points1, edges1);
        console.log('正方形三角剖分结果:', triangles1);

        // 示例2: 带孔的多边形
        const points2 = [
            // 外部正方形
            [0, 0],
            [200, 0],
            [200, 200],
            [0, 200],
            // 内部正方形 (孔)
            [50, 50],
            [150, 50],
            [150, 150],
            [50, 150]
        ];

        const edges2 = [
            // 外部边界
            [0, 1], [1, 2], [2, 3], [3, 0],
            // 内部孔边界
            [4, 5], [5, 6], [6, 7], [7, 4]
        ];

        const triangles2 = cdt2d(points2, edges2, {
            interior: true,  // 只要内部三角形
            exterior: false
        });
        console.log('带孔多边形三角剖分结果:', triangles2);

        // 示例3: 绘制三角形网格
        this.drawTriangles(points2, triangles2);

        console.log('=== CDT2D 测试完成 ===');
    }

    /**
     * 在节点上绘制三角形网格
     */
    drawTriangles(points: number[][], triangles: number[][]) {
        // 创建新的子节点用于绘制
        const graphicsNode = new cc.Node('TriangleGraphics');
        graphicsNode.parent = this.node;
        graphicsNode.setPosition(0, 0);
        
        const graphics = graphicsNode.addComponent(cc.Graphics);
        
        // 设置绘制样式
        graphics.lineWidth = 2;
        graphics.strokeColor = cc.Color.GREEN;
        graphics.fillColor = new cc.Color(0, 255, 0, 50);

        // 绘制每个三角形
        for (const tri of triangles) {
            const p0 = points[tri[0]];
            const p1 = points[tri[1]];
            const p2 = points[tri[2]];

            // 填充三角形
            graphics.moveTo(p0[0] - 100, p0[1] - 100);
            graphics.lineTo(p1[0] - 100, p1[1] - 100);
            graphics.lineTo(p2[0] - 100, p2[1] - 100);
            graphics.close();
            graphics.fill();
            graphics.stroke();
        }

        // 绘制顶点
        graphics.fillColor = cc.Color.RED;
        for (const point of points) {
            graphics.circle(point[0] - 100, point[1] - 100, 3);
            graphics.fill();
        }
    }
}


贴个公众号
image

欢迎关注,后续准备把自定义顶点的功能写一篇发一发

5赞

那必须顶一波,shader我还是从你的教程入门的