mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 12:43:54 +02:00
Merge pull request #1620 from ZappoMan/new_particle_scripting_interface
New particle scripting interface
This commit is contained in:
commit
135f96a18c
46 changed files with 2062 additions and 889 deletions
|
@ -18,14 +18,14 @@ var numberParticlesAdded = 0;
|
|||
var MAX_PARTICLES = 1;
|
||||
|
||||
var velocity = {
|
||||
x: 1/TREE_SCALE,
|
||||
y: 0/TREE_SCALE,
|
||||
z: 1/TREE_SCALE };
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 1 };
|
||||
|
||||
var gravity = {
|
||||
x: 0/TREE_SCALE,
|
||||
y: 0/TREE_SCALE,
|
||||
z: 0/TREE_SCALE };
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0 };
|
||||
|
||||
var damping = 0.1;
|
||||
|
||||
|
@ -65,16 +65,27 @@ function draw() {
|
|||
if (currentIteration == 0) {
|
||||
var colorGreen = { red: 0, green: 255, blue: 0 };
|
||||
var startPosition = {
|
||||
x: 2/TREE_SCALE,
|
||||
y: 0/TREE_SCALE,
|
||||
z: 2/TREE_SCALE };
|
||||
var largeRadius = 0.5/TREE_SCALE;
|
||||
x: 2,
|
||||
y: 0,
|
||||
z: 2 };
|
||||
var largeRadius = 0.5;
|
||||
var verySlow = {
|
||||
x: 0.01/TREE_SCALE,
|
||||
y: 0/TREE_SCALE,
|
||||
z: 0.01/TREE_SCALE };
|
||||
x: 0.01,
|
||||
y: 0,
|
||||
z: 0.01 };
|
||||
|
||||
var properties = {
|
||||
position: startPosition,
|
||||
radius: largeRadius,
|
||||
color: colorGreen,
|
||||
velocity: verySlow,
|
||||
gravity: gravity,
|
||||
damping: damping,
|
||||
inHand: false,
|
||||
script: scriptA
|
||||
};
|
||||
|
||||
Particles.queueParticleAdd(startPosition, largeRadius, colorGreen, verySlow, gravity, damping, false, scriptA);
|
||||
Particles.addParticle(properties);
|
||||
print("hello... added particle... script=\n");
|
||||
print(scriptA);
|
||||
numberParticlesAdded++;
|
||||
|
@ -84,15 +95,15 @@ function draw() {
|
|||
print("draw()... sending another... currentIteration=" +currentIteration + "\n");
|
||||
|
||||
var center = {
|
||||
x: 0/TREE_SCALE,
|
||||
y: 0/TREE_SCALE,
|
||||
z: 0/TREE_SCALE };
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0 };
|
||||
|
||||
var particleSize = 0.1 / TREE_SCALE;
|
||||
var particleSize = 0.1;
|
||||
|
||||
print("number of particles=" + numberParticlesAdded +"\n");
|
||||
|
||||
var velocityStep = 0.1/TREE_SCALE;
|
||||
var velocityStep = 0.1;
|
||||
if (velocity.x > 0) {
|
||||
velocity.x -= velocityStep;
|
||||
velocity.z += velocityStep;
|
||||
|
@ -106,7 +117,17 @@ function draw() {
|
|||
}
|
||||
|
||||
if (numberParticlesAdded <= MAX_PARTICLES) {
|
||||
Particles.queueParticleAdd(center, particleSize, color, velocity, gravity, damping, false, scriptB);
|
||||
var properties = {
|
||||
position: center,
|
||||
radius: particleSize,
|
||||
color: color,
|
||||
velocity: velocity,
|
||||
gravity: gravity,
|
||||
damping: damping,
|
||||
inHand: false,
|
||||
script: scriptB
|
||||
};
|
||||
Particles.addParticle(properties);
|
||||
print("hello... added particle... script=\n");
|
||||
print(scriptB);
|
||||
numberParticlesAdded++;
|
||||
|
|
95
examples/editParticleExample.js
Normal file
95
examples/editParticleExample.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// editParticleExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/31/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates creating and editing a particle
|
||||
//
|
||||
|
||||
var count = 0;
|
||||
|
||||
var originalProperties = {
|
||||
position: { x: 10,
|
||||
y: 0,
|
||||
z: 0 },
|
||||
|
||||
velocity: { x: 0,
|
||||
y: 0,
|
||||
z: 0 },
|
||||
|
||||
gravity: { x: 0,
|
||||
y: 0,
|
||||
z: 0 },
|
||||
|
||||
|
||||
radius : 0.1,
|
||||
|
||||
color: { red: 0,
|
||||
green: 255,
|
||||
blue: 0 }
|
||||
|
||||
};
|
||||
|
||||
var positionDelta = { x: 0.05, y: 0, z: 0 };
|
||||
|
||||
|
||||
var particleID = Particles.addParticle(originalProperties);
|
||||
|
||||
function moveParticle() {
|
||||
if (count >= 100) {
|
||||
//Agent.stop();
|
||||
|
||||
// delete it...
|
||||
if (count == 100) {
|
||||
print("calling Particles.deleteParticle()");
|
||||
Particles.deleteParticle(particleID);
|
||||
}
|
||||
|
||||
// stop it...
|
||||
if (count >= 200) {
|
||||
print("calling Agent.stop()");
|
||||
Agent.stop();
|
||||
}
|
||||
|
||||
count++;
|
||||
return; // break early
|
||||
}
|
||||
|
||||
print("count =" + count);
|
||||
count++;
|
||||
|
||||
print("particleID.creatorTokenID = " + particleID.creatorTokenID);
|
||||
|
||||
var newProperties = {
|
||||
position: {
|
||||
x: originalProperties.position.x + (count * positionDelta.x),
|
||||
y: originalProperties.position.y + (count * positionDelta.y),
|
||||
z: originalProperties.position.z + (count * positionDelta.z)
|
||||
},
|
||||
radius : 0.25,
|
||||
|
||||
};
|
||||
|
||||
|
||||
//print("particleID = " + particleID);
|
||||
print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z);
|
||||
|
||||
Particles.editParticle(particleID, newProperties);
|
||||
|
||||
// also check to see if we can "find" particles...
|
||||
var searchAt = { x: 9, y: 0, z: 0};
|
||||
var searchRadius = 2;
|
||||
var foundParticle = Particles.findClosestParticle(searchAt, searchRadius);
|
||||
if (foundParticle.isKnownID) {
|
||||
print("found particle:" + foundParticle.id);
|
||||
} else {
|
||||
print("could not find particle in or around x=9 to x=11:");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(moveParticle);
|
||||
|
|
@ -37,25 +37,30 @@ function vInterpolate(a, b, fraction) {
|
|||
return rval;
|
||||
}
|
||||
|
||||
var position = { x: 5.0 / TREE_SCALE, y: 5.0 / TREE_SCALE, z: 5.0 / TREE_SCALE };
|
||||
Voxels.queueDestructiveVoxelAdd(position.x, position.y - (1.0 / TREE_SCALE), position.z, 0.5 / TREE_SCALE, 255, 255, 1);
|
||||
var position = { x: 5.0, y: 0.6, z: 5.0 };
|
||||
Voxels.setVoxel(position.x, 0, position.z, 0.5, 0, 0, 255);
|
||||
|
||||
var totalParticles = 0;
|
||||
function makeFountain() {
|
||||
if (Math.random() < 0.06) {
|
||||
//print("Made particle!\n");
|
||||
var properties = {
|
||||
position: position,
|
||||
radius: (0.02 + (Math.random() * 0.05)),
|
||||
color: { red: 0, green: 0, blue: 128 },
|
||||
velocity: { x: (Math.random() * 1.0 - 0.5),
|
||||
y: (1.0 + (Math.random() * 2.0)),
|
||||
z: (Math.random() * 1.0 - 0.5) },
|
||||
gravity: { x: 0, y: -0.5, z: 0 },
|
||||
damping: 0.25,
|
||||
lifetime: 2
|
||||
}
|
||||
|
||||
var size = (0.02 + (Math.random() * 0.05)) / TREE_SCALE;
|
||||
var velocity = { x: (Math.random() * 1.0 - 0.5) / TREE_SCALE,
|
||||
y: (1.0 + (Math.random() * 2.0)) / TREE_SCALE,
|
||||
z: (Math.random() * 1.0 - 0.5) / TREE_SCALE };
|
||||
|
||||
var gravity = { x: 0, y: -0.5 / TREE_SCALE, z: 0 }; // gravity has no effect on these bullets
|
||||
var color = { red: 0, green: 0, blue: 128 };
|
||||
var damping = 0.25; // no damping
|
||||
var inHand = false;
|
||||
var script = "";
|
||||
|
||||
Particles.queueParticleAdd(position, size, color, velocity, gravity, damping, inHand, script);
|
||||
Particles.addParticle(properties);
|
||||
totalParticles++;
|
||||
}
|
||||
if (totalParticles > 100) {
|
||||
Agent.stop();
|
||||
}
|
||||
}
|
||||
// register the call back so it fires before each data send
|
||||
|
|
|
@ -24,7 +24,7 @@ function checkController() {
|
|||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
|
||||
|
||||
// this is expected for hydras
|
||||
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
|
||||
for (var t = 0; t < numberOfTriggers; t++) {
|
||||
|
@ -43,7 +43,7 @@ function checkController() {
|
|||
shootABullet = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (shootABullet) {
|
||||
var palmController = t * controllersPerTrigger;
|
||||
var palmPosition = Controller.getSpatialControlPosition(palmController);
|
||||
|
@ -51,39 +51,22 @@ function checkController() {
|
|||
var fingerTipController = palmController + 1;
|
||||
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
|
||||
|
||||
var bulletSize = 0.05/TREE_SCALE;
|
||||
|
||||
var palmInParticleSpace =
|
||||
{ x: palmPosition.x/TREE_SCALE,
|
||||
y: palmPosition.y/TREE_SCALE,
|
||||
z: palmPosition.z/TREE_SCALE };
|
||||
|
||||
var tipInParticleSpace =
|
||||
{ x: fingerTipPosition.x/TREE_SCALE,
|
||||
y: fingerTipPosition.y/TREE_SCALE,
|
||||
z: fingerTipPosition.z/TREE_SCALE };
|
||||
|
||||
var palmToFingerTipVector =
|
||||
{ x: (tipInParticleSpace.x - palmInParticleSpace.x),
|
||||
y: (tipInParticleSpace.y - palmInParticleSpace.y),
|
||||
z: (tipInParticleSpace.z - palmInParticleSpace.z) };
|
||||
{ x: (fingerTipPosition.x - palmPosition.x),
|
||||
y: (fingerTipPosition.y - palmPosition.y),
|
||||
z: (fingerTipPosition.z - palmPosition.z) };
|
||||
|
||||
// just off the front of the finger tip
|
||||
var position = { x: tipInParticleSpace.x + palmToFingerTipVector.x/2,
|
||||
y: tipInParticleSpace.y + palmToFingerTipVector.y/2,
|
||||
z: tipInParticleSpace.z + palmToFingerTipVector.z/2};
|
||||
var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2,
|
||||
y: fingerTipPosition.y + palmToFingerTipVector.y/2,
|
||||
z: fingerTipPosition.z + palmToFingerTipVector.z/2};
|
||||
|
||||
var linearVelocity = 5;
|
||||
var linearVelocity = 25;
|
||||
|
||||
var velocity = { x: palmToFingerTipVector.x * linearVelocity,
|
||||
y: palmToFingerTipVector.y * linearVelocity,
|
||||
z: palmToFingerTipVector.z * linearVelocity };
|
||||
|
||||
var gravity = { x: 0, y: -0.1/TREE_SCALE, z: 0 }; // gravity has no effect on these bullets
|
||||
var color = { red: 128, green: 128, blue: 128 };
|
||||
var damping = 0; // no damping
|
||||
var inHand = false;
|
||||
|
||||
// This is the script for the particles that this gun shoots.
|
||||
var script =
|
||||
" function collisionWithVoxel(voxel) { " +
|
||||
|
@ -96,17 +79,24 @@ function checkController() {
|
|||
" Particle.setColor(voxelColor); " +
|
||||
" var voxelAt = voxel.getPosition();" +
|
||||
" var voxelScale = voxel.getScale();" +
|
||||
" Voxels.queueVoxelDelete(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " +
|
||||
" print('Voxels.queueVoxelDelete(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
||||
" Voxels.eraseVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " +
|
||||
" print('Voxels.eraseVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
||||
" } " +
|
||||
" Particle.collisionWithVoxel.connect(collisionWithVoxel); ";
|
||||
|
||||
Particles.queueParticleAdd(position, bulletSize, color, velocity, gravity, damping, inHand, script);
|
||||
Particles.addParticle(
|
||||
{ position: position,
|
||||
radius: 0.05,
|
||||
color: { red: 128, green: 128, blue: 128 },
|
||||
velocity: velocity,
|
||||
gravity: { x: 0, y: -0.1, z: 0 },
|
||||
damping: 0,
|
||||
script: script });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(checkController);
|
||||
|
|
|
@ -28,9 +28,9 @@ function moveVoxel() {
|
|||
thisColor = colorEdge;
|
||||
}
|
||||
// Create a new voxel
|
||||
Voxels.queueDestructiveVoxelAdd(position.x / TREE_SCALE, position.y / TREE_SCALE, position.z / TREE_SCALE, size / TREE_SCALE, thisColor.r, thisColor.g, thisColor.b);
|
||||
Voxels.setVoxel(position.x, position.y, position.z, size, thisColor.r, thisColor.g, thisColor.b);
|
||||
// delete old voxel
|
||||
Voxels.queueVoxelDelete(oldPosition.x / TREE_SCALE, oldPosition.y / TREE_SCALE, oldPosition.z / TREE_SCALE, size / TREE_SCALE);
|
||||
Voxels.eraseVoxel(oldPosition.x, oldPosition.y, oldPosition.z, size);
|
||||
// Copy old location to new
|
||||
oldPosition.x = position.x;
|
||||
oldPosition.y = position.y;
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
//
|
||||
// gun.js
|
||||
// paintGun.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/31/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that turns the hydra controllers into a particle gun.
|
||||
// It reads the controller, watches for trigger pulls, and launches particles.
|
||||
// The particles it creates have a script that when they collide with Voxels, the
|
||||
// particle will change it's color to match the voxel it hits, and then delete the
|
||||
// voxel.
|
||||
//
|
||||
//
|
||||
|
||||
// initialize our triggers
|
||||
var triggerPulled = new Array();
|
||||
|
@ -51,27 +44,15 @@ function checkController() {
|
|||
var fingerTipController = palmController + 1;
|
||||
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
|
||||
|
||||
var bulletSize = 0.01/TREE_SCALE;
|
||||
|
||||
var palmInParticleSpace =
|
||||
{ x: palmPosition.x/TREE_SCALE,
|
||||
y: palmPosition.y/TREE_SCALE,
|
||||
z: palmPosition.z/TREE_SCALE };
|
||||
|
||||
var tipInParticleSpace =
|
||||
{ x: fingerTipPosition.x/TREE_SCALE,
|
||||
y: fingerTipPosition.y/TREE_SCALE,
|
||||
z: fingerTipPosition.z/TREE_SCALE };
|
||||
|
||||
var palmToFingerTipVector =
|
||||
{ x: (tipInParticleSpace.x - palmInParticleSpace.x),
|
||||
y: (tipInParticleSpace.y - palmInParticleSpace.y),
|
||||
z: (tipInParticleSpace.z - palmInParticleSpace.z) };
|
||||
{ x: (fingerTipPosition.x - palmPosition.x),
|
||||
y: (fingerTipPosition.y - palmPosition.y),
|
||||
z: (fingerTipPosition.z - palmPosition.z) };
|
||||
|
||||
// just off the front of the finger tip
|
||||
var position = { x: tipInParticleSpace.x + palmToFingerTipVector.x/2,
|
||||
y: tipInParticleSpace.y + palmToFingerTipVector.y/2,
|
||||
z: tipInParticleSpace.z + palmToFingerTipVector.z/2};
|
||||
var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2,
|
||||
y: fingerTipPosition.y + palmToFingerTipVector.y/2,
|
||||
z: fingerTipPosition.z + palmToFingerTipVector.z/2};
|
||||
|
||||
var linearVelocity = 25;
|
||||
|
||||
|
@ -79,11 +60,6 @@ function checkController() {
|
|||
y: palmToFingerTipVector.y * linearVelocity,
|
||||
z: palmToFingerTipVector.z * linearVelocity };
|
||||
|
||||
var gravity = { x: 0, y: -0.1/TREE_SCALE, z: 0 }; // gravity has no effect on these bullets
|
||||
var color = { red: 128, green: 128, blue: 128 };
|
||||
var damping = 0; // no damping
|
||||
var inHand = false;
|
||||
|
||||
// This is the script for the particles that this gun shoots.
|
||||
var script =
|
||||
" function collisionWithVoxel(voxel) { " +
|
||||
|
@ -96,12 +72,20 @@ function checkController() {
|
|||
" Particle.setColor(voxelColor); " +
|
||||
" var voxelAt = voxel.getPosition();" +
|
||||
" var voxelScale = voxel.getScale();" +
|
||||
" Voxels.queueVoxelAdd(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " +
|
||||
" print('Voxels.queueVoxelDelete(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
||||
" Voxels.setVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " +
|
||||
" print('Voxels.setVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
||||
" } " +
|
||||
" Particle.collisionWithVoxel.connect(collisionWithVoxel); ";
|
||||
|
||||
Particles.queueParticleAdd(position, bulletSize, color, velocity, gravity, damping, inHand, script);
|
||||
Particles.addParticle(
|
||||
{ position: position,
|
||||
radius: 0.01,
|
||||
color: { red: 128, green: 128, blue: 128 },
|
||||
velocity: velocity,
|
||||
gravity: { x: 0, y: -0.1, z: 0 },
|
||||
damping: 0,
|
||||
script: script }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
227
examples/toyball.js
Normal file
227
examples/toyball.js
Normal file
|
@ -0,0 +1,227 @@
|
|||
//
|
||||
// toyball.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/20/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that turns the hydra controllers into a toy ball catch and throw game.
|
||||
// It reads the controller, watches for button presses and trigger pulls, and launches particles.
|
||||
//
|
||||
// The particles it creates have a script that when they collide with Voxels, the
|
||||
// particle will change it's color to match the voxel it hits.
|
||||
//
|
||||
//
|
||||
|
||||
// maybe we should make these constants...
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_TIP = 1;
|
||||
var LEFT_BUTTON_FWD = 5;
|
||||
var LEFT_BUTTON_3 = 3;
|
||||
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_TIP = 3;
|
||||
var RIGHT_BUTTON_FWD = 11;
|
||||
var RIGHT_BUTTON_3 = 9;
|
||||
|
||||
var leftBallAlreadyInHand = false;
|
||||
var rightBallAlreadyInHand = false;
|
||||
var leftHandParticle;
|
||||
var rightHandParticle;
|
||||
|
||||
var throwSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw");
|
||||
var catchSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw");
|
||||
var targetRadius = 0.25;
|
||||
|
||||
|
||||
var wantDebugging = false;
|
||||
function debugPrint(message) {
|
||||
if (wantDebugging) {
|
||||
print(message);
|
||||
}
|
||||
}
|
||||
|
||||
function getBallHoldPosition(whichSide) {
|
||||
var normal;
|
||||
var tipPosition;
|
||||
if (whichSide == LEFT_PALM) {
|
||||
normal = Controller.getSpatialControlNormal(LEFT_PALM);
|
||||
tipPosition = Controller.getSpatialControlPosition(LEFT_TIP);
|
||||
} else {
|
||||
normal = Controller.getSpatialControlNormal(RIGHT_PALM);
|
||||
tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP);
|
||||
}
|
||||
|
||||
var BALL_FORWARD_OFFSET = 0.08; // put the ball a bit forward of fingers
|
||||
position = { x: BALL_FORWARD_OFFSET * normal.x,
|
||||
y: BALL_FORWARD_OFFSET * normal.y,
|
||||
z: BALL_FORWARD_OFFSET * normal.z };
|
||||
|
||||
position.x += tipPosition.x;
|
||||
position.y += tipPosition.y;
|
||||
position.z += tipPosition.z;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
function checkControllerSide(whichSide) {
|
||||
var BUTTON_FWD;
|
||||
var BUTTON_3;
|
||||
var palmPosition;
|
||||
var ballAlreadyInHand;
|
||||
var handMessage;
|
||||
|
||||
if (whichSide == LEFT_PALM) {
|
||||
BUTTON_FWD = LEFT_BUTTON_FWD;
|
||||
BUTTON_3 = LEFT_BUTTON_3;
|
||||
palmPosition = Controller.getSpatialControlPosition(LEFT_PALM);
|
||||
ballAlreadyInHand = leftBallAlreadyInHand;
|
||||
handMessage = "LEFT";
|
||||
} else {
|
||||
BUTTON_FWD = RIGHT_BUTTON_FWD;
|
||||
BUTTON_3 = RIGHT_BUTTON_3;
|
||||
palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM);
|
||||
ballAlreadyInHand = rightBallAlreadyInHand;
|
||||
handMessage = "RIGHT";
|
||||
}
|
||||
|
||||
var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3));
|
||||
|
||||
// If I don't currently have a ball in my hand, then try to catch closest one
|
||||
if (!ballAlreadyInHand && grabButtonPressed) {
|
||||
var closestParticle = Particles.findClosestParticle(palmPosition, targetRadius);
|
||||
|
||||
if (closestParticle.isKnownID) {
|
||||
|
||||
debugPrint(handMessage + " HAND- CAUGHT SOMETHING!!");
|
||||
|
||||
if (whichSide == LEFT_PALM) {
|
||||
leftBallAlreadyInHand = true;
|
||||
leftHandParticle = closestParticle;
|
||||
} else {
|
||||
rightBallAlreadyInHand = true;
|
||||
rightHandParticle = closestParticle;
|
||||
}
|
||||
var ballPosition = getBallHoldPosition(whichSide);
|
||||
var properties = { position: { x: ballPosition.x,
|
||||
y: ballPosition.y,
|
||||
z: ballPosition.z },
|
||||
velocity : { x: 0, y: 0, z: 0}, inHand: true };
|
||||
Particles.editParticle(closestParticle, properties);
|
||||
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = ballPosition;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(catchSound, options);
|
||||
|
||||
return; // exit early
|
||||
}
|
||||
}
|
||||
|
||||
// change ball color logic...
|
||||
//
|
||||
//if (wasButtonJustPressed()) {
|
||||
// rotateColor();
|
||||
//}
|
||||
|
||||
// If '3' is pressed, and not holding a ball, make a new one
|
||||
if (Controller.isButtonPressed(BUTTON_3) && !ballAlreadyInHand) {
|
||||
var ballPosition = getBallHoldPosition(whichSide);
|
||||
var properties = { position: { x: ballPosition.x,
|
||||
y: ballPosition.y,
|
||||
z: ballPosition.z },
|
||||
velocity: { x: 0, y: 0, z: 0},
|
||||
gravity: { x: 0, y: 0, z: 0},
|
||||
inHand: true,
|
||||
radius: 0.05,
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
lifetime: 10 // 10 seconds
|
||||
};
|
||||
|
||||
newParticle = Particles.addParticle(properties);
|
||||
if (whichSide == LEFT_PALM) {
|
||||
leftBallAlreadyInHand = true;
|
||||
leftHandParticle = newParticle;
|
||||
} else {
|
||||
rightBallAlreadyInHand = true;
|
||||
rightHandParticle = newParticle;
|
||||
}
|
||||
|
||||
// Play a new ball sound
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = ballPosition;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(catchSound, options);
|
||||
|
||||
return; // exit early
|
||||
}
|
||||
|
||||
if (ballAlreadyInHand) {
|
||||
if (whichSide == LEFT_PALM) {
|
||||
handParticle = leftHandParticle;
|
||||
whichTip = LEFT_TIP;
|
||||
} else {
|
||||
handParticle = rightHandParticle;
|
||||
whichTip = RIGHT_TIP;
|
||||
}
|
||||
|
||||
// If holding the ball keep it in the palm
|
||||
if (grabButtonPressed) {
|
||||
debugPrint(">>>>> " + handMessage + "-BALL IN HAND, grabbing, hold and move");
|
||||
var ballPosition = getBallHoldPosition(whichSide);
|
||||
var properties = { position: { x: ballPosition.x,
|
||||
y: ballPosition.y,
|
||||
z: ballPosition.z },
|
||||
};
|
||||
Particles.editParticle(handParticle, properties);
|
||||
} else {
|
||||
debugPrint(">>>>> " + handMessage + "-BALL IN HAND, not grabbing, THROW!!!");
|
||||
// If toy ball just released, add velocity to it!
|
||||
var tipVelocity = Controller.getSpatialControlVelocity(whichTip);
|
||||
var THROWN_VELOCITY_SCALING = 1.5;
|
||||
var properties = {
|
||||
velocity: { x: tipVelocity.x * THROWN_VELOCITY_SCALING,
|
||||
y: tipVelocity.y * THROWN_VELOCITY_SCALING,
|
||||
z: tipVelocity.z * THROWN_VELOCITY_SCALING } ,
|
||||
inHand: false,
|
||||
gravity: { x: 0, y: -2, z: 0},
|
||||
};
|
||||
|
||||
Particles.editParticle(handParticle, properties);
|
||||
|
||||
if (whichSide == LEFT_PALM) {
|
||||
leftBallAlreadyInHand = false;
|
||||
leftHandParticle = false;
|
||||
} else {
|
||||
rightBallAlreadyInHand = false;
|
||||
rightHandParticle = false;
|
||||
}
|
||||
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = ballPosition;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(throwSound, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkController() {
|
||||
var numberOfButtons = Controller.getNumberOfButtons();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
|
||||
// this is expected for hydras
|
||||
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
|
||||
debugPrint("no hydra connected?");
|
||||
return; // bail if no hydra
|
||||
}
|
||||
|
||||
checkControllerSide(LEFT_PALM);
|
||||
checkControllerSide(RIGHT_PALM);
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(checkController);
|
|
@ -110,17 +110,17 @@ function moveBird() {
|
|||
if (tweeting > 0) {
|
||||
// Change color of voxel to blinky red a bit while playing the sound
|
||||
var blinkColor = { r: Math.random() * 255, g: 0, b: 0 };
|
||||
Voxels.queueDestructiveVoxelAdd(position.x / TREE_SCALE,
|
||||
position.y / TREE_SCALE,
|
||||
position.z / TREE_SCALE,
|
||||
size / TREE_SCALE,
|
||||
blinkColor.r, blinkColor.g, blinkColor.b);
|
||||
Voxels.setVoxel(position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
size,
|
||||
blinkColor.r, blinkColor.g, blinkColor.b);
|
||||
}
|
||||
if (moved) {
|
||||
Voxels.queueDestructiveVoxelAdd(position.x / TREE_SCALE, position.y / TREE_SCALE, position.z / TREE_SCALE, size / TREE_SCALE, thisColor.r, thisColor.g, thisColor.b);
|
||||
Voxels.setVoxel(position.x, position.y, position.z, size, thisColor.r, thisColor.g, thisColor.b);
|
||||
// delete old voxel
|
||||
|
||||
Voxels.queueVoxelDelete(oldPosition.x / TREE_SCALE, oldPosition.y / TREE_SCALE, oldPosition.z / TREE_SCALE, size / TREE_SCALE);
|
||||
Voxels.eraseVoxel(oldPosition.x, oldPosition.y, oldPosition.z, size);
|
||||
// Copy old location to new
|
||||
vCopy(oldPosition, position);
|
||||
moved = false;
|
||||
|
|
|
@ -661,8 +661,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
|
||||
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
|
||||
switch (event->key()) {
|
||||
case Qt::Key_N:
|
||||
shootParticle();
|
||||
break;
|
||||
case Qt::Key_Shift:
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) {
|
||||
|
@ -1467,60 +1465,6 @@ void Application::removeVoxel(glm::vec3 position,
|
|||
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
|
||||
}
|
||||
|
||||
void Application::shootParticle() {
|
||||
|
||||
glm::vec3 position = _viewFrustum.getPosition();
|
||||
glm::vec3 direction = _viewFrustum.getDirection();
|
||||
const float LINEAR_VELOCITY = 5.f;
|
||||
glm::vec3 lookingAt = position + (direction * LINEAR_VELOCITY);
|
||||
|
||||
const float radius = 0.125 / TREE_SCALE;
|
||||
xColor color = { 0, 255, 255};
|
||||
glm::vec3 velocity = lookingAt - position;
|
||||
glm::vec3 gravity = DEFAULT_GRAVITY * 0.f;
|
||||
float damping = DEFAULT_DAMPING * 0.01f;
|
||||
QString script(
|
||||
" function collisionWithVoxel(voxel) { "
|
||||
" print('collisionWithVoxel(voxel)... '); "
|
||||
" print('myID=' + Particle.getID() + '\\n'); "
|
||||
" var voxelColor = voxel.getColor();"
|
||||
" print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); "
|
||||
" var myColor = Particle.getColor();"
|
||||
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); "
|
||||
" Particle.setColor(voxelColor); "
|
||||
" var voxelAt = voxel.getPosition();"
|
||||
" var voxelScale = voxel.getScale();"
|
||||
" Voxels.queueVoxelDelete(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); "
|
||||
" print('Voxels.queueVoxelDelete(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); "
|
||||
" } "
|
||||
" Particle.collisionWithVoxel.connect(collisionWithVoxel); " );
|
||||
|
||||
|
||||
ParticleEditHandle* particleEditHandle = makeParticle(position / (float)TREE_SCALE, radius, color,
|
||||
velocity / (float)TREE_SCALE, gravity, damping, NOT_IN_HAND, script);
|
||||
|
||||
// If we wanted to be able to edit this particle after shooting, then we could store this value
|
||||
// and use it for editing later. But we don't care about that for "shooting" and therefore we just
|
||||
// clean up our memory now. deleting a ParticleEditHandle does not effect the underlying particle,
|
||||
// it just removes your ability to edit that particle later.
|
||||
delete particleEditHandle;
|
||||
}
|
||||
|
||||
// Caller is responsible for managing this EditableParticle
|
||||
ParticleEditHandle* Application::newParticleEditHandle(uint32_t id) {
|
||||
ParticleEditHandle* particleEditHandle = new ParticleEditHandle(&_particleEditSender, _particles.getTree(), id);
|
||||
return particleEditHandle;
|
||||
}
|
||||
|
||||
// Caller is responsible for managing this EditableParticle
|
||||
ParticleEditHandle* Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, bool inHand, QString updateScript) {
|
||||
|
||||
ParticleEditHandle* particleEditHandle = newParticleEditHandle();
|
||||
particleEditHandle->createParticle(position, radius, color, velocity, gravity, damping, inHand, updateScript);
|
||||
return particleEditHandle;
|
||||
}
|
||||
|
||||
|
||||
void Application::makeVoxel(glm::vec3 position,
|
||||
float scale,
|
||||
|
@ -2053,14 +1997,14 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
|
|||
glm::vec3 rayOrigin, rayDirection;
|
||||
_viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection);
|
||||
lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE;
|
||||
|
||||
|
||||
} else if (!_lookatTargetAvatar) {
|
||||
if (_isHoverVoxel) {
|
||||
// Look at the hovered voxel
|
||||
lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel);
|
||||
|
||||
} else {
|
||||
// Just look in direction of the mouse ray
|
||||
// Just look in direction of the mouse ray
|
||||
lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE;
|
||||
}
|
||||
}
|
||||
|
@ -4137,11 +4081,12 @@ void Application::processDatagrams() {
|
|||
break;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_ADD_RESPONSE:
|
||||
// look up our ParticleEditHanders....
|
||||
ParticleEditHandle::handleAddResponse(_incomingPacket, bytesReceived);
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
Particle::handleAddParticleResponse(_incomingPacket, bytesReceived);
|
||||
break;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
case PACKET_TYPE_PARTICLE_ERASE:
|
||||
case PACKET_TYPE_VOXEL_DATA:
|
||||
case PACKET_TYPE_VOXEL_ERASE:
|
||||
case PACKET_TYPE_OCTREE_STATS:
|
||||
|
@ -4261,6 +4206,7 @@ void Application::loadScript(const QString& fileNameString){
|
|||
// we can use the same ones from the application.
|
||||
scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
||||
scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
||||
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
|
||||
|
||||
// hook our avatar object into this script engine
|
||||
scriptEngine->setAvatarData(&_myAvatar, "MyAvatar");
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
#include "ui/UpdateDialog.h"
|
||||
#include "FileLogger.h"
|
||||
#include "ParticleTreeRenderer.h"
|
||||
#include "ParticleEditHandle.h"
|
||||
#include "ControllerScriptingInterface.h"
|
||||
|
||||
|
||||
|
@ -128,11 +127,6 @@ public:
|
|||
|
||||
void wheelEvent(QWheelEvent* event);
|
||||
|
||||
void shootParticle(); // shoots a particle in the direction you're looking
|
||||
ParticleEditHandle* newParticleEditHandle(uint32_t id = NEW_PARTICLE);
|
||||
ParticleEditHandle* makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, bool inHand, QString updateScript);
|
||||
|
||||
void makeVoxel(glm::vec3 position,
|
||||
float scale,
|
||||
unsigned char red,
|
||||
|
|
|
@ -73,7 +73,7 @@ void BuckyBalls::grab(PalmData& palm, const glm::vec3& fingerTipPosition, glm::q
|
|||
diff = _bballPosition[_bballIsGrabbed[palm.getSixenseID()]] - fingerTipPosition;
|
||||
penetration = glm::length(diff) - (_bballRadius[_bballIsGrabbed[palm.getSixenseID()]] + COLLISION_RADIUS);
|
||||
_bballPosition[_bballIsGrabbed[palm.getSixenseID()]] -= glm::normalize(diff) * penetration;
|
||||
glm::vec3 fingerTipVelocity = avatarOrientation * palm.getTipVelocity();
|
||||
glm::vec3 fingerTipVelocity = palm.getTipVelocity();
|
||||
if (_bballElement[_bballIsGrabbed[palm.getSixenseID()]] != 1) {
|
||||
_bballVelocity[_bballIsGrabbed[palm.getSixenseID()]] = fingerTipVelocity;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include "ParticleTreeRenderer.h"
|
||||
|
||||
ParticleTreeRenderer::ParticleTreeRenderer() :
|
||||
ParticleTreeRenderer::ParticleTreeRenderer() :
|
||||
OctreeRenderer() {
|
||||
}
|
||||
|
||||
|
@ -31,13 +31,13 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
// actually render it here...
|
||||
// we need to iterate the actual particles of the element
|
||||
ParticleTreeElement* particleTreeElement = (ParticleTreeElement*)element;
|
||||
|
||||
|
||||
const QList<Particle>& particles = particleTreeElement->getParticles();
|
||||
|
||||
|
||||
uint16_t numberOfParticles = particles.size();
|
||||
|
||||
|
||||
bool drawAsSphere = true;
|
||||
|
||||
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
const Particle& particle = particles[i];
|
||||
// render particle aspoints
|
||||
|
@ -46,7 +46,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
float sphereRadius = particle.getRadius() * (float)TREE_SCALE;
|
||||
|
||||
args->_renderedItems++;
|
||||
|
||||
|
||||
if (drawAsSphere) {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
|
@ -60,3 +60,8 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleTreeRenderer::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr,
|
||||
Node* sourceNode) {
|
||||
static_cast<ParticleTree*>(_tree)->processEraseMessage(dataByteArray, senderSockAddr, sourceNode);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
|
||||
ParticleTree* getTree() { return (ParticleTree*)_tree; }
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"VoxelPacketProcessor::processPacket()");
|
||||
|
||||
|
||||
const int WAY_BEHIND = 300;
|
||||
|
||||
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
|
||||
|
@ -27,19 +27,19 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
|
||||
Application* app = Application::getInstance();
|
||||
bool wasStatsPacket = false;
|
||||
|
||||
|
||||
|
||||
|
||||
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
|
||||
if (app->_wantToKillLocalVoxels) {
|
||||
app->_voxels.killLocalVoxels();
|
||||
app->_wantToKillLocalVoxels = false;
|
||||
}
|
||||
|
||||
|
||||
// note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA
|
||||
// immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first
|
||||
// then process any remaining bytes as if it was another packet
|
||||
if (packetData[0] == PACKET_TYPE_OCTREE_STATS) {
|
||||
|
||||
|
||||
int statsMessageLength = app->parseOctreeStats(packetData, messageLength, senderSockAddr);
|
||||
wasStatsPacket = true;
|
||||
if (messageLength > statsMessageLength) {
|
||||
|
@ -56,20 +56,25 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
|
||||
|
||||
|
||||
SharedNodePointer serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (serverNode && serverNode->getActiveSocket() && *serverNode->getActiveSocket() == senderSockAddr) {
|
||||
|
||||
|
||||
switch(packetData[0]) {
|
||||
case PACKET_TYPE_PARTICLE_ERASE: {
|
||||
app->_particles.processEraseMessage(QByteArray((char*) packetData, messageLength),
|
||||
senderSockAddr, serverNode.data());
|
||||
} break;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA: {
|
||||
app->_particles.processDatagram(QByteArray((char*) packetData, messageLength),
|
||||
senderSockAddr, serverNode.data());
|
||||
} break;
|
||||
|
||||
|
||||
case PACKET_TYPE_ENVIRONMENT_DATA: {
|
||||
app->_environment.parseData(senderSockAddr, packetData, messageLength);
|
||||
} break;
|
||||
|
||||
|
||||
default : {
|
||||
app->_voxels.setDataSourceUUID(serverNode->getUUID());
|
||||
app->_voxels.parseData(packetData, messageLength);
|
||||
|
|
|
@ -21,25 +21,7 @@ using namespace std;
|
|||
|
||||
const float FINGERTIP_COLLISION_RADIUS = 0.01f;
|
||||
const float FINGERTIP_VOXEL_SIZE = 0.05f;
|
||||
const int TOY_BALL_HAND = 1;
|
||||
const float TOY_BALL_RADIUS = 0.05f;
|
||||
const float TOY_BALL_DAMPING = 0.1f;
|
||||
const glm::vec3 NO_VELOCITY = glm::vec3(0,0,0);
|
||||
const glm::vec3 NO_GRAVITY = glm::vec3(0,0,0);
|
||||
const float NO_DAMPING = 0.f;
|
||||
const glm::vec3 TOY_BALL_GRAVITY = glm::vec3(0,-2.0f,0);
|
||||
const QString TOY_BALL_UPDATE_SCRIPT("");
|
||||
const float PALM_COLLISION_RADIUS = 0.03f;
|
||||
const float CATCH_RADIUS = 0.3f;
|
||||
const xColor TOY_BALL_ON_SERVER_COLOR[] =
|
||||
{
|
||||
{ 255, 0, 0 },
|
||||
{ 0, 255, 0 },
|
||||
{ 0, 0, 255 },
|
||||
{ 255, 255, 0 },
|
||||
{ 0, 255, 255 },
|
||||
{ 255, 0, 255 },
|
||||
};
|
||||
|
||||
|
||||
Hand::Hand(Avatar* owningAvatar) :
|
||||
|
@ -55,16 +37,8 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
_grabDelta(0, 0, 0),
|
||||
_grabDeltaVelocity(0, 0, 0),
|
||||
_grabStartRotation(0, 0, 0, 1),
|
||||
_grabCurrentRotation(0, 0, 0, 1),
|
||||
_throwSound(QUrl("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw")),
|
||||
_catchSound(QUrl("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"))
|
||||
_grabCurrentRotation(0, 0, 0, 1)
|
||||
{
|
||||
for (int i = 0; i < MAX_HANDS; i++) {
|
||||
_toyBallInHand[i] = false;
|
||||
_ballParticleEditHandles[i] = NULL;
|
||||
_whichBallColor[i] = 0;
|
||||
}
|
||||
_lastControllerButtons = 0;
|
||||
}
|
||||
|
||||
void Hand::init() {
|
||||
|
@ -80,157 +54,7 @@ void Hand::init() {
|
|||
void Hand::reset() {
|
||||
}
|
||||
|
||||
void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, float deltaTime) {
|
||||
Application* app = Application::getInstance();
|
||||
ParticleTree* particles = app->getParticles()->getTree();
|
||||
int handID = palm.getSixenseID();
|
||||
|
||||
const int NEW_BALL_BUTTON = BUTTON_3;
|
||||
|
||||
bool grabButtonPressed = ((palm.getControllerButtons() & BUTTON_FWD) ||
|
||||
(palm.getControllerButtons() & BUTTON_3));
|
||||
|
||||
bool ballAlreadyInHand = _toyBallInHand[handID];
|
||||
|
||||
glm::vec3 targetPosition;
|
||||
palm.getBallHoldPosition(targetPosition);
|
||||
float targetRadius = CATCH_RADIUS / (float)TREE_SCALE;
|
||||
|
||||
// If I don't currently have a ball in my hand, then try to catch closest one
|
||||
if (!ballAlreadyInHand && grabButtonPressed) {
|
||||
|
||||
const Particle* closestParticle = particles->findClosestParticle(targetPosition, targetRadius);
|
||||
|
||||
if (closestParticle) {
|
||||
ParticleEditHandle* caughtParticle = app->newParticleEditHandle(closestParticle->getID());
|
||||
glm::vec3 newPosition = targetPosition;
|
||||
glm::vec3 newVelocity = NO_VELOCITY;
|
||||
|
||||
// update the particle with it's new state...
|
||||
caughtParticle->updateParticle(newPosition,
|
||||
closestParticle->getRadius(),
|
||||
closestParticle->getXColor(),
|
||||
newVelocity,
|
||||
NO_GRAVITY,
|
||||
NO_DAMPING,
|
||||
IN_HAND, // we just grabbed it!
|
||||
closestParticle->getScript());
|
||||
|
||||
|
||||
// now tell our hand about us having caught it...
|
||||
_toyBallInHand[handID] = true;
|
||||
|
||||
//printf(">>>>>>> caught... handID:%d particle ID:%d _toyBallInHand[handID] = true\n", handID, closestParticle->getID());
|
||||
_ballParticleEditHandles[handID] = caughtParticle;
|
||||
caughtParticle = NULL;
|
||||
|
||||
// use the threadSound static method to inject the catch sound
|
||||
// pass an AudioInjectorOptions struct to set position and disable loopback
|
||||
AudioInjectorOptions injectorOptions;
|
||||
injectorOptions.setPosition(newPosition);
|
||||
injectorOptions.setLoopbackAudioInterface(app->getAudio());
|
||||
|
||||
AudioScriptingInterface::playSound(&_catchSound, &injectorOptions);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a ball in hand, and the user presses the skinny button, then change the color of the ball
|
||||
int currentControllerButtons = palm.getControllerButtons();
|
||||
|
||||
if (currentControllerButtons != _lastControllerButtons && (currentControllerButtons & BUTTON_0)) {
|
||||
_whichBallColor[handID]++;
|
||||
if (_whichBallColor[handID] >= sizeof(TOY_BALL_ON_SERVER_COLOR)/sizeof(TOY_BALL_ON_SERVER_COLOR[0])) {
|
||||
_whichBallColor[handID] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If '3' is pressed, and not holding a ball, make a new one
|
||||
if ((palm.getControllerButtons() & NEW_BALL_BUTTON) && (_toyBallInHand[handID] == false)) {
|
||||
_toyBallInHand[handID] = true;
|
||||
// Create a particle on the particle server
|
||||
glm::vec3 ballPosition;
|
||||
palm.getBallHoldPosition(ballPosition);
|
||||
_ballParticleEditHandles[handID] = app->makeParticle(
|
||||
ballPosition / (float)TREE_SCALE,
|
||||
TOY_BALL_RADIUS / (float) TREE_SCALE,
|
||||
TOY_BALL_ON_SERVER_COLOR[_whichBallColor[handID]],
|
||||
NO_VELOCITY / (float)TREE_SCALE,
|
||||
TOY_BALL_GRAVITY / (float) TREE_SCALE,
|
||||
TOY_BALL_DAMPING,
|
||||
IN_HAND,
|
||||
TOY_BALL_UPDATE_SCRIPT);
|
||||
// Play a new ball sound
|
||||
app->getAudio()->startDrumSound(1.0f, 2000, 0.5f, 0.02f);
|
||||
}
|
||||
|
||||
if (grabButtonPressed) {
|
||||
// If we don't currently have a ball in hand, then create it...
|
||||
if (_toyBallInHand[handID]) {
|
||||
// Update ball that is in hand
|
||||
uint32_t particleInHandID = _ballParticleEditHandles[handID]->getID();
|
||||
const Particle* particleInHand = particles->findParticleByID(particleInHandID);
|
||||
xColor colorForParticleInHand = particleInHand ? particleInHand->getXColor()
|
||||
: TOY_BALL_ON_SERVER_COLOR[_whichBallColor[handID]];
|
||||
|
||||
glm::vec3 ballPosition;
|
||||
palm.getBallHoldPosition(ballPosition);
|
||||
_ballParticleEditHandles[handID]->updateParticle(ballPosition / (float)TREE_SCALE,
|
||||
TOY_BALL_RADIUS / (float) TREE_SCALE,
|
||||
colorForParticleInHand,
|
||||
NO_VELOCITY / (float)TREE_SCALE,
|
||||
TOY_BALL_GRAVITY / (float) TREE_SCALE,
|
||||
TOY_BALL_DAMPING,
|
||||
IN_HAND,
|
||||
TOY_BALL_UPDATE_SCRIPT);
|
||||
}
|
||||
} else {
|
||||
// If toy ball just released, add velocity to it!
|
||||
if (_toyBallInHand[handID]) {
|
||||
|
||||
const float THROWN_VELOCITY_SCALING = 1.5f;
|
||||
_toyBallInHand[handID] = false;
|
||||
palm.updateCollisionlessPaddleExpiry();
|
||||
glm::vec3 ballPosition;
|
||||
palm.getBallHoldPosition(ballPosition);
|
||||
glm::vec3 ballVelocity = palm.getTipVelocity();
|
||||
glm::quat avatarRotation = _owningAvatar->getOrientation();
|
||||
ballVelocity = avatarRotation * ballVelocity;
|
||||
ballVelocity *= THROWN_VELOCITY_SCALING;
|
||||
|
||||
uint32_t particleInHandID = _ballParticleEditHandles[handID]->getID();
|
||||
const Particle* particleInHand = particles->findParticleByID(particleInHandID);
|
||||
xColor colorForParticleInHand = particleInHand ? particleInHand->getXColor()
|
||||
: TOY_BALL_ON_SERVER_COLOR[_whichBallColor[handID]];
|
||||
|
||||
_ballParticleEditHandles[handID]->updateParticle(ballPosition / (float)TREE_SCALE,
|
||||
TOY_BALL_RADIUS / (float) TREE_SCALE,
|
||||
colorForParticleInHand,
|
||||
ballVelocity / (float)TREE_SCALE,
|
||||
TOY_BALL_GRAVITY / (float) TREE_SCALE,
|
||||
TOY_BALL_DAMPING,
|
||||
NOT_IN_HAND,
|
||||
TOY_BALL_UPDATE_SCRIPT);
|
||||
|
||||
// after releasing the ball, we free our ParticleEditHandle so we can't edit it further
|
||||
// note: deleting the edit handle doesn't effect the actual particle
|
||||
delete _ballParticleEditHandles[handID];
|
||||
_ballParticleEditHandles[handID] = NULL;
|
||||
|
||||
// use the threadSound static method to inject the throw sound
|
||||
// pass an AudioInjectorOptions struct to set position and disable loopback
|
||||
AudioInjectorOptions injectorOptions;
|
||||
injectorOptions.setPosition(ballPosition);
|
||||
injectorOptions.setLoopbackAudioInterface(app->getAudio());
|
||||
|
||||
AudioScriptingInterface::playSound(&_throwSound, &injectorOptions);
|
||||
}
|
||||
}
|
||||
|
||||
// remember the last pressed button state
|
||||
if (currentControllerButtons != 0) {
|
||||
_lastControllerButtons = currentControllerButtons;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Hand::getAndResetGrabDelta() {
|
||||
const float HAND_GRAB_SCALE_DISTANCE = 2.f;
|
||||
|
@ -277,8 +101,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger
|
||||
glm::vec3 fingerTipPosition = finger.getTipPosition();
|
||||
|
||||
simulateToyBall(palm, fingerTipPosition, deltaTime);
|
||||
|
||||
_buckyBalls.grab(palm, fingerTipPosition, _owningAvatar->getOrientation(), deltaTime);
|
||||
|
||||
if (palm.getControllerButtons() & BUTTON_4) {
|
||||
|
@ -512,7 +334,6 @@ void Hand::render(bool isMine) {
|
|||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayLeapHands)) {
|
||||
renderLeapHands(isMine);
|
||||
|
@ -541,7 +362,6 @@ void Hand::render(bool isMine) {
|
|||
void Hand::renderLeapHands(bool isMine) {
|
||||
|
||||
const float alpha = 1.0f;
|
||||
const float TARGET_ALPHA = 0.5f;
|
||||
|
||||
//const glm::vec3 handColor = _ballColor;
|
||||
const glm::vec3 handColor(1.0, 0.84, 0.66); // use the skin color
|
||||
|
@ -559,19 +379,6 @@ void Hand::renderLeapHands(bool isMine) {
|
|||
palm.getBallHoldPosition(targetPosition);
|
||||
glPushMatrix();
|
||||
|
||||
ParticleTree* particles = Application::getInstance()->getParticles()->getTree();
|
||||
const Particle* closestParticle = particles->findClosestParticle(targetPosition / (float)TREE_SCALE,
|
||||
CATCH_RADIUS / (float)TREE_SCALE);
|
||||
|
||||
// If we are hitting a particle then draw the target green, otherwise yellow
|
||||
if (closestParticle) {
|
||||
glColor4f(0,1,0, TARGET_ALPHA);
|
||||
} else {
|
||||
glColor4f(1,1,0, TARGET_ALPHA);
|
||||
}
|
||||
glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z);
|
||||
glutWireSphere(CATCH_RADIUS, 10.f, 10.f);
|
||||
|
||||
const float collisionRadius = 0.05f;
|
||||
glColor4f(0.5f,0.5f,0.5f, alpha);
|
||||
glutWireSphere(collisionRadius, 10.f, 10.f);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <AvatarData.h>
|
||||
#include <AudioScriptingInterface.h>
|
||||
#include <HandData.h>
|
||||
#include <ParticleEditHandle.h>
|
||||
|
||||
#include "BuckyBalls.h"
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -102,24 +101,12 @@ private:
|
|||
|
||||
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
|
||||
|
||||
void simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, float deltaTime);
|
||||
|
||||
#define MAX_HANDS 2
|
||||
bool _toyBallInHand[MAX_HANDS];
|
||||
int _whichBallColor[MAX_HANDS];
|
||||
ParticleEditHandle* _ballParticleEditHandles[MAX_HANDS];
|
||||
int _lastControllerButtons;
|
||||
|
||||
float _pitchUpdate;
|
||||
|
||||
glm::vec3 _grabDelta;
|
||||
glm::vec3 _grabDeltaVelocity;
|
||||
glm::quat _grabStartRotation;
|
||||
glm::quat _grabCurrentRotation;
|
||||
|
||||
Sound _throwSound;
|
||||
Sound _catchSound;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -169,10 +169,11 @@ public:
|
|||
|
||||
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
||||
const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); }
|
||||
const glm::vec3 getTipRawPosition() const { return _tipPosition; }
|
||||
const glm::vec3& getTipRawPosition() const { return _tipPosition; }
|
||||
|
||||
const glm::vec3& getTipVelocity() const { return _tipVelocity; }
|
||||
void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; }
|
||||
const glm::vec3 getTipVelocity() const { return _owningHandData->leapDirectionToWorldDirection(_tipVelocity); }
|
||||
const glm::vec3& getTipRawVelocity() const { return _tipVelocity; }
|
||||
|
||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
||||
|
|
|
@ -34,7 +34,7 @@ void OctreeInboundPacketProcessor::resetStats() {
|
|||
_totalLockWaitTime = 0;
|
||||
_totalElementsInPacket = 0;
|
||||
_totalPackets = 0;
|
||||
|
||||
|
||||
_singleSenderStats.clear();
|
||||
}
|
||||
|
||||
|
@ -43,14 +43,14 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA
|
|||
unsigned char* packetData, ssize_t packetLength) {
|
||||
|
||||
bool debugProcessPacket = _myServer->wantsVerboseDebug();
|
||||
|
||||
|
||||
if (debugProcessPacket) {
|
||||
printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
|
||||
}
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
|
||||
|
||||
|
||||
// Ask our tree subclass if it can handle the incoming packet...
|
||||
PACKET_TYPE packetType = packetData[0];
|
||||
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
|
||||
|
@ -58,7 +58,7 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA
|
|||
_receivedPacketCount++;
|
||||
|
||||
SharedNodePointer senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
|
||||
|
||||
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
|
||||
uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence))));
|
||||
uint64_t arrivedAt = usecTimestampNow();
|
||||
|
@ -66,10 +66,10 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA
|
|||
int editsInPacket = 0;
|
||||
uint64_t processTime = 0;
|
||||
uint64_t lockWaitTime = 0;
|
||||
|
||||
|
||||
if (_myServer->wantsDebugReceiving()) {
|
||||
qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount
|
||||
<< " command from client receivedBytes=" << packetLength
|
||||
qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount
|
||||
<< " command from client receivedBytes=" << packetLength
|
||||
<< " sequence=" << sequence << " transitTime=" << transitTime << " usecs";
|
||||
}
|
||||
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
|
||||
|
@ -86,7 +86,7 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA
|
|||
uint64_t startLock = usecTimestampNow();
|
||||
_myServer->getOctree()->lockForWrite();
|
||||
uint64_t startProcess = usecTimestampNow();
|
||||
int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType,
|
||||
int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType,
|
||||
packetData,
|
||||
packetLength,
|
||||
editData, maxSize, senderNode.data());
|
||||
|
@ -129,15 +129,15 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
|
||||
void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
|
||||
int editsInPacket, uint64_t processTime, uint64_t lockWaitTime) {
|
||||
|
||||
|
||||
_totalTransitTime += transitTime;
|
||||
_totalProcessTime += processTime;
|
||||
_totalLockWaitTime += lockWaitTime;
|
||||
_totalElementsInPacket += editsInPacket;
|
||||
_totalPackets++;
|
||||
|
||||
|
||||
// find the individual senders stats and track them there too...
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
|
||||
|
@ -162,7 +162,7 @@ void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, in
|
|||
|
||||
|
||||
SingleSenderStats::SingleSenderStats() {
|
||||
_totalTransitTime = 0;
|
||||
_totalTransitTime = 0;
|
||||
_totalProcessTime = 0;
|
||||
_totalLockWaitTime = 0;
|
||||
_totalElementsInPacket = 0;
|
||||
|
|
|
@ -525,7 +525,8 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b
|
|||
|
||||
// Here's where we can/should allow the server to send other data...
|
||||
// send the environment packet
|
||||
if (_myServer->hasSpecialPacketToSend()) {
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketToSend(node)) {
|
||||
trueBytesSent += _myServer->sendSpecialPacket(node);
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
|
|
@ -100,27 +100,27 @@ void OctreeServer::initHTTPManager(int port) {
|
|||
}
|
||||
|
||||
bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
|
||||
|
||||
|
||||
#ifdef FORCE_CRASH
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation
|
||||
&& path == "/force_crash") {
|
||||
|
||||
|
||||
qDebug() << "About to force a crash!";
|
||||
|
||||
|
||||
int foo;
|
||||
int* forceCrash = &foo;
|
||||
|
||||
|
||||
QString responseString("forcing a crash...");
|
||||
connection->respond(HTTPConnection::StatusCode200, qPrintable(responseString));
|
||||
|
||||
|
||||
delete[] forceCrash;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool showStats = false;
|
||||
|
||||
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||
if (path == "/") {
|
||||
showStats = true;
|
||||
|
@ -129,28 +129,28 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
showStats = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (showStats) {
|
||||
uint64_t checkSum;
|
||||
// return a 200
|
||||
QString statsString("<html><doc>\r\n<pre>\r\n");
|
||||
statsString += QString("<b>Your %1 Server is running... <a href='/'>[RELOAD]</a></b>\r\n").arg(getMyServerName());
|
||||
|
||||
|
||||
tm* localtm = localtime(&_started);
|
||||
const int MAX_TIME_LENGTH = 128;
|
||||
char buffer[MAX_TIME_LENGTH];
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
|
||||
statsString += QString("Running since: %1").arg(buffer);
|
||||
|
||||
|
||||
// Convert now to tm struct for UTC
|
||||
tm* gmtm = gmtime(&_started);
|
||||
if (gmtm) {
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
|
||||
statsString += (QString(" [%1 UTM] ").arg(buffer));
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
const int USECS_PER_MSEC = 1000;
|
||||
uint64_t msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC;
|
||||
|
@ -158,13 +158,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
const int SECS_PER_MIN = 60;
|
||||
const int MIN_PER_HOUR = 60;
|
||||
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
||||
|
||||
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
|
||||
statsString += "Uptime: ";
|
||||
|
||||
|
||||
if (hours > 0) {
|
||||
statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : "");
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf("%.3f seconds", seconds);
|
||||
}
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
|
||||
// display voxel file load time
|
||||
if (isInitialLoadComplete()) {
|
||||
if (isPersistEnabled()) {
|
||||
|
@ -183,14 +183,14 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
} else {
|
||||
statsString += QString("%1 File Persist Disabled...\r\n").arg(getMyServerName());
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
uint64_t msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
|
||||
statsString += QString("%1 File Load Took").arg(getMyServerName());
|
||||
if (hours > 0) {
|
||||
statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : "");
|
||||
|
@ -202,25 +202,25 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf("%.3f seconds", seconds);
|
||||
}
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
} else {
|
||||
statsString += "Voxels not yet loaded...\r\n";
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
statsString += "<b>Configuration:</b>\r\n";
|
||||
|
||||
|
||||
for (int i = 1; i < _argc; i++) {
|
||||
statsString += _argv[i];
|
||||
}
|
||||
statsString += "\r\n"; //one to end the config line
|
||||
statsString += "\r\n\r\n"; // two more for spacing
|
||||
|
||||
|
||||
// display scene stats
|
||||
unsigned long nodeCount = OctreeElement::getNodeCount();
|
||||
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
|
||||
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
|
||||
|
||||
|
||||
QLocale locale(QLocale::English);
|
||||
const float AS_PERCENT = 100.0;
|
||||
statsString += "<b>Current Nodes in scene:</b>\r\n";
|
||||
|
@ -234,7 +234,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||
statsString += "\r\n";
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
// display outbound packet stats
|
||||
statsString += QString("<b>%1 Outbound Packet Statistics...</b>\r\n").arg(getMyServerName());
|
||||
uint64_t totalOutboundPackets = OctreeSendThread::_totalPackets;
|
||||
|
@ -243,7 +243,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
uint64_t totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
|
||||
uint64_t totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
|
||||
uint64_t totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
||||
|
||||
|
||||
const int COLUMN_WIDTH = 10;
|
||||
statsString += QString(" Total Outbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
@ -260,10 +260,10 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf(" Total Color Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
// display inbound packet stats
|
||||
statsString += QString().sprintf("<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
|
||||
getMyServerName());
|
||||
|
@ -274,9 +274,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
uint64_t averageLockWaitTimePerElement = _octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
|
||||
uint64_t totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed();
|
||||
uint64_t totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed();
|
||||
|
||||
|
||||
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
|
@ -292,18 +292,18 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
.arg(locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
|
||||
|
||||
int senderNumber = 0;
|
||||
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
||||
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
|
||||
senderNumber++;
|
||||
QUuid senderID = i->first;
|
||||
SingleSenderStats& senderStats = i->second;
|
||||
|
||||
|
||||
statsString += QString("\r\n Stats for sender %1 uuid: %2\r\n")
|
||||
.arg(senderNumber).arg(senderID.toString());
|
||||
|
||||
|
||||
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
|
||||
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
|
||||
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
|
||||
|
@ -311,9 +311,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
averageLockWaitTimePerElement = senderStats.getAverageLockWaitTimePerElement();
|
||||
totalElementsProcessed = senderStats.getTotalElementsProcessed();
|
||||
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
|
||||
|
||||
|
||||
averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
|
@ -330,16 +330,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
.arg(locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
|
||||
// display memory usage stats
|
||||
statsString += "<b>Current Memory Usage Statistics</b>\r\n";
|
||||
statsString += QString().sprintf("\r\nOctreeElement size... %ld bytes\r\n", sizeof(OctreeElement));
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
const char* memoryScaleLabel;
|
||||
const float MEGABYTES = 1000000.f;
|
||||
const float GIGABYTES = 1000000000.f;
|
||||
|
@ -351,7 +351,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
memoryScaleLabel = "GB";
|
||||
memoryScale = GIGABYTES;
|
||||
}
|
||||
|
||||
|
||||
statsString += QString().sprintf("Element Node Memory Usage: %8.2f %s\r\n",
|
||||
OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
statsString += QString().sprintf("Octcode Memory Usage: %8.2f %s\r\n",
|
||||
|
@ -362,7 +362,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf(" Total: %8.2f %s\r\n",
|
||||
OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
statsString += "OctreeElement Children Population Statistics...\r\n";
|
||||
checkSum = 0;
|
||||
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
|
||||
|
@ -374,11 +374,11 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += " ----------------------\r\n";
|
||||
statsString += QString(" Total: %1 nodes\r\n")
|
||||
.arg(locale.toString((uint)checkSum).rightJustified(16, ' '));
|
||||
|
||||
|
||||
#ifdef BLENDED_UNION_CHILDREN
|
||||
statsString += "\r\n";
|
||||
statsString += "OctreeElement Children Encoding Statistics...\r\n";
|
||||
|
||||
|
||||
statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getSingleChildrenCount(),
|
||||
((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT));
|
||||
|
@ -397,31 +397,31 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getExternalChildrenCount(),
|
||||
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
||||
|
||||
|
||||
checkSum = OctreeElement::getSingleChildrenCount() +
|
||||
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
|
||||
OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() +
|
||||
OctreeElement::getExternalChildrenCount();
|
||||
|
||||
|
||||
statsString += " ----------------\r\n";
|
||||
statsString += QString().sprintf(" Total: %10.llu nodes\r\n", checkSum);
|
||||
statsString += QString().sprintf(" Expected: %10.lu nodes\r\n", nodeCount);
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
statsString += "In other news....\r\n";
|
||||
|
||||
|
||||
statsString += QString().sprintf("could store 4 children internally: %10.llu nodes\r\n",
|
||||
OctreeElement::getCouldStoreFourChildrenInternally());
|
||||
statsString += QString().sprintf("could NOT store 4 children internally: %10.llu nodes\r\n",
|
||||
OctreeElement::getCouldNotStoreFourChildrenInternally());
|
||||
#endif
|
||||
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
statsString += "</pre>\r\n";
|
||||
statsString += "</doc></html>";
|
||||
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode200, qPrintable(statsString), "text/html");
|
||||
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// have HTTPManager attempt to process this request from the document_root
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
/// Handles assignments of type OctreeServer - sending octrees to various clients.
|
||||
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
OctreeServer(const unsigned char* dataBuffer, int numBytes);
|
||||
~OctreeServer();
|
||||
|
||||
|
@ -58,27 +58,27 @@ public:
|
|||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun() { };
|
||||
virtual bool hasSpecialPacketToSend() { return false; }
|
||||
virtual bool hasSpecialPacketToSend(Node* node) { return false; }
|
||||
virtual int sendSpecialPacket(Node* node) { return 0; }
|
||||
|
||||
static void attachQueryNodeToNode(Node* newNode);
|
||||
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
|
||||
public slots:
|
||||
/// runs the voxel server assignment
|
||||
void run();
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
protected:
|
||||
void parsePayload();
|
||||
void initHTTPManager(int port);
|
||||
|
||||
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
char** _parsedArgV;
|
||||
|
||||
|
||||
HTTPManager* _httpManager;
|
||||
|
||||
char _persistFilename[MAX_FILENAME_LENGTH];
|
||||
|
@ -94,9 +94,9 @@ protected:
|
|||
OctreePersistThread* _persistThread;
|
||||
|
||||
static OctreeServer* _instance;
|
||||
|
||||
|
||||
time_t _started;
|
||||
uint64_t _startedUSecs;
|
||||
uint64_t _startedUSecs;
|
||||
};
|
||||
|
||||
#endif // __octree_server__OctreeServer__
|
||||
|
|
|
@ -1367,7 +1367,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) {
|
|||
PACKET_TYPE expectedType = expectedDataPacketType();
|
||||
PACKET_VERSION expectedVersion = versionForPacketType(expectedType);
|
||||
file.write(&expectedType, sizeof(expectedType));
|
||||
file.write(&expectedVersion, sizeof(expectedType));
|
||||
file.write(&expectedVersion, sizeof(expectedVersion));
|
||||
}
|
||||
|
||||
OctreeElementBag nodeBag;
|
||||
|
|
|
@ -21,14 +21,14 @@ EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssiz
|
|||
_nodeUUID = nodeUUID;
|
||||
_currentType = type;
|
||||
_currentSize = length;
|
||||
memcpy(_currentBuffer, buffer, length);
|
||||
memcpy(_currentBuffer, buffer, length);
|
||||
};
|
||||
|
||||
const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND;
|
||||
const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND;
|
||||
|
||||
|
||||
OctreeEditPacketSender::OctreeEditPacketSender(PacketSenderNotify* notify) :
|
||||
PacketSender(notify),
|
||||
OctreeEditPacketSender::OctreeEditPacketSender(PacketSenderNotify* notify) :
|
||||
PacketSender(notify),
|
||||
_shouldSend(true),
|
||||
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
|
||||
_releaseQueuedMessagesPending(false),
|
||||
|
@ -57,7 +57,7 @@ bool OctreeEditPacketSender::serversExist() const {
|
|||
bool hasServers = false;
|
||||
bool atLeastOnJurisdictionMissing = false; // assume the best
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
// only send to the NodeTypes that are getMyNodeType()
|
||||
if (node->getType() == getMyNodeType()) {
|
||||
|
@ -78,15 +78,15 @@ bool OctreeEditPacketSender::serversExist() const {
|
|||
break; // no point in looking further...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (hasServers && !atLeastOnJurisdictionMissing);
|
||||
}
|
||||
|
||||
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
|
||||
// a known nodeID.
|
||||
// a known nodeID.
|
||||
void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
// only send to the NodeTypes that are getMyNodeType()
|
||||
if (node->getType() == getMyNodeType() &&
|
||||
|
@ -94,7 +94,7 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
|
|||
if (nodeList->getNodeActiveSocketOrPing(node.data())) {
|
||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||
queuePacketForSending(*nodeAddress, buffer, length);
|
||||
|
||||
|
||||
// debugging output...
|
||||
bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
|
@ -103,10 +103,10 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
|
|||
uint64_t createdAt = (*((uint64_t*)(buffer + numBytesPacketHeader + sizeof(sequence))));
|
||||
uint64_t queuedAt = usecTimestampNow();
|
||||
uint64_t transitTime = queuedAt - createdAt;
|
||||
|
||||
qDebug() << "OctreeEditPacketSender::queuePacketToNode() queued " << buffer[0] <<
|
||||
" - command to node bytes=" << length <<
|
||||
" sequence=" << sequence <<
|
||||
|
||||
qDebug() << "OctreeEditPacketSender::queuePacketToNode() queued " << buffer[0] <<
|
||||
" - command to node bytes=" << length <<
|
||||
" sequence=" << sequence <<
|
||||
" transitTimeSoFar=" << transitTime << " usecs";
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
|
|||
|
||||
void OctreeEditPacketSender::processPreServerExistsPackets() {
|
||||
assert(serversExist()); // we should only be here if we have jurisdictions
|
||||
|
||||
|
||||
// First send out all the single message packets...
|
||||
while (!_preServerSingleMessagePackets.empty()) {
|
||||
EditPacketBuffer* packet = _preServerSingleMessagePackets.front();
|
||||
|
@ -133,7 +133,7 @@ void OctreeEditPacketSender::processPreServerExistsPackets() {
|
|||
_preServerPackets.erase(_preServerPackets.begin());
|
||||
}
|
||||
|
||||
// if while waiting for the jurisdictions the caller called releaseQueuedMessages()
|
||||
// if while waiting for the jurisdictions the caller called releaseQueuedMessages()
|
||||
// then we want to honor that request now.
|
||||
if (_releaseQueuedMessagesPending) {
|
||||
releaseQueuedMessages();
|
||||
|
@ -160,17 +160,17 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l
|
|||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
|
||||
assert(serversExist()); // we must have jurisdictions to be here!!
|
||||
|
||||
int headerBytes = numBytesForPacketHeader(buffer) + sizeof(short) + sizeof(uint64_t);
|
||||
unsigned char* octCode = buffer + headerBytes; // skip the packet header to get to the octcode
|
||||
|
||||
|
||||
// We want to filter out edit messages for servers based on the server's Jurisdiction
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// for a different server... So we need to actually manage multiple queued packets... one
|
||||
// for each server
|
||||
|
||||
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
// only send to the NodeTypes that are getMyNodeType()
|
||||
if (node->getActiveSocket() != NULL && node->getType() == getMyNodeType()) {
|
||||
|
@ -190,10 +190,11 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l
|
|||
|
||||
// NOTE: codeColorBuffer - is JUST the octcode/color and does not contain the packet header!
|
||||
void OctreeEditPacketSender::queueOctreeEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length) {
|
||||
|
||||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
|
||||
// If we don't have jurisdictions, then we will simply queue up all of these packets and wait till we have
|
||||
// jurisdictions for processing
|
||||
if (!serversExist()) {
|
||||
|
@ -211,52 +212,61 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PACKET_TYPE type, unsigned c
|
|||
}
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
|
||||
//qDebug() << "queueOctreeEditMessage() line:" << __LINE__;
|
||||
|
||||
// We want to filter out edit messages for servers based on the server's Jurisdiction
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// for a different server... So we need to actually manage multiple queued packets... one
|
||||
// for each server
|
||||
|
||||
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
// only send to the NodeTypes that are getMyNodeType()
|
||||
if (node->getActiveSocket() != NULL && node->getType() == getMyNodeType()) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
bool isMyJurisdiction = true;
|
||||
|
||||
|
||||
if (_serverJurisdictions) {
|
||||
// we need to get the jurisdiction for this
|
||||
// here we need to get the "pending packet" for this server
|
||||
if ((*_serverJurisdictions).find(nodeUUID) != (*_serverJurisdictions).end()) {
|
||||
const JurisdictionMap& map = (*_serverJurisdictions)[nodeUUID];
|
||||
isMyJurisdiction = (map.isMyJurisdiction(codeColorBuffer, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN);
|
||||
//qDebug() << "queueOctreeEditMessage() line:" << __LINE__ << " isMyJurisdiction=" << isMyJurisdiction;
|
||||
} else {
|
||||
isMyJurisdiction = false;
|
||||
//qDebug() << "queueOctreeEditMessage() line:" << __LINE__;
|
||||
}
|
||||
}
|
||||
if (isMyJurisdiction) {
|
||||
EditPacketBuffer& packetBuffer = _pendingEditPackets[nodeUUID];
|
||||
packetBuffer._nodeUUID = nodeUUID;
|
||||
|
||||
|
||||
//qDebug() << "queueOctreeEditMessage() line:" << __LINE__;
|
||||
|
||||
// If we're switching type, then we send the last one and start over
|
||||
if ((type != packetBuffer._currentType && packetBuffer._currentSize > 0) ||
|
||||
(packetBuffer._currentSize + length >= _maxPacketSize)) {
|
||||
releaseQueuedPacket(packetBuffer);
|
||||
initializePacket(packetBuffer, type);
|
||||
}
|
||||
|
||||
|
||||
// If the buffer is empty and not correctly initialized for our type...
|
||||
if (type != packetBuffer._currentType && packetBuffer._currentSize == 0) {
|
||||
initializePacket(packetBuffer, type);
|
||||
}
|
||||
|
||||
|
||||
// This is really the first time we know which server/node this particular edit message
|
||||
// is going to, so we couldn't adjust for clock skew till now. But here's our chance.
|
||||
// We call this virtual function that allows our specific type of EditPacketSender to
|
||||
// fixup the buffer for any clock skew
|
||||
if (node->getClockSkewUsec() != 0) {
|
||||
adjustEditPacketForClockSkew(codeColorBuffer, length, node->getClockSkewUsec());
|
||||
//qDebug() << "queueOctreeEditMessage() line:" << __LINE__;
|
||||
}
|
||||
|
||||
|
||||
//qDebug() << "queueOctreeEditMessage() line:" << __LINE__;
|
||||
|
||||
memcpy(&packetBuffer._currentBuffer[packetBuffer._currentSize], codeColorBuffer, length);
|
||||
packetBuffer._currentSize += length;
|
||||
}
|
||||
|
@ -265,7 +275,7 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PACKET_TYPE type, unsigned c
|
|||
}
|
||||
|
||||
void OctreeEditPacketSender::releaseQueuedMessages() {
|
||||
// if we don't yet have jurisdictions then we can't actually release messages yet because we don't
|
||||
// if we don't yet have jurisdictions then we can't actually release messages yet because we don't
|
||||
// know where to send them to. Instead, just remember this request and when we eventually get jurisdictions
|
||||
// call release again at that time.
|
||||
if (!serversExist()) {
|
||||
|
@ -273,12 +283,14 @@ void OctreeEditPacketSender::releaseQueuedMessages() {
|
|||
} else {
|
||||
for (std::map<QUuid, EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
|
||||
releaseQueuedPacket(i->second);
|
||||
//qDebug() << "releaseQueuedMessages() line:" << __LINE__;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) {
|
||||
if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PACKET_TYPE_UNKNOWN) {
|
||||
//qDebug() << "OctreeEditPacketSender::releaseQueuedPacket() line:" << __LINE__;
|
||||
queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize);
|
||||
}
|
||||
packetBuffer._currentSize = 0;
|
||||
|
|
|
@ -15,8 +15,17 @@
|
|||
|
||||
class ParticleNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
ParticleNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { };
|
||||
ParticleNodeData(Node* owningNode) :
|
||||
OctreeQueryNode(owningNode),
|
||||
_lastDeletedParticlesSentAt(0) { };
|
||||
|
||||
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
|
||||
|
||||
uint64_t getLastDeletedParticlesSentAt() const { return _lastDeletedParticlesSentAt; }
|
||||
void setLastDeletedParticlesSentAt(uint64_t sentAt) { _lastDeletedParticlesSentAt = sentAt; }
|
||||
|
||||
private:
|
||||
uint64_t _lastDeletedParticlesSentAt;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleNodeData__) */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QTimer>
|
||||
#include <ParticleTree.h>
|
||||
|
||||
#include "ParticleServer.h"
|
||||
|
@ -36,7 +37,10 @@ Octree* ParticleServer::createTree() {
|
|||
}
|
||||
|
||||
void ParticleServer::beforeRun() {
|
||||
// nothing special to do...
|
||||
QTimer* pruneDeletedParticlesTimer = new QTimer(this);
|
||||
connect(pruneDeletedParticlesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedParticles()));
|
||||
const int PRUNE_DELETED_PARTICLES_INTERVAL_MSECS = 1 * 1000; // once every second
|
||||
pruneDeletedParticlesTimer->start(PRUNE_DELETED_PARTICLES_INTERVAL_MSECS);
|
||||
}
|
||||
|
||||
void ParticleServer::particleCreated(const Particle& newParticle, Node* node) {
|
||||
|
@ -46,20 +50,89 @@ void ParticleServer::particleCreated(const Particle& newParticle, Node* node) {
|
|||
int numBytesPacketHeader = populateTypeAndVersion(outputBuffer, PACKET_TYPE_PARTICLE_ADD_RESPONSE);
|
||||
int packetLength = numBytesPacketHeader;
|
||||
copyAt += numBytesPacketHeader;
|
||||
|
||||
|
||||
// encode the creatorTokenID
|
||||
uint32_t creatorTokenID = newParticle.getCreatorTokenID();
|
||||
memcpy(copyAt, &creatorTokenID, sizeof(creatorTokenID));
|
||||
copyAt += sizeof(creatorTokenID);
|
||||
packetLength += sizeof(creatorTokenID);
|
||||
|
||||
|
||||
// encode the particle ID
|
||||
uint32_t particleID = newParticle.getID();
|
||||
memcpy(copyAt, &particleID, sizeof(particleID));
|
||||
copyAt += sizeof(particleID);
|
||||
packetLength += sizeof(particleID);
|
||||
|
||||
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) outputBuffer, packetLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
|
||||
|
||||
// ParticleServer will use the "special packets" to send list of recently deleted particles
|
||||
bool ParticleServer::hasSpecialPacketToSend(Node* node) {
|
||||
bool shouldSendDeletedParticles = false;
|
||||
|
||||
// check to see if any new particles have been added since we last sent to this node...
|
||||
ParticleNodeData* nodeData = static_cast<ParticleNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
uint64_t deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
|
||||
|
||||
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
|
||||
shouldSendDeletedParticles = tree->hasParticlesDeletedSince(deletedParticlesSentAt);
|
||||
}
|
||||
|
||||
return shouldSendDeletedParticles;
|
||||
}
|
||||
|
||||
int ParticleServer::sendSpecialPacket(Node* node) {
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
size_t packetLength = 0;
|
||||
|
||||
ParticleNodeData* nodeData = static_cast<ParticleNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
uint64_t deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
|
||||
uint64_t deletePacketSentAt = usecTimestampNow();
|
||||
|
||||
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles?
|
||||
while (hasMoreToSend) {
|
||||
hasMoreToSend = tree->encodeParticlesDeletedSince(deletedParticlesSentAt,
|
||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||
|
||||
//qDebug() << "sending PACKET_TYPE_PARTICLE_ERASE packetLength:" << packetLength;
|
||||
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) outputBuffer, packetLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
|
||||
nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt);
|
||||
}
|
||||
|
||||
// TODO: caller is expecting a packetLength, what if we send more than one packet??
|
||||
return packetLength;
|
||||
}
|
||||
|
||||
void ParticleServer::pruneDeletedParticles() {
|
||||
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
|
||||
if (tree->hasAnyDeletedParticles()) {
|
||||
|
||||
//qDebug() << "there are some deleted particles to consider...";
|
||||
uint64_t earliestLastDeletedParticlesSent = usecTimestampNow() + 1; // in the future
|
||||
foreach (const SharedNodePointer& otherNode, NodeList::getInstance()->getNodeHash()) {
|
||||
if (otherNode->getLinkedData()) {
|
||||
ParticleNodeData* nodeData = static_cast<ParticleNodeData*>(otherNode->getLinkedData());
|
||||
uint64_t nodeLastDeletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
|
||||
if (nodeLastDeletedParticlesSentAt < earliestLastDeletedParticlesSent) {
|
||||
earliestLastDeletedParticlesSent = nodeLastDeletedParticlesSentAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
//qDebug() << "earliestLastDeletedParticlesSent=" << earliestLastDeletedParticlesSent;
|
||||
tree->forgetParticlesDeletedBefore(earliestLastDeletedParticlesSent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
|
||||
/// Handles assignments of type ParticleServer - sending particles to various clients.
|
||||
class ParticleServer : public OctreeServer, public NewlyCreatedParticleHook {
|
||||
public:
|
||||
Q_OBJECT
|
||||
public:
|
||||
ParticleServer(const unsigned char* dataBuffer, int numBytes);
|
||||
~ParticleServer();
|
||||
|
||||
// Subclasses must implement these methods
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
|
||||
|
@ -30,12 +31,17 @@ public:
|
|||
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
|
||||
|
||||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend(Node* node);
|
||||
virtual int sendSpecialPacket(Node* node);
|
||||
|
||||
virtual void particleCreated(const Particle& newParticle, Node* senderNode);
|
||||
|
||||
public slots:
|
||||
void pruneDeletedParticles();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,57 +23,169 @@ class VoxelsScriptingInterface;
|
|||
class ParticlesScriptingInterface;
|
||||
class VoxelEditPacketSender;
|
||||
class ParticleEditPacketSender;
|
||||
|
||||
class ParticleProperties;
|
||||
class Particle;
|
||||
class ParticleTree;
|
||||
|
||||
const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
|
||||
const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
|
||||
const uint32_t UNKNOWN_PARTICLE_ID = 0xFFFFFFFF;
|
||||
|
||||
class ParticleDetail {
|
||||
public:
|
||||
uint32_t id;
|
||||
uint64_t lastEdited;
|
||||
glm::vec3 position;
|
||||
float radius;
|
||||
rgbColor color;
|
||||
glm::vec3 velocity;
|
||||
glm::vec3 gravity;
|
||||
float damping;
|
||||
bool inHand;
|
||||
QString updateScript;
|
||||
uint32_t creatorTokenID;
|
||||
};
|
||||
const uint16_t PACKET_CONTAINS_RADIUS = 1;
|
||||
const uint16_t PACKET_CONTAINS_POSITION = 2;
|
||||
const uint16_t PACKET_CONTAINS_COLOR = 4;
|
||||
const uint16_t PACKET_CONTAINS_VELOCITY = 8;
|
||||
const uint16_t PACKET_CONTAINS_GRAVITY = 16;
|
||||
const uint16_t PACKET_CONTAINS_DAMPING = 32;
|
||||
const uint16_t PACKET_CONTAINS_LIFETIME = 64;
|
||||
const uint16_t PACKET_CONTAINS_INHAND = 128;
|
||||
const uint16_t PACKET_CONTAINS_SCRIPT = 256;
|
||||
const uint16_t PACKET_CONTAINS_SHOULDDIE = 512;
|
||||
|
||||
const float DEFAULT_LIFETIME = 60.0f * 60.0f * 24.0f; // particles live for 1 day by default
|
||||
const float DEFAULT_DAMPING = 0.99f;
|
||||
const float DEFAULT_RADIUS = 0.1f / TREE_SCALE;
|
||||
const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
|
||||
const glm::vec3 DEFAULT_GRAVITY(0, (-9.8f / TREE_SCALE), 0);
|
||||
const QString DEFAULT_SCRIPT("");
|
||||
const bool IN_HAND = true; // it's in a hand
|
||||
const bool NOT_IN_HAND = !IN_HAND; // it's not in a hand
|
||||
|
||||
/// A collection of properties of a particle used in the scripting API. Translates between the actual properties of a particle
|
||||
/// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of
|
||||
/// particle properties via JavaScript hashes/QScriptValues
|
||||
/// all units for position, velocity, gravity, radius, etc are in meter units
|
||||
class ParticleProperties {
|
||||
public:
|
||||
ParticleProperties();
|
||||
|
||||
QScriptValue copyToScriptValue(QScriptEngine* engine) const;
|
||||
void copyFromScriptValue(const QScriptValue& object);
|
||||
|
||||
void copyToParticle(Particle& particle) const;
|
||||
void copyFromParticle(const Particle& particle);
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
xColor getColor() const { return _color; }
|
||||
float getRadius() const { return _radius; }
|
||||
const glm::vec3& getVelocity() const { return _velocity; }
|
||||
const glm::vec3& getGravity() const { return _gravity; }
|
||||
float getDamping() const { return _damping; }
|
||||
float getLifetime() const { return _lifetime; }
|
||||
const QString& getScript() const { return _script; }
|
||||
bool getInHand() const { return _inHand; }
|
||||
bool getShouldDie() const { return _shouldDie; }
|
||||
|
||||
uint64_t getLastEdited() const { return _lastEdited; }
|
||||
uint16_t getChangedBits() const;
|
||||
|
||||
/// set position in meter units
|
||||
void setPosition(const glm::vec3& value) { _position = value; _positionChanged = true; }
|
||||
|
||||
/// set velocity in meter units
|
||||
void setVelocity(const glm::vec3& value) { _velocity = value; _velocityChanged = true; }
|
||||
void setColor(const xColor& value) { _color = value; _colorChanged = true; }
|
||||
void setRadius(float value) { _radius = value; _radiusChanged = true; }
|
||||
|
||||
/// set gravity in meter units
|
||||
void setGravity(const glm::vec3& value) { _gravity = value; _gravityChanged = true; }
|
||||
void setInHand(bool inHand) { _inHand = inHand; _inHandChanged = true; }
|
||||
void setDamping(float value) { _damping = value; _dampingChanged = true; }
|
||||
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; }
|
||||
void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; }
|
||||
void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true; }
|
||||
|
||||
private:
|
||||
glm::vec3 _position;
|
||||
xColor _color;
|
||||
float _radius;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _gravity;
|
||||
float _damping;
|
||||
float _lifetime;
|
||||
QString _script;
|
||||
bool _inHand;
|
||||
bool _shouldDie;
|
||||
|
||||
uint64_t _lastEdited;
|
||||
bool _positionChanged;
|
||||
bool _colorChanged;
|
||||
bool _radiusChanged;
|
||||
bool _velocityChanged;
|
||||
bool _gravityChanged;
|
||||
bool _dampingChanged;
|
||||
bool _lifetimeChanged;
|
||||
bool _scriptChanged;
|
||||
bool _inHandChanged;
|
||||
bool _shouldDieChanged;
|
||||
bool _defaultSettings;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ParticleProperties);
|
||||
QScriptValue ParticlePropertiesToScriptValue(QScriptEngine* engine, const ParticleProperties& properties);
|
||||
void ParticlePropertiesFromScriptValue(const QScriptValue &object, ParticleProperties& properties);
|
||||
|
||||
|
||||
/// Abstract ID for editing particles. Used in Particle JS API - When particles are created in the JS api, they are given a
|
||||
/// local creatorTokenID, the actual id for the particle is not known until the server responds to the creator with the
|
||||
/// correct mapping. This class works with the scripting API an allows the developer to edit particles they created.
|
||||
class ParticleID {
|
||||
public:
|
||||
ParticleID() :
|
||||
id(NEW_PARTICLE), creatorTokenID(UNKNOWN_TOKEN), isKnownID(false) { };
|
||||
|
||||
ParticleID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) :
|
||||
id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { };
|
||||
|
||||
uint32_t id;
|
||||
uint32_t creatorTokenID;
|
||||
bool isKnownID;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ParticleID);
|
||||
QScriptValue ParticleIDtoScriptValue(QScriptEngine* engine, const ParticleID& properties);
|
||||
void ParticleIDfromScriptValue(const QScriptValue &object, ParticleID& properties);
|
||||
|
||||
|
||||
|
||||
/// Particle class - this is the actual particle class.
|
||||
class Particle {
|
||||
|
||||
public:
|
||||
Particle();
|
||||
|
||||
/// all position, velocity, gravity, radius units are in domain units (0.0 to 1.0)
|
||||
Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND,
|
||||
QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
|
||||
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME,
|
||||
bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
|
||||
|
||||
/// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer
|
||||
static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes);
|
||||
static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree);
|
||||
|
||||
virtual ~Particle();
|
||||
virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND,
|
||||
QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
|
||||
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME,
|
||||
bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
|
||||
|
||||
/// get position in domain scale units (0.0 - 1.0)
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
|
||||
const rgbColor& getColor() const { return _color; }
|
||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
||||
|
||||
/// get radius in domain scale units (0.0 - 1.0)
|
||||
float getRadius() const { return _radius; }
|
||||
float getMass() const { return _mass; }
|
||||
|
||||
/// get velocity in domain scale units (0.0 - 1.0)
|
||||
const glm::vec3& getVelocity() const { return _velocity; }
|
||||
|
||||
/// get gravity in domain scale units (0.0 - 1.0)
|
||||
const glm::vec3& getGravity() const { return _gravity; }
|
||||
|
||||
bool getInHand() const { return _inHand; }
|
||||
float getDamping() const { return _damping; }
|
||||
float getLifetime() const { return _lifetime; }
|
||||
ParticleProperties getProperties() const;
|
||||
|
||||
/// The last updated/simulated time of this particle from the time perspective of the authoritative server/source
|
||||
uint64_t getLastUpdated() const { return _lastUpdated; }
|
||||
|
@ -82,7 +194,7 @@ public:
|
|||
uint64_t getLastEdited() const { return _lastEdited; }
|
||||
|
||||
/// lifetime of the particle in seconds
|
||||
float getLifetime() const { return static_cast<float>(usecTimestampNow() - _created) / static_cast<float>(USECS_PER_SECOND); }
|
||||
float getAge() const { return static_cast<float>(usecTimestampNow() - _created) / static_cast<float>(USECS_PER_SECOND); }
|
||||
float getEditedAgo() const { return static_cast<float>(usecTimestampNow() - _lastEdited) / static_cast<float>(USECS_PER_SECOND); }
|
||||
uint32_t getID() const { return _id; }
|
||||
bool getShouldDie() const { return _shouldDie; }
|
||||
|
@ -90,7 +202,10 @@ public:
|
|||
uint32_t getCreatorTokenID() const { return _creatorTokenID; }
|
||||
bool isNewlyCreated() const { return _newlyCreated; }
|
||||
|
||||
/// set position in domain scale units (0.0 - 1.0)
|
||||
void setPosition(const glm::vec3& value) { _position = value; }
|
||||
|
||||
/// set velocity in domain scale units (0.0 - 1.0)
|
||||
void setVelocity(const glm::vec3& value) { _velocity = value; }
|
||||
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
||||
void setColor(const xColor& value) {
|
||||
|
@ -98,21 +213,26 @@ public:
|
|||
_color[GREEN_INDEX] = value.green;
|
||||
_color[BLUE_INDEX] = value.blue;
|
||||
}
|
||||
/// set radius in domain scale units (0.0 - 1.0)
|
||||
void setRadius(float value) { _radius = value; }
|
||||
void setMass(float value);
|
||||
|
||||
/// set gravity in domain scale units (0.0 - 1.0)
|
||||
void setGravity(const glm::vec3& value) { _gravity = value; }
|
||||
void setInHand(bool inHand) { _inHand = inHand; }
|
||||
void setDamping(float value) { _damping = value; }
|
||||
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; }
|
||||
void setLifetime(float value) { _lifetime = value; }
|
||||
void setScript(QString updateScript) { _script = updateScript; }
|
||||
void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; }
|
||||
void setProperties(const ParticleProperties& properties);
|
||||
|
||||
bool appendParticleData(OctreePacketData* packetData) const;
|
||||
int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
static int expectedBytes();
|
||||
static int expectedEditMessageBytes();
|
||||
|
||||
static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details,
|
||||
static bool encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID id, const ParticleProperties& details,
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut);
|
||||
|
||||
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
|
||||
|
@ -136,6 +256,11 @@ public:
|
|||
{ _particleEditSender = senderInterface; }
|
||||
|
||||
|
||||
// these methods allow you to create particles, and later edit them.
|
||||
static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID);
|
||||
static uint32_t getNextCreatorTokenID();
|
||||
static void handleAddParticleResponse(unsigned char* packetData , int packetLength);
|
||||
|
||||
protected:
|
||||
static VoxelEditPacketSender* _voxelEditSender;
|
||||
static ParticleEditPacketSender* _particleEditSender;
|
||||
|
@ -146,7 +271,7 @@ protected:
|
|||
static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color);
|
||||
static void xColorFromScriptValue(const QScriptValue &object, xColor& color);
|
||||
|
||||
void setLifetime(float lifetime);
|
||||
void setAge(float age);
|
||||
|
||||
glm::vec3 _position;
|
||||
rgbColor _color;
|
||||
|
@ -158,6 +283,7 @@ protected:
|
|||
bool _shouldDie;
|
||||
glm::vec3 _gravity;
|
||||
float _damping;
|
||||
float _lifetime;
|
||||
QString _script;
|
||||
bool _inHand;
|
||||
|
||||
|
@ -169,8 +295,14 @@ protected:
|
|||
|
||||
// this doesn't go on the wire, we send it as lifetime
|
||||
uint64_t _created;
|
||||
|
||||
// used by the static interfaces for creator token ids
|
||||
static uint32_t _nextCreatorTokenID;
|
||||
static std::map<uint32_t,uint32_t> _tokenIDsToIDs;
|
||||
};
|
||||
|
||||
/// Scriptable interface to a single Particle object. Used exclusively in the JavaScript API for interacting with single
|
||||
/// Particles.
|
||||
class ParticleScriptObject : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -182,23 +314,44 @@ public:
|
|||
|
||||
public slots:
|
||||
unsigned int getID() const { return _particle->getID(); }
|
||||
glm::vec3 getPosition() const { return _particle->getPosition(); }
|
||||
glm::vec3 getVelocity() const { return _particle->getVelocity(); }
|
||||
xColor getColor() const { return _particle->getXColor(); }
|
||||
glm::vec3 getGravity() const { return _particle->getGravity(); }
|
||||
float getDamping() const { return _particle->getDamping(); }
|
||||
float getRadius() const { return _particle->getRadius(); }
|
||||
bool getShouldDie() { return _particle->getShouldDie(); }
|
||||
float getLifetime() const { return _particle->getLifetime(); }
|
||||
|
||||
/// get position in meter units
|
||||
glm::vec3 getPosition() const { return _particle->getPosition() * (float)TREE_SCALE; }
|
||||
|
||||
void setPosition(glm::vec3 value) { _particle->setPosition(value); }
|
||||
void setVelocity(glm::vec3 value) { _particle->setVelocity(value); }
|
||||
void setGravity(glm::vec3 value) { _particle->setGravity(value); }
|
||||
/// get velocity in meter units
|
||||
glm::vec3 getVelocity() const { return _particle->getVelocity() * (float)TREE_SCALE; }
|
||||
xColor getColor() const { return _particle->getXColor(); }
|
||||
|
||||
/// get gravity in meter units
|
||||
glm::vec3 getGravity() const { return _particle->getGravity() * (float)TREE_SCALE; }
|
||||
|
||||
float getDamping() const { return _particle->getDamping(); }
|
||||
|
||||
/// get radius in meter units
|
||||
float getRadius() const { return _particle->getRadius() * (float)TREE_SCALE; }
|
||||
bool getShouldDie() { return _particle->getShouldDie(); }
|
||||
float getAge() const { return _particle->getAge(); }
|
||||
float getLifetime() const { return _particle->getLifetime(); }
|
||||
ParticleProperties getProperties() const { return _particle->getProperties(); }
|
||||
|
||||
/// set position in meter units
|
||||
void setPosition(glm::vec3 value) { _particle->setPosition(value / (float)TREE_SCALE); }
|
||||
|
||||
/// set velocity in meter units
|
||||
void setVelocity(glm::vec3 value) { _particle->setVelocity(value / (float)TREE_SCALE); }
|
||||
|
||||
/// set gravity in meter units
|
||||
void setGravity(glm::vec3 value) { _particle->setGravity(value / (float)TREE_SCALE); }
|
||||
|
||||
void setDamping(float value) { _particle->setDamping(value); }
|
||||
void setColor(xColor value) { _particle->setColor(value); }
|
||||
void setRadius(float value) { _particle->setRadius(value); }
|
||||
|
||||
/// set radius in meter units
|
||||
void setRadius(float value) { _particle->setRadius(value / (float)TREE_SCALE); }
|
||||
void setShouldDie(bool value) { _particle->setShouldDie(value); }
|
||||
void setScript(const QString& script) { _particle->setScript(script); }
|
||||
void setLifetime(float value) const { return _particle->setLifetime(value); }
|
||||
void setProperties(const ParticleProperties& properties) { return _particle->setProperties(properties); }
|
||||
|
||||
signals:
|
||||
void update();
|
||||
|
@ -210,4 +363,5 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__hifi__Particle__) */
|
||||
|
|
|
@ -120,14 +120,16 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
|||
particleA->setVelocity(particleA->getVelocity() - axialVelocity * (2.0f * massB / totalMass));
|
||||
|
||||
ParticleEditHandle particleEditHandle(_packetSender, _particles, particleA->getID());
|
||||
particleEditHandle.updateParticle(particleA->getPosition(), particleA->getRadius(), particleA->getXColor(), particleA->getVelocity(),
|
||||
particleA->getGravity(), particleA->getDamping(), particleA->getInHand(), particleA->getScript());
|
||||
particleEditHandle.updateParticle(particleA->getPosition(), particleA->getRadius(), particleA->getXColor(),
|
||||
particleA->getVelocity(), particleA->getGravity(), particleA->getDamping(), particleA->getLifetime(),
|
||||
particleA->getInHand(), particleA->getScript());
|
||||
|
||||
particleB->setVelocity(particleB->getVelocity() + axialVelocity * (2.0f * massA / totalMass));
|
||||
|
||||
ParticleEditHandle penetratedparticleEditHandle(_packetSender, _particles, particleB->getID());
|
||||
penetratedparticleEditHandle.updateParticle(particleB->getPosition(), particleB->getRadius(), particleB->getXColor(), particleB->getVelocity(),
|
||||
particleB->getGravity(), particleB->getDamping(), particleB->getInHand(), particleB->getScript());
|
||||
penetratedparticleEditHandle.updateParticle(particleB->getPosition(), particleB->getRadius(),
|
||||
particleB->getXColor(), particleB->getVelocity(), particleB->getGravity(), particleB->getDamping(),
|
||||
particleB->getLifetime(), particleB->getInHand(), particleB->getScript());
|
||||
|
||||
penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particleA, penetration, COLLISION_FREQUENCY);
|
||||
|
@ -137,7 +139,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
|||
|
||||
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame
|
||||
const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second
|
||||
const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
|
||||
const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
||||
|
||||
|
@ -164,7 +166,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
// only collide when particle and collision point are moving toward each other
|
||||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
|
@ -173,7 +175,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
if (attenuationFactor < 1.f) {
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
|
@ -196,7 +198,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
|
@ -205,7 +207,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
if (attenuationFactor < 1.f) {
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
|
@ -213,7 +215,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
|
||||
applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +258,8 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elast
|
|||
|
||||
ParticleEditHandle particleEditHandle(_packetSender, _particles, particle->getID());
|
||||
particleEditHandle.updateParticle(position, particle->getRadius(), particle->getXColor(), velocity,
|
||||
particle->getGravity(), particle->getDamping(), particle->getInHand(), particle->getScript());
|
||||
particle->getGravity(), particle->getDamping(), particle->getLifetime(),
|
||||
particle->getInHand(), particle->getScript());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,24 +13,22 @@
|
|||
#include "ParticleTree.h"
|
||||
|
||||
std::map<uint32_t,ParticleEditHandle*> ParticleEditHandle::_allHandles;
|
||||
uint32_t ParticleEditHandle::_nextCreatorTokenID = 0;
|
||||
|
||||
ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree, uint32_t id) {
|
||||
if (id == NEW_PARTICLE) {
|
||||
_creatorTokenID = _nextCreatorTokenID;
|
||||
_nextCreatorTokenID++;
|
||||
_creatorTokenID = Particle::getNextCreatorTokenID();
|
||||
_id = NEW_PARTICLE;
|
||||
_isKnownID = false;
|
||||
_allHandles[_creatorTokenID] = this;
|
||||
} else {
|
||||
_creatorTokenID = UNKNOWN_TOKEN;
|
||||
_creatorTokenID = UNKNOWN_TOKEN;
|
||||
_id = id;
|
||||
_isKnownID = true;
|
||||
// don't add to _allHandles because we already know it...
|
||||
}
|
||||
_packetSender = packetSender;
|
||||
_localTree = localTree;
|
||||
|
||||
|
||||
}
|
||||
|
||||
ParticleEditHandle::~ParticleEditHandle() {
|
||||
|
@ -40,54 +38,59 @@ ParticleEditHandle::~ParticleEditHandle() {
|
|||
}
|
||||
}
|
||||
|
||||
void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, bool inHand, QString updateScript) {
|
||||
void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript) {
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
/****
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail addParticleDetail = { NEW_PARTICLE, now,
|
||||
position, radius, {color.red, color.green, color.blue },
|
||||
velocity, gravity, damping, inHand, updateScript, _creatorTokenID };
|
||||
|
||||
position, radius, {color.red, color.green, color.blue },
|
||||
velocity, gravity, damping, lifetime, inHand, updateScript, _creatorTokenID };
|
||||
|
||||
// queue the packet
|
||||
_packetSender->queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &addParticleDetail);
|
||||
|
||||
_packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &addParticleDetail);
|
||||
|
||||
// release them
|
||||
_packetSender->releaseQueuedMessages();
|
||||
|
||||
|
||||
// if we have a local tree, also update it...
|
||||
if (_localTree) {
|
||||
// we can't really do this here, because if we create a particle locally, we'll get a ghost particle
|
||||
// because we can't really handle updating/deleting it locally
|
||||
}
|
||||
****/
|
||||
|
||||
}
|
||||
|
||||
bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, bool inHand, QString updateScript) {
|
||||
bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript) {
|
||||
|
||||
if (!isKnownID()) {
|
||||
return false; // not allowed until we know the id
|
||||
}
|
||||
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
/****
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail newParticleDetail = { _id, now,
|
||||
position, radius, {color.red, color.green, color.blue },
|
||||
velocity, gravity, damping, inHand, updateScript, _creatorTokenID };
|
||||
position, radius, {color.red, color.green, color.blue },
|
||||
velocity, gravity, damping, lifetime, inHand, updateScript, _creatorTokenID };
|
||||
|
||||
// queue the packet
|
||||
_packetSender->queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &newParticleDetail);
|
||||
|
||||
|
||||
// release them
|
||||
_packetSender->releaseQueuedMessages();
|
||||
|
||||
// if we have a local tree, also update it...
|
||||
if (_localTree) {
|
||||
rgbColor rcolor = {color.red, color.green, color.blue };
|
||||
Particle tempParticle(position, radius, rcolor, velocity, gravity, damping, inHand, updateScript, _id);
|
||||
Particle tempParticle(position, radius, rcolor, velocity, gravity, damping, lifetime, inHand, updateScript, _id);
|
||||
_localTree->storeParticle(tempParticle);
|
||||
}
|
||||
|
||||
***/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,7 +98,7 @@ void ParticleEditHandle::handleAddResponse(unsigned char* packetData , int packe
|
|||
unsigned char* dataAt = packetData;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
|
||||
uint32_t creatorTokenID;
|
||||
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
||||
dataAt += sizeof(creatorTokenID);
|
||||
|
|
|
@ -34,18 +34,17 @@ public:
|
|||
|
||||
bool isKnownID() const { return _isKnownID; }
|
||||
|
||||
void createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, bool inHand, QString updateScript);
|
||||
void createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript);
|
||||
|
||||
bool updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript);
|
||||
|
||||
bool updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity,
|
||||
glm::vec3 gravity, float damping, bool inHand, QString updateScript);
|
||||
|
||||
static void handleAddResponse(unsigned char* packetData , int packetLength);
|
||||
private:
|
||||
uint32_t _creatorTokenID;
|
||||
uint32_t _id;
|
||||
bool _isKnownID;
|
||||
static uint32_t _nextCreatorTokenID;
|
||||
static std::map<uint32_t,ParticleEditHandle*> _allHandles;
|
||||
ParticleEditPacketSender* _packetSender;
|
||||
ParticleTree* _localTree;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "Particle.h"
|
||||
|
||||
|
||||
void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, const ParticleDetail& detail) {
|
||||
void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties) {
|
||||
// allows app to disable sending if for example voxels have been disabled
|
||||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
|
@ -26,7 +26,7 @@ void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, const P
|
|||
int sizeOut = 0;
|
||||
|
||||
// This encodes the voxel edit message into a buffer...
|
||||
if (Particle::encodeParticleEditMessageDetails(type, 1, &detail, &bufferOut[0], _maxPacketSize, sizeOut)){
|
||||
if (Particle::encodeParticleEditMessageDetails(type, particleID, properties, &bufferOut[0], _maxPacketSize, sizeOut)){
|
||||
// If we don't have voxel jurisdictions, then we will simply queue up these packets and wait till we have
|
||||
// jurisdictions for processing
|
||||
if (!serversExist()) {
|
||||
|
@ -37,24 +37,22 @@ void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, const P
|
|||
}
|
||||
}
|
||||
|
||||
void ParticleEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
|
||||
void ParticleEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
|
||||
Particle::adjustEditPacketForClockSkew(codeColorBuffer, length, clockSkew);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParticleEditPacketSender::queueParticleEditMessages(PACKET_TYPE type, int numberOfDetails, ParticleDetail* details) {
|
||||
void ParticleEditPacketSender::queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties) {
|
||||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfDetails; i++) {
|
||||
// use MAX_PACKET_SIZE since it's static and guaranteed to be larger than _maxPacketSize
|
||||
static unsigned char bufferOut[MAX_PACKET_SIZE];
|
||||
int sizeOut = 0;
|
||||
|
||||
if (Particle::encodeParticleEditMessageDetails(type, 1, &details[i], &bufferOut[0], _maxPacketSize, sizeOut)) {
|
||||
queueOctreeEditMessage(type, bufferOut, sizeOut);
|
||||
}
|
||||
}
|
||||
// use MAX_PACKET_SIZE since it's static and guaranteed to be larger than _maxPacketSize
|
||||
static unsigned char bufferOut[MAX_PACKET_SIZE];
|
||||
int sizeOut = 0;
|
||||
|
||||
if (Particle::encodeParticleEditMessageDetails(type, particleID, properties, &bufferOut[0], _maxPacketSize, sizeOut)) {
|
||||
queueOctreeEditMessage(type, bufferOut, sizeOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,14 @@ class ParticleEditPacketSender : public OctreeEditPacketSender {
|
|||
public:
|
||||
ParticleEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { }
|
||||
~ParticleEditPacketSender() { }
|
||||
|
||||
/// Send particle add message immediately
|
||||
void sendEditParticleMessage(PACKET_TYPE type, const ParticleDetail& detail);
|
||||
|
||||
/// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines
|
||||
/// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in
|
||||
/// Send particle add message immediately
|
||||
void sendEditParticleMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
/// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines
|
||||
/// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in
|
||||
/// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
|
||||
void queueParticleEditMessages(PACKET_TYPE type, int numberOfDetails, ParticleDetail* details);
|
||||
void queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
// My server type is the particle server
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
|
||||
|
|
|
@ -15,7 +15,7 @@ ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) {
|
|||
}
|
||||
|
||||
ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) const {
|
||||
ParticleTreeElement* newElement = new ParticleTreeElement(octalCode);
|
||||
ParticleTreeElement* newElement = new ParticleTreeElement(octalCode);
|
||||
return newElement;
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,55 @@ bool ParticleTree::handlesEditPacketType(PACKET_TYPE packetType) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
class FindAndDeleteParticlesArgs {
|
||||
public:
|
||||
QList<uint32_t> _idsToDelete;
|
||||
};
|
||||
|
||||
bool ParticleTree::findAndDeleteOperation(OctreeElement* element, void* extraData) {
|
||||
//qDebug() << "findAndDeleteOperation()";
|
||||
|
||||
FindAndDeleteParticlesArgs* args = static_cast< FindAndDeleteParticlesArgs*>(extraData);
|
||||
|
||||
// if we've found and deleted all our target particles, then we can stop looking
|
||||
if (args->_idsToDelete.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
//qDebug() << "findAndDeleteOperation() args->_idsToDelete.size():" << args->_idsToDelete.size();
|
||||
|
||||
for (QList<uint32_t>::iterator it = args->_idsToDelete.begin(); it != args->_idsToDelete.end(); it++) {
|
||||
uint32_t particleID = *it;
|
||||
//qDebug() << "findAndDeleteOperation() particleID:" << particleID;
|
||||
|
||||
if (particleTreeElement->removeParticleWithID(particleID)) {
|
||||
// if the particle was in this element, then remove it from our search list.
|
||||
//qDebug() << "findAndDeleteOperation() it = args->_idsToDelete.erase(it)";
|
||||
it = args->_idsToDelete.erase(it);
|
||||
}
|
||||
|
||||
if (it == args->_idsToDelete.end()) {
|
||||
//qDebug() << "findAndDeleteOperation() breaking";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we've found and deleted all our target particles, then we can stop looking
|
||||
if (args->_idsToDelete.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class FindAndUpdateParticleArgs {
|
||||
public:
|
||||
const Particle& searchParticle;
|
||||
bool found;
|
||||
};
|
||||
|
||||
|
||||
bool ParticleTree::findAndUpdateOperation(OctreeElement* element, void* extraData) {
|
||||
FindAndUpdateParticleArgs* args = static_cast<FindAndUpdateParticleArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
@ -51,15 +93,15 @@ void ParticleTree::storeParticle(const Particle& particle, Node* senderNode) {
|
|||
// First, look for the existing particle in the tree..
|
||||
FindAndUpdateParticleArgs args = { particle, false };
|
||||
recurseTreeWithOperation(findAndUpdateOperation, &args);
|
||||
|
||||
|
||||
// if we didn't find it in the tree, then store it...
|
||||
if (!args.found) {
|
||||
glm::vec3 position = particle.getPosition();
|
||||
float size = particle.getRadius();
|
||||
float size = std::max(MINIMUM_PARTICLE_ELEMENT_SIZE, particle.getRadius());
|
||||
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
|
||||
|
||||
element->storeParticle(particle, senderNode);
|
||||
}
|
||||
}
|
||||
// what else do we need to do here to get reaveraging to work
|
||||
_isDirty = true;
|
||||
}
|
||||
|
@ -72,25 +114,25 @@ public:
|
|||
const Particle* closestParticle;
|
||||
float closestParticleDistance;
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool ParticleTree::findNearPointOperation(OctreeElement* element, void* extraData) {
|
||||
FindNearPointArgs* args = static_cast<FindNearPointArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
glm::vec3 penetration;
|
||||
bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position,
|
||||
bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position,
|
||||
args->targetRadius, penetration);
|
||||
|
||||
// If this particleTreeElement contains the point, then search it...
|
||||
if (sphereIntersection) {
|
||||
const Particle* thisClosestParticle = particleTreeElement->getClosestParticle(args->position);
|
||||
|
||||
|
||||
// we may have gotten NULL back, meaning no particle was available
|
||||
if (thisClosestParticle) {
|
||||
glm::vec3 particlePosition = thisClosestParticle->getPosition();
|
||||
float distanceFromPointToParticle = glm::distance(particlePosition, args->position);
|
||||
|
||||
|
||||
// If we're within our target radius
|
||||
if (distanceFromPointToParticle <= args->targetRadius) {
|
||||
// we are closer than anything else we've found
|
||||
|
@ -101,11 +143,11 @@ bool ParticleTree::findNearPointOperation(OctreeElement* element, void* extraDat
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we should be able to optimize this...
|
||||
return true; // keep searching in case children have closer particles
|
||||
}
|
||||
|
||||
|
||||
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
|
||||
return false;
|
||||
}
|
||||
|
@ -124,9 +166,11 @@ public:
|
|||
bool found;
|
||||
const Particle* foundParticle;
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool ParticleTree::findByIDOperation(OctreeElement* element, void* extraData) {
|
||||
//qDebug() << "ParticleTree::findByIDOperation()....";
|
||||
|
||||
FindByIDArgs* args = static_cast<FindByIDArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
|
@ -134,7 +178,7 @@ bool ParticleTree::findByIDOperation(OctreeElement* element, void* extraData) {
|
|||
if (args->found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// as the tree element if it has this particle
|
||||
const Particle* foundParticle = particleTreeElement->getParticleWithID(args->id);
|
||||
if (foundParticle) {
|
||||
|
@ -142,17 +186,23 @@ bool ParticleTree::findByIDOperation(OctreeElement* element, void* extraData) {
|
|||
args->found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// keep looking
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const Particle* ParticleTree::findParticleByID(uint32_t id) {
|
||||
const Particle* ParticleTree::findParticleByID(uint32_t id, bool alreadyLocked) {
|
||||
FindByIDArgs args = { id, false, NULL };
|
||||
lockForRead();
|
||||
if (!alreadyLocked) {
|
||||
//qDebug() << "ParticleTree::findParticleByID().... about to call lockForRead()....";
|
||||
lockForRead();
|
||||
//qDebug() << "ParticleTree::findParticleByID().... after call lockForRead()....";
|
||||
}
|
||||
recurseTreeWithOperation(findByIDOperation, &args);
|
||||
unlock();
|
||||
if (!alreadyLocked) {
|
||||
unlock();
|
||||
}
|
||||
return args.foundParticle;
|
||||
}
|
||||
|
||||
|
@ -164,13 +214,17 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
|
|||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: {
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes);
|
||||
//qDebug() << " got PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this);
|
||||
storeParticle(newParticle, senderNode);
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
}
|
||||
//qDebug() << " DONE... PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
} break;
|
||||
|
||||
|
||||
// TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages
|
||||
// instead of using PACKET_TYPE_PARTICLE_ADD_OR_EDIT messages to delete particles
|
||||
case PACKET_TYPE_PARTICLE_ERASE: {
|
||||
processedBytes = 0;
|
||||
} break;
|
||||
|
@ -227,7 +281,7 @@ void ParticleTree::update() {
|
|||
|
||||
ParticleTreeUpdateArgs args = { };
|
||||
recurseTreeWithOperation(updateOperation, &args);
|
||||
|
||||
|
||||
// now add back any of the particles that moved elements....
|
||||
int movingParticles = args._movingParticles.size();
|
||||
for (int i = 0; i < movingParticles; i++) {
|
||||
|
@ -235,14 +289,160 @@ void ParticleTree::update() {
|
|||
|
||||
// if the particle is still inside our total bounds, then re-add it
|
||||
AABox treeBounds = getRoot()->getAABox();
|
||||
|
||||
|
||||
if (!shouldDie && treeBounds.contains(args._movingParticles[i].getPosition())) {
|
||||
storeParticle(args._movingParticles[i]);
|
||||
} else {
|
||||
uint32_t particleID = args._movingParticles[i].getID();
|
||||
uint64_t deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedParticlesLock.lockForWrite();
|
||||
_recentlyDeletedParticleIDs.insert(deletedAt, particleID);
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// prune the tree...
|
||||
recurseTreeWithOperation(pruneOperation, NULL);
|
||||
}
|
||||
|
||||
|
||||
bool ParticleTree::hasParticlesDeletedSince(uint64_t sinceTime) {
|
||||
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
||||
bool hasSomethingNewer = false;
|
||||
|
||||
_recentlyDeletedParticlesLock.lockForRead();
|
||||
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
|
||||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
//qDebug() << "considering... time/key:" << iterator.key();
|
||||
if (iterator.key() > sinceTime) {
|
||||
//qDebug() << "YES newer... time/key:" << iterator.key();
|
||||
hasSomethingNewer = true;
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
return hasSomethingNewer;
|
||||
}
|
||||
|
||||
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
||||
bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned char* outputBuffer, size_t maxLength,
|
||||
size_t& outputLength) {
|
||||
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
size_t numBytesPacketHeader = populateTypeAndVersion(outputBuffer, PACKET_TYPE_PARTICLE_ERASE);
|
||||
copyAt += numBytesPacketHeader;
|
||||
outputLength = numBytesPacketHeader;
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
unsigned char* numberOfIDsAt = copyAt;
|
||||
memcpy(copyAt, &numberOfIds, sizeof(numberOfIds));
|
||||
copyAt += sizeof(numberOfIds);
|
||||
outputLength += sizeof(numberOfIds);
|
||||
|
||||
// we keep a multi map of particle IDs to timestamps, we only want to include the particle IDs that have been
|
||||
// deleted since we last sent to this node
|
||||
_recentlyDeletedParticlesLock.lockForRead();
|
||||
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
|
||||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
QList<uint32_t> values = _recentlyDeletedParticleIDs.values(iterator.key());
|
||||
for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
|
||||
//qDebug() << "considering... " << iterator.key() << ": " << values.at(valueItem);
|
||||
|
||||
// if the timestamp is more recent then out last sent time, include it
|
||||
if (iterator.key() > sinceTime) {
|
||||
//qDebug() << "including... " << iterator.key() << ": " << values.at(valueItem);
|
||||
uint32_t particleID = values.at(valueItem);
|
||||
memcpy(copyAt, &particleID, sizeof(particleID));
|
||||
copyAt += sizeof(particleID);
|
||||
outputLength += sizeof(particleID);
|
||||
numberOfIds++;
|
||||
|
||||
// check to make sure we have room for one more id...
|
||||
if (outputLength + sizeof(uint32_t) > maxLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check to make sure we have room for one more id...
|
||||
if (outputLength + sizeof(uint32_t) > maxLength) {
|
||||
|
||||
// let our caller know how far we got
|
||||
sinceTime = iterator.key();
|
||||
break;
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
|
||||
// if we got to the end, then we're done sending
|
||||
if (iterator == _recentlyDeletedParticleIDs.constEnd()) {
|
||||
hasMoreToSend = false;
|
||||
}
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
|
||||
// replace the correct count for ids included
|
||||
memcpy(numberOfIDsAt, &numberOfIds, sizeof(numberOfIds));
|
||||
|
||||
return hasMoreToSend;
|
||||
}
|
||||
|
||||
// called by the server when it knows all nodes have been sent deleted packets
|
||||
void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
|
||||
_recentlyDeletedParticlesLock.lockForWrite();
|
||||
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
|
||||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
//qDebug() << "considering... time/key:" << iterator.key();
|
||||
if (iterator.key() <= sinceTime) {
|
||||
//qDebug() << "YES older... time/key:" << iterator.key();
|
||||
_recentlyDeletedParticleIDs.remove(iterator.key());
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr,
|
||||
Node* sourceNode) {
|
||||
//qDebug() << "ParticleTree::processEraseMessage()...";
|
||||
|
||||
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
|
||||
const unsigned char* dataAt = packetData;
|
||||
size_t packetLength = dataByteArray.size();
|
||||
|
||||
size_t numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
size_t processedBytes = numBytesPacketHeader;
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
memcpy(&numberOfIds, dataAt, sizeof(numberOfIds));
|
||||
dataAt += sizeof(numberOfIds);
|
||||
processedBytes += sizeof(numberOfIds);
|
||||
|
||||
//qDebug() << "got erase message for numberOfIds:" << numberOfIds;
|
||||
|
||||
if (numberOfIds > 0) {
|
||||
FindAndDeleteParticlesArgs args;
|
||||
|
||||
for (size_t i = 0; i < numberOfIds; i++) {
|
||||
if (processedBytes + sizeof(uint32_t) > packetLength) {
|
||||
//qDebug() << "bailing?? processedBytes:" << processedBytes << " packetLength:" << packetLength;
|
||||
break; // bail to prevent buffer overflow
|
||||
}
|
||||
|
||||
uint32_t particleID = 0; // placeholder for now
|
||||
memcpy(&particleID, dataAt, sizeof(particleID));
|
||||
dataAt += sizeof(particleID);
|
||||
processedBytes += sizeof(particleID);
|
||||
|
||||
//qDebug() << "got erase message for particleID:" << particleID;
|
||||
args._idsToDelete.push_back(particleID);
|
||||
}
|
||||
|
||||
// calling recurse to actually delete the particles
|
||||
//qDebug() << "calling recurse to actually delete the particles";
|
||||
recurseTreeWithOperation(findAndDeleteOperation, &args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@ class ParticleTree : public Octree {
|
|||
Q_OBJECT
|
||||
public:
|
||||
ParticleTree(bool shouldReaverage = false);
|
||||
|
||||
|
||||
/// Implements our type specific root element factory
|
||||
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL) const;
|
||||
|
||||
|
||||
/// Type safe version of getRoot()
|
||||
ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; }
|
||||
|
||||
|
||||
|
||||
|
||||
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
|
||||
// own definition. Implement these to allow your octree based server to support editing
|
||||
virtual bool getWantSVOfileVersions() const { return true; }
|
||||
|
@ -37,15 +37,22 @@ public:
|
|||
virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
|
||||
unsigned char* editData, int maxLength, Node* senderNode);
|
||||
|
||||
virtual void update();
|
||||
virtual void update();
|
||||
|
||||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
|
||||
const Particle* findParticleByID(uint32_t id);
|
||||
const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
|
||||
|
||||
void addNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
|
||||
bool hasAnyDeletedParticles() const { return _recentlyDeletedParticleIDs.size() > 0; }
|
||||
bool hasParticlesDeletedSince(uint64_t sinceTime);
|
||||
bool encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
|
||||
void forgetParticlesDeletedBefore(uint64_t sinceTime);
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
private:
|
||||
|
||||
static bool updateOperation(OctreeElement* element, void* extraData);
|
||||
|
@ -53,11 +60,16 @@ private:
|
|||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool pruneOperation(OctreeElement* element, void* extraData);
|
||||
static bool findByIDOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
void notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode);
|
||||
|
||||
|
||||
QReadWriteLock _newlyCreatedHooksLock;
|
||||
std::vector<NewlyCreatedParticleHook*> _newlyCreatedHooks;
|
||||
|
||||
|
||||
QReadWriteLock _recentlyDeletedParticlesLock;
|
||||
QMultiMap<uint64_t, uint32_t> _recentlyDeletedParticleIDs;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleTree__) */
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "ParticleTree.h"
|
||||
#include "ParticleTreeElement.h"
|
||||
|
||||
ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement(), _particles(NULL) {
|
||||
ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement(), _particles(NULL) {
|
||||
init(octalCode);
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@ ParticleTreeElement::~ParticleTreeElement() {
|
|||
}
|
||||
|
||||
// This will be called primarily on addChildAt(), which means we're adding a child of our
|
||||
// own type to our own tree. This means we should initialize that child with any tree and type
|
||||
// own type to our own tree. This means we should initialize that child with any tree and type
|
||||
// specific settings that our children must have. One example is out VoxelSystem, which
|
||||
// we know must match ours.
|
||||
OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) const {
|
||||
|
@ -37,8 +37,8 @@ void ParticleTreeElement::init(unsigned char* octalCode) {
|
|||
_voxelMemoryUsage += sizeof(ParticleTreeElement);
|
||||
}
|
||||
|
||||
ParticleTreeElement* ParticleTreeElement::addChildAtIndex(int index) {
|
||||
ParticleTreeElement* newElement = (ParticleTreeElement*)OctreeElement::addChildAtIndex(index);
|
||||
ParticleTreeElement* ParticleTreeElement::addChildAtIndex(int index) {
|
||||
ParticleTreeElement* newElement = (ParticleTreeElement*)OctreeElement::addChildAtIndex(index);
|
||||
newElement->setTree(_myTree);
|
||||
return newElement;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ void ParticleTreeElement::update(ParticleTreeUpdateArgs& args) {
|
|||
// into the arguments moving particles. These will be added back or deleted completely
|
||||
if (particle.getShouldDie() || !_box.contains(particle.getPosition())) {
|
||||
args._movingParticles.push_back(particle);
|
||||
|
||||
|
||||
// erase this particle
|
||||
particleItr = _particles->erase(particleItr);
|
||||
} else {
|
||||
|
@ -90,16 +90,15 @@ void ParticleTreeElement::update(ParticleTreeUpdateArgs& args) {
|
|||
// roaming piles of particles.
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||
bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const {
|
||||
|
||||
QList<Particle>::iterator particleItr = _particles->begin();
|
||||
QList<Particle>::const_iterator particleEnd = _particles->end();
|
||||
while(particleItr != particleEnd) {
|
||||
Particle& particle = (*particleItr);
|
||||
glm::vec3 particleCenter = particle.getPosition();
|
||||
float particleRadius = particle.getRadius();
|
||||
|
||||
|
||||
// don't penetrate yourself
|
||||
if (particleCenter == center && particleRadius == radius) {
|
||||
return false;
|
||||
|
@ -115,7 +114,7 @@ bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float r
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (findSphereSpherePenetration(center, radius, particleCenter, particleRadius, penetration)) {
|
||||
// return true on first valid particle penetration
|
||||
*penetratedObject = (void*)(&particle);
|
||||
|
@ -135,12 +134,12 @@ bool ParticleTreeElement::containsParticle(const Particle& particle) const {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
||||
// NOTE: this method must first lookup the particle by ID, hence it is O(N)
|
||||
// and "particle is not found" is worst-case (full N) but maybe we don't care?
|
||||
// NOTE: this method must first lookup the particle by ID, hence it is O(N)
|
||||
// and "particle is not found" is worst-case (full N) but maybe we don't care?
|
||||
// (guaranteed that num particles per elemen is small?)
|
||||
const bool wantDebug = false;
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
|
@ -150,27 +149,27 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
|||
int difference = thisParticle.getLastUpdated() - particle.getLastUpdated();
|
||||
bool changedOnServer = thisParticle.getLastEdited() < particle.getLastEdited();
|
||||
bool localOlder = thisParticle.getLastUpdated() < particle.getLastUpdated();
|
||||
if (changedOnServer || localOlder) {
|
||||
if (changedOnServer || localOlder) {
|
||||
if (wantDebug) {
|
||||
printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
|
||||
printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
|
||||
particle.getID(), (changedOnServer ? "CHANGED" : "same"),
|
||||
(localOlder ? "OLDER" : "NEWER"),
|
||||
(localOlder ? "OLDER" : "NEWER"),
|
||||
difference, debug::valueOf(particle.isNewlyCreated()) );
|
||||
}
|
||||
thisParticle.copyChangedProperties(particle);
|
||||
} else {
|
||||
if (wantDebug) {
|
||||
printf(">>> IGNORING SERVER!!! Would've caused jutter! <<< "
|
||||
"local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
|
||||
"local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
|
||||
particle.getID(), (changedOnServer ? "CHANGED" : "same"),
|
||||
(localOlder ? "OLDER" : "NEWER"),
|
||||
(localOlder ? "OLDER" : "NEWER"),
|
||||
difference, debug::valueOf(particle.isNewlyCreated()) );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const {
|
||||
|
@ -183,7 +182,7 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons
|
|||
closestParticle = &(*_particles)[i];
|
||||
}
|
||||
}
|
||||
return closestParticle;
|
||||
return closestParticle;
|
||||
}
|
||||
|
||||
const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
||||
|
@ -196,26 +195,40 @@ const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
|||
break;
|
||||
}
|
||||
}
|
||||
return foundParticle;
|
||||
return foundParticle;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::removeParticleWithID(uint32_t id) {
|
||||
bool foundParticle = false;
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if ((*_particles)[i].getID() == id) {
|
||||
foundParticle = true;
|
||||
_particles->removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return foundParticle;
|
||||
}
|
||||
|
||||
|
||||
int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
|
||||
|
||||
int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
|
||||
const unsigned char* dataAt = data;
|
||||
int bytesRead = 0;
|
||||
uint16_t numberOfParticles = 0;
|
||||
int expectedBytesPerParticle = Particle::expectedBytes();
|
||||
|
||||
if (bytesLeftToRead >= sizeof(numberOfParticles)) {
|
||||
|
||||
|
||||
// read our particles in....
|
||||
numberOfParticles = *(uint16_t*)dataAt;
|
||||
dataAt += sizeof(numberOfParticles);
|
||||
bytesLeftToRead -= sizeof(numberOfParticles);
|
||||
bytesRead += sizeof(numberOfParticles);
|
||||
|
||||
|
||||
if (bytesLeftToRead >= (numberOfParticles * expectedBytesPerParticle)) {
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
Particle tempParticle;
|
||||
|
@ -227,7 +240,7 @@ int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, in
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
@ -238,7 +251,7 @@ void ParticleTreeElement::calculateAverageFromChildren() {
|
|||
|
||||
// will detect if children are leaves AND collapsable into the parent node
|
||||
// and in that case will collapse children and make this node
|
||||
// a leaf, returns TRUE if all the leaves are collapsed into a
|
||||
// a leaf, returns TRUE if all the leaves are collapsed into a
|
||||
// single node
|
||||
bool ParticleTreeElement::collapseChildren() {
|
||||
// nothing to do here yet...
|
||||
|
|
|
@ -28,11 +28,11 @@ public:
|
|||
|
||||
class ParticleTreeElement : public OctreeElement {
|
||||
friend class ParticleTree; // to allow createElement to new us...
|
||||
|
||||
|
||||
ParticleTreeElement(unsigned char* octalCode = NULL);
|
||||
|
||||
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL) const;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~ParticleTreeElement();
|
||||
|
||||
|
@ -40,31 +40,31 @@ public:
|
|||
ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); }
|
||||
|
||||
// methods you can and should override to implement your tree functionality
|
||||
|
||||
|
||||
/// Adds a child to the current element. Override this if there is additional child initialization your class needs.
|
||||
virtual ParticleTreeElement* addChildAtIndex(int index);
|
||||
|
||||
/// Override this to implement LOD averaging on changes to the tree.
|
||||
/// Override this to implement LOD averaging on changes to the tree.
|
||||
virtual void calculateAverageFromChildren();
|
||||
|
||||
/// Override this to implement LOD collapsing and identical child pruning on changes to the tree.
|
||||
/// Override this to implement LOD collapsing and identical child pruning on changes to the tree.
|
||||
virtual bool collapseChildren();
|
||||
|
||||
/// Should this element be considered to have content in it. This will be used in collision and ray casting methods.
|
||||
/// By default we assume that only leaves are actual content, but some octrees may have different semantics.
|
||||
virtual bool hasContent() const { return isLeaf(); }
|
||||
|
||||
|
||||
/// Override this to break up large octree elements when an edit operation is performed on a smaller octree element.
|
||||
/// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the
|
||||
/// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the
|
||||
/// meaningful split would be to break the larger cube into smaller cubes of the same color/texture.
|
||||
virtual void splitChildren() { }
|
||||
|
||||
|
||||
/// Override to indicate that this element requires a split before editing lower elements in the octree
|
||||
virtual bool requiresSplit() const { return false; }
|
||||
|
||||
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
||||
virtual bool appendElementData(OctreePacketData* packetData) const;
|
||||
|
||||
|
||||
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
|
||||
/// from the network.
|
||||
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
|
@ -76,21 +76,23 @@ public:
|
|||
virtual bool isRendered() const { return getShouldRender(); }
|
||||
virtual bool deleteApproved() const { return !hasParticles(); }
|
||||
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
const QList<Particle>& getParticles() const { return *_particles; }
|
||||
QList<Particle>& getParticles() { return *_particles; }
|
||||
bool hasParticles() const { return _particles->size() > 0; }
|
||||
|
||||
|
||||
void update(ParticleTreeUpdateArgs& args);
|
||||
void setTree(ParticleTree* tree) { _myTree = tree; }
|
||||
|
||||
|
||||
bool containsParticle(const Particle& particle) const;
|
||||
bool updateParticle(const Particle& particle);
|
||||
const Particle* getClosestParticle(glm::vec3 position) const;
|
||||
const Particle* getParticleWithID(uint32_t id) const;
|
||||
|
||||
|
||||
bool removeParticleWithID(uint32_t id);
|
||||
|
||||
|
||||
protected:
|
||||
virtual void init(unsigned char * octalCode);
|
||||
|
|
|
@ -7,42 +7,112 @@
|
|||
//
|
||||
|
||||
#include "ParticlesScriptingInterface.h"
|
||||
#include "ParticleTree.h"
|
||||
|
||||
|
||||
|
||||
void ParticlesScriptingInterface::queueParticleMessage(PACKET_TYPE packetType, ParticleDetail& particleDetails) {
|
||||
getParticlePacketSender()->queueParticleEditMessages(packetType, 1, &particleDetails);
|
||||
ParticlesScriptingInterface::ParticlesScriptingInterface() :
|
||||
_nextCreatorTokenID(0),
|
||||
_particleTree(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned int ParticlesScriptingInterface::queueParticleAdd(glm::vec3 position, float radius,
|
||||
xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString script) {
|
||||
|
||||
void ParticlesScriptingInterface::queueParticleMessage(PACKET_TYPE packetType,
|
||||
ParticleID particleID, const ParticleProperties& properties) {
|
||||
getParticlePacketSender()->queueParticleEditMessage(packetType, particleID, properties);
|
||||
}
|
||||
|
||||
ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& properties) {
|
||||
|
||||
// The application will keep track of creatorTokenID
|
||||
uint32_t creatorTokenID = _nextCreatorTokenID;
|
||||
_nextCreatorTokenID++;
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail addParticleDetail = { NEW_PARTICLE, now,
|
||||
position, radius, {color.red, color.green, color.blue }, velocity,
|
||||
gravity, damping, inHand, script, creatorTokenID };
|
||||
|
||||
uint32_t creatorTokenID = Particle::getNextCreatorTokenID();
|
||||
|
||||
ParticleID id(NEW_PARTICLE, creatorTokenID, false );
|
||||
|
||||
// queue the packet
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, addParticleDetail);
|
||||
|
||||
return creatorTokenID;
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, id, properties);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
|
||||
uint32_t actualID = particleID.id;
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
// hmmm... we kind of want to bail if someone attempts to edit an unknown
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
//qDebug() << "ParticlesScriptingInterface::editParticle()... BAILING!!! particleID.creatorTokenID="
|
||||
// << particleID.creatorTokenID;
|
||||
return; // bailing early
|
||||
}
|
||||
}
|
||||
|
||||
particleID.id = actualID;
|
||||
particleID.isKnownID = true;
|
||||
//qDebug() << "ParticlesScriptingInterface::editParticle()... FOUND IT!!! actualID=" << actualID;
|
||||
|
||||
bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
uint16_t containsBits = properties.getChangedBits();
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... containsBits=" << containsBits;
|
||||
if ((containsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION) {
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getPositon()="
|
||||
<< properties.getPosition().x << ", "
|
||||
<< properties.getPosition().y << ", "
|
||||
<< properties.getPosition().z << "...";
|
||||
}
|
||||
if ((containsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY) {
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getVelocity()="
|
||||
<< properties.getVelocity().x << ", "
|
||||
<< properties.getVelocity().y << ", "
|
||||
<< properties.getVelocity().z << "...";
|
||||
}
|
||||
if ((containsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND) {
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand();
|
||||
}
|
||||
}
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
|
||||
}
|
||||
|
||||
|
||||
void ParticlesScriptingInterface::queueParticleEdit(unsigned int particleID, glm::vec3 position, float radius,
|
||||
xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString script) {
|
||||
// TODO: This deleteParticle() method uses the PACKET_TYPE_PARTICLE_ADD_OR_EDIT message to send
|
||||
// a changed particle with a shouldDie() property set to true. This works and is currently the only
|
||||
// way to tell the particle server to delete a particle. But we should change this to use the PACKET_TYPE_PARTICLE_ERASE
|
||||
// message which takes a list of particle id's to delete.
|
||||
void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) {
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail editParticleDetail = { particleID, now,
|
||||
position, radius, {color.red, color.green, color.blue }, velocity,
|
||||
gravity, damping, inHand, script, UNKNOWN_TOKEN };
|
||||
|
||||
// queue the packet
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, editParticleDetail);
|
||||
// setup properties to kill the particle
|
||||
ParticleProperties properties;
|
||||
properties.setShouldDie(true);
|
||||
|
||||
uint32_t actualID = particleID.id;
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
|
||||
// hmmm... we kind of want to bail if someone attempts to edit an unknown
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
//qDebug() << "ParticlesScriptingInterface::deleteParticle(), bailing - unknown particle...";
|
||||
return; // bailing early
|
||||
}
|
||||
}
|
||||
|
||||
particleID.id = actualID;
|
||||
particleID.isKnownID = true;
|
||||
|
||||
//qDebug() << "ParticlesScriptingInterface::deleteParticle(), queueParticleMessage......";
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
|
||||
}
|
||||
|
||||
ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& center, float radius) const {
|
||||
ParticleID result(UNKNOWN_PARTICLE_ID, UNKNOWN_TOKEN, false);
|
||||
if (_particleTree) {
|
||||
const Particle* closestParticle = _particleTree->findClosestParticle(center/(float)TREE_SCALE,
|
||||
radius/(float)TREE_SCALE);
|
||||
|
||||
if (closestParticle) {
|
||||
result.id = closestParticle->getID();
|
||||
result.isKnownID = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,23 +19,26 @@
|
|||
class ParticlesScriptingInterface : public OctreeScriptingInterface {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ParticlesScriptingInterface();
|
||||
|
||||
ParticleEditPacketSender* getParticlePacketSender() const { return (ParticleEditPacketSender*)getPacketSender(); }
|
||||
virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
|
||||
virtual OctreeEditPacketSender* createPacketSender() { return new ParticleEditPacketSender(); }
|
||||
|
||||
public slots:
|
||||
/// queues the creation of a Particle which will be sent by calling process on the PacketSender
|
||||
/// returns the creatorTokenID for the newly created particle
|
||||
unsigned int queueParticleAdd(glm::vec3 position, float radius,
|
||||
xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString script);
|
||||
void setParticleTree(ParticleTree* particleTree) { _particleTree = particleTree; }
|
||||
ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; }
|
||||
|
||||
void queueParticleEdit(unsigned int particleID, glm::vec3 position, float radius,
|
||||
xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString script);
|
||||
public slots:
|
||||
ParticleID addParticle(const ParticleProperties& properties);
|
||||
void editParticle(ParticleID particleID, const ParticleProperties& properties);
|
||||
void deleteParticle(ParticleID particleID);
|
||||
ParticleID findClosestParticle(const glm::vec3& center, float radius) const;
|
||||
|
||||
private:
|
||||
void queueParticleMessage(PACKET_TYPE packetType, ParticleDetail& particleDetails);
|
||||
|
||||
void queueParticleMessage(PACKET_TYPE packetType, ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
uint32_t _nextCreatorTokenID;
|
||||
ParticleTree* _particleTree;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticlesScriptingInterface__) */
|
||||
|
|
|
@ -115,6 +115,8 @@ void ScriptEngine::init() {
|
|||
|
||||
// register meta-type for glm::vec3 conversions
|
||||
registerMetaTypes(&_engine);
|
||||
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
|
||||
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
|
||||
|
||||
QScriptValue agentValue = _engine.newQObject(this);
|
||||
_engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
@ -167,7 +169,6 @@ void ScriptEngine::evaluate() {
|
|||
}
|
||||
|
||||
QScriptValue result = _engine.evaluate(_scriptContents);
|
||||
qDebug("Evaluated script.");
|
||||
|
||||
if (_engine.hasUncaughtException()) {
|
||||
int line = _engine.uncaughtExceptionLineNumber();
|
||||
|
@ -182,8 +183,6 @@ void ScriptEngine::run() {
|
|||
_isRunning = true;
|
||||
|
||||
QScriptValue result = _engine.evaluate(_scriptContents);
|
||||
qDebug("Evaluated script");
|
||||
|
||||
if (_engine.hasUncaughtException()) {
|
||||
int line = _engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << result.toString();
|
||||
|
|
|
@ -18,18 +18,18 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
case PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO:
|
||||
case PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO:
|
||||
return 2;
|
||||
|
||||
|
||||
case PACKET_TYPE_HEAD_DATA:
|
||||
return 15;
|
||||
|
||||
case PACKET_TYPE_OCTREE_STATS:
|
||||
return 2;
|
||||
|
||||
|
||||
case PACKET_TYPE_DOMAIN:
|
||||
case PACKET_TYPE_DOMAIN_LIST_REQUEST:
|
||||
case PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY:
|
||||
return 2;
|
||||
|
||||
|
||||
case PACKET_TYPE_VOXEL_QUERY:
|
||||
return 2;
|
||||
|
||||
|
@ -40,19 +40,19 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
|
||||
case PACKET_TYPE_VOXEL_DATA:
|
||||
return 1;
|
||||
|
||||
|
||||
case PACKET_TYPE_JURISDICTION:
|
||||
return 1;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT:
|
||||
return 2;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT:
|
||||
return 4;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
return 5;
|
||||
|
||||
return 8;
|
||||
|
||||
case PACKET_TYPE_PING_REPLY:
|
||||
return 1;
|
||||
|
||||
|
||||
case PACKET_TYPE_DATA_SERVER_GET:
|
||||
case PACKET_TYPE_DATA_SERVER_PUT:
|
||||
case PACKET_TYPE_DATA_SERVER_SEND:
|
||||
|
@ -78,7 +78,7 @@ bool packetVersionMatch(unsigned char* packetHeader) {
|
|||
int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type) {
|
||||
destinationHeader[0] = type;
|
||||
destinationHeader[1] = versionForPacketType(type);
|
||||
|
||||
|
||||
// return the number of bytes written for pointer pushing
|
||||
return 2;
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ int numBytesForPacketVersion(const unsigned char* packetVersion) {
|
|||
int numBytesForPacketHeader(const unsigned char* packetHeader) {
|
||||
// int numBytesType = numBytesForPacketType(packetHeader);
|
||||
// return numBytesType + numBytesForPacketVersion(packetHeader + numBytesType);
|
||||
|
||||
|
||||
// currently this need not be dynamic - there are 2 bytes for each packet header
|
||||
return 2;
|
||||
}
|
||||
|
|
|
@ -56,3 +56,4 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color) {
|
|||
color.green = object.property("green").toVariant().toInt();
|
||||
color.blue = object.property("blue").toVariant().toInt();
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ Octree* VoxelServer::createTree() {
|
|||
return new VoxelTree(true);
|
||||
}
|
||||
|
||||
bool VoxelServer::hasSpecialPacketToSend() {
|
||||
bool VoxelServer::hasSpecialPacketToSend(Node* node) {
|
||||
bool shouldSendEnvironments = _sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, OCTREE_SEND_INTERVAL_USECS);
|
||||
return shouldSendEnvironments;
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ int VoxelServer::sendSpecialPacket(Node* node) {
|
|||
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
|
||||
int envPacketLength = numBytesPacketHeader;
|
||||
int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount();
|
||||
|
||||
|
||||
for (int i = 0; i < environmentsToSend; i++) {
|
||||
envPacketLength += getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
|
||||
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
/// Handles assignments of type VoxelServer - sending voxels to various clients.
|
||||
class VoxelServer : public OctreeServer {
|
||||
public:
|
||||
public:
|
||||
VoxelServer(const unsigned char* dataBuffer, int numBytes);
|
||||
~VoxelServer();
|
||||
|
||||
|
@ -33,7 +33,7 @@ public:
|
|||
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
|
||||
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
|
||||
|
||||
// Subclasses must implement these methods
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
|
||||
|
@ -41,10 +41,10 @@ public:
|
|||
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
|
||||
|
||||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend();
|
||||
virtual bool hasSpecialPacketToSend(Node* node);
|
||||
virtual int sendSpecialPacket(Node* node);
|
||||
|
||||
|
||||
|
|
|
@ -12,28 +12,32 @@ void VoxelsScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDet
|
|||
getVoxelPacketSender()->queueVoxelEditMessages(addPacketType, 1, &addVoxelDetails);
|
||||
}
|
||||
|
||||
void VoxelsScriptingInterface::queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue) {
|
||||
void VoxelsScriptingInterface::setVoxelNonDestructive(float x, float y, float z, float scale,
|
||||
uchar red, uchar green, uchar blue) {
|
||||
// setup a VoxelDetail struct with the data
|
||||
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
|
||||
|
||||
VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE, red, green, blue};
|
||||
|
||||
// queue the packet
|
||||
queueVoxelAdd(PACKET_TYPE_VOXEL_SET, addVoxelDetail);
|
||||
}
|
||||
|
||||
void VoxelsScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z, float scale,
|
||||
void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
|
||||
uchar red, uchar green, uchar blue) {
|
||||
// setup a VoxelDetail struct with the data
|
||||
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
|
||||
|
||||
VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE, red, green, blue};
|
||||
|
||||
// queue the destructive add
|
||||
queueVoxelAdd(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, addVoxelDetail);
|
||||
}
|
||||
|
||||
void VoxelsScriptingInterface::queueVoxelDelete(float x, float y, float z, float scale) {
|
||||
|
||||
void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale) {
|
||||
|
||||
// setup a VoxelDetail struct with data
|
||||
VoxelDetail deleteVoxelDetail = {x, y, z, scale, 0, 0, 0};
|
||||
|
||||
VoxelDetail deleteVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE,
|
||||
scale / (float)TREE_SCALE, 0, 0, 0};
|
||||
|
||||
getVoxelPacketSender()->queueVoxelEditMessages(PACKET_TYPE_VOXEL_ERASE, 1, &deleteVoxelDetail);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <JurisdictionListener.h>
|
||||
#include <OctreeScriptingInterface.h>
|
||||
|
||||
#include "VoxelConstants.h"
|
||||
#include "VoxelEditPacketSender.h"
|
||||
|
||||
/// handles scripting of voxel commands from JS passed to assigned clients
|
||||
|
@ -26,31 +28,31 @@ public:
|
|||
|
||||
public slots:
|
||||
/// queues the creation of a voxel which will be sent by calling process on the PacketSender
|
||||
/// \param x the x-coordinate of the voxel (in VS space)
|
||||
/// \param y the y-coordinate of the voxel (in VS space)
|
||||
/// \param z the z-coordinate of the voxel (in VS space)
|
||||
/// \param scale the scale of the voxel (in VS space)
|
||||
/// \param x the x-coordinate of the voxel (in meter units)
|
||||
/// \param y the y-coordinate of the voxel (in meter units)
|
||||
/// \param z the z-coordinate of the voxel (in meter units)
|
||||
/// \param scale the scale of the voxel (in meter units)
|
||||
/// \param red the R value for RGB color of voxel
|
||||
/// \param green the G value for RGB color of voxel
|
||||
/// \param blue the B value for RGB color of voxel
|
||||
void queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
|
||||
|
||||
void setVoxelNonDestructive(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
|
||||
|
||||
/// queues the destructive creation of a voxel which will be sent by calling process on the PacketSender
|
||||
/// \param x the x-coordinate of the voxel (in VS space)
|
||||
/// \param y the y-coordinate of the voxel (in VS space)
|
||||
/// \param z the z-coordinate of the voxel (in VS space)
|
||||
/// \param scale the scale of the voxel (in VS space)
|
||||
/// \param x the x-coordinate of the voxel (in meter units)
|
||||
/// \param y the y-coordinate of the voxel (in meter units)
|
||||
/// \param z the z-coordinate of the voxel (in meter units)
|
||||
/// \param scale the scale of the voxel (in meter units)
|
||||
/// \param red the R value for RGB color of voxel
|
||||
/// \param green the G value for RGB color of voxel
|
||||
/// \param blue the B value for RGB color of voxel
|
||||
void queueDestructiveVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
|
||||
|
||||
void setVoxel(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
|
||||
|
||||
/// queues the deletion of a voxel, sent by calling process on the PacketSender
|
||||
/// \param x the x-coordinate of the voxel (in VS space)
|
||||
/// \param y the y-coordinate of the voxel (in VS space)
|
||||
/// \param z the z-coordinate of the voxel (in VS space)
|
||||
/// \param scale the scale of the voxel (in VS space)
|
||||
void queueVoxelDelete(float x, float y, float z, float scale);
|
||||
/// \param x the x-coordinate of the voxel (in meter units)
|
||||
/// \param y the y-coordinate of the voxel (in meter units)
|
||||
/// \param z the z-coordinate of the voxel (in meter units)
|
||||
/// \param scale the scale of the voxel (in meter units)
|
||||
void eraseVoxel(float x, float y, float z, float scale);
|
||||
|
||||
private:
|
||||
void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails);
|
||||
|
@ -62,9 +64,11 @@ public:
|
|||
VoxelDetailScriptObject(VoxelDetail* voxelDetail) { _voxelDetail = voxelDetail; }
|
||||
|
||||
public slots:
|
||||
glm::vec3 getPosition() const { return glm::vec3(_voxelDetail->x, _voxelDetail->y, _voxelDetail->z); }
|
||||
/// position in meter units
|
||||
glm::vec3 getPosition() const { return glm::vec3(_voxelDetail->x, _voxelDetail->y, _voxelDetail->z) * (float)TREE_SCALE; }
|
||||
xColor getColor() const { xColor color = { _voxelDetail->red, _voxelDetail->green, _voxelDetail->blue }; return color; }
|
||||
float getScale() const { return _voxelDetail->s; }
|
||||
/// scale in meter units
|
||||
float getScale() const { return _voxelDetail->s * (float)TREE_SCALE; }
|
||||
|
||||
private:
|
||||
VoxelDetail* _voxelDetail;
|
||||
|
|
Loading…
Reference in a new issue