wireframe/shaded render modes

This commit is contained in:
Val Erastov 2022-08-07 16:11:40 -07:00
parent ed5b2a41da
commit 68fe161e30
12 changed files with 118 additions and 73 deletions

View file

@ -77,7 +77,6 @@ export function CADTrackballControls( object, domElement ) {
// events
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
@ -304,7 +303,7 @@ export function CADTrackballControls( object, domElement ) {
};
this.update = function () {
this.evaluate = function () {
_eye.subVectors( _this.object.position, _this.target );
@ -332,16 +331,12 @@ export function CADTrackballControls( object, domElement ) {
_this.object.lookAt( _this.target );
if ( lastPosition.distanceToSquared( _this.object.position ) > EPS || this.projectionChanged) {
const needsRender = lastPosition.distanceToSquared( _this.object.position ) > EPS || this.projectionChanged;
if ( needsRender) {
this.projectionChanged = false;
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
return needsRender;
};
this.reset = function () {
@ -357,8 +352,6 @@ export function CADTrackballControls( object, domElement ) {
_this.object.lookAt( _this.target );
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
};
@ -656,11 +649,7 @@ export function CADTrackballControls( object, domElement ) {
window.addEventListener( 'keyup', keyup, false );
this.handleResize();
// force an update at start
this.update();
};
}
CADTrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
CADTrackballControls.prototype.constructor = CADTrackballControls;

View file

@ -16,7 +16,6 @@ import {
Vector3,
WebGLRenderer
} from "three";
import {TransformControls} from "three/examples/jsm/controls/TransformControls";
import {stream} from "lstream";
export default class SceneSetUp {
@ -33,10 +32,9 @@ export default class SceneSetUp {
private _prevContainerWidth: number;
private _prevContainerHeight: number;
trackballControls: CADTrackballControls;
transformControls: TransformControls;
updateControlsAndHelpers: () => void;
viewportSizeUpdate$ = stream();
renderRequested: boolean;
constructor(container, onRendered) {
this.workingSphere = 10000;
@ -45,7 +43,8 @@ export default class SceneSetUp {
this.rootGroup = this.scene;
this.onRendered = onRendered;
this.scene.userData.sceneSetUp = this;
this.renderRequested = false;
this.setUpCamerasAndLights();
this.setUpControls();
@ -55,7 +54,11 @@ export default class SceneSetUp {
aspect() {
return this.container.clientWidth / this.container.clientHeight;
}
requestRender() {
this.renderRequested = true;
}
createOrthographicCamera() {
let width = this.container.clientWidth;
let height = this.container.clientHeight;
@ -101,7 +104,7 @@ export default class SceneSetUp {
this.updateOrthographicCameraViewport();
this.renderer.setSize( this.container.clientWidth, this.container.clientHeight );
this.viewportSizeUpdate$.next();
this.render();
this.__render_NeverCallMeFromOutside();
}
}
@ -148,8 +151,7 @@ export default class SceneSetUp {
this.camera = camera;
this.trackballControls.object = camera;
this.transformControls.camera = camera;
this.updateControlsAndHelpers();
this.requestRender();
}
setUpControls() {
@ -173,29 +175,7 @@ export default class SceneSetUp {
trackballControls.dynamicDampingFactor = 0.3;
trackballControls.keys = [ 65, 83, 68 ];
trackballControls.addEventListener( 'change', () => this.render());
let transformControls: any = new TransformControls( this.camera, this.renderer.domElement );
transformControls.addEventListener( 'change', () => this.render() );
this.scene.add( transformControls );
this.trackballControls = trackballControls;
this.transformControls = transformControls;
let updateTransformControls = () => {
if (transformControls.object !== undefined) {
if (transformControls.object.parent === undefined) {
transformControls.detach();
this.render();
}
transformControls.update();
}
};
this.updateControlsAndHelpers = function() {
trackballControls.update();
updateTransformControls();
};
}
createRaycaster(viewX, viewY) {
@ -288,11 +268,15 @@ export default class SceneSetUp {
animate() {
requestAnimationFrame( () => this.animate() );
this.updateControlsAndHelpers();
const controlsChangedViewpoint = this.trackballControls.evaluate();
if (controlsChangedViewpoint || this.renderRequested) {
this.__render_NeverCallMeFromOutside();
}
this.updateViewportSizeIfNeeded();
};
render() {
private __render_NeverCallMeFromOutside() {
this.renderRequested = false;
this.light.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z);
this.renderer.render(this.scene, this.camera);
this.onRendered();

View file

@ -1,5 +1,7 @@
import Vector, {AXIS, ORIGIN} from 'math/vector';
import {RiCamera2Line} from "react-icons/ri";
import {ViewMode} from "cad/scene/viewer";
import {GiCube, HiCube, HiOutlineCube} from "react-icons/all";
const NEG_X = AXIS.X.negate();
const NEG_Y = AXIS.Y.negate();
@ -192,4 +194,37 @@ export default [
noWizardFocus: true
})
},
{
id: 'ViewMode_WIREFRAME_ON',
appearance: {
label: 'wireframe',
icon: HiOutlineCube,
},
invoke: ctx => {
ctx.services.viewer.viewMode$.next(ViewMode.WIREFRAME);
ctx.services.viewer.requestRender();
}
},
{
id: 'ViewMode_SHADED_ON',
appearance: {
label: 'shaded',
icon: HiCube,
},
invoke: ctx => {
ctx.services.viewer.viewMode$.next(ViewMode.SHADED);
ctx.services.viewer.requestRender();
}
},
{
id: 'ViewMode_SHADED_WITH_EDGES_ON',
appearance: {
label: 'shaded with edges',
icon: GiCube,
},
invoke: ctx => {
ctx.services.viewer.viewMode$.next(ViewMode.SHADED_WITH_EDGES);
ctx.services.viewer.requestRender();
}
},
]

View file

@ -136,7 +136,7 @@ export interface OperationDescriptor<R> {
previewGeomProvider?: (params: R) => OperationGeometryProvider,
previewer?: any,
form: FormDefinition | React.FunctionComponent,
defaultActiveField: string,
defaultActiveField?: string,
schema?: OperationSchema,
onParamsUpdate?: (params, name, value) => void,
masking?: {

View file

@ -20,7 +20,7 @@
.left, .right {
display: flex;
align-items: baseline;
align-items: center;
}
.right {
@ -30,6 +30,8 @@
@border: 1px solid @border-color;
.left .button {
border-right: @border;
display: flex;
align-items: center;
}
.right .button {
border-left: @border;

View file

@ -23,17 +23,21 @@ function ButtonGroup({actions}) {
class ActionButton extends React.Component {
render() {
let {label, cssIcons, enabled, visible, actionId, ...props} = this.props;
let {label, cssIcons, icon, enabled, visible, actionId, ...props} = this.props;
if (!visible) {
return null;
}
const Icon = icon ? icon : null;
if (isMenuAction(actionId)) {
let onClick = props.onClick;
props.onClick = e => onClick(menuAboveElementHint(this.el));
}
return <ControlBarButton disabled={!enabled} onElement={el => this.el = el} {...props} >
{cssIcons && <Fa fa={cssIcons} fw/>} {label}
{cssIcons && <Fa fa={cssIcons} fw/>}
{Icon && <Icon />}
{label}
</ControlBarButton>;
}
}

View file

@ -1,11 +1,18 @@
import {printRaycastDebugInfo, RayCastDebugInfo} from "./rayCastDebug";
import {LOG_FLAGS} from "../../logFlags";
import {LOG_FLAGS} from "cad/logFlags";
import {stream} from "lstream";
const MouseStates = {
IDLE: 'IDLE',
DOWN: 'DOWN'
}
export function activate(ctx) {
const {services, streams} = ctx;
const domElement = services.viewer.sceneSetup.domElement();
const event = {
viewer: services.viewer
viewer: services.viewer,
mouseState: MouseStates.IDLE
};
domElement.addEventListener('mousedown', mousedown, false);
@ -13,6 +20,12 @@ export function activate(ctx) {
domElement.addEventListener('mousemove', mousemove, false);
domElement.addEventListener('dblclick', dblclick, false);
const onMoveLogicRequest$ = stream();
onMoveLogicRequest$.throttle(100).attach(() => {
const hits = performRaycast(event.mouseEvent);
dispatchMousemove(event.mouseEvent, hits)
});
let performRaycast = e => {
const hits = services.viewer.raycast(e, services.cadScene.workGroup.children, RayCastDebugInfo);
@ -43,6 +56,7 @@ export function activate(ctx) {
}
function mousedown(e) {
event.mouseState = MouseStates.DOWN;
let hits = performRaycast(e);
dispatchMousedown(e, hits);
}
@ -69,6 +83,7 @@ export function activate(ctx) {
}
function mouseup(e) {
event.mouseState = MouseStates.IDLE;
event.mouseEvent = e;
if (toDrag) {
stopDrag(e);
@ -111,8 +126,9 @@ export function activate(ctx) {
if (toDrag) {
toDrag.dragMove(event);
} else {
let hits = performRaycast(e);
dispatchMousemove(e, hits)
if (event.mouseState === MouseStates.IDLE) {
onMoveLogicRequest$.next();
}
}
}

View file

@ -1,35 +1,33 @@
import SceneSetup from 'scene/sceneSetup';
import {Emitter, externalState, StateStream, stream} from "lstream";
import {Emitter, externalState, state, StateStream, stream} from "lstream";
import SceneSetUp from "scene/sceneSetup";
export enum ViewMode {
WIREFRAME = 'WIREFRAME',
SHADED = 'SHADED',
SHADED_WITH_EDGES = 'SHADED_WITH_EDGES'
}
export default class Viewer {
sceneRendered$: Emitter<any> = stream();
cameraMode$: StateStream<any>;
sceneSetup: SceneSetUp;
renderRequested: boolean;
viewMode$: StateStream<ViewMode> = state(ViewMode.SHADED_WITH_EDGES);
sceneSetup: SceneSetUp;
constructor(container, onRendered) {
this.cameraMode$ = externalState(() => this.getCameraMode(), mode => this.setCameraMode(mode))
this.sceneSetup = new SceneSetup(container, onRendered);
this.renderRequested = false;
this.sceneSetup = new SceneSetUp(container, onRendered);
}
render() {
this.sceneSetup.render();
this.requestRender();
}
requestRender = () => {
if (this.renderRequested) {
return;
}
setTimeout(() => {
this.renderRequested = false;
this.render();
});
this.sceneSetup.requestRender();
};
setVisualProp = (obj, prop, value) => {

View file

@ -1,4 +1,5 @@
import {CurveBasedView} from './curveBasedView';
import {ViewMode} from "cad/scene/viewer";
const MarkerTable = [
{
@ -19,5 +20,8 @@ export class EdgeView extends CurveBasedView {
let brepEdge = edge.brepEdge;
let tess = brepEdge.data.tessellation ? brepEdge.data.tessellation : brepEdge.curve.tessellateToData();
super(ctx, edge, tess, 3, 0x000000, MarkerTable);
this.addDisposer(ctx.viewer.viewMode$.attach(mode => {
this.representation.visible = (mode === ViewMode.SHADED_WITH_EDGES || mode === ViewMode.WIREFRAME);
}));
}
}

View file

@ -7,6 +7,7 @@ import {createSolidMaterial} from "cad/scene/views/viewUtils";
import {SketchMesh} from "cad/scene/views/shellView";
import {FACE} from "cad/model/entities";
import {setAttribute} from "scene/objectData";
import {ViewMode} from "cad/scene/viewer";
export class SketchingView extends View {
@ -69,7 +70,6 @@ export class FaceView extends SketchingView {
super(ctx, face, parent);
let geom;
this.meshFaces = [];
if (face.brepFace.data.tessellation) {
geom = tessDataToGeom(face.brepFace.data.tessellation.data)
} else {
@ -86,6 +86,10 @@ export class FaceView extends SketchingView {
this.ctx.highlightService.unHighlight(this.model.id);
}
this.rootGroup.add(this.mesh);
this.addDisposer(ctx.viewer.viewMode$.attach(mode => {
this.mesh.visible = (mode === ViewMode.SHADED_WITH_EDGES || mode === ViewMode.SHADED);
}));
}
dispose() {

View file

@ -1,3 +1,5 @@
import {GiCube} from "react-icons/all";
export default [
{
id: 'file',
@ -26,6 +28,13 @@ export default [
actions: ['StandardViewFront', 'StandardViewBack', 'StandardViewLeft', 'StandardViewRight',
'StandardViewTop', 'StandardViewBottom', 'StandardView3Way']
},
{
id: 'viewModes',
label: 'mode',
icon: GiCube,
info: 'view/render mode',
actions: ['ViewMode_WIREFRAME_ON', 'ViewMode_SHADED_ON', 'ViewMode_SHADED_WITH_EDGES_ON']
},
{
id: 'boolean',
label: 'bool',

View file

@ -12,7 +12,7 @@ import {Explorer} from "cad/dom/components/Explorer";
export function activate(ctx) {
const {services, streams} = ctx;
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'Donate', 'GitHub'];
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'menu.viewModes', 'Donate', 'GitHub'];
streams.ui.controlBars.right.value = [
['Info', {label: null}],
['RefreshSketches', {label: null}],