Skip to content

Commit d061cec

Browse files
committed
feat: integrate the blender script
1 parent 2acd332 commit d061cec

File tree

3 files changed

+92
-27
lines changed

3 files changed

+92
-27
lines changed

src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
export interface IChuckIfcConfig {
2-
ifcConvertPath: string;
2+
blenderPath: string;
33
}
44

55
const config: IChuckIfcConfig = {
6-
ifcConvertPath: process.env.CHUCK_IFC_IFCCONVERTPATH || 'IfcConvert'
6+
blenderPath: process.env.CHUCK_BLENDER_BLENDERPATH || 'blender'
77
};
88

99
export default config;

src/convert.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import sys
2+
import bpy
3+
4+
# -- STEP Import --
5+
6+
# Dependencies :
7+
# - https://gumroad.com/l/stepper
8+
# - https://blenderartists.org/t/step-import/1203804/199
9+
10+
# How we proceed:
11+
# 1. First, we use the very good plugin by ambi: https://gumroad.com/l/stepper
12+
# 2. Then, we use a second one by vink to take the color from the vertices and put them as material
13+
# 3. Finally, we export the file as FBX
14+
15+
# 0. Extract the src/dest paths.
16+
# The argv look like this. We don't care about whats before the "--"
17+
# blender -b --python .\step_to_fbx.py -- "Source (STEP)/LUMINAIRE AIRLIE N°1 PORTE.stp" "Blender (FBX)/LUMINAIRE AIRLIE N°1 PORTE.fbx"
18+
argv = sys.argv
19+
separator_index = argv.index("--")
20+
src = None
21+
dest = None
22+
23+
if separator_index and len(argv) >= separator_index + 3:
24+
argv = argv[separator_index + 1:]
25+
src = argv[0]
26+
dest = argv[1]
27+
28+
if src is None or dest is None:
29+
print('\nYou must provide the file to process and the destination folder like this:', file=sys.stderr)
30+
print('> blender -b --python .\step_to_fbx.py -- "Source (STEP)/LUMINAIRE AIRLIE N°1 PORTE.stp" "Blender (FBX)"', file=sys.stderr)
31+
exit(1)
32+
33+
# And remove the cube, while we're at it
34+
print('\nRemoving the damn cube...')
35+
bpy.ops.object.delete()
36+
37+
# 1. Import the STEP file
38+
print('\nSTEP import starting... \'' + src + '\'')
39+
bpy.ops.import_scene.occ_import_step(filepath=src)
40+
41+
# 2. Vertex color => materials
42+
bpy.ops.wi_cadtools.vertexc_mat()
43+
44+
# 3. Export as FBX
45+
bpy.ops.export_scene.fbx(filepath = dest, use_selection = True)
46+
47+
print('\nFinished!')

src/index.ts

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,75 @@ import * as os from 'os';
55
import * as path from 'path';
66
import config from './config';
77

8-
export interface IExecIfcStepsContext extends IDownloadAssetsStepsContext {
8+
export interface IExecBlenderStepsContext extends IDownloadAssetsStepsContext {
99
convertedAssetsDir: string;
1010
}
1111

1212
export function describe(): IStepDescription {
1313
return {
14-
code: 'exec-ifcopenshell',
15-
name: 'Execute IfcOpenShell to convert IFC files into Collada files',
14+
code: 'exec-blender',
15+
name: 'Execute Blender to convert STEP files into FBX files',
1616
priority: 11, // after assets download
1717
};
1818
}
1919

20-
export function shouldProcess(conv: IConversion, context: IExecIfcStepsContext) {
20+
/**
21+
* The predicate to process only STEP/STP files
22+
* @param path
23+
*/
24+
function isStep(path: string) {
25+
const lowerCasePath = path.toLowerCase();
26+
return lowerCasePath.endsWith('.step') || lowerCasePath.endsWith('.stp');
27+
}
28+
29+
export function shouldProcess(conv: IConversion, context: IExecBlenderStepsContext) {
2130
return context.assetsPaths &&
2231
context.assetsPaths.length &&
23-
context.assetsPaths.some(assetPath => assetPath.toLowerCase().endsWith('.ifc'));
32+
context.assetsPaths.some(isStep);
2433
}
2534

26-
export async function process(conv: IConversion, context: IExecIfcStepsContext, progress: ProgressFn): Promise<void> {
35+
export async function process(conv: IConversion,
36+
context: IExecBlenderStepsContext,
37+
progress: ProgressFn): Promise<void> {
2738
//=> Create a temporary folder for the assets
28-
const tmpDir = path.resolve(`${os.tmpdir()}/chuck/exec-ifcopenshell-${Date.now()}`);
39+
const tmpDir = path.resolve(`${os.tmpdir()}/chuck/exec-blender-${Date.now()}`);
2940
await fs.mkdirp(tmpDir);
3041

3142
context.convertedAssetsDir = tmpDir;
3243
const convertOptions = conv.conversionOptions;
3344

3445
//=> Execute each conversion sequentially
35-
// @todo If IfcConvert is mono-threaded, we could make it concurrent
3646
for (const assetPath of context.assetsPaths) {
37-
if (assetPath.toLowerCase().endsWith('.ifc')) {
38-
await progress('convert-start', `Converting "${assetPath}" from IFC to Collada`);
47+
if (isStep(assetPath)) {
48+
await progress('convert-start', `Converting "${assetPath}" from STEP to FBX`);
3949
await convertAndStoreAssets(convertOptions, context, assetPath);
4050
}
4151
}
4252
}
4353

44-
export async function cleanup(context: IExecIfcStepsContext): Promise<void> {
54+
export async function cleanup(context: IExecBlenderStepsContext): Promise<void> {
4555
await fs.remove(context.convertedAssetsDir);
4656
}
4757

4858
async function convertAndStoreAssets(
4959
convertOptions: string[],
50-
context: IExecIfcStepsContext,
51-
ifcFilePath: string
60+
context: IExecBlenderStepsContext,
61+
stepFilePath: string
5262
): Promise<string> {
53-
const colladaFileName = path.parse(ifcFilePath).name + '.dae';
54-
const colladaFilePath = path.resolve(`${context.convertedAssetsDir}/${colladaFileName}`);
63+
const fbxFileName = path.parse(stepFilePath).name + '.fbx';
64+
const fbxFilePath = path.resolve(`${context.convertedAssetsDir}/${fbxFileName}`);
5565

56-
const spawnArgs = [ifcFilePath, colladaFilePath, '-y'].concat(convertOptions);
66+
// Expected command:
67+
// blender -b --python .\step_to_fbx.py --python-exit-code 1 -- "path/file.stp" "path/file.fbx"
68+
const spawnArgs = [
69+
'-b',
70+
'--python', path.join(__dirname, '..', 'src', 'convert.py').toString(),
71+
'--python-exit-code', '1',
72+
'--',
73+
stepFilePath,
74+
fbxFilePath].concat(convertOptions);
5775

58-
const converterProcess = spawn(config.ifcConvertPath, spawnArgs);
76+
const converterProcess = spawn(config.blenderPath, spawnArgs);
5977

6078
//=> Watch process' stdout to log in real time, and keep the complete output in case of crash
6179
let stdoutAggregator = '';
@@ -70,22 +88,22 @@ async function convertAndStoreAssets(
7088
//=> Watch for the process to terminate, check return code
7189
converterProcess.once('close', (code) => {
7290
if (code !== 0) {
73-
const message = `Conversion of IFC file ${path.basename(ifcFilePath)} to Collada has failed!`;
74-
return reject(new IfcConvertCrashError(message, stdoutAggregator));
91+
const message = `Conversion of STEP file ${path.basename(stepFilePath)} to FBX has failed!`;
92+
return reject(new BlenderCrashError(message, stdoutAggregator));
7593
}
7694

77-
const index = context.assetsPaths.findIndex(path => path === ifcFilePath);
78-
context.assetsPaths[index] = colladaFilePath;
95+
const index = context.assetsPaths.findIndex(path => path === stepFilePath);
96+
context.assetsPaths[index] = fbxFilePath;
7997

80-
resolve(colladaFilePath);
98+
resolve(fbxFilePath);
8199
});
82100
});
83101
}
84102

85-
export class IfcConvertCrashError extends Error {
86-
constructor(message: string, public readonly ifcConvertLog: string) {
103+
export class BlenderCrashError extends Error {
104+
constructor(message: string, public readonly BlenderLog: string) {
87105
super(message);
88106

89-
Object.setPrototypeOf(this, IfcConvertCrashError.prototype);
107+
Object.setPrototypeOf(this, BlenderCrashError.prototype);
90108
}
91109
}

0 commit comments

Comments
 (0)