I'm trying to assign a new material to an object, but when I assign a new (color) map, the object renders as white, and the AO and shadows no longer show up. It's as if the emissive attribute is 100%. I can change the color attribute (e.g. 'red' or 'blue'), ao, normal, etc. without issues. The glb loaded in already has a working material with a color map and ao, but I want to be able to replace it.
I'm using 8th Wall with A-Frame, but I've registered the following as a custom Three.js component.
const customMat = {
schema: {}, // will pass textures via aframe later
init() {
this.el.addEventListener('model-loaded', (e) => {
const material = new THREE.MeshStandardMaterial()
const texLoader = new THREE.TextureLoader()
texLoader.crossOrigin = ''
const mapColor = texLoader.load('assets/cover_color.jpg')
const mapAO = texLoader.load('assets/cover_ao.jpg')
material.map = mapColor // makes everything 100% white likes it's emissive
// material.color = new THREE.Color('red') // works fine no problem
material.aoMap = mapAO
material.aoMapIntensity = 1
e.detail.model.traverse((mesh) => {
if (mesh.isMesh) {
mesh.material = material
mesh.material.needsUpdate = true // not sure if needed
}
})
})
},
}
export {customMat}
Any suggestions would be much appreciated. I've tried this with primitive geometry too, but the same issue occurs. I don't seem to be able to modify the existing material's attributes either, so maybe my approach is fundamentally wrong.
I'm working on some screen space shaders using THREE.JS and GLSL. I'd like to both render a "diffuse" buffer with per object materials/textures AND render an "ID" buffer where each object has a unique color for object edge detection. This would also be really useful for object picking as some of the THREE.JS examples show.
I am already using Scene.overrideMaterial to render normal buffers (and some other cool stuff) but haven't figured out a way to do something similar for an ID buffer. Is there something like this that exists? Seems useful enough that it would be likely. Have other people developed solutions to this problem? Maybe by mapping an objects UUID to a color value?
-----> EDIT
Here is my attempt using this example:
https://threejs.org/examples/webgl_interactive_instances_gpu.html
I'm not concerned with selection at this point, regardless this isn't working. I think I may be setting up onBeforeRender() incorrectly. Any help on this?
Thanks!
initPieces = function(geometry){
...
//set id Color to unique color
//for picking will use setHex(i) method
m.userData.idColor = material.color;
...
m.onBeforeRender = function (){
if(Viewer._scene.overrideMaterial){
if(Viewer._scene.overrideMaterial._name == "id"){
var updateList = [];
var u = scene.overrideMaterial.uniforms;
//picking color in user data
var d = this.userData;
//Is this just equivalent to checking whether this is the picking material?
if(u.idColor){
u.idColor.value = (d.idColor);
// u.needsUpdate = true;
updateList.push("idColor");
}
//Don't understand what this is doing
//Throws "WebGL INVALID_OPERATION: uniformMatrix4fv: location is not from current program"
if (updateList.length){
var materialProperties = renderer.properties.get(material);
if( materialProperties.program){
var gl = renderer.getContext();
var p = materialProperties.program;
gl.useProgram( p.program );
var pu = p.getUniforms();
updateList.forEach(function(name){
pu.setValue( gl, name, u[name].value);
});
}
}
}
}
}
// Doesn't show onBeforeRender set as I expected?
console.log(m);
...
}
...
}
...
function render(scene, camera){
...
scene.overrideMaterial = getMaterial("id");
renderer.render(scene, camera, getTarget("id"));
scene.overrideMaterial = null;
...
}
I have a model, which is displayed in Three.js correctly. Top at the top, bottom at the bottom. However, model has a preset rotation of -1.57 on X axis. It means If I add any new object to the scene, axis of object will be not the same as the model axis. How can clear out or reset this preset rotation so the axis of model and axis of world will match and top will be still at the top? I hope I explained myself clear. Thank you.
How about to rotate yours model once by 1.57, "burn" this orientation back to the vertices of the model then saving it to new file.
// reset mesh rotation
mesh.rotation.y = 1.57;
// make current oreintation, the base of the model
applyMeshTransformation(mesh);
// export the model to new blob file (can saved from this to new file)
exportObject(mesh)
function applyMeshTransformation(mesh) {
// apply local matrix on geometry
mesh.updateMatrixWorld();
mesh.geometry.applyMatrix(mesh.matrixWorld);
// reset local matrix
mesh.position.set(0,0,0);
mesh.rotation.set(0,0,0);
mesh.scale.set(1,1,1);
mesh.updateMatrixWorld();
};
function exportObject(mesh) {
var objExporter = new THREE.ObjectExporter();
var output = JSON.stringify( objExporter.parse( mesh ), null, '\t' );
output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
var blob = new Blob( [ output ], { type: 'text/plain' } );
var objectURL = window.URL.createObjectURL( blob );
window.open( objectURL, '_blank' );
};
Here is the structure of my HTML body.
<body class="claro">
<div id="BorderContainerMain" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="width: 100%; border:0; padding:0; margin:0;">
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'" style="border: 0;">
<div id="topBanner" data-dojo-type="core.widget.GeneralMainHeader" ></div>
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'left'" style="border: 0;">
<div id="leftNav" data-dojo-type="core.widget.GeneralLeftNavigation"></div>
</div>
<div id="contentStackContainer" data-dojo-type="dijit.layout.StackContainer" data-dojo-props="region:'center', style: 'border: 0; padding: 0; margin: 0;'"></div>
</div>
Now, my desire is to make the whole screen auto grow in height depending on the content of the center region and let the viewport handle the vertical scrolling.
I am fairly new into dijit layout and I am using DOJO 1.6. I have already learned that I have to write code to achieve this. So, I am trying to get some experience guidance on this.
Thanks,
Rishi
So, I wrote up a custom resize for my purpose but it is not working. Is there any why it not working. Here is the code:
dojo.provide("core.widget.ClientBorderContainer");
dojo.require("dijit.layout.BorderContainer");
dojo.declare("core.widget.ClientBorderContainer", [dijit.layout.BorderContainer], {
totalHeight: 0,
_setupChild: function(/*dijit._Widget*/ child){
// Override BorderContainer._setupChild().
var region = child.region;
console.debug("region :: "+region);
if(region){
this.inherited(arguments);
dojo.addClass(child.domNode, this.baseClass+"Pane");
var ltr = this.isLeftToRight();
if(region == "leading"){ region = ltr ? "left" : "right"; }
if(region == "trailing"){ region = ltr ? "right" : "left"; }
// Create draggable splitter for resizing pane,
// or alternately if splitter=false but BorderContainer.gutters=true then
// insert dummy div just for spacing
if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
var splitter = new _Splitter({
id: child.id + "_splitter",
container: this,
child: child,
region: region,
live: this.liveSplitters
});
splitter.isSplitter = true;
child._splitterWidget = splitter;
dojo.place(splitter.domNode, child.domNode, "after");
// Splitters aren't added as Contained children, so we need to call startup explicitly
splitter.startup();
console.debug("Should not be printed as there is no splitter");
}
child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
}
},
resize: function(newSize, currentSize){
// Overrides BorderContainer.resize().
// resetting potential padding to 0px to provide support for 100% width/height + padding
// TODO: this hack doesn't respect the box model and is a temporary fix
if(!this.cs || !this.pe){
var node = this.domNode;
this.cs = dojo.getComputedStyle(node);
this.pe = dojo._getPadExtents(node, this.cs);
this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
dojo.style(node, "padding", "0px");
}
//Following section calculates the desired height of the BorderContainer
console.debug(this.id+" contentBox height ::: "+dojo.contentBox(this.domNode).h);
this.totalHeight = 0;
dojo.forEach(this.getChildren(), this.calculateHeight, this);
console.debug("this.totalHeight ::: "+this.totalHeight);
console.debug("newSize "+newSize);
console.debug("currentSize "+currentSize);
////////////////////////////////////////////////
// summary:
// Call this to resize a widget, or after its size has changed.
// description:
// Change size mode:
// When changeSize is specified, changes the marginBox of this widget
// and forces it to relayout its contents accordingly.
// changeSize may specify height, width, or both.
//
// If resultSize is specified it indicates the size the widget will
// become after changeSize has been applied.
//
// Notification mode:
// When changeSize is null, indicates that the caller has already changed
// the size of the widget, or perhaps it changed because the browser
// window was resized. Tells widget to relayout its contents accordingly.
//
// If resultSize is also specified it indicates the size the widget has
// become.
//
// In either mode, this method also:
// 1. Sets this._borderBox and this._contentBox to the new size of
// the widget. Queries the current domNode size if necessary.
// 2. Calls layout() to resize contents (and maybe adjust child widgets).
//
// changeSize: Object?
// Sets the widget to this margin-box size and position.
// May include any/all of the following properties:
// | {w: int, h: int, l: int, t: int}
//
// resultSize: Object?
// The margin-box size of this widget after applying changeSize (if
// changeSize is specified). If caller knows this size and
// passes it in, we don't need to query the browser to get the size.
// | {w: int, h: int}
var node = this.domNode;
// set margin box size, unless it wasn't specified, in which case use current size
if(newSize){
dojo.marginBox(node, newSize);
// set offset of the node
if(newSize.t){ node.style.top = newSize.t + "px"; }
if(newSize.l){ node.style.left = newSize.l + "px"; }
}
// If either height or width wasn't specified by the user, then query node for it.
// But note that setting the margin box and then immediately querying dimensions may return
// inaccurate results, so try not to depend on it.
var mb = currentSize || {};
dojo.mixin(mb, {h: this.totalHeight}); // calculated height overrides currentSize
if( !("h" in mb) || !("w" in mb) ){
mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values
}
// Compute and save the size of my border box and content box
// (w/out calling dojo.contentBox() since that may fail if size was recently set)
var cs = dojo.getComputedStyle(node);
var me = dojo._getMarginExtents(node, cs);
var be = dojo._getBorderExtents(node, cs);
console.debug("mb.w "+mb.w);
console.debug("mb.h "+mb.h);
var bb = (this._borderBox = {
w: mb.w - (me.w + be.w),
h: mb.h - (me.h + be.h)
});
var pe = dojo._getPadExtents(node, cs);
console.debug("bb.w "+bb.w);
console.debug("bb.h "+bb.h);
this._contentBox = {
l: dojo._toPixelValue(node, cs.paddingLeft),
t: dojo._toPixelValue(node, cs.paddingTop),
w: bb.w - pe.w,
h: bb.h - pe.h
};
// Callback for widget to adjust size of its children
this.layout();
///////////////////////////////////////////////
console.debug(this.id+" contentBox height ::: "+dojo.contentBox(this.domNode).h);
},
calculateHeight: function(/*dijit._Widget*/ child){
var region = child.region;
console.debug("region :: "+region);
if(region && (region == "top" || region == "center" || region == "bottom")){
var childHeight = 0;
if(child instanceof dijit.layout.StackContainer){
console.debug("selectedChildWidget "+child.selectedChildWidget);
if(child.selectedChildWidget)
childHeight = dojo.contentBox(child.selectedChildWidget.domNode).h;
else
childHeight = dojo.contentBox(child.domNode).h;
}else{
childHeight = dojo.contentBox(child.domNode).h;
}
this.totalHeight = this.totalHeight+childHeight;
console.debug("childHeight = "+childHeight+" child.declaredClass"+child.declaredClass);
}
},
_layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
// summary:
// This is the main routine for setting size/position of each child.
// description:
// With no arguments, measures the height of top/bottom panes, the width
// of left/right panes, and then sizes all panes accordingly.
//
// With changedRegion specified (as "left", "top", "bottom", or "right"),
// it changes that region's width/height to changedRegionSize and
// then resizes other regions that were affected.
// changedChildId:
// Id of the child which should be resized because splitter was dragged.
// changedChildSize:
// The new width/height (in pixels) to make specified child
if(!this._borderBox || !this._borderBox.h){
// We are currently hidden, or we haven't been sized by our parent yet.
// Abort. Someone will resize us later.
return;
}
console.debug("custom lay out children called");
// Generate list of wrappers of my children in the order that I want layoutChildren()
// to process them (i.e. from the outside to the inside)
var wrappers = dojo.map(this.getChildren(), function(child, idx){
return {
pane: child,
weight: [
child.region == "center" ? Infinity : 0,
child.layoutPriority,
(this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
idx
]
};
}, this);
wrappers.sort(function(a, b){
var aw = a.weight, bw = b.weight;
for(var i=0; i<aw.length; i++){
if(aw[i] != bw[i]){
return aw[i] - bw[i];
}
}
return 0;
});
// Make new list, combining the externally specified children with splitters and gutters
var childrenAndSplitters = [];
dojo.forEach(wrappers, function(wrapper){
var pane = wrapper.pane;
childrenAndSplitters.push(pane);
if(pane._splitterWidget){
childrenAndSplitters.push(pane._splitterWidget);
}
});
// Compute the box in which to lay out my children
console.debug("this._borderBox.h :: "+this._borderBox.h);
var dim = {
l: this.pe.l,
t: this.pe.t,
w: this._borderBox.w - this.pe.w,
h: this._borderBox.h - this.pe.h
};
// Layout the children, possibly changing size due to a splitter drag
dijit.layout.layoutChildren(this.domNode, dim, childrenAndSplitters,
changedChildId, changedChildSize);
}
});
BorderContainer was written to take a sized box and calculate the center based on what's left over, sort of the opposite of what you're trying to do. AFAIK it won't support this.
This has been really bugging me and I can't seem to ask the right question while searching for a solution! The problem is simple enough: Why can I not attach an event to a programmatically created MovieClip in Actionscript 3? This has got to be something basic I am missing, right?
package {
//imports
import flash.display.*;
import flash.events.*;
//document class
public class Document extends MovieClip {
//variables
public var utilities:Object;
public var test:MovieClip;
//constructor
public function Document()
{
//instantiate the utilities object - this will contain some
//simple functions for setting dimensions and background color of
//a display object.
this.utilities = new Object();
//utility function: set dimensions.
this.utilities.setDimensions = function(what:*, width:uint, height:uint)
{
what.graphics.drawRect(0, 0, width, height);
}
//utility function: set opaque background color.
this.utilities.setBgColor = function(what:DisplayObject, color:uint)
{
what.cacheAsBitmap = true;
what.opaqueBackground = color;
}
//create/add a test movie clip.
test = new MovieClip();
//set dimensions and background color of the test movie clip.
this.utilities.setDimensions(test, 100, 100);
this.utilities.setBgColor(test, 0xff0000);
//add test movie clip to document class.
this.addChild(test);
//add a click event to the test movie clip.
test.addEventListener(MouseEvent.CLICK, onClick);
}
//click event handler
private function onClick(evt:Event):void
{
trace('click');
}
}
}
//update based on Casey Yee's answer
//the set dimension function needs to contain a fill to be clickable
//a fill is added with alpha set to zero to maintain transparency.
//utility function: set dimensions
this.utilities.setDimensions = function(what:*, width:uint, height:uint)
{
what.graphics.beginFill(0x000000, 0);
what.graphics.drawRect(0, 0, width, height);
what.graphics.endFill();
}
test is still a empty movie clip, try adding drawing something into it:
test = new MovieClip();
test.graphics.beginFill(0xFF0000);
test.graphics.drawRect(0,0,100,100);
test.graphics.endFill();