Opacity change in rendering - three.js

In my code I have defined a circle:
var material1 = new THREE.LineBasicMaterial( { color: 0x000000, opacity: contentionopacity} );
scene.add(circle);
var resolution1 = 100;
var amplitude1 = 60;
var size1 = 360 / resolution1;
geometry1 = new THREE.Geometry();
for(var i = 0; i <= resolution1; i++) {
var segment1 = ( i * size1 ) * Math.PI / 180;
geometry1.vertices.push(new THREE.Vertex(
new THREE.Vector3(
(Math.cos(segment1) * amplitude1) ,
400,
(Math.sin(segment1) * amplitude1) -100 )
)
);
}
feetcircle = new THREE.Line( geometry1, material1 );
scene.add(feetcircle)
In there is the part where I defined the opacity with 'contentionopacity'.
The question is... Is it still possible that while it's being rendered, I can change it's opacity (and thus make it appear/dissapear with the 'push of a button'?:
'rendering part':
if (circleX1 == undefined) circleX1 = (neckcoords[0][1] -100 )//( ((neckcoords[0][1])-305) * Math.cos(rad) - (neckcoords[0][2] * Math.sin(rad)) ) +330 ;
if (circleY1 == undefined) circleY1 = (neckcoords[0][2] -530) //( neckcoords[0][2] * Math.cos(rad) + neckcoords[0][1] * Math.sin(rad) ) ;
// if (circleZ1 == undefined) circleZ1 = ( neckcoords[0][0] -90) ;
for(var i = 0; i <= 100; i++) {
feetcircle.geometry.vertices[i].x += (neckcoords[0][1] - circleX1 ) //(neckcoords[0][1] * Math.cos(rad) - neckcoords[0][2] * Math.sin(rad)) - circleX ;
feetcircle.geometry.vertices[i].z += ( neckcoords[0][2] - circleY1) //- ( neckcoords[0][2] * Math.cos(rad) + neckcoords[0][1] * Math.sin(rad) - circleY);
//feetcircle.geometry.vertices[i].y += ( neckcoords[0][0] - circleZ1);
}
circleX1 = neckcoords[0][1] //* Math.cos(rad) - neckcoords[0][2] * Math.sin(rad) ;
circleY1 = neckcoords[0][2] //* Math.cos(rad) + neckcoords[0][1] * Math.sin(rad);
//circleZ1 = neckcoords[0][0] //* Math.cos(rad) + neckcoords[0][0] * Math.sin(rad);
feetcircle.geometry.verticesNeedUpdate = true;
'push of the button':
document.addEventListener('keypress', function (e) {
if (e.keyCode == '17') { contentionopacity=contentionopacity+0.1}
console.log(contentionopacity)
},
false)
document.addEventListener('keypress', function (e) {
if (e.keyCode == '61') { contentionopacity=contentionopacity-0.1}
console.log(contentionopacity)
},
false)
At the moment, the console log shows that the 'contentionopacity' is getting higher, but it doesn't change the opacity of the circle at all.
Thanks in advance!

Related

handling objects offscreen for a view frustum

I have a view frustum that works great when looking at stuff from a distance. But for example, when i stand in the middle of a square, my current system struggles to place vertices which are behind me.
For example the image below demonstrates looking at a tile from a distance vs standing on top of it ...
My proccess for putting things onto screen can be described in 5 steps
cross product the co-ordinates of the object with my camera matrix
cross product the result of 1 with my projection_matrix
normalize the result of 2 by dividing through the 4th dimension of my co-ordinates (my w co-ordinate)
cull results of 3 (its not causing the problem, i tried without culling)
cross product 4 with to_screen_matrix
Basically the problem i have, is this procces is great at putting things on the screen but sometimes an object isnt on the screen... what co-ordinates should be used then?
Below is a drawing of what i think the problem is
below is my screen_projection function
return_screen_projection(dont_cull = false){
var position = cross_product(this.position , player.camera_matrix())
position = cross_product(position , projection.projection_matrix) // does this just convert the position to cameras reference frame.
for (let i = 0; i < position.length; i++) {
position[i] = position[i]/position[3]
}
if (dont_cull == false){
for (let i = 0; i < position.length; i++) {
if (i != 1){
if (this.is_this_object_behind_player()){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling1")}
if (position[i] > 2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling2")}
if (position[i] < -2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling3")}
}
} // also all examples say set position = 0 if culling
}
position = cross_product(position , projection.to_screen_matrix)
return [position[0],position[1]]
}
how could i better handle the position of a vertex offscreen, when some vertexes of the object im dealing with are on screen?
################## extra info below
below is a blob of 300 lines of code (sorry , i cant make it more minimal and reproducable with just a copy n paste)
running the below in a web browser will give you an example of the problem (w,a,s,d) to move (you start mired in the middle of an object, you may wish to step back to see better the first time)
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.3.3/math.js"></script>
<script async src="https://unpkg.com/es-module-shims#1.3.6/dist/es-module-shims.js"></script>
</head>
<body>
<div id="canvas div" style = "position: relative; left: 0px; float:left; top: 0px;" >
<h1> first person below </h1>
<canvas id="mi_canvas" width="300" height="300" style="border-style: solid;"></canvas> <br>
</div>
<script>
var floor_y_pos = 9
canvas = document.getElementById("mi_canvas");
ctx = canvas.getContext("2d");
render_distance = 1000;
fov = math.pi / 2
class Projection{
constructor(){
var NEAR = player.near_plane
var FAR = player.far_plane
var RIGHT = Math.tan(player.h_fov/2)
var LEFT = - RIGHT
var TOP = Math.tan(player.v_fov /2)
var BOTTOM = -TOP
var m00 = 2*NEAR / (RIGHT - LEFT)
var m02 = (RIGHT + LEFT)/(RIGHT - LEFT)
var m11 = 2*NEAR / (TOP - BOTTOM)
var m12 = (TOP + BOTTOM) /(TOP - BOTTOM)
var m22 = (FAR * NEAR) / (FAR - NEAR)
var m23 = -2 * NEAR * FAR / (FAR-NEAR)
this.projection_matrix = [
[-m00,0,m02,0],
[0,m11,0,0],
[m02,m12,-m22,-1],
[0,0,m23,0]
]
var HW=player.H_WIDTH
var HH = player.H_HEIGHT
this.to_screen_matrix = [
[HW,0,0,0],
[0,HH,0,0],
[0,0,1,0],
[HW,HH,0,1]
]
}
}
function multiply(a, b) {
var aNumRows = a.length, aNumCols = a[0].length,
bNumRows = b.length, bNumCols = b[0].length,
m = new Array(aNumRows); // initialize array of rows
for (var r = 0; r < aNumRows; ++r) {
m[r] = new Array(bNumCols); // initialize the current row
for (var c = 0; c < bNumCols; ++c) {
m[r][c] = 0; // initialize the current cell
for (var i = 0; i < aNumCols; ++i) {
m[r][c] += a[r][i] * b[i][c];
}
}
}
return m;
}
function mi_position_matrix_multiplier(A, B)
{
var new_matrix = []
for (var new_num_ind = 0; new_num_ind < A.length; ++new_num_ind)
{
this_num = 0;
for (var a_ind = 0; a_ind < A.length; ++a_ind)
{
this_num += (A[a_ind] * B[a_ind][new_num_ind])
}
new_matrix.push(this_num)
}
return new_matrix;
}
function pythagoras(thing1, thing2)
{
dist = (((thing1[0]-thing2[0])**2)+((thing1[1]-thing2[1])**2))**0.5
return dist
}
class vertex{
constructor(x, y,z , id){
this.id = id
this.position = [x,y,z,1]
this.min_dist = 1.5 // minimum possible distance between player and object
}
is_this_object_behind_player(){
var arrow_length = 0.0001;
var pointing_position = [player.position[0]+(player.forward[0]*arrow_length) , player.position[2]-(player.forward[2]*arrow_length)]
var dist1 = pythagoras([this.position[0],this.position[2]], pointing_position)
var dist2 = pythagoras([this.position[0],this.position[2]], [player.position[0],player.position[2]])
if (dist1 < dist2){
return true;}
else if (dist1 > dist2){
return false;}
else{}
}
return_screen_projection(dont_cull = false){
var position = mi_position_matrix_multiplier(this.position , player.camera_matrix())
position = mi_position_matrix_multiplier(position , projection.projection_matrix) // does this just convert the position to cameras reference frame.
for (let i = 0; i < position.length; i++) {
position[i] = position[i]/position[3]
}
if (dont_cull == false){
for (let i = 0; i < position.length; i++) {
if (i != 1){
if (this.is_this_object_behind_player()){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling1")}
if (position[i] > 2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling2")}
if (position[i] < -2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling3")}
}
} // also all examples say set position = 0 if culling
}
position = mi_position_matrix_multiplier(position , projection.to_screen_matrix)
return [position[0],position[1]]
}
}
class player{
constructor(){
this.position =[0,0,0,1.0]
this.forward = [0,0,1,1]
this.up = [0,1,0,1]
this.right =[1,0,0,1]
this.h_fov = 3.1415926535/3
this.v_fov = this.h_fov * (canvas.height / canvas.width)
this.near_plane = 1
this.far_plane = 100
this.moving_speed = 0.2
this.rotation_speed = 0.1
this.H_WIDTH = canvas.width/2
this.H_HEIGHT = canvas.height/2
this.anglePitch = 0
this.angleYaw = 0
}
set_camera_angle(){
var rotate = multiply(rotate_x(this.anglePitch) , rotate_y(this.angleYaw))
this.forward = [0, 0, 1, 1]
this.up = [0, 1, 0, 1]
this.right = [1, 0, 0, 1]
this.forward = mi_position_matrix_multiplier(this.forward , rotate)
this.right = mi_position_matrix_multiplier(this.right , rotate)
this.up = mi_position_matrix_multiplier(this.up , rotate)
}
camera_yaw(angle){
this.angleYaw += angle}
translate_matrix(self){
var x = this.position[0];
var y = this.position[1];
var z = this.position[2];
var w = this.position[3];
return [
[1,0,0,0],
[0,1,0,1],
[0,0,1,0],
[-x,-y,z, 1]
]}
rotate_matrix(){
var rx = this.right[0]
var ry = this.right[1]
var rz = this.right[2]
var w = this.right[3]
var fx = this.forward[0]
var fy = this.forward[1]
var fz = this.forward[2]
var w = this.forward[3]
var ux = this.up[0]
var uy = this.up[1]
var uz = this.up[2]
var w = this.up[3]
return [
[rx,ux,fx,0],
[ry,uy,fy,0],
[rz,uz,fz,0],
[0,0,0,1]
]
}
camera_matrix(){
return multiply(this.translate_matrix(), this.rotate_matrix());
}
move(event)
{
var key_code = parseInt(event.keyCode)
if (key_code == 37 || key_code == 39 || key_code == 83 || key_code == 87 || key_code == 119|| key_code == 115)
{
var dx = Math.cos(this.angleYaw)*this.moving_speed
var dy = Math.sin(this.angleYaw)*this.moving_speed
// console.log("that were moving = dx , dy = "+dx.toString()+" , "+dy.toString())
if ( key_code == 37 || key_code == 87 || key_code == 119) {
this.position[0] += -dy
this.position[2] += dx
}
if (key_code == 39 || key_code == 83 || key_code == 115) {
for (let i = 0; i < this.position.length; i++) {
this.position[0] += dy
this.position[2] += -dx
}
}
}
else {
if ( key_code == 38 || key_code == 65 || key_code == 97) {
this.camera_yaw(-this.rotation_speed)
}
if (key_code == 40 || key_code == 68 || key_code == 100) {
this.camera_yaw(this.rotation_speed)
}
this.set_camera_angle()
}
}
}
function translate(pos){
tx,ty,tz=pos
return np.array([
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[tx,ty,tz,1]
])}
function rotate_x(angle){
return [
[1,0,0,0],
[0,Math.cos(angle),Math.sin(angle),0],
[0,-Math.sin(angle),Math.cos(angle),0],
[0,0,0,1]
]
}
function rotate_y(a){
return [
[math.cos(a),0, -math.sin(a),0],
[0,1,0,0],
[math.sin(a), 0 , math.cos(a),0],
[0,0,0,1]
]
}
function update_matrix_info_debug(matrix_name, matrix){
if (matrix[0].length > 1)
{
for (let x = 1; x < matrix.length+1; x++) {
for (let y = 1; y < matrix.length+1; y++) {
document.getElementById(matrix_name.toString()+"_"+x.toString()+y.toString()).innerHTML = matrix[x-1][y-1]
}
}
}
else {
for (let x = 1; x < matrix.length+1; x++) {document.getElementById(matrix_name.toString()+"_"+"1"+x.toString()).innerHTML = matrix[x-1]}
}
}
class two_d_surdace {
constructor(verex1,verex2,verex3,verex4 , colour){
this.vertices = [verex1,verex2,verex3,verex4]
this.colour = colour
}
draw_all_faces(){
var each_point = []
for (let i = 0; i < this.vertices.length; i++) {
each_point.push(this.vertices[i].return_screen_projection(true))
}
ctx.fillStyle = this.colour;
var moved_to_first_yet = false
for (let vertex = 0; vertex < this.vertices.length; vertex++)
{
if (moved_to_first_yet == false)
{
moved_to_first_yet = true
ctx.moveTo( each_point[vertex][0],each_point[vertex][1]);
}
else{ctx.lineTo( each_point[vertex][0],each_point[vertex][1]);}
}
ctx.closePath();
ctx.fill();
}
}
function if_off_screen(x, y)
{
if (x> canvas.width || x < 0){ return true;}
if (y > canvas.height || y < 0){ return true;}
return false;
}
function if_most_of_these_numbers_are_off_screen(numbers){
var threshold = 1; //Math.floor(numbers.length*0.49)
var counter = 0
for (let i = 0; i < numbers.length; i++) { if (if_off_screen(numbers[i][0], numbers[i][1])){ counter +=1} else{} }
if (counter >= threshold){return true}
return false;
}
player = new player();
projection = new Projection()
floor = new two_d_surdace(new vertex(50,floor_y_pos,50) , new vertex(-50,floor_y_pos,50) , new vertex(-50,floor_y_pos,-50) , new vertex(50,floor_y_pos,-50) , '#F90' )
$(document).on("keypress", function (event) {
player.move(event)
ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
floor.draw_all_faces()
});
</script>
</body>

Three.JS - Object rotation

we have following function to create object wall:
function placeWorkareaWithSlope(points, waDetail, drawLine = true) {
const workareaHeight = Number(waDetail.WorkareaHeight) || 10;
const workareaParapet = Number(waDetail.WorkareaParapet) || 3;
// Different color wall for workarea
drawWallForWorkareaWithSlope(points, workareaHeight, waDetail);
drawWallBoxForWorkareaWithSlope(points, workareaHeight, waDetail);
// Convert points array to accrding to 3D space Quadrant
const convertedPoints = convertTo3dQuadrant(points);
const shape = [];
for (let i = 0; i < convertedPoints.length; i++) {
const e = convertedPoints[i];
shape.push(new THREE.Vector2(e.x, e.y));
}
const workareaShape = new THREE.Shape(shape);
const extrusionSettings = {
steps: 1,
depth: workareaHeight,
bevelEnabled: false,
};
const workareaGeometry = new THREE.ExtrudeGeometry(workareaShape, extrusionSettings);
workareaGeometry.computeVertexNormals();
workareaGeometry.computeFaceNormals();
const workarea = new THREE.Mesh(workareaGeometry, workareaMaterial);
workarea.rotation.x = -THREE.Math.degToRad(90);
//added by kd to define slope angle
objRotation(points, waDetail, workarea);
//end kd
workarea.receiveShadow = isRenderShaodw;
workarea.castShadow = isRenderWorkareaShadow;
scene.add(workarea);
// DrawLines
if (drawLine && workarea && workarea.geometry) {
const lineGeo = new THREE.EdgesGeometry(workarea.geometry);
const wireframe = new THREE.LineSegments(lineGeo, workareaLineMaterial);
wireframe.renderOrder = 1;
workarea.add(wireframe);
}
}
once we create wall over scene after that we rotate it base on slope provided with objRotation function with obj.rotation.y but when we rotate it at that movement object change its place, basically we require object should be there with world axis
following is object rotation function :
function objRotation(points, waDetail, obj) {
const Point1 = points[0];
const Point2 = points[1];
const Point3 = points[2];
const Point4 = points[3];
const slopeAngle = Number(waDetail.Angle) || 0;
if (waDetail.SlopeDirection == "P1 to P2") {
xDiff = Point1.x - Point2.x;
yDiff = Point1.y - Point2.y;
}
if (waDetail.SlopeDirection == "P2 to P3") {
xDiff = Point2.x - Point3.x;
yDiff = Point2.y - Point3.y;
}
if (waDetail.SlopeDirection == "P3 to P4") {
xDiff = Point3.x - Point4.x;
yDiff = Point3.y - Point4.y;
}
if (waDetail.SlopeDirection == "P4 to P1") {
xDiff = Point4.x - Point1.x;
yDiff = Point4.y - Point1.y;
}
if (Math.abs(xDiff) > Math.abs(yDiff)) {
if (obj.rotation.x > 0) {
if (xDiff > 0)
obj.rotation.y += THREE.Math.degToRad(slopeAngle);
else
obj.rotation.y -= THREE.Math.degToRad(slopeAngle);
}
else {
if (xDiff > 0)
obj.rotation.y -= THREE.Math.degToRad(slopeAngle);
else
obj.rotation.y += THREE.Math.degToRad(slopeAngle);
}
} else {
if (yDiff<0)
obj.rotation.x += THREE.Math.degToRad(slopeAngle);
else
obj.rotation.x -= THREE.Math.degToRad(slopeAngle);
}
}
So for the above issue, can someone guide me?
The wall created by the code with height and width provided by system and it rotates 90 degrees to setup over map surface but after that we have a requirement to slope its base on an angle so we added one more rotation there with provided slope angle by user. But when this slope angle rotation applied to object the object change its height base on camera left or right distance. We require the object should be same place there as before after rotation.

AmChart- After updating fieldMappings and dataSet,Graph is not plotting

Problem Statement:
after updating fieldMappings and dataSet during runtime(After clicking on a button) for a stockgraph, validateNow() / validteData() is not plotting the graph.
Note: MACD0 is added from 25th element onward and expoSignalLine0 is added from 33rd element onward in the dataprovider and fieldMapping is also getting updated and can be verified same in console.enter code here
Following is the Code snippet:
(addMACD function is called on click of a button)
function addMACD() {
var chart = AmCharts.charts[ 0 ];
AmCharts.MACDGraphs = 0;
AmCharts.expoSignalLineGraphs = 0;
var MACDField = "MACD"+ AmCharts.MACDGraphs;
var expoSignalLineField = "expoSignalLine"+ AmCharts.expoSignalLineGraphs;
chart.dataSets[0].fieldMappings.push( {
fromField : MACDField,
toField : MACDField
},
{
fromField : expoSignalLineField,
toField : expoSignalLineField
});
var currClose;
var prevClose;
var twelveDayEMA =[];
var twentySixDayEMA =[];
var MACDarray = [];
var signalLineArray = [];
var MACDperiod = 9 ;// 9 day exponential average
for ( var i = 1; i < (chart.dataSets[0].dataProvider.length); i++) {
var dp = chart.dataSets[0].dataProvider[i - 1];
prevClose = parseFloat(dp["close"]);
var dp = chart.dataSets[0].dataProvider[i];
currClose = parseFloat(dp["close"]);
if( i==1){
twelveDayEMA[i] = (0.15*currClose) + (0.85*prevClose);
twentySixDayEMA[i] = (0.075*currClose) + (0.925*prevClose);
}
else{
twelveDayEMA[i] = (0.15*currClose) + (0.85*twelveDayEMA[i - 1]);
twentySixDayEMA[i] = (0.075*currClose) + (0.925*twentySixDayEMA[i - 1]);
}
if(i >= 25){
MACDarray[i] = twelveDayEMA[i] - twentySixDayEMA[i] ;
dp[MACDField] = MACDarray[i];
if(i == 25){
signalLineArray[i] = MACDarray[i];
}
else{
signalLineArray[i] = ( MACDarray[i]*(2/( MACDperiod + 1)) ) + ( signalLineArray[i - 1]*(1-(2/( MACDperiod + 1))) )
}
}
if(i >=33){
dp[expoSignalLineField] = signalLineArray[i];
}
}
console.log(chart);
if ( chart.panels.length == 1 || chart.panels.length == 2 || chart.panels.length == 3 || chart.panels.length == 4 || chart.panels.length == 5) {
var newPanel = new AmCharts.StockPanel();
newPanel.allowTurningOff = true;
newPanel.title = "MACD";
newPanel.showCategoryAxis = false;
graph1 = new AmCharts.StockGraph();
graph1.valueField = MACDField;
graph1.useDataSetColors = false;
graph1.lineColor="#6699FF";
graph1.title = "MACD";
newPanel.stockGraphs.push( graph1 );
graph2 = new AmCharts.StockGraph();
graph2.valueField =expoSignalLineField;
graph2.useDataSetColors = false;
graph2.lineColor = "#990000";
graph2.title = "MACD2";
newPanel.stockGraphs.push( graph2 );
var legend = new AmCharts.StockLegend();
legend.markerType = "none";
legend.markerSize = 0;
newPanel.stockLegend = legend;
chart.addPanelAt( newPanel, 1 );
chart.validateData();
chart.validateNow();
//chart.write("chartdiv");
}
}
You have to call validateNow first, then call validateData.
Alternatively, you can call validateNow(true, false) which has the same effect as calling the two functions separately.
Updated fiddle

Geometry intersection converting from direct geometry to buffergeometry

I am using Three.js. Found a really good Decal library written by Benpurdy. It's very easily modifiable and also used the techniques described here
However, the technique uses Geometry. The project I am on, uses BufferGeometry. I traced the code which does the geometry intersects and can't figure out the conversion from faces and vertices to attributes.
this.createGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.faces.length; i++){
var verts = [geom.faces[i].a, geom.faces[i].b, geom.faces[i].c];
var pts = [];
var valid = false;
for(var v = 0; v < 3; v++) {
var vec = geom.vertices[verts[v]].clone();
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
// update UV's
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = geom.faces[i].clone();
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
Appreciate if anyone could shed some light on how to go about pursuing this? Thanks.
I ended up solving the problem by writing another function to compute intersections with buffergeometry. Took me a while trying to understand the original buffer geometry code.
this.createGeometryFromBufferGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.attributes.position.array.length; i+=9){
var pts = [];
var valid = false;
for(var v = 0; v < 9; v+=3) {
var vec = new THREE.Vector3(geom.attributes.position.array[i+v],geom.attributes.position.array[i+v+1],geom.attributes.position.array[i+v+2]);
console.log((i+v) + " " + (i+v+1) + " " + (i+v+2) );
console.log(vec);
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = new THREE.Face3()
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
BufferGeometry() has a method .fromGeometry(). Populates this BufferGeometry with data from a Geometry object.
var geom = new THREE.BoxGeometry(1,1,1);
var bufGeom = new THREE.BufferGeometry().fromGeometry(geom);
UPD. You can use the other way round.
var bufGeom = new THREE.BoxBufferGeometry(1,1,1);
var geom = new THREE.Geometry().fromBufferGeometry(bufGeom);
Quick and dirty solution is to create geometry from bufferGeometry and after calculating dispose created geometry
this.compute = function()
{
this.geometry = mesh.geometry
if(this.geometry.attributes)
{
this.geometry = new THREE.Geometry().fromBufferGeometry(this.geometry);
this.computeDecal();
this.geometry.dispose();
}
else
{
this.computeDecal();
}
}

Is there any solution to Firefox 37's poor Canvas rendering performance

I've deliberately provided the version number in the question as this is the kind of question that will go out of date at some point.
Here is a simple animation that will run perfectly smoothly in Chrome and Safari, but will be very jerky in Firefox:
function lerp(a,b,λ) {
return a + λ*(b-a);
}
function random(a,b) {
return lerp( a, b, Math.random() );
}
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
$(document).ready( function()
{
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var balls = new Array(12);
for( var i=0; i<balls.length; i++ )
{
while(true)
{
var density = Math.sqrt( random(1,100) );
var r = random(5, 30);
var x = random( r+1, canvas.width-1 -(r+1) ),
y = random( r+1, canvas.height-1 -(r+1) );
var overlap = false;
for( var j=0; j<i; j++ )
{
var _x = balls[j].x - x,
_y = balls[j].y - y,
d2 = _x*_x + _y*_y,
_r = balls[j].r + r;
if( d2 < _r*_r )
overlap = true;
}
if( overlap )
continue;
balls[i] = {
color : d3.hsl( lerp(0,240,density/10), random(.3,.7), random(.3,.7) ).toString(),
x : x,
y : y,
vx : random(0, 0.2),
vy : random(0, 0.2),
r : r,
advance: function(t) {
this.x += t * this.vx;
this.y += t * this.vy;
}
};
break;
}
};
window.requestAnimationFrame(vsync);
var t_last;
function vsync(t)
{
if( t_last )
render( t - t_last );
t_last = t;
window.requestAnimationFrame(vsync);
}
function render(t_frame)
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.fillStyle="gray";
ctx.fillRect(0,0, canvas.width, canvas.height);
function advance_all(t) {
balls.forEach( function(b) {
b.advance(t);
});
}
var t_remaining = t_frame;
while(true) {
var hit = get_next_collision( balls, canvas.width, canvas.height );
if( t_remaining < hit.t )
break;
advance_all( hit.t );
t_remaining -= hit.t;
collide_wall( balls[hit.i], hit.wall );
balls[hit.i].advance(.001);
};
advance_all( t_remaining );
// draw balls
balls.forEach( function(ball) {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI*2, true);
ctx.closePath();
ctx.fillStyle = ball.color;
ctx.fill();
});
}
});
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function get_next_collision(balls,W,H)
{
var winner;
// ball-wall
balls.forEach( function(ball,i)
{
var t = [];
t['L'] = (ball.r - ball.x) / ball.vx; // s.x + t v.x = r
t['T'] = (ball.r - ball.y) / ball.vy;
t['R'] = (W-1-ball.r - ball.x) / ball.vx; // s.x + t v.x = (W-1)-r
t['B'] = (H-1-ball.r - ball.y) / ball.vy;
// get index of smallest positive t
var LR = t['L'] >= 0 ? 'L' : 'R',
TB = t['T'] >= 0 ? 'T' : 'B',
wall = t[LR] < t[TB] ? LR : TB;
if( ! winner || ( t[wall] <= winner.t ) )
winner = {
t : t[wall],
i : i,
wall: wall
};
});
return winner;
}
function collide_wall( A, wall )
{
if( wall == 'L' || wall == 'R' )
A.vx *= -1;
else
A.vy *= -1;
}
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/1.5.1/math.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<canvas id="myCanvas">
<!-- Insert fallback content here -->
</canvas>
Why is Firefox performing significantly worse than its competitors?
If I take the number of balls to 500 Chrome is still smooth, Firefox is seriously choppy.
If I take the number of balls down to 1 Firefox is still burring it.
Another experiment I did was applying a fixed velocity increment each frame, so that the smoothness of the animation accurately reflects the evenness of the render callback. This showed Firefox to be all over the place. Chrome on the other hand was smooth.
If there is sufficient interest, I can provide a snippet for that also and maybe tidy up the first one so that it offers a slider to modify the ball count.
As far as I can see, (1) Firefox definitely isn't giving us a genuine VSYNC callback, I suspect it is just using a timer, (2) even correcting for that by manually calculating elapsed time for each frame, it burrs the animation, maybe suggesting that it sometimes the callback fires in time to catch the VSYNC and sometimes it misses the boat, (3) there is an additional compositing hit that is disproportionate compared with Chrome.
Is there anything to be done about this?
EDIT: I found this question from four years ago! Poor performance of html5 canvas under firefox -- please don't mark this question as the duplicate unless it is certain that the answer to that question is still the only relevant answer four years later. I've deliberately included the version number in the question, as the question pertains specifically to the current version.

Resources