I've been working on a rubik's cube program, and the rendering works fine on my macbook pro/google chrome browser. For computers with slower GPUs, however, my cube breaks because the rendering is slower than the animations. I've tried looking up ways to optimize the rendering and haven't had any success yet.
Rubik's Cube live link
Github code repository
I'm using react and the componentDidMount function is line 1463 where the cube meshes get generated and the animate function is line 1539. Appreciate any help, thanks!
This all depends on how you're building your geometry. I ran a WebGL inspector on your live link, and I'm getting 182 drawcalls for a 3x3x3 cube. This is unnecessarily large, since a 3x3 should only have 54 faces. Each time you add a new Mesh to the scene, it creates a new drawcall on render, and having too many drawcalls is almost always the primary reason for slow performance in WebGL.
You should consider nesting each face into its corresponding "cubelet" (one black cube with 1, 2, or 3 colored faces), as done in this Roobik's demo. This way you'd only have to draw 27 cubes + 54 faces = 81 drawcalls! The pseudocode would go something like this:
for (let i = 0; i < 27; i++) {
let cubelet = new THREE.Mesh(geom, mat);
let face1 = new THREE.Mesh(geom, faceMat1);
let face2 = new THREE.Mesh(geom, faceMat2);
let face3 = new THREE.Mesh(geom, faceMat3);
cubelet.add(face1);
cubelet.add(face2);
cubelet.add(face3);
scene.add(cubelet);
}
Of course, you'd need to set the positions and rotations, to get a result like this:
Secondly, you're going to have to re-think the way your animate() function is set up. You're starting the next rotation after a set time has progressed, but instead you should start the next rotation only after the first one is complete. Have you considered using an animation library like GSAP?
Related
I've been trying to make a small horizontal scroll with react three fiber so I can later add some WebGL Distorsion on the elements and even though i succeeded in the most basic way, there are still some things that need improvement :
(here is the codesandbox corresponding :https://codesandbox.io/s/horizontal-scroll-with-react-three-fiber-c0okfu?file=/src/Scene.js)
first and foremost I want a smooth scroll and can't seem to be able to make it, I used the lerp function to make it but the result doesn't work very well :
let scroll = 0;
scroll = (scroll - scrollTargetMapped) * 0.03;
// any other frame, groupRef.current is undefined, don't really know why
// but because of it, i must put my logic inside an if loop
if (groupRef.current) {
groupRef.current.position.x = THREE.MathUtils.lerp(
scroll,
-scrollTarget,
0.01
);
}
secondly, the elements on my scene are placed kind of in a random way and the scene is not at all responsive. I would love to mimic the html logic and put my first element like 50px away from the left side of the screen but not sure if it's really possible with react threejs :)
If someone has any answer to one of those question, I take it 🙂
Thanks in advance !
For those interested, I managed to find a solution, using one of drei components : ScrollControl, it works perfectly !
https://codesandbox.io/s/horizontal-scroll-with-react-three-fiber-c0okfu?file=/src/Scene.js
For more info on the said component, check out the doc : https://docs.pmnd.rs/drei/controls/scroll-controls
My graph contains a high number of links among a high number of nodes (300 nodes).
Since I upgrade D3 from v3 to v4 and adjusted to the new API and concepts, the graph keeps going in dancing mode.
Here is a brief screencast that shows the effect:
https://www.youtube.com/watch?v=DCkBMzs1wWI
I have tried to alternatively remove the forces below:
collide force
center force
change force
.. butt hey don't seem to be the culprit of this issue. The dancing seems to relate to the link force only.
This is how my simulation is defined:
// force definitions
this.forceCharge = d3.forceManyBody();
this.forceCenter = d3.forceCenter(1800,1200);
this.forceCollide = d3.forceCollide().radius(10);
this.forceLink = d3.forceLink().id(function(d) {return d.id;});
// simulation definition
this.simulation = d3.forceSimulation()
.force("charge", this.forceCharge)
.force("link", this.forceLink)
.force("center", this.forceCenter)
.force("collide", this.forceCollide)
.nodes(this.nodes)
.nodes(this.links)
.on("tick", this.tick)
.on("end", this.end);
It is to be said that the link strength and distance properties were modified to various settings without any help. I have tried with strength from 0.1 to 1 but that did not help either.
I have also noticed:
the simulation
The end event does not get triggered, meaning that the simulation keeps
going on for a very long time
I could call the simulation.stop() event. It does stop the dancing but I have to restart it with data changes and the dancing would start again
UPDATE:
removing the collision detection feature seems to calm down the dancing, but the resulting graph shows that most nodes overlap due tot the fact they have similar connections, creating a geometry that forces them to the same place
I have trouble getting Map behave properly when calling ZoomToResolution and PanTo
I need to be able to Zoom into specific coordinate and center map.
The only way I got it working is by removing animations:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
Otherwise if I make call like this:
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
It does one or another (i.e. pan or zoom, but not both). If (after animation) I call this code second time (map already zoomed or panned to needed position/level) - it does second part.
Tried this:
control.MapControl.ZoomToResolution(ZoomLevel, MapPoint());
Same issue, internally it calls above commands
So, my only workaround right now is to set Zoom/Pan duration to 0. And it makes for bad UX when using mouse.
I also tried something like this:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
this.MapControl.ZoomDuration = new TimeSpan(750);
this.MapControl.PanDuration = new TimeSpan(750);
Which seems to be working, but then mouse interaction becomes "crazy". Mouse scroll will make map jump and zoom to random places.
Is there known solution?
The problem is the second operation replaces the previous one. You would have to wait for one to complete before starting the next one. But that probably doesn't give the effect you want.
Instead zoom to an extent, and you'll get the desired behavior. If you don't have the extent but only center and resolution, you can create one using the following:
var zoomToExtent = new Envelope(point.X - resolution * MapControl.ActualWidth/2, point.Y, point.X + resolution * MapControl.ActualWidth/2, point.Y);
Btw it's a little confusing in your code that you call your resolution "ZoomLevel". I assume this is a map resolution, and not a level number right? The esri map control doesn't deal with service-specific levels, but is agnostic to the data's levels and uses a more generic "units per pixels" resolution value.
Our app has a rotating map view which aligns with the compass heading. We counter-rotate the annotations so that their callouts remain horizontal for reading. This works fine on iOS5 devices but is broken on iOS6 (problem seen with same binary as used on iOS5 device and with binary built with iOS6 SDK). The annotations initially rotate to the correct horizontal position and then a short time later revert to the un-corrected rotation. We cannot see any events that are causing this. This is the code snippet we are using in - (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id )annotation
CATransform3D transformZ = CATransform3DIdentity;
transformZ = CATransform3DRotate(transformZ, _rotationZ, 0, 0, 1);
annotation.myView.layer.transform = transformZ;
Anyone else seen this and anyone got any suggestions on how to fix it on iOS6?
I had an identical problem so my workaround may work for you. I've also submitted a bug to Apple on it. For me, every time the map got panned by the user the Annotations would get "unrotated".
In my code I set the rotations using CGAffineTransformMakeRotation and I don't set it in viewForAnnotation but whenever the users location get's updated. So that is a bit different than you.
My workaround was to add an additional minor rotation at the bottom of my viewForAnnotation method.
if(is6orMore) {
[annView setTransform:CGAffineTransformMakeRotation(.001)]; //iOS6 BUG WORKAROUND !!!!!!!
}
So for you, I'm not sure if that works, since you are rotating differently and doing it in viewForAnnotation. But give it a try.
Took me forever to find and I just happened across this fix.
Is there some reason that identical math operations would take significantly longer in one Silverlight app than in another?
For example, I have some code that takes a list of points and transforms them (scales and translates them) and populates another list of points. It's important that I keep the original points intact, hence the second list.
Here's the relevant code (scale is a double and origin is a point):
public Point transformPoint(Point point) {
// scale, then translate the x
point.X = (point.X - origin.X) * scale;
// scale, then translate the y
point.Y = (point.Y - origin.Y) * scale;
// return the point
return point;
}
Here's how I'm doing the loop and timing, in case it's important:
DateTime startTime = DateTime.Now;
foreach (Point point in rawPoints) transformedPoints.Add(transformPoint(point));
Debug.Print("ASPX milliseconds: {0}", (DateTime.Now - startTime).Milliseconds);
On a run of 14356 points (don't ask, it's modeled off a real world number in the desktop app), the breakdown is as follows:
Silverlight app #1: 46 ms
Silverlight app #2: 859 ms
The first app is an otherwise empty app that is doing the loop in the MainPage constructor. The second is doing the loop in a method in another class, and the method is called during an event handler in the GUI thread, I think. But should any of that matter, considering that identical operations are happening within the loop itself?
There maybe something huge I'm missing in how threading works or something, but this discrepancy doesn't make sense to me at all.
In addition to the other comments and answers I'm going to read between the lines a little.
In the first app you have pretty much this code in isolation running in the MainPage constructor. IWO you've create a fresh Silverlight app and slapped this code in it and thats it.
In the second app you have more actual real world stuff. At the very least you have this code running as the result of a button click on a rudimentory UI. Therein lies the clue.
Take a blank app and drop a button on it. Run it and click the button, what does the button do? There are animations attached to visual states of the button. This animation (or other animations or loops) are likely running in parrallel with your code when you click the button. Timers (whether you do it properly with StopWatch or not) record elapsed time, not just the time your thread takes. Hence when other threads are doing other things (like animations) your timing will be off.
My first suspicion would be that Silverlight App #2 triggers a garbage collection. Scaling ~15,000 points should be taking a millisecond, not nearly a second.
Try to reduce memory allocations in your code. Can transformedPoints be an array, rather than a dynamically grown data structure?
You can also look at the GC performance counters, but simply reducing the memory allocation may turn out to be simpler.
Could it be possible your code is not being inlined in the CLR by the app that is running slower?
I'm not sure how the CLR in SL handles inlining, but here is a link to some of the prerequisites for inlining in 3.5 SP1.
http://udooz.net/blog/2009/04/clr-improvements-in-net-35-sp1/