Skip to content

Commit

Permalink
speed up brep meshing with lower quality
Browse files Browse the repository at this point in the history
  • Loading branch information
ghackenberg committed Oct 9, 2024
1 parent e44670b commit 854a7b9
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 45 deletions.
85 changes: 45 additions & 40 deletions packages/freecad/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ export class FreeCADObject {
public label: string
public visibility: boolean

public shape_file: string
public brep: string

constructor(public name: string, public type: string) {}

hasShapeBRep() {
if (this.shape_file) {
isVisible() {
if (this.brep) {
return this.visibility === undefined || this.visibility
} else {
for (const child of this.group || []) {
if (child.hasShapeBRep()) {
if (child.isVisible()) {
return true
}
}
Expand All @@ -56,36 +56,39 @@ function traverse(object: Object3D, material: MeshStandardMaterial) {
}

export async function parseFCStdModel(data: ReadableStream | BlobReader, brep2Glb: (content: string) => Promise<Uint8Array>) {
const diffuse: {[name: string]: MeshStandardMaterial[]} = {}
// Data stuctures
const colors: {[name: string]: MeshStandardMaterial[]} = {}
const breps: {[name: string]: Entry} = {}
const gltfs: {[name: string]: GLTF} = {}
// Main document
let doc: FreeCADDocument
// Read ZIP file
const reader = new ZipReader(data)
const parser = new DOMParser()
// Read files in ZIP archive
const entries = await reader.getEntries()
for (const entry of entries) {
//console.log(entry.filename)
// Check file type
if (entry.filename == 'Document.xml') {
// Parse XML file
// Read main document
const writer = new TextWriter()
const content = await entry.getData(writer)
const parser = new DOMParser()
const document = parser.parseFromString(content, 'application/xml')
doc = parseFCStdDocument(document)
} else if (entry.filename.startsWith('DiffuseColor')) {
// Read color specification
const writer = new Uint8ArrayWriter()
const content = await entry.getData(writer)
diffuse[entry.filename] = []
colors[entry.filename] = []
for (let i = 1; i < content.length / 4; i++) {
const a = 1 - content[i * 4 + 0] / 255
const b = 1 - content[i * 4 + 1] / 255
const g = 1 - content[i * 4 + 2] / 255
const r = 1 - content[i * 4 + 3] / 255
const color = new Color(r, g, b)
const material = new MeshStandardMaterial({ color, opacity: a })
diffuse[entry.filename].push(material)
colors[entry.filename].push(material)
}
} else if (entry.filename.endsWith('.brp')) {
// Remember brep entry
breps[entry.filename] = entry
}
}
Expand All @@ -96,57 +99,59 @@ export async function parseFCStdModel(data: ReadableStream | BlobReader, brep2Gl
delete doc.objects[object.name]
}
}
// Log result
console.log(doc)
// Convert to THREEJS
const model = new Group()
model.name = doc.label
model.rotateX(-Math.PI / 2)
for (const obj of Object.values(doc.objects)) {
if (obj.hasShapeBRep()) {
model.add(await convertFCObject(obj, diffuse, breps, brep2Glb))
if (obj.isVisible()) {
model.add(await convertFCObject(obj, colors, breps, gltfs, brep2Glb))
}
}
return model
}

async function convertFCObject(obj: FreeCADObject, diffuse: {[name: string]: MeshStandardMaterial[]}, breps: {[name: string]: Entry}, brep2Glb: (content: string) => Promise<Uint8Array>) {
async function convertFCObject(obj: FreeCADObject, colors: {[name: string]: MeshStandardMaterial[]}, breps: {[name: string]: Entry}, gltfs: {[name: string]: GLTF}, brep2Glb: (content: string) => Promise<Uint8Array>) {
const container = new Group()
container.name = obj.label

if (obj.shape_file) {
if (obj.brep) {
try {
// Parse BRep file
const entry = breps[obj.shape_file]
const writer = new TextWriter()
const content = await entry.getData(writer)
console.log('Parsing', entry.filename)
const glbFileData = await brep2Glb(content)
const gltf = await new Promise<GLTF>((resolve, reject) => {
GLTF.parse(glbFileData.buffer, undefined, resolve, reject)
})
const clone = gltf.scene.clone(true)
const file = obj.brep
// Parse brep
if (!(file in gltfs)) {
const entry = breps[obj.brep]
const writer = new TextWriter()
const content = await entry.getData(writer)
console.log('Converting', file)
const data = await brep2Glb(content)
console.log('Parsing', file)
gltfs[file] = await new Promise<GLTF>((resolve, reject) => {
GLTF.parse(data.buffer, undefined, resolve, reject)
})
}
// Clone scene
const face = gltfs[file].scene.clone(true)
const wire = gltfs[file].scene.clone(true)
// Update mesh materials
const other = entry.filename.replace('PartShape', 'DiffuseColor').replace('.brp', '')
if (other in diffuse) {
const material = diffuse[other][0]
traverse(gltf.scene, material)
const other = file.replace('PartShape', 'DiffuseColor').replace('.brp', '')
if (other in colors) {
const material = colors[other][0]
traverse(face, material)
}
traverse(clone, new MeshStandardMaterial({ color: 'black', wireframe: true }))
traverse(wire, new MeshStandardMaterial({ color: 'black', wireframe: true }))
// Add scene objects
container.add(clone)
container.add(gltf.scene)
container.add(face)
container.add(wire)
} catch (e) {
console.log(e)
}
} else if (obj.group) {
for (const child of obj.group) {
if (child.hasShapeBRep()) {
container.add(await convertFCObject(child, diffuse, breps, brep2Glb))
if (child.isVisible()) {
container.add(await convertFCObject(child, colors, breps, gltfs, brep2Glb))
}
}
}

return container
}

Expand Down Expand Up @@ -226,7 +231,7 @@ function parseFCStdDocumentObjectProperty(data: Element, obj: FreeCADObject, doc
} else if (name == 'Shape') {
if (type == 'Part::PropertyPartShape') {
const child = data.getElementsByTagName('Part')[0]
obj.shape_file = child.getAttribute('file')
obj.brep = child.getAttribute('file')
} else if (type == 'App::PropertyLink') {
const child = data.getElementsByTagName('LinkSub')[0]
const other = doc.objects[child.getAttribute('value')]
Expand Down
10 changes: 5 additions & 5 deletions packages/worker/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ addEventListener('message', async message => {
const occt = await OCCT

// Parse shape
//console.log('Reading BRep')
console.log('Reading BRep')
const shape = new occt.TopoDS_Shape()
occt.FS.createDataFile('.', 'part.brp', content, true, true, true)
const builder = new occt.BRep_Builder()
Expand All @@ -25,15 +25,15 @@ addEventListener('message', async message => {
occt.FS.unlink('./part.brp')

// Visualize shape
//console.log('Meshing BRep')
console.log('Meshing BRep')
const storageformat = new occt.TCollection_ExtendedString_1()
const doc = new occt.TDocStd_Document(storageformat)
const shapeTool = occt.XCAFDoc_DocumentTool.ShapeTool(doc.Main()).get()
shapeTool.SetShape(shapeTool.NewShape(), shape)
new occt.BRepMesh_IncrementalMesh_2(shape, 0.1, false, 0.1, false)
new occt.BRepMesh_IncrementalMesh_2(shape, 1, false, 1, false)

// Export a GLB file (this will also perform the meshing)
//console.log('Writing GLB')
console.log('Writing GLB')
const glbFileName = new occt.TCollection_AsciiString_2('./part.glb')
const cafWriter = new occt.RWGltf_CafWriter(glbFileName, true)
const docHandle = new occt.Handle_TDocStd_Document_2(doc)
Expand All @@ -42,7 +42,7 @@ addEventListener('message', async message => {
cafWriter.Perform_2(docHandle, fileInfo, writeProgress)

// Read the GLB file from the virtual file system
//console.log('Readling GLB')
console.log('Readling GLB')
const glbFileData = occt.FS.readFile('./part.glb', { encoding: "binary" })
occt.FS.unlink('./part.glb')

Expand Down

0 comments on commit 854a7b9

Please sign in to comment.