I'm trying to modify an existent project, I would like to set a fallback texture when I load a GLTF file
BABYLON.GLTFFileLoader.IncrementalLoading = false;
BABYLON.SceneLoader.AppendAsync(rootPath, 'data:' + gltfContent, scene, undefined, '.gltf').then(function () {
scene.createDefaultCameraOrLight(true);
scene.activeCamera.attachControl(canvas);
scene.activeCamera.wheelDeltaPercentage = 0.005;
I don't know exactly how to do it.
what's the better way to proceed? should I read the GLTF and modify the URI?
I think it is a better solution to use some callback
Anyone is an expert with babylon.js?
Thanks
Babylon's GLTF 2.0 loader has an extension system that can be seen as promise-based plugin for each step of the loading process.
You can see a few examples for the extensions here:
https://github.com/BabylonJS/Babylon.js/tree/master/loaders/src/glTF/2.0/Extensions
What can be very interesting in your case is here:
https://github.com/BabylonJS/Babylon.js/blob/master/loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts
As you can see, this extension contains a function called loadTextureInfoAsync that returns a promise when done, after receiving the texture info that is about to be loaded.
Depending on your use case you can completely replace the load texture functionality or extend it (like in the example above). To override it you should just implement the function yourself:
public loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>> {
const texture = loadTheTextureYourselfWithFallback();
return Promise.resolve(texture);
}
Of course, the load function needs to be implemented according to your logic.
Related
Scenario
I am trying to create a 2 component 'system' whereby there is one master, aggregator component and multiple sub source components that feed data to the aggregator. Once all the data is received, the aggregator component will then run some functions on the parent entity. I am using a-frame 0.8.2.
Minimal example
To demonstrate the logic in its simplest form, below is an example where 3 sub components are attached to an entity and each pass a string to the parent component by pushing them into an array that is part of the aggregator component's schema.
Here is the master component aggregator
AFRAME.registerComponent('aggregator', {
schema: {
testarray: {type: 'array', default: []},
},
update: function(){
//to demonstrate problem
console.log(this.data.testarray)
},
})
And the sub component source
AFRAME.registerComponent('source', {
dependencies: ['aggregator'],
multiple: true,
schema: {
teststring: {type: 'string', default: ''},
},
init: function(){
var string = this.data.teststring
var aggArray = this.el.getAttribute('aggregator').testarray
aggArray.push(string)
this.el.setAttribute('aggregator', 'testarray', aggArray)
},
})
And here is the HTML to test it
<a-scene>
<a-entity
id="aggregatortest"
source__one="teststring:testone"
source__two="teststring:testtwo"
source__three="teststring:testthree">
</a-entity>
</a-scene>
Here is a glitch (not much to see obviously but if you open the console you will see an 'empty' array (not actually empty but appears so).
The problem
As you will see if you run or look at the above example, the aggregator component's testarray is 'empty' on update. I know that the data is actually being successfully passed as if I look at the component in the console, I can see the array has been updated with the values as it is in the components data, it is not updated however. This is an issue because if I try to run some functions (depending on the passed data) in the aggregator's update, it does not work.
Am I doing something wrong? Do I need to wait for something to load? or some internal event? I would expect that every time the source component is initialised (in this case 3 times), it would force an update in the aggregator component which apparently is it is, somehow, but not in a way that I can work with since I cannot access the updated schema's properties in the update function.
Any advice would be much appreciated, if any more info or real context would be helpful, please let me know.
tldr: instead of grabbing the reference to testarray clone it:
var aggArray = this.el.getAttribute('aggregator').testarray.slice(0)
like i did here. Cloning #slice(0) was recommended by David Walsh here.
With this simple statement
var aggArray = this.el.getAttribute('aggregator').testarray
You grab a reference to the testarray.
In consequence, any changes to the aggArray are applied to the aggregator.testarray. Check it out in this fiddle. I removed the setAttribute bit, and the array still has new elements.
Now, keep in mind, if both arrays are the same, the update won't even fire - why would it, both the old data, and new are exactly the same.
In conclusion, you must provide a copy / clone of the array -> so the update function can work properly.
I know, that to create a new path in Qt from a given absolute path, you use QDir::makepath() as dir.makepath(path), as it is suggested in this question. I do not have any trouble in using it and it works fine. My question is directed, as to why the developers would not provide a static function to call in a way like QDir::makepath("/Users/me/somepath/");. Needing to create a new QDir instance seems unnecessary to me.
I can only think of two possible reasons:
1. The developers were "lazy" or did not have time so they did not add one as it is not absolutely necessary.
2. The instance of QDir on which mkpath(path) is called, will be set to path as well, so it would be convenient for further usage - but I can not seem to find any hints that this is the actual behaviour within the docs.
I know I repeat myself, but again, I do not need help as of how to do it, but I am much interested as of why one has to do it that way.
Thanks for any reason I might have missed.
Let's have a look at the code of said method:
bool QDir::mkdir(const QString &dirName) const
{
const QDirPrivate* d = d_ptr.constData();
if (dirName.isEmpty()) {
qWarning("QDir::mkdir: Empty or null file name");
return false;
}
QString fn = filePath(dirName);
if (d->fileEngine.isNull())
return QFileSystemEngine::createDirectory(QFileSystemEntry(fn), false);
return d->fileEngine->mkdir(fn, false);
}
Source: http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/io/qdir.cpp#n1381
As we can see, a static version would be simple to implement:
bool QDir::mkdir(const QString &dirName) const
{
if (dirName.isEmpty()) {
qWarning("QDir::mkdir: Empty or null file name");
return false;
}
return QFileSystemEngine::createDirectory(QFileSystemEntry(dirName), false);
}
(see also http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/io/qdir.cpp#n681)
First, the non-static method comes with a few advantages. Obviously there is something to using the object's existing file engine. But also, I would imagine the use-case of creating several directories under a specific directory (that the QDir already points to).
So why not provide both?
Verdict (tl/dr): I think the reason is simple code hygiene. When you use the API, the difference between QDir::makepath(path); and QDir().makepath(path); is slim. The performance hit of creating the object is also negligible, as you would reuse the same object if you happen to perform the operation more often. But on the side of the code maintainers, it is arguably much more convenient (less work and less error prone) to not maintain two versions of the same method.
I'm looking to make an element flash on screen when the underlying collection is updated.
It seems to me that it would make to have an equivalent of Template.my_template.rendered = function(){} which is fired every time the template is updated.
Ideally, this function would not fire the first time the template is rendered.
This is seems like such an obvious use case, am I missing something?
For Meteor < 0.8
You should use Template.myTemplate.created to do something for the very first time. From the docs Template.myTemplate.rendered is fired everytime there is a change including the first time. If you want to limit it to only the second time and changes after that (I'm guessing this is what you want), you have to write some custom logic. Currently there is no Meteor api that supports that.
For Meteor-0.8
It seems like this API underwent some backwards incompatible changes in Meteor-0.8.
There is an elegant way to achieve this as described Adaptation to the new Meteor rendered callback here. Bascially you should modify whatever function you are attaching to the variables inside your Template's javascript by calling a helper function once. For example,
var renderCount = 1;
var myHelper = function () {
console.log("rendered #" + renderCount);
renderCount++;
};
Template.myTemplate.myVariable = function () {
myHelper();
return this.name;
};
Hope this helps. There is also another alternative approach in that same repo. You might like that one.
There is no simple solution; more discussion here: https://groups.google.com/forum/#!topic/meteor-talk/iQ37mTP3hLg
I am trying to duplicate a flex component at run time.
For example if i have this
mx:Button label="btn" id="btn" click="handleClick(event)"/>
i should be able to call a function called DuplicateComponent() and it should return me a UI component thts exactly same as above button including the event listeners with it.
Can some one help me please??
Thanks in advance
Do a Byte Array Copy. This code segment should do it for you:
// ActionScript file
import flash.utils.ByteArray;
private function clone(source:Object):*
{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
One note, I did not write this code myself, I'm pretty sure I got it from a post on the Flex Coder's list.
To solve that problem you should use actionscript and create the buttons dynamically.
Lets say you want the button(s) to go in a VBox called 'someVbox'
for (var i:uint = 0; i< 10; i++){
var but:Button = new Button();
but.label = 'some_id_'+i;
but.id = 'some_id_'+i;
but.addEventListener(MouseEvent.CLICK, 'handleClick');
someVbox.addChild(but);
}
I haven't tested it, but that should add 10 buttons to a vbox with a bit of luck.
You can't take a deep copy of UIComponents natively. You're best bet would be to create a new one and analyse the one you have to add a duplicate setup. To be honest this does sound like a bit of a code smell. I wonder if there may be a better solution to the problem with a bit of a rethink..
Same question as: http://www.flexforum.org/viewtopic.php?f=4&t=1421
Showing up in a google search for the same thing. So you've cut&pasted the same question a month later. No luck eh?
There is no easy way to do this that I know of. Many of a component's settings are dependent on the container/context/etc... and get instantiated during the creation process, so there's no reason to clone from that perspective.
You can clone key settings in actionscript and use those when creating new elements.
For instance, assuming you only care about properties, you might have an array ["styleName","width","height",...], and you can maybe use the array like this:
var newUI:UIComponent = new UIComponent();
for each(var s:String in propArray) {
newUI[s] = clonedUI[s];
}
If you want more bites on your question (rather than waiting a month), tell us what you are trying to achieve.
mx.utils.ObjectUtil often comes in handy, however for complex object types, it's typically good practice to implement an interface that requires a .clone() method, similar to how Events are cloned.
For example:
class MyClass implements ICanvasObject
{
...
public function clone():ICanvasObject
{
var obj:MyClass = new MyClass(parameters...);
return obj;
}
}
This gives your code more clarity and properly encapsulates concerns in the context of how the object is being used / cloned.
You are right but as per my understanding UI Components are not cloned by mx.utils.ObjectUtil.
from : http://livedocs.adobe.com/flex/201/langref/mx/utils/ObjectUtil.html#copy()
copy () method
public static function copy(value:Object):Object
Copies the specified Object and returns a reference to the copy. The copy is made using a native serialization technique. This means that custom serialization will be respected during the copy.
This method is designed for copying data objects, such as elements of a collection. It is not intended for copying a UIComponent object, such as a TextInput control. If you want to create copies of specific UIComponent objects, you can create a subclass of the component and implement a clone() method, or other method to perform the copy.
Parameters value:Object — Object that should be copied.
Returns Object — Copy of the specified Object
I can't get NVelocity to initialize. I'm not trying to do anything complicated, so it's just fine if it initializes at the defaults, but it won't even do that.
This:
VelocityEngine velocity = new VelocityEngine();
ExtendedProperties props = new ExtendedProperties();
velocity.Init(props);
Results in: "It appears that no class was specified as the ResourceManager..."
So does this:
VelocityEngine velocity = new VelocityEngine();
velocity.Init();
I can find precious little documentation on what the properties should be, nor how to get it to initialize with the simple defaults. Can anyone point to a resource?
A lot of pages point back to this page:
http://www.castleproject.org/others/nvelocity/usingit.html
But this page skips over the (seemingly) most important point -- how to set the properties and what to set them to.
I just want to load a simple template from a file.
Here's what I found out --
I was using the original NVelocity library, which hasn't had an update since 2003. I think it's a dead project.
I switched to the Castle Project version, and it's much easier -- in fact, it runs much like the examples on the page I linked to. It seems to set intelligent defaults for properties. I can initialize it without any properties set, but the template directory defaults to ".", so I generally set that one (do it before running "init").
To get the correct DLL, you need to download the latest NVelocity release (as of this writing it's 1.1).
Castle Project Download Page
You need to include the following files in your assembly, and make sure that their type is set to "Resource"
src\Runtime\Defaults\directive.properties
src\Runtime\Defaults\nvelocity.properties
These will then be found by ResourceLocator
src\Runtime\Resource\Loader\ResourceLocator.cs
If you get an exception on GetManifestResourceNames() as I did when trying to run Dvsl, then try modifying the ResourceLocator constructor to catch and ignore the error since the required files are in your local assembly (if you included them above) and the exception is only thrown by external assemblies (no idea why).
foreach(Assembly a in assemblies) {
String prefix = a.FullName.Substring(0,a.FullName.IndexOf(",")).ToLower();
try
{
String[] names = a.GetManifestResourceNames();
foreach (String s in names)
{
if (s.ToLower().Equals(fn) || s.ToLower().Equals(prefix + "." + fn))
{
this.filename = s;
assembly = a;
isResource = true;
}
}
} catch {
}
}