Download to AsciiEffect to GLB/GLTF

So i’ve been using the AsciiEffect from these two:

  1. three.js examples
  2. STL To ASCII Generator

From what I understand is they are using the AsciiEffect from threejs to create an effect using the scene rendered from the STL.

Now instead of having the Image as the output, I want the entire model in ASCII art style instead.
I tried the GLTFExporter from threejs tried both passing the mesh and the scene, but the downloaded GLTF file is an empty geometry without any material. Any idea how can I achieve this?

here’s my current script:

import "./style.css";
import * as THREE from "three";
import { OrbitControls } from "../node_modules/three/examples/jsm/controls/OrbitControls.js";
import { STLLoader } from "../node_modules/three/examples/jsm/loaders/STLLoader.js";
import { AsciiEffect } from "../node_modules/three/examples/jsm/effects/AsciiEffect.js";
import { GLTFExporter } from "../node_modules/three/examples/jsm/exporters/GLTFExporter.js";
import html2canvas from "../node_modules/html2canvas";

//LightMode
let lightMode = true;

//Create a clock for rotation
const clock = new THREE.Clock();

// Set rotate boolean variable
let rotateModel = false;

//Ugh, don't ask about this stuff
var userUploaded = false;
let controls;

// Creates empty mesh container
const myMesh = new THREE.Mesh();

// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0, 0, 0);

//Lights
const pointLight1 = new THREE.PointLight(0xffffff, 1);
pointLight1.position.set(100, 100, 400);
scene.add(pointLight1);

const pointLight2 = new THREE.PointLight(0xffffff, 0.5);
pointLight2.position.set(-500, 100, -400);
scene.add(pointLight2);

// Parameters
const stlLoader = new STLLoader();

//Material
const material = new THREE.MeshStandardMaterial();
material.flatShading = true;
material.side = THREE.DoubleSide;

// Sizes
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight,
};

// Camera
const camera = new THREE.PerspectiveCamera(
  45,
  sizes.width / sizes.height,
  0.1,
  2000
);

// Renderer
const renderer = new THREE.WebGLRenderer();

let effect;

let characters = " .:-+*=%@#";
const effectSize = { amount: 0.205 };
let backgroundColor = "black";
let ASCIIColor = "white";

function createEffect() {
  effect = new AsciiEffect(renderer, characters, {
    invert: true,
    resolution: effectSize.amount,
  });

  effect.setSize(sizes.width, sizes.height);

  effect.domElement.style.color = ASCIIColor;

  effect.domElement.style.backgroundColor = backgroundColor;
}

createEffect();

document.body.appendChild(effect.domElement);

document.getElementById("ascii").style.whiteSpace = "prewrap";

stlLoader.load("../models/test2.stl", function (geometry) {
  myMesh.material = material;
  myMesh.geometry = geometry;

  var tempGeometry = new THREE.Mesh(geometry, material);
  myMesh.position.copy = tempGeometry.position;

  geometry.computeVertexNormals();
  myMesh.geometry.center();

  myMesh.rotation.x = (-90 * Math.PI) / 180;

  myMesh.geometry.computeBoundingBox();
  var bbox = myMesh.geometry.boundingBox;

  myMesh.position.y = (bbox.max.z - bbox.min.z) / 5;

  camera.position.x = bbox.max.x * 4;
  camera.position.y = bbox.max.y;
  camera.position.z = bbox.max.z * 3;

  scene.add(myMesh);

  controls = new OrbitControls(camera, effect.domElement);

  function tick() {
    if (rotateModel == true) {
      const elapsedTime = clock.getElapsedTime();
      myMesh.rotation.z = elapsedTime / 3;
      render();
      window.requestAnimationFrame(tick);
    } else {
      render();
      window.requestAnimationFrame(tick);
    }
  }

  function render() {
    effect.render(scene, camera);
  }

  tick();

  document
    .getElementById("file-selector")
    .addEventListener("change", openFile, false);

  function openFile(evt) {
    const fileObject = evt.target.files[0];

    const reader = new FileReader();

    reader.readAsArrayBuffer(fileObject);

    reader.onload = function () {
      if (userUploaded == false) {
        userUploaded = true;
      }

      const geometry = stlLoader.parse(this.result);

      tempGeometry = geometry;

      myMesh.geometry = geometry;

      myMesh.geometry.center();

      myMesh.rotation.x = (-90 * Math.PI) / 180;

      myMesh.geometry.computeBoundingBox();

      var bbox = myMesh.geometry.boundingBox;

      // camera.position.x = ((bbox.max.x * 4));
      // camera.position.y = ((bbox.max.y));
      // camera.position.z = ((bbox.max.z * 3));

      myMesh.position.y = (bbox.max.z - bbox.min.z) / 6;

      scene.add(myMesh);
    };
  }
});

document
  .getElementById("screenshotButton")
  .addEventListener("click", takeScreenshot);

function takeScreenshot() {
  var container = document.body; // full page
  html2canvas(container).then(function (canvas) {
    var link = document.createElement("a");
    document.body.appendChild(link);
    link.download = "ASCII.jpg";
    link.href = canvas.toDataURL("image/jpg");
    console.log(link.href);
    // link.target = '_blank';
    link.click();
  });
}

document.getElementById("rotateButton").addEventListener("click", rotateMode);

function rotateMode() {
  rotateModel = !rotateModel;
}

document.getElementById("updateASCII").addEventListener("click", updateASCII);

function updateASCII() {
  document.body.removeChild(effect.domElement);

  characters = " " + "." + document.getElementById("newASCII").value;

  createEffect();
  onWindowResize();

  document.body.appendChild(effect.domElement);

  controls = new OrbitControls(camera, effect.domElement);
}

document.getElementById("resetASCII").addEventListener("click", resetASCII);

function resetASCII() {
  document.body.removeChild(effect.domElement);

  characters = " .:-+*=%@#";

  createEffect();
  onWindowResize();

  document.body.appendChild(effect.domElement);

  controls = new OrbitControls(camera, effect.domElement);
}

document.getElementById("lightDark").addEventListener("click", lightDark);

function lightDark() {
  lightMode = !lightMode;
  if (lightMode === true) {
    document.getElementById("kofi").style.color = "white";
    document.body.style.backgroundColor = "black";

    backgroundColor = "black";
    ASCIIColor = "white";

    effect.domElement.style.color = ASCIIColor;
    effect.domElement.style.backgroundColor = backgroundColor;
  } else {
    document.getElementById("kofi").style.color = "black";
    document.body.style.backgroundColor = "white";

    backgroundColor = "white";
    ASCIIColor = "black";

    effect.domElement.style.color = ASCIIColor;
    effect.domElement.style.backgroundColor = backgroundColor;
  }
}

window.addEventListener("resize", onWindowResize);

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
  effect.setSize(window.innerWidth, window.innerHeight);
}

function download(filename, text) {
  var element = document.createElement("a");
  element.setAttribute(
    "href",
    "data:text/plain;charset=utf-8," + encodeURIComponent(text)
  );
  element.setAttribute("download", filename);

  element.style.display = "none";
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}

document.getElementById("copyASCII").addEventListener(
  "click",
  function () {
    var text = document.getElementsByTagName("table")[0].innerText;
    var filename = "ASCII.txt";

    download(filename, text);
  },
  false
);

document.getElementById("clipboardASCII").addEventListener(
  "click",
  function () {
    const textArea = document.createElement("textarea");
    textArea.textContent = document.getElementsByTagName("td")[0].innerText;
    document.body.appendChild(textArea);
    textArea.select();
    document.execCommand("copy");
    document.body.removeChild(textArea);
    window.alert("ASCII copied to clipboard");
  },
  false
);

const test = document.getElementById("downloadStlButton");

const link = document.createElement("a");
link.style.display = "none";
document.body.appendChild(link); // Firefox workaround, see #6594

function save(blob, filename) {
  link.href = URL.createObjectURL(blob);
  link.download = filename;
  link.click();
}

function saveArrayBuffer(buffer, filename) {
  save(new Blob([buffer], { type: "application/octet-stream" }), filename);
}

function saveString(text, filename) {
  save(new Blob([text], { type: "text/plain" }), filename);
}

const downloadStl = () => {
  const exporter = new GLTFExporter();
  const options = { binary: true };
  exporter.parse(
    scene,
    function (result) {
      if (result instanceof ArrayBuffer) {
        saveArrayBuffer(result, "scene.glb");
      } else {
        const output = JSON.stringify(result, null, 2);
        saveString(output, "scene.gltf");
      }
    },
    function (error) {
      console.log("An error happened during parsing", error);
    },
    options
  );
};

document
  .getElementById("downloadStlButton")
  .addEventListener("click", downloadStl);

ascii is an effect, either postprocessing or something else. you can’t export that except as an image or video.

1 Like