Skip to content

Commit

Permalink
Added POV-Ray exporter to online vZome
Browse files Browse the repository at this point in the history
This completes the POV-Ray support, which was started in a recent commit.

I had to make the exporter ResourceLoader-aware, map the light direction
vectors to world coordinates, and to get rid of the leading "0." from shape
UUIDs.
  • Loading branch information
vorth committed Sep 15, 2024
1 parent 38c80b6 commit fc130db
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 121 deletions.
15 changes: 3 additions & 12 deletions core/src/main/java/com/vzome/core/exporters/POVRayExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.vzome.core.math.symmetry.Embedding;
import com.vzome.core.render.RenderedManifestation;
import com.vzome.core.viewing.CameraIntf;
import com.vzome.xml.ResourceLoader;

/**
* Renders out to POV-Ray using #declare statements to reuse geometry.
Expand Down Expand Up @@ -67,18 +68,8 @@ public void doExport( File povFile, Writer writer, int height, int width ) throw
output .println( "#declare parallel_proj = " + (mScene .isPerspective()?0:1) + ";" );
output .println();

InputStream input = getClass() .getClassLoader()
.getResourceAsStream( PREAMBLE_FILE );
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int num;
try {
while ( ( num = input .read( buf, 0, 1024 )) > 0 )
out .write( buf, 0, num );
} catch (IOException e) {
e.printStackTrace();
}
output .println( new String( out .toByteArray() ) );
String preamble = ResourceLoader.loadStringResource( PREAMBLE_FILE );
output .println( preamble );
output .println();

for ( int i = 0; i<3; i++ ) {
Expand Down
10 changes: 7 additions & 3 deletions online/src/app/classic/menus/filemenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { saveFileAs, openFile, saveTextFileAs, saveTextFile } from "../../../vie
import { CommandAction, Divider, Menu, MenuAction, MenuItem, SubMenu } from "../../framework/menus.jsx";
import { UrlDialog } from '../dialogs/webloader.jsx'
import { SvgPreviewDialog } from "../dialogs/svgpreview.jsx";
import { useCamera } from "../../../viewer/context/camera.jsx";
import { INITIAL_DISTANCE, useCamera } from "../../../viewer/context/camera.jsx";
import { useImageCapture } from "../../../viewer/context/export.jsx";

const queryParams = new URLSearchParams( window.location.search );
Expand All @@ -33,7 +33,7 @@ export const FileMenu = () =>
{
const { rootController, state, setState,
createDesign, openDesignFile, fetchDesignUrl, importMeshFile, guard, edited } = useEditor();
const { state: cameraState } = useCamera();
const { state: cameraState, mapViewToWorld } = useCamera();
const [ showDialog, setShowDialog ] = createSignal( false );
const fields = () => controllerProperty( rootController(), 'fields', 'fields', true );

Expand Down Expand Up @@ -98,7 +98,11 @@ export const FileMenu = () =>
const exportAs = ( extension, mimeType, format=extension, params={} ) => evt =>
{
const camera = unwrap( cameraState.camera );
camera .magnification = Math.log( camera.distance / INITIAL_DISTANCE );

const lighting = unwrap( cameraState.lighting );
lighting .directionalLights .forEach( light => light .worldDirection = mapViewToWorld( light.direction ) );

controllerExportAction( rootController(), format, { camera, lighting, ...params } )
.then( text => {
const name = (state.designName || 'untitled') .concat( "." + extension );
Expand Down Expand Up @@ -163,7 +167,7 @@ export const FileMenu = () =>

<SubMenu label="Export 3D Rendering">
<ExportItem label="Collada DAE" ext="dae" mime="text/plain" disabled={true} />
<ExportItem label="POV-Ray" ext="pov" mime="text/plain" disabled={true} />
<ExportItem label="POV-Ray" ext="pov" mime="text/plain" />
<ExportItem label="vZome Shapes JSON" format="shapes" ext="shapes.json" mime="application/json" />
<ExportItem label="VRML" ext="vrml" mime="text/plain" />
</SubMenu>
Expand Down
11 changes: 9 additions & 2 deletions online/src/viewer/context/camera.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import { createContext, createEffect, useContext } from 'solid-js';
import { PerspectiveCamera } from "three";
import { PerspectiveCamera, Vector3 } from "three";

import { createStore } from 'solid-js/store';

Expand Down Expand Up @@ -154,6 +154,13 @@ const CameraProvider = ( props ) =>
}
const trackballProps = { camera: trackballCamera, sync }; // no need (or desire) for reactivity here

const mapViewToWorld = ( [ x, y, z ] ) =>
{
const vec = new Vector3( x, y, z );
vec .transformDirection( trackballCamera.matrixWorldInverse );
return [ vec.x, vec.y, vec.z ];
}

const setCamera = loadedCamera =>
{
setState( 'camera', loadedCamera );
Expand All @@ -179,7 +186,7 @@ const CameraProvider = ( props ) =>
const providerValue = {
name: props.name,
perspectiveProps, trackballProps, state,
resetCamera, setCamera, setLighting, togglePerspective, toggleOutlines, setDistance,
resetCamera, setCamera, setLighting, togglePerspective, toggleOutlines, setDistance, mapViewToWorld,
};

// The perspectiveProps is used to initialize PerspectiveCamera in clients.
Expand Down
2 changes: 1 addition & 1 deletion online/src/worker/java/java/util/UUID.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private UUID( String s )

public static UUID randomUUID()
{
return new UUID( Double.toString( Math.random() ) );
return new UUID( Double.toString( Math.random() ) .substring( 2 ) );
}

public String toString()
Expand Down
43 changes: 15 additions & 28 deletions online/src/worker/legacy/core-java.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { java, javaemul } from "./candies/j4ts-2.1.0-SNAPSHOT/bundle.js"
this.value = s;
}
static randomUUID() {
return new UUID(/* toString */ ('' + (Math.random())));
return new UUID(/* toString */ ('' + (Math.random())).substring(2));
}
toString() {
return this.value;
Expand Down Expand Up @@ -3952,10 +3952,6 @@ export var com;
this.orbits = new com.vzome.core.math.symmetry.OrbitSet(symmetry);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations$() {
return this.getOrientations(false);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getZone(orbit, orientation) {
return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation);
}
Expand Down Expand Up @@ -3983,6 +3979,10 @@ export var com;
return embedding;
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations$() {
return this.getOrientations(false);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations(rowMajor) {
if (((typeof rowMajor === 'boolean') || rowMajor === null)) {
let __args = arguments;
Expand Down Expand Up @@ -16761,10 +16761,6 @@ export var com;
this.setStyle(styleName);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations$() {
return this.getOrientations(false);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getZone(orbit, orientation) {
return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation);
}
Expand Down Expand Up @@ -16792,6 +16788,10 @@ export var com;
return embedding;
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations$() {
return this.getOrientations(false);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations(rowMajor) {
if (((typeof rowMajor === 'boolean') || rowMajor === null)) {
let __args = arguments;
Expand Down Expand Up @@ -36960,21 +36960,8 @@ export var com;
this.output.println$();
this.output.println$java_lang_Object("#declare parallel_proj = " + (this.mScene.isPerspective() ? 0 : 1) + ";");
this.output.println$();
const input = this.constructor.getClassLoader().getResourceAsStream(POVRayExporter.PREAMBLE_FILE);
const out = new java.io.ByteArrayOutputStream();
const buf = (s => { let a = []; while (s-- > 0)
a.push(0); return a; })(1024);
let num;
try {
while (((num = input.read(buf, 0, 1024)) > 0)) {
out.write(buf, 0, num);
}
;
}
catch (e) {
console.error(e.message, e);
}
this.output.println$java_lang_Object(new String(out.toByteArray()));
const preamble = com.vzome.xml.ResourceLoader.loadStringResource(POVRayExporter.PREAMBLE_FILE);
this.output.println$java_lang_Object(preamble);
this.output.println$();
for (let i = 0; i < 3; i++) {
{
Expand Down Expand Up @@ -48051,10 +48038,6 @@ export var com;
this.__parent = __parent;
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations$() {
return this.getOrientations(false);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getZone(orbit, orientation) {
return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation);
}
Expand Down Expand Up @@ -48082,6 +48065,10 @@ export var com;
return embedding;
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations$() {
return this.getOrientations(false);
}
/* Default method injected from com.vzome.core.editor.api.OrbitSource */
getOrientations(rowMajor) {
if (((typeof rowMajor === 'boolean') || rowMajor === null)) {
let __args = arguments;
Expand Down
111 changes: 70 additions & 41 deletions online/src/worker/legacy/exporters.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const exporterClasses = {
'off' : 'OffExporter',
'ply' : 'PlyExporter',
'vrml' : 'VRMLExporter',
'pov' : 'PovRayExporter',
'pov' : 'POVRayExporter',
'partgeom' : 'PartGeometryExporter',
'openscad' : 'OpenScadExporter',
'math' : 'MathTableExporter',
Expand All @@ -29,44 +29,6 @@ const exporterClasses = {
// 'FORMAT' : 'VefModelExporter',
}

export const export3d = ( scene, configuration ) =>
{
const { format, height, width } = configuration;
const { renderedModel } = scene;
const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ]();
const out = new java.io.StringWriter();
exporter .exportGeometry( renderedModel, null, out, height, width );
return out.toString();
}

const createDocument = ( legacyDesign, camera, lighting ) =>
{
// TODO
return {
// CameraIntf getCameraModel();

// Lights getSceneLighting();

// RenderedModel getRenderedModel();

// ToolsModel getToolsModel();

// Element getDetailsXml( Document dom, boolean b );

// EditorModel getEditorModel();
}
}

export const export3dDocument = ( legacyDesign, camera, lighting, configuration ) =>
{
const { format, height, width } = configuration;
const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ]();
const out = new java.io.StringWriter();
const document = createDocument( legacyDesign, camera, lighting );
exporter .exportDocument( document, null, out, height, width );
return out.toString();
}

const parseColor = input =>
{
const m = input .match( /^#([0-9a-f]{6})$/i )[1];
Expand All @@ -86,7 +48,9 @@ const createLights = lighting =>
const lights = new com.vzome.core.viewing.Lights();
lights .setBackgroundColor( parseColor( backgroundColor ) );
lights .setAmbientColor( parseColor( ambientColor ) );
for ( const { direction: [x,y,z], color } of directionalLights ) {
for ( const { worldDirection: [x,y,z], color } of directionalLights ) {
// Because we are using worldDirection rather than direction, these vectors are in world coordinates already.
// Apparently, POVRayExporter is the only exporter that uses directional lights.
lights .addDirectionLight( parseColor( color ), new com.vzome.core.math.RealVector( x, y, z ) );
}
return lights;
Expand All @@ -110,6 +74,49 @@ const createProjectionMatrix = ( camera, aspectRatio ) =>
return com.vzome.core.math.RealMatrix4.perspective( fovX, aspectRatio, near, far );
}

const createCamera = ( camera ) =>
{
const { distance, width, perspective, lookAt, up, lookDir, magnification } = camera; // This camera always comes from the client context
const halfX = width / 2;
const fov = 2 * Math.atan( halfX / distance );
let [ x, y, z ] = lookAt;
const lookAtRV = new com.vzome.core.math.RealVector( x, y, z );
[ x, y, z ] = up;
const upRV = new com.vzome.core.math.RealVector( x, y, z );
[ x, y, z ] = lookDir;
const lookDirRV = new com.vzome.core.math.RealVector( x, y, z );
return {
isPerspective: () => perspective,
getFieldOfView: () => fov,
getViewDistance: () => distance,
getMagnification: () => magnification,
getLookAtPointRV: () => lookAtRV,
getLookDirectionRV: () => lookDirRV,
getUpDirectionRV: () => upRV,

// POVRayExporter will call this to map light directions to world coordinates, in the Java code,
// but here in Javascript our light directions are *already* in world coordinates.
mapViewToWorld: rv => rv,
}
}

const createDocument = ( legacyDesign, camera, lighting ) =>
{
const { renderedModel, editor, toolsModel } = legacyDesign;
const lights = createLights( lighting );
const cameraModel = createCamera( camera );
return {
getCameraModel: () => cameraModel,
getSceneLighting: () => lights,
getRenderedModel: () => renderedModel,
getToolsModel: () => toolsModel,
getEditorModel: () => editor,
getDetailsXml: ( dom, deep ) => null, // TODO: implement this so more exporters work
}
}

////////////////////////////////////////////// main entry points:

export const export2d = ( scene, configuration ) =>
{
const { format, height, width, useShapes, drawOutlines, monochrome, showBackground, useLighting } = configuration;
Expand All @@ -123,4 +130,26 @@ export const export2d = ( scene, configuration ) =>
const out = new java.io.StringWriter();
exporter .export( snapshot, out, drawOutlines, monochrome, showBackground );
return out.toString();
}
}

export const export3d = ( scene, configuration ) =>
{
const { format, height, width } = configuration;
const { renderedModel } = scene;
const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ]();
const out = new java.io.StringWriter();
exporter .exportGeometry( renderedModel, null, out, height, width );
return out.toString();
}

export const export3dDocument = ( legacyDesign, camera, lighting, configuration ) =>
{
const { format, height, width } = configuration;
const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ]();
const out = new java.io.StringWriter();
// Satisfy the DocumentIntf contract required by DocumentExporter
const document = createDocument( legacyDesign, camera, lighting );
exporter .exportDocument( document, null, out, height, width );
return out.toString();
}

Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,8 @@ namespace com.vzome.core.exporters {
this.output.println$();
this.output.println$java_lang_Object("#declare parallel_proj = " + (this.mScene.isPerspective() ? 0 : 1) + ";");
this.output.println$();
const input: java.io.InputStream = (<any>this.constructor).getClassLoader().getResourceAsStream(POVRayExporter.PREAMBLE_FILE);
const out: java.io.ByteArrayOutputStream = new java.io.ByteArrayOutputStream();
const buf: number[] = (s => { let a=[]; while(s-->0) a.push(0); return a; })(1024);
let num: number;
try {
while(((num = input.read(buf, 0, 1024)) > 0)) {out.write(buf, 0, num)};
} catch(e) {
console.error(e.message, e);
}
this.output.println$java_lang_Object(<string>new String(out.toByteArray()));
const preamble: string = com.vzome.xml.ResourceLoader.loadStringResource(POVRayExporter.PREAMBLE_FILE);
this.output.println$java_lang_Object(preamble);
this.output.println$();
for(let i: number = 0; i < 3; i++) {{
const color: com.vzome.core.construction.Color = this.mLights.getDirectionalLightColor(i);
Expand Down
Loading

0 comments on commit fc130db

Please sign in to comment.