Script: GUI Example CAD Configurator

GUI Example CAD Configurator picture
Type
Typescript logo indicatortypescript
Author
matas
Date Created
Nov 15, 2023, 3:10:39 PM
Last Edit Date
Jan 18, 2024, 1:13:10 PM

Project Information

Possibility to create Graphical User Interfaces via BabylonJS brings plenty of opportunities to create 3D CAD configurators and games directly on our platform and share them with the world by sending out one link.

View Full Project

Script Code

let computing = false;
let previousStarMesh: BABYLON.Mesh;
let previousStarShape: Bit.Inputs.OCCT.TopoDSShapePointer;
let previousMeshInstances: BABYLON.InstancedMesh[] = [];

type Model = {
    nrRays: number,
    innerRadius: number,
    outerRadius: number,
    nrSections: number
}

const model: Model = {
    nrRays: 5,
    nrSections: 3,
    innerRadius: 1,
    outerRadius: 3,
}

const start = async () => {
    bitbybit.draw.drawGridMesh(new Bit.Inputs.Draw.SceneDrawGridMeshDto())
    createGUI();

    const dirLight = new Bit.Inputs.BabylonScene.DirectionalLightDto();
    dirLight.direction = [-20, -20, -20];
    dirLight.shadowGeneratorMapSize = 2056;
    dirLight.intensity = 4;
    bitbybit.babylon.scene.drawDirectionalLight(dirLight);

    createShape(model);

    const skyBoxOptions = new Bit.Inputs.BabylonScene.SkyboxDto();

    skyBoxOptions.skybox = Bit.Inputs.Base.skyboxEnum.default;
    skyBoxOptions.blur = 5;
    bitbybit.babylon.scene.enableSkybox(skyBoxOptions);
}

start();

async function createShape(m: Model) {
    computing = true;
    await disposePrevious();

    const shapesToDelete = [];

    const star1 = await bitbybit.occt.shapes.wire.createStarWire({
        numRays: m.nrRays,
        innerRadius: m.innerRadius,
        outerRadius: m.outerRadius,
        center: [0, 0, 0],
        direction: [0, 1, 0],
        half: false,
    });

    const star2 = await bitbybit.occt.shapes.wire.createStarWire({
        numRays: m.nrRays,
        innerRadius: m.innerRadius,
        outerRadius: m.outerRadius,
        center: [0, Math.random(), 0],
        direction: [0, 1, 0],
        half: false,
    });

    shapesToDelete.push(star1, star2);

    const cornerPoints1 = await bitbybit.occt.shapes.edge.getCornerPointsOfEdgesForShape({ shape: star1 });
    const cornerPoints2 = await bitbybit.occt.shapes.edge.getCornerPointsOfEdgesForShape({ shape: star2 });
    const pts = [];

    for (let i = 0; i < cornerPoints1.length; i++) {
        if (i % 2 === 0) {
            const pt = cornerPoints2[i];
            pt[1] += Math.random() * 2;
            pts.push(pt);
        } else {
            const pt = cornerPoints1[i];
            pts.push(pt);
        }
    }

    const flower = await bitbybit.occt.shapes.wire.createPolygonWire({
        points: pts,
    });

    shapesToDelete.push(flower);

    const flowerFillet = await bitbybit.occt.fillets.fillet3DWire({
        shape: flower,
        direction: [0, 10, 0],
        radius: 360 / m.nrRays / 600,
    });

    shapesToDelete.push(flowerFillet);

    const flowers = [];
    for (let r = 1; r <= m.nrSections; r++) {

        const scaled = await bitbybit.occt.transforms.scale3d({
            shape: flowerFillet,
            scale: [1 / r, r % 2 === 0 ? r / m.nrSections * 2 : r / m.nrSections * 1.5, 1 / r],
            center: [0, 0, 0],
        });

        shapesToDelete.push(scaled);

        const translated = await bitbybit.occt.transforms.translate({
            shape: scaled,
            translation: [0, r % 2 === 0 ? r / 4 : r / 3, 0]
        });

        flowers.push(translated);
        shapesToDelete.push(translated);
    }

    const mirrored = await bitbybit.occt.transforms.mirrorAlongNormalShapes({
        shapes: flowers,
        normals: flowers.map(() => [0, -1, 0]),
        origins: flowers.map(() => [0, 0, 0]),
    });

    mirrored.reverse().forEach(m => {
        flowers.push(m);
        shapesToDelete.push(m);
    })

    const loftOptions = new Bit.Inputs.OCCT.LoftAdvancedDto<Bit.Inputs.OCCT.TopoDSWirePointer>(flowers);
    loftOptions.straight = true;
    loftOptions.closed = true;
    loftOptions.makeSolid = true;
    previousStarShape = await bitbybit.occt.operations.loftAdvanced(loftOptions)

    const options = new Bit.Inputs.Draw.DrawOcctShapeOptions();
    options.precision = 0.01;
    options.drawEdges = true;
    options.edgeWidth = 0.5;
    options.edgeColour = "#f0b89d";

    const mat = new BABYLON.PBRMetallicRoughnessMaterial("mat");
    mat.roughness = 0.1;
    mat.baseColor = new BABYLON.Color3(1, 1, 1);
    mat.zOffset = 1;
    mat.metallic = 0.9;
    options.faceMaterial = mat;

    previousStarMesh = await bitbybit.draw.drawAnyAsync({ entity: previousStarShape, options });

    await bitbybit.occt.deleteShapes({
        shapes: shapesToDelete
    })

    // previousStarMesh.receiveShadows = true;
    const meshes = previousStarMesh.getChildMeshes();
    const coord = 60;
    const step = 10;
    for (let x = -coord; x < coord; x += step) {
        for (let z = -coord; z < coord; z += step) {
            const randomAngle = Math.random();
            meshes.forEach((m: BABYLON.Mesh, index) => {
                const inst = m.createInstance(`inst${index}-${x}-${z}`);
                inst.rotateAround(new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, 1, 0), Math.PI * 2 * randomAngle);
                inst.scaling = new BABYLON.Vector3(1 + randomAngle / 4, 1 + randomAngle, 1 + randomAngle / 4)
                inst.position = new BABYLON.Vector3(x, 0, z);
                previousMeshInstances.push(inst);
            });
        }
    }

    meshes.forEach(m => {
        m.disableEdgesRendering();
        m.isVisible = false;
    });
    previousStarMesh.isVisible = false;
    computing = false;
}


async function disposePrevious() {
    if (previousStarMesh) {
        previousStarMesh.dispose();
    }
    if (previousStarShape) {
        await bitbybit.occt.deleteShape({ shape: previousStarShape });
    }
    if (previousMeshInstances.length) {
        previousMeshInstances.forEach(s => s.dispose());
        previousMeshInstances = [];
    }
}


async function createGUI() {
    var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");

    var panel = new BABYLON.GUI.StackPanel();
    panel.width = "800px";
    panel.background = "#00000055";
    panel.paddingLeftInPixels = 40;
    panel.paddingRightInPixels = 40;
    panel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    panel.adaptHeightToChildren = true;
    advancedTexture.addControl(panel);

    var header1 = new BABYLON.GUI.TextBlock("GUI EXAMPLE");
    header1.text = "GUI EXAMPLE";
    header1.height = "80px";
    header1.color = "#f0b89d";
    header1.fontSize = "40px"

    panel.addControl(header1);

    const label1 = "Nr Rays:";
    createSliderWithLabel(panel, label1, model.nrRays, 3, 10, 1,
        (slider: BABYLON.GUI.Slider, header: BABYLON.GUI.TextBlock) => {
            header.text = label1 + " " + slider.value;
            model.nrRays = slider.value;
            if (!computing) {
                createShape(model);
            }
        }
    );

    const label2 = "Nr Sections:";
    createSliderWithLabel(panel, label2, model.nrSections, 2, 20, 1,
        (slider: BABYLON.GUI.Slider, header: BABYLON.GUI.TextBlock) => {
            header.text = label2 + " " + slider.value;
            model.nrSections = slider.value;
            if (!computing) {
                createShape(model);
            }
        }
    );

    const label3 = "Inner Radius:";
    createSliderWithLabel(panel, label3, model.innerRadius, 0.4, 3, 0.1,
        (slider: BABYLON.GUI.Slider, header: BABYLON.GUI.TextBlock) => {
            if (slider.value > model.outerRadius) {
                model.innerRadius = model.outerRadius - 0.1;
                slider.value = model.innerRadius;
            } else {
                model.innerRadius = slider.value;
            }
            header.text = label3 + " " + slider.value.toFixed(2);
            if (!computing) {
                createShape(model);
            }
        }
    );

    const label4 = "Outer Radius:";
    createSliderWithLabel(panel, label4, model.outerRadius, 0.4, 3, 0.1,
        (slider: BABYLON.GUI.Slider, header: BABYLON.GUI.TextBlock) => {
            if (slider.value < model.innerRadius) {
                model.outerRadius = model.innerRadius + 0.1;
                slider.value = model.outerRadius;
            } else {
                model.outerRadius = slider.value;
            }
            header.text = label4 + " " + slider.value.toFixed(2);
            if (!computing) {
                createShape(model);
            }
        }
    );

    var header2 = new BABYLON.GUI.TextBlock("bitbybit.dev");
    header2.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
    header2.text = "bitbybit.dev";
    header2.height = "120px";
    header2.color = "#f0b89d";
    header2.fontSize = "40px";


    panel.addControl(header2);
}

function createSliderWithLabel(
    panel: BABYLON.GUI.StackPanel,
    name: string,
    defaultVal: number,
    min: number,
    max: number,
    step: number,
    funcToRun: (slider: BABYLON.GUI.Slider, label: BABYLON.GUI.TextBlock) => void
) {
    var header = new BABYLON.GUI.TextBlock(name);
    header.paddingTopInPixels = 10;
    header.text = name + " " + defaultVal;
    header.height = "60px";
    header.color = "white";
    header.fontSize = "30px"

    panel.addControl(header);

    var slider = new BABYLON.GUI.Slider(name);
    slider.thumbColor = "#f0b89d";
    slider.isThumbCircle = true;

    slider.borderColor = "#f0b89d"
    slider.minimum = min;
    slider.maximum = max;
    slider.step = step;
    slider.value = defaultVal;
    slider.paddingLeftInPixels = 10;
    slider.paddingRightInPixels = 10;
    slider.isVertical = false;
    slider.alpha = 1;

    slider.height = "25px";
    slider.onPointerUpObservable.add(() => {

        funcToRun(slider, header);
    });

    panel.addControl(slider);
}

Plans & Pricing

Choose Your Plan

Editor plans for 3D development, API keys for server-side CAD algorithms

B2B

ENTERPRISE

Custom pricing

Custom software development, dedicated servers & CAD automation at scale.

CAD Automation & Software
  • Custom software development
  • Cloud CAD automation pipelines
  • 3D configurators (STEP & GLTF)
  • Batch export jobs
  • Custom algorithms & deployment
Infrastructure & Support
  • Custom compute allocation
  • Dedicated / VPS server tenants
  • Long-running computation jobs
  • Custom upload limits & overage
  • SLA & premium support