mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 20:13:09 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into protocol
This commit is contained in:
commit
25aeda2e18
30 changed files with 1685 additions and 103 deletions
|
@ -49,7 +49,8 @@
|
|||
if (!_this.beingGrabbed) {
|
||||
// remember we're being grabbed so we can detect being released
|
||||
_this.beingGrabbed = true;
|
||||
breakdanceStart();
|
||||
var props = Entities.getEntityProperties(entityID);
|
||||
breakdanceStart(props.modelURL, props.position);
|
||||
print("I'm was grabbed...");
|
||||
} else {
|
||||
breakdanceUpdate();
|
||||
|
|
217
examples/entityScripts/sprayPaintCan.js
Normal file
217
examples/entityScripts/sprayPaintCan.js
Normal file
|
@ -0,0 +1,217 @@
|
|||
//
|
||||
// sprayPaintCan.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Eric Levin on 9/3/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example of an entity script for painting with a spraypaint can
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
(function() {
|
||||
this.userData = {};
|
||||
|
||||
var TIP_OFFSET_Z = 0.14;
|
||||
var TIP_OFFSET_Y = 0.04;
|
||||
|
||||
var ZERO_VEC = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
|
||||
var MAX_POINTS_PER_LINE = 40;
|
||||
var MIN_POINT_DISTANCE = 0.01;
|
||||
var STROKE_WIDTH = 0.02;
|
||||
|
||||
var self = this;
|
||||
|
||||
var stopSetting = JSON.stringify({
|
||||
running: false
|
||||
});
|
||||
var startSetting = JSON.stringify({
|
||||
running: true
|
||||
});
|
||||
|
||||
this.getUserData = function() {
|
||||
if (this.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateUserData = function() {
|
||||
Entities.editEntity(this.entityId, {
|
||||
userData: JSON.stringify(this.userData)
|
||||
});
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
self.properties = Entities.getEntityProperties(self.entityId);
|
||||
self.getUserData();
|
||||
//Only run the logic if this is the client whose avatar is grabbing
|
||||
if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId === MyAvatar.sessionUUID) {
|
||||
if (self.activated !== true) {
|
||||
Entities.editEntity(self.paintStream, {
|
||||
animationSettings: startSetting
|
||||
});
|
||||
self.activated = true;
|
||||
}
|
||||
//Move emitter to where entity is always when its activated
|
||||
self.sprayStream();
|
||||
} else if (self.userData.grabKey && self.userData.grabKey.activated === false && self.activated) {
|
||||
Entities.editEntity(self.paintStream, {
|
||||
animationSettings: stopSetting
|
||||
});
|
||||
self.activated = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.sprayStream = function() {
|
||||
var forwardVec = Quat.getFront(self.properties.rotation);
|
||||
var upVec = Quat.getUp(self.properties.rotation);
|
||||
var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
|
||||
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
||||
Entities.editEntity(self.paintStream, {
|
||||
position: position,
|
||||
emitVelocity: forwardVec
|
||||
});
|
||||
|
||||
//Now check for an intersection with an entity
|
||||
|
||||
//move forward so ray doesnt intersect with gun
|
||||
var origin = Vec3.sum(position, forwardVec);
|
||||
var pickRay = {
|
||||
origin: origin,
|
||||
direction: Vec3.multiply(forwardVec, 2)
|
||||
}
|
||||
|
||||
var intersection = Entities.findRayIntersection(pickRay, true);
|
||||
if (intersection.intersects) {
|
||||
var normal = Vec3.multiply(-1, Quat.getFront(intersection.properties.rotation));
|
||||
this.paint(intersection.intersection, normal);
|
||||
}
|
||||
}
|
||||
|
||||
this.paint = function(position, normal) {
|
||||
if (!this.painting) {
|
||||
print("position " + JSON.stringify(position))
|
||||
|
||||
this.newStroke(position);
|
||||
this.painting = true;
|
||||
}
|
||||
|
||||
if (this.strokePoints.length > MAX_POINTS_PER_LINE) {
|
||||
this.painting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var localPoint = Vec3.subtract(position, this.strokeBasePosition);
|
||||
//Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on
|
||||
localPoint = Vec3.sum(localPoint, Vec3.multiply(normal, .1));
|
||||
|
||||
if (this.strokePoints.length > 0 && Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]) < MIN_POINT_DISTANCE) {
|
||||
//need a minimum distance to avoid binormal NANs
|
||||
return;
|
||||
}
|
||||
|
||||
this.strokePoints.push(localPoint);
|
||||
this.strokeNormals.push(normal);
|
||||
this.strokeWidths.push(STROKE_WIDTH);
|
||||
Entities.editEntity(this.currentStroke, {
|
||||
linePoints: this.strokePoints,
|
||||
normals: this.strokeNormals,
|
||||
strokeWidths: this.strokeWidths
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
this.newStroke = function(position) {
|
||||
this.strokeBasePosition = position;
|
||||
this.currentStroke = Entities.addEntity({
|
||||
position: position,
|
||||
type: "PolyLine",
|
||||
color: {
|
||||
red: randInt(160, 250),
|
||||
green: randInt(10, 20),
|
||||
blue: randInt(190, 250)
|
||||
},
|
||||
dimensions: {
|
||||
x: 5,
|
||||
y: 5,
|
||||
z: 5
|
||||
},
|
||||
lifetime: 100
|
||||
});
|
||||
this.strokePoints = [];
|
||||
this.strokeNormals = [];
|
||||
this.strokeWidths = [];
|
||||
|
||||
this.strokes.push(this.currentStroke);
|
||||
}
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.strokes = [];
|
||||
this.activated = false;
|
||||
this.entityId = entityId;
|
||||
this.properties = Entities.getEntityProperties(self.entityId);
|
||||
this.getUserData();
|
||||
print("USER DATA " + JSON.stringify(this.userData))
|
||||
if (this.userData.activated) {
|
||||
this.activated = true;
|
||||
}
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
this.initialize = function() {
|
||||
var animationSettings = JSON.stringify({
|
||||
fps: 30,
|
||||
loop: true,
|
||||
firstFrame: 1,
|
||||
lastFrame: 10000,
|
||||
running: false
|
||||
});
|
||||
|
||||
this.paintStream = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
animationSettings: animationSettings,
|
||||
position: this.properties.position,
|
||||
textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png",
|
||||
emitVelocity: ZERO_VEC,
|
||||
emitAcceleration: ZERO_VEC,
|
||||
velocitySpread: {
|
||||
x: .02,
|
||||
y: .02,
|
||||
z: 0.02
|
||||
},
|
||||
emitRate: 100,
|
||||
particleRadius: 0.01,
|
||||
color: {
|
||||
red: 170,
|
||||
green: 20,
|
||||
blue: 150
|
||||
},
|
||||
lifespan: 5,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
Script.update.disconnect(this.update);
|
||||
Entities.deleteEntity(this.paintStream);
|
||||
this.strokes.forEach(function(stroke) {
|
||||
Entities.deleteEntity(stroke);
|
||||
});
|
||||
}
|
||||
Script.update.connect(this.update);
|
||||
});
|
||||
|
||||
|
||||
function randFloat(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
|
@ -9,11 +9,13 @@
|
|||
var currentSortOrder = 'asc';
|
||||
var entityList = null;
|
||||
var refreshEntityListTimer = null;
|
||||
var ASC_STRING = ' ▾';
|
||||
var DESC_STRING = ' ▴';
|
||||
const ASCENDING_STRING = ' ▾';
|
||||
const DESCENDING_STRING = ' ▴';
|
||||
const DELETE = 46; // Key code for the delete key.
|
||||
const MAX_ITEMS = Number.MAX_VALUE; // Used to set the max length of the list of discovered entities.
|
||||
|
||||
function loaded() {
|
||||
entityList = new List('entity-list', { valueNames: ['name', 'type', 'url']});
|
||||
entityList = new List('entity-list', { valueNames: ['name', 'type', 'url'], page: MAX_ITEMS});
|
||||
entityList.clear();
|
||||
elEntityTable = document.getElementById("entity-table");
|
||||
elEntityTableBody = document.getElementById("entity-table-body");
|
||||
|
@ -34,19 +36,19 @@
|
|||
setSortColumn('url');
|
||||
};
|
||||
|
||||
function onRowClicked(e) {
|
||||
function onRowClicked(clickEvent) {
|
||||
var id = this.dataset.entityId;
|
||||
var selection = [this.dataset.entityId];
|
||||
if (e.ctrlKey) {
|
||||
if (clickEvent.ctrlKey) {
|
||||
selection = selection.concat(selectedEntities);
|
||||
} else if (e.shiftKey && selectedEntities.length > 0) {
|
||||
} else if (clickEvent.shiftKey && selectedEntities.length > 0) {
|
||||
var previousItemFound = -1;
|
||||
var clickedItemFound = -1;
|
||||
for (var i in entityList.visibleItems) {
|
||||
if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[i].values().id) {
|
||||
clickedItemFound = i;
|
||||
} else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[i].values().id) {
|
||||
previousItemFound = i;
|
||||
for (var entity in entityList.visibleItems) {
|
||||
if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[entity].values().id) {
|
||||
clickedItemFound = entity;
|
||||
} else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[entity].values().id) {
|
||||
previousItemFound = entity;
|
||||
}
|
||||
}
|
||||
if (previousItemFound !== -1 && clickedItemFound !== -1) {
|
||||
|
@ -90,19 +92,19 @@
|
|||
var filename = urlParts[urlParts.length - 1];
|
||||
|
||||
entityList.add([{ id: id, name: name, type: type, url: filename }], function(items) {
|
||||
var el = items[0].elm;
|
||||
var currentElement = items[0].elm;
|
||||
var id = items[0]._values.id;
|
||||
entities[id] = {
|
||||
id: id,
|
||||
name: name,
|
||||
el: el,
|
||||
el: currentElement,
|
||||
item: items[0],
|
||||
};
|
||||
el.setAttribute('id', 'entity_' + id);
|
||||
el.setAttribute('title', url);
|
||||
el.dataset.entityId = id;
|
||||
el.onclick = onRowClicked;
|
||||
el.ondblclick = onRowDoubleClicked;
|
||||
currentElement.setAttribute('id', 'entity_' + id);
|
||||
currentElement.setAttribute('title', url);
|
||||
currentElement.dataset.entityId = id;
|
||||
currentElement.onclick = onRowClicked;
|
||||
currentElement.ondblclick = onRowDoubleClicked;
|
||||
});
|
||||
|
||||
if (refreshEntityListTimer) {
|
||||
|
@ -134,7 +136,7 @@
|
|||
currentSortColumn = column;
|
||||
currentSortOrder = "asc";
|
||||
}
|
||||
elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASC_STRING : DESC_STRING;
|
||||
elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASCENDING_STRING : DESCENDING_STRING;
|
||||
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||
}
|
||||
|
||||
|
@ -177,12 +179,12 @@
|
|||
refreshEntities();
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (e.target.nodeName === "INPUT") {
|
||||
document.addEventListener("keydown", function (keyDownEvent) {
|
||||
if (keyDownEvent.target.nodeName === "INPUT") {
|
||||
return;
|
||||
}
|
||||
var keyCode = e.keyCode;
|
||||
if (keyCode === 46) {
|
||||
var keyCode = keyDownEvent.keyCode;
|
||||
if (keyCode === DELETE) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
refreshEntities();
|
||||
}
|
||||
|
|
102
examples/shaders/grid.fs
Normal file
102
examples/shaders/grid.fs
Normal file
|
@ -0,0 +1,102 @@
|
|||
#line 2
|
||||
|
||||
// https://www.shadertoy.com/view/lss3WS
|
||||
|
||||
// srtuss, 2013
|
||||
|
||||
// collecting some design ideas for a new game project.
|
||||
// no raymarching is used.
|
||||
|
||||
// if i could add a custom soundtrack, it'd use this one (essential for desired sensation)
|
||||
// http://www.youtube.com/watch?v=1uFAu65tZpo
|
||||
|
||||
//#define GREEN_VERSION
|
||||
|
||||
// ** improved camera shaking
|
||||
// ** cleaned up code
|
||||
// ** added stuff to the gates
|
||||
|
||||
// srtuss, 2013
|
||||
float time = iGlobalTime;
|
||||
|
||||
// some noise functions for fast developing
|
||||
float rand11(float p) {
|
||||
return fract(sin(p * 591.32) * 43758.5357);
|
||||
}
|
||||
|
||||
float rand12(vec2 p) {
|
||||
return fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5357);
|
||||
}
|
||||
|
||||
vec2 rand21(float p) {
|
||||
return fract(vec2(sin(p * 591.32), cos(p * 391.32)));
|
||||
}
|
||||
|
||||
vec2 rand22(in vec2 p)
|
||||
{
|
||||
return fract(vec2(sin(p.x * 591.32 + p.y * 154.077),
|
||||
cos(p.x * 391.32 + p.y * 49.077)));
|
||||
}
|
||||
|
||||
vec3 voronoi(in vec2 x) {
|
||||
vec2 n = floor(x); // grid cell id
|
||||
vec2 f = fract(x);// grid internal position
|
||||
vec2 mg;// shortest distance...
|
||||
vec2 mr;// ..and second shortest distance
|
||||
float md = 8.0, md2 = 8.0;
|
||||
for(int j = -1; j <= 1; j ++)
|
||||
{
|
||||
for(int i = -1; i <= 1; i ++)
|
||||
{
|
||||
vec2 g = vec2(float(i), float(j)); // cell id
|
||||
vec2 o = rand22(n + g);// offset to edge point
|
||||
vec2 r = g + o - f;
|
||||
|
||||
float d = max(abs(r.x), abs(r.y));// distance to the edge
|
||||
|
||||
if(d < md)
|
||||
{ md2 = md; md = d; mr = r; mg = g;}
|
||||
else if(d < md2)
|
||||
{ md2 = d;}
|
||||
}
|
||||
}
|
||||
return vec3(n + mg, md2 - md);
|
||||
}
|
||||
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
float inten = 0.0;
|
||||
vec3 its;
|
||||
float v, g;
|
||||
// voronoi floor layers
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float layer = float(i);
|
||||
vec2 pos = _position.xz * 100.0;
|
||||
if (i == 2) {
|
||||
pos.x += time * 0.05;
|
||||
} else if (i == 1) {
|
||||
pos.y += time * 0.07;
|
||||
}
|
||||
vec3 vo = voronoi(pos + 8.0 * rand21(float(i)));
|
||||
|
||||
v = exp(-100.0 * (vo.z - 0.02));
|
||||
float fx = 0.0;
|
||||
|
||||
// add some special fx to lowest layer
|
||||
if (i == 3) {
|
||||
float crd = 0.0; //fract(time * 0.2) * 50.0 - 25.0;
|
||||
float fxi = cos(vo.x * 0.2 + time * 0.5); //abs(crd - vo.x);
|
||||
fx = clamp(smoothstep(0.9, 1.0, fxi), 0.0, 0.9) * 1.0 * rand12(vo.xy);
|
||||
fx *= exp(-3.0 * vo.z) * 2.0;
|
||||
}
|
||||
inten += v * 0.1 + fx;
|
||||
}
|
||||
inten *= 0.4 + (sin(time) * 0.5 + 0.5) * 0.6;
|
||||
vec3 cr = vec3(0.15, 2.0, 9.0);
|
||||
vec3 cg = vec3(2.0, 0.15, 9.0);
|
||||
vec3 cb = vec3(9.0, 2.0, 0.15);
|
||||
vec3 ct = vec3(9.0, 0.25, 0.3);
|
||||
vec3 cy = vec3(0.25, 0.3, 9.3);
|
||||
vec3 col = pow(vec3(inten), 1.5 * cy);
|
||||
return vec4(col, 1.0);
|
||||
}
|
48
examples/shaders/hex.fs
Normal file
48
examples/shaders/hex.fs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#line 2
|
||||
// Created by inigo quilez - iq/2014
|
||||
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
|
||||
// { 2d cell id, distance to border, distnace to center )
|
||||
vec4 hexagon(vec2 p) {
|
||||
vec2 q = vec2(p.x * 2.0 * 0.5773503, p.y + p.x * 0.5773503);
|
||||
|
||||
vec2 pi = floor(q);
|
||||
vec2 pf = fract(q);
|
||||
|
||||
float v = mod(pi.x + pi.y, 3.0);
|
||||
|
||||
float ca = step(1.0, v);
|
||||
float cb = step(2.0, v);
|
||||
vec2 ma = step(pf.xy, pf.yx);
|
||||
|
||||
// distance to borders
|
||||
float e = dot(ma,
|
||||
1.0 - pf.yx + ca * (pf.x + pf.y - 1.0) + cb * (pf.yx - 2.0 * pf.xy));
|
||||
|
||||
// distance to center
|
||||
p = vec2(q.x + floor(0.5 + p.y / 1.5), 4.0 * p.y / 3.0) * 0.5 + 0.5;
|
||||
float f = length((fract(p) - 0.5) * vec2(1.0, 0.85));
|
||||
|
||||
return vec4(pi + ca - cb * ma, e, f);
|
||||
}
|
||||
|
||||
|
||||
float hash1(vec2 p) {
|
||||
float n = dot(p, vec2(127.1, 311.7));
|
||||
return fract(sin(n) * 43758.5453);
|
||||
}
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
vec2 uv = _position.xz + 0.5;
|
||||
vec2 pos = _position.xz * iWorldScale.xz;
|
||||
// gray
|
||||
vec4 h = hexagon(8.0 * pos + 0.5);
|
||||
float n = snoise(vec3(0.3 * h.xy + iGlobalTime * 0.1, iGlobalTime));
|
||||
vec3 col = 0.15 + 0.15 * hash1(h.xy + 1.2) * vec3(1.0);
|
||||
col *= smoothstep(0.10, 0.11, h.z);
|
||||
col *= smoothstep(0.10, 0.11, h.w);
|
||||
col *= 1.0 + 0.15 * sin(40.0 * h.z);
|
||||
col *= 0.75 + 0.5 * h.z * n;
|
||||
col *= pow(16.0 * uv.x * (1.0 - uv.x) * uv.y * (1.0 - uv.y), 0.1);
|
||||
return vec4(col, 1.0);
|
||||
}
|
48
examples/shaders/test.fs
Normal file
48
examples/shaders/test.fs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#line 2
|
||||
// Created by inigo quilez - iq/2014
|
||||
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
|
||||
// { 2d cell id, distance to border, distnace to center )
|
||||
vec4 hexagon(vec2 p) {
|
||||
vec2 q = vec2(p.x * 2.0 * 0.5773503, p.y + p.x * 0.5773503);
|
||||
|
||||
vec2 pi = floor(q);
|
||||
vec2 pf = fract(q);
|
||||
|
||||
float v = mod(pi.x + pi.y, 3.0);
|
||||
|
||||
float ca = step(1.0, v);
|
||||
float cb = step(2.0, v);
|
||||
vec2 ma = step(pf.xy, pf.yx);
|
||||
|
||||
// distance to borders
|
||||
float e = dot(ma,
|
||||
1.0 - pf.yx + ca * (pf.x + pf.y - 1.0) + cb * (pf.yx - 2.0 * pf.xy));
|
||||
|
||||
// distance to center
|
||||
p = vec2(q.x + floor(0.5 + p.y / 1.5), 4.0 * p.y / 3.0) * 0.5 + 0.5;
|
||||
float f = length((fract(p) - 0.5) * vec2(1.0, 0.85));
|
||||
|
||||
return vec4(pi + ca - cb * ma, e, f);
|
||||
}
|
||||
|
||||
|
||||
float hash1(vec2 p) {
|
||||
float n = dot(p, vec2(127.1, 311.7));
|
||||
return fract(sin(n) * 43758.5453);
|
||||
}
|
||||
|
||||
vec4 getProceduralColor() {
|
||||
vec2 uv = _position.xz + 0.5;
|
||||
vec2 pos = _position.xz * 40.0;
|
||||
// gray
|
||||
vec4 h = hexagon(8.0 * pos + 0.5);
|
||||
float n = snoise(vec3(0.3 * h.xy + iGlobalTime * 0.1, iGlobalTime));
|
||||
vec3 col = 0.15 + 0.15 * hash1(h.xy + 1.2) * vec3(1.0);
|
||||
col *= smoothstep(0.10, 0.11, h.z);
|
||||
col *= smoothstep(0.10, 0.11, h.w);
|
||||
col *= 1.0 + 0.15 * sin(40.0 * h.z);
|
||||
col *= 0.75 + 0.5 * h.z * n;
|
||||
col *= pow(16.0 * uv.x * (1.0 - uv.x) * uv.y * (1.0 - uv.y), 0.1);
|
||||
return vec4(col, 1.0);
|
||||
}
|
74
examples/sprayPaintSpawner.js
Normal file
74
examples/sprayPaintSpawner.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
// sprayPaintSpawner.js
|
||||
//
|
||||
// Created by Eric Levin on 9/3/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is script spwans a spreay paint can model with the sprayPaintCan.js entity script attached
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
var scriptURL = "entityScripts/sprayPaintCan.js";
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var paintGun = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/sprayGun.fbx?=v4",
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.03,
|
||||
y: 0.15,
|
||||
z: 0.34
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
|
||||
var whiteboard = Entities.addEntity({
|
||||
type: "Box",
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 1.5,
|
||||
z: .01
|
||||
},
|
||||
rotation: orientationOf(Vec3.subtract(MyAvatar.position, center)),
|
||||
color: {
|
||||
red: 250,
|
||||
green: 250,
|
||||
blue: 250
|
||||
},
|
||||
// visible: false
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(paintGun);
|
||||
Entities.deleteEntity(whiteboard);
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
|
||||
function orientationOf(vector) {
|
||||
var Y_AXIS = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var X_AXIS = {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var theta = 0.0;
|
||||
|
||||
var RAD_TO_DEG = 180.0 / Math.PI;
|
||||
var direction, yaw, pitch;
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
|
@ -358,13 +358,19 @@ var NATURAL_DIMENSIONS = { x: 1.63, y: 1.67, z: 0.31 };
|
|||
var DIMENSIONS = Vec3.multiply(NATURAL_DIMENSIONS, 0.3);
|
||||
var puppetEntityID;
|
||||
|
||||
function createPuppet() {
|
||||
function createPuppet(model, location) {
|
||||
if (model === undefined) {
|
||||
model = "https://hifi-public.s3.amazonaws.com/models/Bboys/bboy1/bboy1.fbx";
|
||||
}
|
||||
if (location == undefined) {
|
||||
location = getPositionPuppet();
|
||||
}
|
||||
puppetEntityID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/bboy1/bboy1.fbx",
|
||||
modelURL: model,
|
||||
animationURL: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx",
|
||||
animationSettings: ANIMATION_SETTINGS,
|
||||
position: getPositionPuppet(),
|
||||
position: location,
|
||||
ignoreForCollisions: true,
|
||||
dimensions: DIMENSIONS,
|
||||
lifetime: TEMPORARY_LIFETIME
|
||||
|
@ -452,10 +458,10 @@ poses[LEFT_SIDE + RIGHT_FRONT ] = { name: "Left Side + Right Front",
|
|||
poses[LEFT_FRONT + RIGHT_FRONT ] = { name: "Left Front + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1_end.fbx" };
|
||||
|
||||
|
||||
breakdanceStart = function() {
|
||||
breakdanceStart = function(model, location) {
|
||||
print("breakdanceStart...");
|
||||
createOverlays();
|
||||
createPuppet();
|
||||
createPuppet(model, location);
|
||||
}
|
||||
|
||||
breakdanceUpdate = function(deltaTime) {
|
||||
|
|
54
examples/toys/bubblewand/bubble.js
Normal file
54
examples/toys/bubblewand/bubble.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
// bubble.js
|
||||
// part of bubblewand
|
||||
//
|
||||
// Created by James B. Pollack @imgntn -- 09/03/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// example of a nested entity. it doesn't do much now besides delete itself if it collides with something (bubbles are fragile! it would be cool if it sometimes merged with other bubbbles it hit)
|
||||
// todo: play bubble sounds from the bubble itself instead of the wand.
|
||||
// blocker: needs some sound fixes and a way to find its own position before unload for spatialization
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
(function() {
|
||||
// Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js");
|
||||
// Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js");
|
||||
|
||||
//var popSound;
|
||||
this.preload = function(entityID) {
|
||||
// print('bubble preload')
|
||||
this.entityID = entityID;
|
||||
// popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav");
|
||||
|
||||
}
|
||||
|
||||
this.collisionWithEntity = function(myID, otherID, collision) {
|
||||
//if(Entites.getEntityProperties(otherID).userData.objectType==='') { merge bubbles?}
|
||||
Entities.deleteEntity(myID);
|
||||
// this.burstBubbleSound(collision.contactPoint)
|
||||
|
||||
};
|
||||
|
||||
this.unload = function(entityID) {
|
||||
// this.properties = Entities.getEntityProperties(entityID);
|
||||
//var location = this.properties.position;
|
||||
//this.burstBubbleSound();
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.burstBubbleSound = function(location) {
|
||||
|
||||
// var audioOptions = {
|
||||
// volume: 0.5,
|
||||
// position: location
|
||||
// }
|
||||
|
||||
//Audio.playSound(popSound, audioOptions);
|
||||
|
||||
}
|
||||
|
||||
|
||||
})
|
42
examples/toys/bubblewand/createWand.js
Normal file
42
examples/toys/bubblewand/createWand.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
// createWand.js
|
||||
// part of bubblewand
|
||||
//
|
||||
// Created by James B. Pollack @imgntn -- 09/03/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Loads a wand model and attaches the bubble wand behavior.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
|
||||
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js");
|
||||
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js");
|
||||
|
||||
var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx?" + randInt(0, 10000);
|
||||
var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?" + randInt(1, 100500)
|
||||
|
||||
|
||||
//create the wand in front of the avatar
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||
var wand = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: wandModel,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 1,
|
||||
z: 0.1
|
||||
},
|
||||
//must be enabled to be grabbable in the physics engine
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
//Entities.deleteEntity(wand);
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
317
examples/toys/bubblewand/wand.js
Normal file
317
examples/toys/bubblewand/wand.js
Normal file
|
@ -0,0 +1,317 @@
|
|||
// wand.js
|
||||
// part of bubblewand
|
||||
//
|
||||
// Created by James B. Pollack @imgntn -- 09/03/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Makes bubbles when you wave the object around, or hold it near your mouth and make noise into the microphone.
|
||||
//
|
||||
// For the example, it's attached to a wand -- but you can attach it to whatever entity you want. I dream of BubbleBees :) bzzzz...pop!
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
function convertRange(value, r1, r2) {
|
||||
return (value - r1[0]) * (r2[1] - r2[0]) / (r1[1] - r1[0]) + r2[0];
|
||||
}
|
||||
|
||||
(function() {
|
||||
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js");
|
||||
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js");
|
||||
|
||||
var bubbleModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx";
|
||||
var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(1, 10000);
|
||||
var popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav");
|
||||
|
||||
var TARGET_SIZE = 0.4;
|
||||
var TARGET_COLOR = {
|
||||
red: 128,
|
||||
green: 128,
|
||||
blue: 128
|
||||
};
|
||||
var TARGET_COLOR_HIT = {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var HAND_SIZE = 0.25;
|
||||
var leftCubePosition = MyAvatar.getLeftPalmPosition();
|
||||
var rightCubePosition = MyAvatar.getRightPalmPosition();
|
||||
|
||||
var leftHand = Overlays.addOverlay("cube", {
|
||||
position: leftCubePosition,
|
||||
size: HAND_SIZE,
|
||||
color: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
},
|
||||
alpha: 1,
|
||||
solid: false
|
||||
});
|
||||
|
||||
var rightHand = Overlays.addOverlay("cube", {
|
||||
position: rightCubePosition,
|
||||
size: HAND_SIZE,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
alpha: 1,
|
||||
solid: false
|
||||
});
|
||||
|
||||
var gustZoneOverlay = Overlays.addOverlay("cube", {
|
||||
position: getGustDetectorPosition(),
|
||||
size: TARGET_SIZE,
|
||||
color: TARGET_COLOR,
|
||||
alpha: 1,
|
||||
solid: false
|
||||
});
|
||||
|
||||
|
||||
function getGustDetectorPosition() {
|
||||
//put the zone in front of your avatar's face
|
||||
var DISTANCE_IN_FRONT = 0.2;
|
||||
var DISTANCE_UP = 0.5;
|
||||
var DISTANCE_TO_SIDE = 0.0;
|
||||
|
||||
var up = Quat.getUp(MyAvatar.orientation);
|
||||
var front = Quat.getFront(MyAvatar.orientation);
|
||||
var right = Quat.getRight(MyAvatar.orientation);
|
||||
|
||||
var upOffset = Vec3.multiply(up, DISTANCE_UP);
|
||||
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
|
||||
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
|
||||
|
||||
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), upOffset);
|
||||
var position = Vec3.sum(MyAvatar.position, offset);
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
var BUBBLE_GRAVITY = {
|
||||
x: 0,
|
||||
y: -0.05,
|
||||
z: 0
|
||||
}
|
||||
|
||||
|
||||
var wandEntity = this;
|
||||
|
||||
this.preload = function(entityID) {
|
||||
// print('PRELOAD')
|
||||
this.entityID = entityID;
|
||||
this.properties = Entities.getEntityProperties(this.entityID);
|
||||
}
|
||||
|
||||
this.unload = function(entityID) {
|
||||
Overlays.deleteOverlay(leftHand);
|
||||
Overlays.deleteOverlay(rightHand);
|
||||
Overlays.deleteOverlay(gustZoneOverlay)
|
||||
Entities.editEntity(entityID, {
|
||||
name: ""
|
||||
});
|
||||
Script.update.disconnect(BubbleWand.update);
|
||||
Entities.deleteEntity(BubbleWand.currentBubble);
|
||||
while (BubbleWand.bubbles.length > 0) {
|
||||
Entities.deleteEntity(BubbleWand.bubbles.pop());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
var BubbleWand = {
|
||||
bubbles: [],
|
||||
currentBubble: null,
|
||||
update: function() {
|
||||
BubbleWand.internalUpdate();
|
||||
},
|
||||
internalUpdate: function() {
|
||||
var _t = this;
|
||||
//get the current position of the wand
|
||||
var properties = Entities.getEntityProperties(wandEntity.entityID);
|
||||
var wandPosition = properties.position;
|
||||
|
||||
//debug overlays for mouth mode
|
||||
var leftHandPos = MyAvatar.getLeftPalmPosition();
|
||||
var rightHandPos = MyAvatar.getRightPalmPosition();
|
||||
|
||||
Overlays.editOverlay(leftHand, {
|
||||
position: leftHandPos
|
||||
});
|
||||
Overlays.editOverlay(rightHand, {
|
||||
position: rightHandPos
|
||||
});
|
||||
|
||||
//if the wand is in the gust detector, activate mouth mode and change the overlay color
|
||||
var hitTargetWithWand = findSphereSphereHit(wandPosition, HAND_SIZE / 2, getGustDetectorPosition(), TARGET_SIZE / 2)
|
||||
|
||||
var mouthMode;
|
||||
if (hitTargetWithWand) {
|
||||
Overlays.editOverlay(gustZoneOverlay, {
|
||||
position: getGustDetectorPosition(),
|
||||
color: TARGET_COLOR_HIT
|
||||
})
|
||||
mouthMode = true;
|
||||
|
||||
} else {
|
||||
Overlays.editOverlay(gustZoneOverlay, {
|
||||
position: getGustDetectorPosition(),
|
||||
color: TARGET_COLOR
|
||||
})
|
||||
mouthMode = false;
|
||||
}
|
||||
|
||||
var volumeLevel = MyAvatar.audioAverageLoudness;
|
||||
//volume numbers are pretty large, so lets scale them down.
|
||||
var convertedVolume = convertRange(volumeLevel, [0, 5000], [0, 10]);
|
||||
|
||||
// default is 'wave mode', where waving the object around grows the bubbles
|
||||
var velocity = Vec3.subtract(wandPosition, BubbleWand.lastPosition)
|
||||
|
||||
//store the last position of the wand for velocity calculations
|
||||
_t.lastPosition = wandPosition;
|
||||
|
||||
// velocity numbers are pretty small, so lets make them a bit bigger
|
||||
var velocityStrength = Vec3.length(velocity) * 100;
|
||||
|
||||
if (velocityStrength > 10) {
|
||||
velocityStrength = 10
|
||||
}
|
||||
|
||||
//actually grow the bubble
|
||||
var dimensions = Entities.getEntityProperties(_t.currentBubble).dimensions;
|
||||
|
||||
if (velocityStrength > 1 || convertedVolume > 1) {
|
||||
|
||||
//add some variation in bubble sizes
|
||||
var bubbleSize = randInt(1, 5);
|
||||
bubbleSize = bubbleSize / 10;
|
||||
|
||||
//release the bubble if its dimensions are bigger than the bubble size
|
||||
if (dimensions.x > bubbleSize) {
|
||||
//bubbles pop after existing for a bit -- so set a random lifetime
|
||||
var lifetime = randInt(3, 8);
|
||||
|
||||
//sound is somewhat unstable at the moment so this is commented out. really audio should be played by the bubbles, but there's a blocker.
|
||||
// Script.setTimeout(function() {
|
||||
// _t.burstBubbleSound(_t.currentBubble)
|
||||
// }, lifetime * 1000)
|
||||
|
||||
|
||||
//todo: angular velocity without the controller -- forward velocity for mouth mode bubbles
|
||||
// var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip);
|
||||
|
||||
Entities.editEntity(_t.currentBubble, {
|
||||
velocity: Vec3.normalize(velocity),
|
||||
// angularVelocity: Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip),
|
||||
lifetime: lifetime
|
||||
});
|
||||
|
||||
//release the bubble -- when we create a new bubble, it will carry on and this update loop will affect the new bubble
|
||||
BubbleWand.spawnBubble();
|
||||
|
||||
return
|
||||
} else {
|
||||
if (mouthMode) {
|
||||
dimensions.x += 0.015 * convertedVolume;
|
||||
dimensions.y += 0.015 * convertedVolume;
|
||||
dimensions.z += 0.015 * convertedVolume;
|
||||
|
||||
} else {
|
||||
dimensions.x += 0.015 * velocityStrength;
|
||||
dimensions.y += 0.015 * velocityStrength;
|
||||
dimensions.z += 0.015 * velocityStrength;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if (dimensions.x >= 0.02) {
|
||||
dimensions.x -= 0.001;
|
||||
dimensions.y -= 0.001;
|
||||
dimensions.z -= 0.001;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//update the bubble to stay with the wand tip
|
||||
Entities.editEntity(_t.currentBubble, {
|
||||
position: _t.wandTipPosition,
|
||||
dimensions: dimensions
|
||||
});
|
||||
|
||||
},
|
||||
burstBubbleSound: function(bubble) {
|
||||
//we want to play the sound at the same location and orientation as the bubble
|
||||
var position = Entities.getEntityProperties(bubble).position;
|
||||
var orientation = Entities.getEntityProperties(bubble).orientation;
|
||||
|
||||
//set the options for the audio injector
|
||||
var audioOptions = {
|
||||
volume: 0.5,
|
||||
position: position,
|
||||
orientation: orientation
|
||||
}
|
||||
|
||||
|
||||
//var audioInjector = Audio.playSound(popSound, audioOptions);
|
||||
|
||||
//remove this bubble from the array to keep things clean
|
||||
var i = BubbleWand.bubbles.indexOf(bubble);
|
||||
if (i != -1) {
|
||||
BubbleWand.bubbles.splice(i, 1);
|
||||
}
|
||||
|
||||
},
|
||||
spawnBubble: function() {
|
||||
var _t = this;
|
||||
//create a new bubble at the tip of the wand
|
||||
//the tip of the wand is going to be in a different place than the center, so we move in space relative to the model to find that position
|
||||
|
||||
var properties = Entities.getEntityProperties(wandEntity.entityID);
|
||||
var wandPosition = properties.position;
|
||||
var upVector = Quat.getUp(properties.rotation);
|
||||
var frontVector = Quat.getFront(properties.rotation);
|
||||
var upOffset = Vec3.multiply(upVector, 0.5);
|
||||
var forwardOffset = Vec3.multiply(frontVector, 0.1);
|
||||
var offsetVector = Vec3.sum(upOffset, forwardOffset);
|
||||
var wandTipPosition = Vec3.sum(wandPosition, offsetVector);
|
||||
_t.wandTipPosition = wandTipPosition;
|
||||
|
||||
//store the position of the tip on spawn for use in velocity calculations
|
||||
_t.lastPosition = wandTipPosition;
|
||||
|
||||
//create a bubble at the wand tip
|
||||
_t.currentBubble = Entities.addEntity({
|
||||
type: 'Model',
|
||||
modelURL: bubbleModel,
|
||||
position: wandTipPosition,
|
||||
dimensions: {
|
||||
x: 0.01,
|
||||
y: 0.01,
|
||||
z: 0.01
|
||||
},
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
gravity: BUBBLE_GRAVITY,
|
||||
// collisionSoundURL:popSound,
|
||||
shapeType: "sphere",
|
||||
script: bubbleScript,
|
||||
});
|
||||
|
||||
//add this bubble to an array of bubbles so we can keep track of them
|
||||
_t.bubbles.push(_t.currentBubble)
|
||||
|
||||
},
|
||||
init: function() {
|
||||
this.spawnBubble();
|
||||
Script.update.connect(BubbleWand.update);
|
||||
}
|
||||
}
|
||||
|
||||
BubbleWand.init();
|
||||
|
||||
})
|
|
@ -3,62 +3,95 @@ type = body+head
|
|||
scale = 1
|
||||
filename = defaultAvatar_full/defaultAvatar_full.fbx
|
||||
texdir = defaultAvatar_full/textures
|
||||
joint = jointRightHand = RightHand
|
||||
joint = jointNeck = Head
|
||||
joint = jointLeftHand = LeftHand
|
||||
joint = jointRoot = Hips
|
||||
joint = jointHead = HeadTop_End
|
||||
joint = jointRightHand = RightHand
|
||||
joint = jointLean = Spine
|
||||
joint = jointLeftHand = LeftHand
|
||||
freeJoint = LeftArm
|
||||
freeJoint = LeftForeArm
|
||||
freeJoint = RightArm
|
||||
freeJoint = RightForeArm
|
||||
jointIndex = LeftHand = 35
|
||||
jointIndex = Reye = 3
|
||||
jointIndex = Hips = 10
|
||||
jointIndex = LeftHandIndex1 = 36
|
||||
jointIndex = LeftHandIndex2 = 37
|
||||
jointIndex = LeftHandIndex3 = 38
|
||||
jointIndex = LeftHandIndex4 = 39
|
||||
jointIndex = LeftShoulder = 32
|
||||
jointIndex = RightLeg = 12
|
||||
jointIndex = Grp_blendshapes = 0
|
||||
jointIndex = Leye = 4
|
||||
jointIndex = headphone = 8
|
||||
jointIndex = RightForeArm = 26
|
||||
jointIndex = Spine = 21
|
||||
jointIndex = LeftFoot = 18
|
||||
jointIndex = RightToeBase = 14
|
||||
jointIndex = face = 1
|
||||
jointIndex = LeftToe_End = 20
|
||||
jointIndex = Spine1 = 22
|
||||
jointIndex = body = 9
|
||||
jointIndex = Spine2 = 23
|
||||
jointIndex = RightUpLeg = 11
|
||||
jointIndex = top1 = 7
|
||||
jointIndex = Neck = 40
|
||||
jointIndex = HeadTop_End = 42
|
||||
jointIndex = RightShoulder = 24
|
||||
jointIndex = RightArm = 25
|
||||
jointIndex = Head = 41
|
||||
jointIndex = LeftLeg = 17
|
||||
jointIndex = LeftForeArm = 34
|
||||
jointIndex = hair = 6
|
||||
jointIndex = RightHand = 27
|
||||
jointIndex = LeftToeBase = 19
|
||||
jointIndex = LeftUpLeg = 16
|
||||
jointIndex = mouth = 2
|
||||
jointIndex = RightFoot = 13
|
||||
jointIndex = LeftArm = 33
|
||||
jointIndex = shield = 5
|
||||
jointIndex = RightHandIndex1 = 28
|
||||
jointIndex = RightHandIndex2 = 29
|
||||
jointIndex = RightToe_End = 15
|
||||
jointIndex = RightHandIndex3 = 30
|
||||
jointIndex = RightHandIndex4 = 31
|
||||
bs = MouthFrown_R = Mouth.MouthFrown_R = 1
|
||||
bs = EyeOpen_L = Leye1.EyeOpen_L = 1
|
||||
bs = LipsLowerDown_L = Mouth.LipsLowerDown = 0.5
|
||||
bs = LipsStretch_L = Mouth.LipsStretch_L = 1
|
||||
bs = MouthLeft = Mouth.MouthLeft = 1
|
||||
bs = MouthSmile_L = Mouth.MouthSmile_L = 1
|
||||
bs = Sneer_R = Mouth.Sneer = 0.61
|
||||
bs = LipsPucker = Mouth.LipsPucker = 1
|
||||
bs = EyeOpen_R = Reye1.EyeOut_R = 1
|
||||
bs = LipsLowerDown_R = Mouth.LipsLowerDown = 0.43
|
||||
bs = LipsStretch_R = Mouth.LipsStretch_R = 1
|
||||
bs = MouthSmile_R = Mouth.MouthSmile_R = 1
|
||||
bs = LipsFunnel = Mouth.LipsFunnel = 1
|
||||
bs = EyeUp_L = Leye1.EyeUp_L = 1
|
||||
bs = MouthDimple_L = Mouth.MouthDimple_L = 1
|
||||
bs = Puff = Mouth.Puff = 1
|
||||
bs = EyeIn_L = Leye1.EyeIn_L = 1
|
||||
bs = EyeUp_R = Reye1.EyeUp_R = 0.99
|
||||
bs = MouthDimple_R = Mouth.MouthDimple_R = 1
|
||||
bs = MouthRight = Mouth.MouthRight = 1
|
||||
bs = EyeOut_L = Leye1.EyeOut_L = 1
|
||||
bs = JawOpen = Mouth.JawOpen = 1
|
||||
bs = EyeIn_R = Reye1.EyeIn_R = 1
|
||||
bs = BrowsD_L = Leye1.BrowsD_L = 1
|
||||
bs = EyeDown_L = Leye1.EyeDown_L = 1
|
||||
bs = EyeBlink_L = Leye1.EyeBlink_L = 1
|
||||
bs = EyeOut_R = Reye1.EyeOut_R = 1
|
||||
bs = LipsUpperUp_L = Mouth.LipsUpperUp = 0.49
|
||||
bs = MouthFrown_L = Mouth.MouthFrown_L = 1
|
||||
bs = EyeDown_R = Reye1.EyeDown_R = 1
|
||||
bs = BrowsD_R = Reye1.BrowsD_R = 1
|
||||
bs = EyeBlink_R = Reye1.EyeBlink_R = 1
|
||||
bs = LipsUpperUp_R = Mouth.LipsUpperUp = 0.47
|
||||
bs = Sneer_L = Mouth.Sneer = 0.5
|
||||
jointIndex = headphone = 7
|
||||
jointIndex = LeftUpLeg = 15
|
||||
jointIndex = Spine = 20
|
||||
jointIndex = LeftArm = 32
|
||||
jointIndex = Head = 40
|
||||
jointIndex = RightUpLeg = 10
|
||||
jointIndex = hair = 5
|
||||
jointIndex = Spine1 = 21
|
||||
jointIndex = RightHandIndex1 = 27
|
||||
jointIndex = Spine2 = 22
|
||||
jointIndex = RightHandIndex2 = 28
|
||||
jointIndex = RightHandIndex3 = 29
|
||||
jointIndex = RightHandIndex4 = 30
|
||||
jointIndex = RightToe_End = 14
|
||||
jointIndex = shield = 4
|
||||
jointIndex = LeftHandIndex1 = 35
|
||||
jointIndex = LeftHandIndex2 = 36
|
||||
jointIndex = RightHand = 26
|
||||
jointIndex = LeftHandIndex3 = 37
|
||||
jointIndex = LeftHandIndex4 = 38
|
||||
jointIndex = LeftShoulder = 31
|
||||
jointIndex = LeftHand = 34
|
||||
jointIndex = RightForeArm = 25
|
||||
jointIndex = RightLeg = 11
|
||||
jointIndex = RightFoot = 12
|
||||
jointIndex = mouth = 1
|
||||
jointIndex = LeftToe_End = 19
|
||||
jointIndex = Reye = 2
|
||||
jointIndex = Hips = 9
|
||||
jointIndex = RightToeBase = 13
|
||||
jointIndex = HeadTop_End = 41
|
||||
jointIndex = LeftFoot = 17
|
||||
jointIndex = RightShoulder = 23
|
||||
jointIndex = LeftLeg = 16
|
||||
jointIndex = Leye = 3
|
||||
jointIndex = LeftForeArm = 33
|
||||
jointIndex = face = 0
|
||||
jointIndex = body = 8
|
||||
jointIndex = LeftToeBase = 18
|
||||
jointIndex = RightArm = 24
|
||||
jointIndex = top1 = 6
|
||||
jointIndex = Neck = 39
|
||||
rx = 0
|
||||
ry = 0
|
||||
rz = 0
|
||||
tx = 0
|
||||
ty = 0
|
||||
tz = 0
|
||||
rx = 0
|
||||
|
|
Binary file not shown.
|
@ -622,6 +622,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// Setup the userInputMapper with the actions
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent);
|
||||
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
|
||||
if (state) {
|
||||
switch (action) {
|
||||
case UserInputMapper::Action::TOGGLE_MUTE:
|
||||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
|
||||
_keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
|
||||
|
@ -3070,9 +3079,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x * TREE_SCALE,
|
||||
rootDetails.y * TREE_SCALE,
|
||||
rootDetails.z * TREE_SCALE),
|
||||
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
|
||||
rootDetails.s * TREE_SCALE);
|
||||
|
||||
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
|
||||
|
||||
if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
|
||||
|
@ -3137,11 +3145,10 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x * TREE_SCALE,
|
||||
rootDetails.y * TREE_SCALE,
|
||||
rootDetails.z * TREE_SCALE),
|
||||
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
|
||||
rootDetails.s * TREE_SCALE);
|
||||
|
||||
|
||||
|
||||
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
|
||||
if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
|
||||
inView = true;
|
||||
|
@ -3160,7 +3167,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
} else if (unknownView) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of "
|
||||
<< perUnknownServer << " to send us jurisdiction.";
|
||||
<< perUnknownServer << " to send us jurisdiction.";
|
||||
}
|
||||
|
||||
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
|
||||
|
@ -5101,11 +5108,10 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index)
|
|||
}
|
||||
|
||||
void Application::crashApplication() {
|
||||
qCDebug(interfaceapp) << "Intentionally crashed Interface";
|
||||
QObject* object = nullptr;
|
||||
bool value = object->isWindowType();
|
||||
Q_UNUSED(value);
|
||||
|
||||
qCDebug(interfaceapp) << "Intentionally crashed Interface";
|
||||
}
|
||||
|
||||
void Application::setActiveDisplayPlugin(const QString& pluginName) {
|
||||
|
|
|
@ -447,7 +447,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false,
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, true,
|
||||
avatar, SLOT(setEnableRigAnimations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false,
|
||||
avatar, SLOT(setEnableAnimGraph(bool)));
|
||||
|
|
|
@ -173,10 +173,9 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
|
||||
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
|
||||
float frameFraction = glm::fract(frameIndex);
|
||||
assert(_rig->getJointStateCount() >= _jointMappings.size());
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
if (mapping != -1) { // allow missing bones
|
||||
_rig->setJointRotationInConstrainedFrame(mapping,
|
||||
safeMix(floorFrame.rotations.at(i),
|
||||
ceilFrame.rotations.at(i),
|
||||
|
|
|
@ -105,7 +105,7 @@ const char IS_FINGER_POINTING_FLAG = 4;
|
|||
static const float MAX_AVATAR_SCALE = 1000.0f;
|
||||
static const float MIN_AVATAR_SCALE = .005f;
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
|
||||
|
||||
const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||
const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000;
|
||||
|
@ -114,7 +114,7 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000;
|
|||
const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default");
|
||||
|
||||
// how often should we send a full report about joint rotations, even if they haven't changed?
|
||||
const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02;
|
||||
const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02f;
|
||||
// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer
|
||||
const float AVATAR_MIN_ROTATION_DOT = 0.9999999f;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <gpu/Batch.h>
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <ObjectMotionState.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
|
@ -25,15 +26,31 @@ EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID,
|
|||
return std::make_shared<RenderableBoxEntityItem>(entityID, properties);
|
||||
}
|
||||
|
||||
void RenderableBoxEntityItem::setUserData(const QString& value) {
|
||||
if (value != getUserData()) {
|
||||
BoxEntityItem::setUserData(value);
|
||||
_procedural.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableBoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Box);
|
||||
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
|
||||
|
||||
if (!_procedural) {
|
||||
_procedural.reset(new ProceduralInfo(this));
|
||||
}
|
||||
|
||||
if (_procedural->ready()) {
|
||||
_procedural->prepare(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderUnitCube(batch);
|
||||
} else {
|
||||
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
|
||||
}
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
|
||||
#include <BoxEntityItem.h>
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "RenderableProceduralItem.h"
|
||||
|
||||
class RenderableBoxEntityItem : public BoxEntityItem {
|
||||
class RenderableBoxEntityItem : public BoxEntityItem, RenderableProceduralItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
@ -24,6 +25,7 @@ public:
|
|||
{ }
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual void setUserData(const QString& value);
|
||||
|
||||
SIMPLE_RENDERABLE()
|
||||
};
|
||||
|
|
137
libraries/entities-renderer/src/RenderableProceduralItem.cpp
Normal file
137
libraries/entities-renderer/src/RenderableProceduralItem.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/09/05
|
||||
// Copyright 2013-2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "RenderableProceduralItem.h"
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <ShaderCache.h>
|
||||
#include <EntityItem.h>
|
||||
#include <TextureCache.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include "RenderableProceduralItemShader.h"
|
||||
#include "../render-utils/simple_vert.h"
|
||||
|
||||
static const char* const UNIFORM_TIME_NAME= "iGlobalTime";
|
||||
static const char* const UNIFORM_SCALE_NAME = "iWorldScale";
|
||||
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
||||
|
||||
RenderableProceduralItem::ProceduralInfo::ProceduralInfo(EntityItem* entity) : _entity(entity) {
|
||||
QJsonObject userData;
|
||||
{
|
||||
const QString& userDataJson = entity->getUserData();
|
||||
if (userDataJson.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QJsonParseError parseError;
|
||||
auto doc = QJsonDocument::fromJson(userDataJson.toUtf8(), &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
return;
|
||||
}
|
||||
userData = doc.object();
|
||||
}
|
||||
|
||||
// Example
|
||||
//{
|
||||
// "ProceduralEntity": {
|
||||
// "shaderUrl": "file:///C:/Users/bdavis/Git/hifi/examples/shaders/test.fs",
|
||||
// "color" : "#FFFFFF"
|
||||
// }
|
||||
//}
|
||||
|
||||
auto proceduralData = userData[PROCEDURAL_USER_DATA_KEY];
|
||||
if (proceduralData.isNull()) {
|
||||
return;
|
||||
}
|
||||
auto proceduralDataObject = proceduralData.toObject();
|
||||
QString shaderUrl = proceduralDataObject["shaderUrl"].toString();
|
||||
_shaderUrl = QUrl(shaderUrl);
|
||||
if (!_shaderUrl.isValid()) {
|
||||
qWarning() << "Invalid shader URL: " << shaderUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
_shaderPath = _shaderUrl.toLocalFile();
|
||||
qDebug() << "Shader path: " << _shaderPath;
|
||||
if (!QFile(_shaderPath).exists()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Shader url: " << _shaderUrl;
|
||||
_networkShader = ShaderCache::instance().getShader(_shaderUrl);
|
||||
}
|
||||
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
bool RenderableProceduralItem::ProceduralInfo::ready() {
|
||||
if (!_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_shaderPath.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_networkShader) {
|
||||
return _networkShader->isLoaded();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderableProceduralItem::ProceduralInfo::prepare(gpu::Batch& batch) {
|
||||
if (_shaderUrl.isLocalFile()) {
|
||||
auto lastModified = QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
|
||||
if (lastModified > _shaderModified) {
|
||||
QFile file(_shaderPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
_shaderSource = QTextStream(&file).readAll();
|
||||
_pipelineDirty = true;
|
||||
_shaderModified = lastModified;
|
||||
}
|
||||
} else if (_networkShader && _networkShader->isLoaded()) {
|
||||
_shaderSource = _networkShader->_source;
|
||||
}
|
||||
|
||||
if (!_pipeline || _pipelineDirty) {
|
||||
_pipelineDirty = false;
|
||||
if (!_vertexShader) {
|
||||
_vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert)));
|
||||
}
|
||||
QString framentShaderSource = SHADER_TEMPLATE.arg(_shaderSource);
|
||||
_fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(framentShaderSource.toLocal8Bit().data())));
|
||||
_shader = gpu::ShaderPointer(gpu::Shader::createProgram(_vertexShader, _fragmentShader));
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT));
|
||||
gpu::Shader::makeProgram(*_shader, slotBindings);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(false,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, state));
|
||||
_timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME);
|
||||
_scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME);
|
||||
_start = usecTimestampNow();
|
||||
}
|
||||
|
||||
batch.setPipeline(_pipeline);
|
||||
float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND;
|
||||
batch._glUniform1f(_timeSlot, time);
|
||||
auto scale = _entity->getDimensions();
|
||||
batch._glUniform3f(_scaleSlot, scale.x, scale.y, scale.z);
|
||||
batch.setResourceTexture(DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT, DependencyManager::get<TextureCache>()->getNormalFittingTexture());
|
||||
}
|
51
libraries/entities-renderer/src/RenderableProceduralItem.h
Normal file
51
libraries/entities-renderer/src/RenderableProceduralItem.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/09/05
|
||||
// Copyright 2013-2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_RenderableProcedrualItem_h
|
||||
#define hifi_RenderableProcedrualItem_h
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <ShaderCache.h>
|
||||
#include <gpu/Shader.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
class EntityItem;
|
||||
|
||||
class RenderableProceduralItem {
|
||||
protected:
|
||||
struct ProceduralInfo {
|
||||
ProceduralInfo(EntityItem* entity);
|
||||
bool ready();
|
||||
void prepare(gpu::Batch& batch);
|
||||
|
||||
bool _enabled{ false };
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::ShaderPointer _vertexShader;
|
||||
gpu::ShaderPointer _fragmentShader;
|
||||
gpu::ShaderPointer _shader;
|
||||
QString _shaderSource;
|
||||
QString _shaderPath;
|
||||
QUrl _shaderUrl;
|
||||
quint64 _shaderModified{ 0 };
|
||||
bool _pipelineDirty{ true };
|
||||
int32_t _timeSlot{ gpu::Shader::INVALID_LOCATION };
|
||||
int32_t _scaleSlot{ gpu::Shader::INVALID_LOCATION };
|
||||
uint64_t _start{ 0 };
|
||||
NetworkShaderPointer _networkShader;
|
||||
EntityItem* _entity;
|
||||
};
|
||||
|
||||
QSharedPointer<ProceduralInfo> _procedural;
|
||||
};
|
||||
|
||||
#endif
|
329
libraries/entities-renderer/src/RenderableProceduralItemShader.h
Normal file
329
libraries/entities-renderer/src/RenderableProceduralItemShader.h
Normal file
|
@ -0,0 +1,329 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/09/05
|
||||
// Copyright 2013-2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Shader includes portions of webgl-noise:
|
||||
// Description : Array and textureless GLSL 2D/3D/4D simplex
|
||||
// noise functions.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
const QString SHADER_TEMPLATE = R"SCRIBE(#version 410 core
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
layout(location = 1) out vec4 _fragColor1;
|
||||
layout(location = 2) out vec4 _fragColor2;
|
||||
|
||||
// the glow intensity
|
||||
uniform float glowIntensity;
|
||||
|
||||
// the alpha threshold
|
||||
uniform float alphaThreshold;
|
||||
|
||||
uniform sampler2D normalFittingMap;
|
||||
|
||||
vec3 bestFitNormal(vec3 normal) {
|
||||
vec3 absNorm = abs(normal);
|
||||
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
|
||||
|
||||
vec2 texcoord = (absNorm.z < maxNAbs ?
|
||||
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
|
||||
absNorm.xy);
|
||||
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
|
||||
texcoord.y /= texcoord.x;
|
||||
vec3 cN = normal / maxNAbs;
|
||||
float fittingScale = texture(normalFittingMap, texcoord).a;
|
||||
cN *= fittingScale;
|
||||
return (cN * 0.5 + 0.5);
|
||||
}
|
||||
|
||||
|
||||
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
||||
const float DEFAULT_SHININESS = 10;
|
||||
|
||||
void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) {
|
||||
if (alpha != glowIntensity) {
|
||||
discard;
|
||||
}
|
||||
|
||||
_fragColor0 = vec4(diffuse.rgb, alpha);
|
||||
_fragColor1 = vec4(bestFitNormal(normal), 0.5);
|
||||
_fragColor2 = vec4(emissive, shininess / 128.0);
|
||||
}
|
||||
|
||||
float mod289(float x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec2 mod289(vec2 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec3 mod289(vec3 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec4 mod289(vec4 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
float permute(float x) {
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
vec3 permute(vec3 x) {
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
vec4 permute(vec4 x) {
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
float taylorInvSqrt(float r) {
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
vec4 taylorInvSqrt(vec4 r) {
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
vec4 grad4(float j, vec4 ip) {
|
||||
const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
|
||||
vec4 p, s;
|
||||
|
||||
p.xyz = floor(fract(vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
|
||||
p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
|
||||
s = vec4(lessThan(p, vec4(0.0)));
|
||||
p.xyz = p.xyz + (s.xyz * 2.0 - 1.0) * s.www;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// (sqrt(5) - 1)/4 = F4, used once below
|
||||
#define F4 0.309016994374947451
|
||||
|
||||
float snoise(vec4 v) {
|
||||
const vec4 C = vec4(0.138196601125011, // (5 - sqrt(5))/20 G4
|
||||
0.276393202250021, // 2 * G4
|
||||
0.414589803375032, // 3 * G4
|
||||
-0.447213595499958); // -1 + 4 * G4
|
||||
|
||||
// First corner
|
||||
vec4 i = floor(v + dot(v, vec4(F4)));
|
||||
vec4 x0 = v - i + dot(i, C.xxxx);
|
||||
|
||||
// Other corners
|
||||
|
||||
// Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
|
||||
vec4 i0;
|
||||
vec3 isX = step(x0.yzw, x0.xxx);
|
||||
vec3 isYZ = step(x0.zww, x0.yyz);
|
||||
i0.x = isX.x + isX.y + isX.z;
|
||||
i0.yzw = 1.0 - isX;
|
||||
i0.y += isYZ.x + isYZ.y;
|
||||
i0.zw += 1.0 - isYZ.xy;
|
||||
i0.z += isYZ.z;
|
||||
i0.w += 1.0 - isYZ.z;
|
||||
|
||||
// i0 now contains the unique values 0,1,2,3 in each channel
|
||||
vec4 i3 = clamp(i0, 0.0, 1.0);
|
||||
vec4 i2 = clamp(i0 - 1.0, 0.0, 1.0);
|
||||
vec4 i1 = clamp(i0 - 2.0, 0.0, 1.0);
|
||||
|
||||
vec4 x1 = x0 - i1 + C.xxxx;
|
||||
vec4 x2 = x0 - i2 + C.yyyy;
|
||||
vec4 x3 = x0 - i3 + C.zzzz;
|
||||
vec4 x4 = x0 + C.wwww;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i);
|
||||
float j0 = permute(permute(permute(permute(i.w) + i.z) + i.y) + i.x);
|
||||
vec4 j1 = permute(
|
||||
permute(
|
||||
permute(
|
||||
permute(i.w + vec4(i1.w, i2.w, i3.w, 1.0)) + i.z
|
||||
+ vec4(i1.z, i2.z, i3.z, 1.0)) + i.y
|
||||
+ vec4(i1.y, i2.y, i3.y, 1.0)) + i.x
|
||||
+ vec4(i1.x, i2.x, i3.x, 1.0));
|
||||
|
||||
// Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
|
||||
// 7*7*6 = 294, which is close to the ring size 17*17 = 289.
|
||||
vec4 ip = vec4(1.0 / 294.0, 1.0 / 49.0, 1.0 / 7.0, 0.0);
|
||||
|
||||
vec4 p0 = grad4(j0, ip);
|
||||
vec4 p1 = grad4(j1.x, ip);
|
||||
vec4 p2 = grad4(j1.y, ip);
|
||||
vec4 p3 = grad4(j1.z, ip);
|
||||
vec4 p4 = grad4(j1.w, ip);
|
||||
|
||||
// Normalise gradients
|
||||
vec4 norm = taylorInvSqrt(
|
||||
vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
||||
p0 *= norm.x;
|
||||
p1 *= norm.y;
|
||||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
p4 *= taylorInvSqrt(dot(p4, p4));
|
||||
|
||||
// Mix contributions from the five corners
|
||||
vec3 m0 = max(0.6 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
|
||||
vec2 m1 = max(0.6 - vec2(dot(x3, x3), dot(x4, x4)), 0.0);
|
||||
m0 = m0 * m0;
|
||||
m1 = m1 * m1;
|
||||
return 49.0
|
||||
* (dot(m0 * m0, vec3(dot(p0, x0), dot(p1, x1), dot(p2, x2)))
|
||||
+ dot(m1 * m1, vec2(dot(p3, x3), dot(p4, x4))));
|
||||
|
||||
}
|
||||
|
||||
float snoise(vec3 v) {
|
||||
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
|
||||
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
||||
|
||||
// First corner
|
||||
vec3 i = floor(v + dot(v, C.yyy));
|
||||
vec3 x0 = v - i + dot(i, C.xxx);
|
||||
|
||||
// Other corners
|
||||
vec3 g = step(x0.yzx, x0.xyz);
|
||||
vec3 l = 1.0 - g;
|
||||
vec3 i1 = min(g.xyz, l.zxy);
|
||||
vec3 i2 = max(g.xyz, l.zxy);
|
||||
|
||||
vec3 x1 = x0 - i1 + C.xxx;
|
||||
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
|
||||
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
|
||||
|
||||
// Permutations
|
||||
i = mod289(i);
|
||||
vec4 p = permute(
|
||||
permute(
|
||||
permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y
|
||||
+ vec4(0.0, i1.y, i2.y, 1.0)) + i.x
|
||||
+ vec4(0.0, i1.x, i2.x, 1.0));
|
||||
|
||||
// Gradients: 7x7 points over a square, mapped onto an octahedron.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
|
||||
float n_ = 0.142857142857; // 1.0/7.0
|
||||
vec3 ns = n_ * D.wyz - D.xzx;
|
||||
|
||||
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
|
||||
|
||||
vec4 x_ = floor(j * ns.z);
|
||||
vec4 y_ = floor(j - 7.0 * x_); // mod(j,N)
|
||||
|
||||
vec4 x = x_ * ns.x + ns.yyyy;
|
||||
vec4 y = y_ * ns.x + ns.yyyy;
|
||||
vec4 h = 1.0 - abs(x) - abs(y);
|
||||
|
||||
vec4 b0 = vec4(x.xy, y.xy);
|
||||
vec4 b1 = vec4(x.zw, y.zw);
|
||||
|
||||
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
|
||||
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
|
||||
vec4 s0 = floor(b0) * 2.0 + 1.0;
|
||||
vec4 s1 = floor(b1) * 2.0 + 1.0;
|
||||
vec4 sh = -step(h, vec4(0.0));
|
||||
|
||||
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
||||
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
||||
|
||||
vec3 p0 = vec3(a0.xy, h.x);
|
||||
vec3 p1 = vec3(a0.zw, h.y);
|
||||
vec3 p2 = vec3(a1.xy, h.z);
|
||||
vec3 p3 = vec3(a1.zw, h.w);
|
||||
|
||||
//Normalise gradients
|
||||
vec4 norm = taylorInvSqrt(
|
||||
vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
||||
p0 *= norm.x;
|
||||
p1 *= norm.y;
|
||||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
// Mix final noise value
|
||||
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)),
|
||||
0.0);
|
||||
m = m * m;
|
||||
return 42.0
|
||||
* dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
}
|
||||
|
||||
float snoise(vec2 v) {
|
||||
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
|
||||
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
|
||||
-0.577350269189626, // -1.0 + 2.0 * C.x
|
||||
0.024390243902439); // 1.0 / 41.0
|
||||
// First corner
|
||||
vec2 i = floor(v + dot(v, C.yy));
|
||||
vec2 x0 = v - i + dot(i, C.xx);
|
||||
|
||||
// Other corners
|
||||
vec2 i1;
|
||||
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||
vec4 x12 = x0.xyxy + C.xxzz;
|
||||
x12.xy -= i1;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
vec3 p = permute(
|
||||
permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
|
||||
|
||||
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)),
|
||||
0.0);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
|
||||
|
||||
vec3 x = 2.0 * fract(p * C.www) - 1.0;
|
||||
vec3 h = abs(x) - 0.5;
|
||||
vec3 ox = floor(x + 0.5);
|
||||
vec3 a0 = x - ox;
|
||||
|
||||
// Normalise gradients implicitly by scaling m
|
||||
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
|
||||
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
|
||||
|
||||
// Compute final noise value at P
|
||||
vec3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec3 _color;
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _position;
|
||||
|
||||
// TODO add more uniforms
|
||||
uniform float iGlobalTime; // shader playback time (in seconds)
|
||||
uniform vec3 iWorldScale; // the dimensions of the object being rendered
|
||||
|
||||
// TODO add support for textures
|
||||
// TODO document available inputs other than the uniforms
|
||||
// TODO provide world scale in addition to the untransformed position
|
||||
|
||||
%1
|
||||
|
||||
void main(void) {
|
||||
vec4 texel = getProceduralColor();
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(_normal),
|
||||
glowIntensity * texel.a,
|
||||
_color.rgb,
|
||||
DEFAULT_SPECULAR, DEFAULT_SHININESS,
|
||||
texel.rgb);
|
||||
}
|
||||
)SCRIBE";
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
|
@ -25,21 +26,38 @@ EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entity
|
|||
return std::make_shared<RenderableSphereEntityItem>(entityID, properties);
|
||||
}
|
||||
|
||||
void RenderableSphereEntityItem::setUserData(const QString& value) {
|
||||
if (value != getUserData()) {
|
||||
SphereEntityItem::setUserData(value);
|
||||
_procedural.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableSphereEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Sphere);
|
||||
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
|
||||
|
||||
// TODO: it would be cool to select different slices/stacks geometry based on the size of the sphere
|
||||
// and the distance to the viewer. This would allow us to reduce the triangle count for smaller spheres
|
||||
// that aren't close enough to see the tessellation and use larger triangle count for spheres that would
|
||||
// expose that effect
|
||||
const int SLICES = 15, STACKS = 15;
|
||||
static const int SLICES = 15, STACKS = 15;
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
|
||||
if (!_procedural) {
|
||||
_procedural.reset(new ProceduralInfo(this));
|
||||
}
|
||||
|
||||
if (_procedural->ready()) {
|
||||
_procedural->prepare(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, 0.5f, SLICES, STACKS, vec3(1));
|
||||
} else {
|
||||
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
|
||||
}
|
||||
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
#define hifi_RenderableSphereEntityItem_h
|
||||
|
||||
#include <SphereEntityItem.h>
|
||||
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "RenderableProceduralItem.h"
|
||||
|
||||
class RenderableSphereEntityItem : public SphereEntityItem {
|
||||
class RenderableSphereEntityItem : public SphereEntityItem, RenderableProceduralItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
@ -25,6 +25,7 @@ public:
|
|||
{ }
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual void setUserData(const QString& value);
|
||||
|
||||
SIMPLE_RENDERABLE();
|
||||
};
|
||||
|
|
|
@ -334,7 +334,7 @@ public:
|
|||
void setLocked(bool value) { _locked = value; }
|
||||
|
||||
const QString& getUserData() const { return _userData; }
|
||||
void setUserData(const QString& value) { _userData = value; }
|
||||
virtual void setUserData(const QString& value) { _userData = value; }
|
||||
|
||||
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
||||
void setSimulationOwner(const QUuid& id, quint8 priority);
|
||||
|
|
|
@ -595,6 +595,10 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
|
||||
mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
|
||||
|
||||
// TODO find a mechanism to allow users to navigate the context menu via
|
||||
mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(BUTTON_0, 0));
|
||||
mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(BUTTON_0, 1));
|
||||
|
||||
}
|
||||
|
||||
UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) {
|
||||
|
|
|
@ -162,6 +162,9 @@ public:
|
|||
ACTION1,
|
||||
ACTION2,
|
||||
|
||||
CONTEXT_MENU,
|
||||
TOGGLE_MUTE,
|
||||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
||||
|
|
|
@ -66,10 +66,15 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
|
|||
if (_linearTimeScale < MAX_TIMESCALE) {
|
||||
btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget);
|
||||
float offsetLength = offset.length();
|
||||
float speed = (offsetLength > FLT_EPSILON) ? glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED) : 0.0f;
|
||||
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
|
||||
|
||||
if (offsetLength > 0) {
|
||||
float speed = (offsetLength > FLT_EPSILON) ? glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED) : 0.0f;
|
||||
targetVelocity = (-speed / offsetLength) * offset;
|
||||
}
|
||||
|
||||
// this action is aggresively critically damped and defeats the current velocity
|
||||
rigidBody->setLinearVelocity((- speed / offsetLength) * offset);
|
||||
rigidBody->setLinearVelocity(targetVelocity);
|
||||
}
|
||||
|
||||
if (_angularTimeScale < MAX_TIMESCALE) {
|
||||
|
|
32
libraries/render-utils/src/ShaderCache.cpp
Normal file
32
libraries/render-utils/src/ShaderCache.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/05/26
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "ShaderCache.h"
|
||||
|
||||
NetworkShader::NetworkShader(const QUrl& url, bool delayLoad)
|
||||
: Resource(url, delayLoad) {};
|
||||
|
||||
void NetworkShader::downloadFinished(QNetworkReply* reply) {
|
||||
if (reply) {
|
||||
_source = reply->readAll();
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCache& ShaderCache::instance() {
|
||||
static ShaderCache _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
|
||||
return ResourceCache::getResource(url, QUrl(), false, nullptr).staticCast<NetworkShader>();
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::allReferencesCleared);
|
||||
}
|
||||
|
34
libraries/render-utils/src/ShaderCache.h
Normal file
34
libraries/render-utils/src/ShaderCache.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/05/26
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_ShaderCache_h
|
||||
#define hifi_ShaderCache_h
|
||||
|
||||
#include <ResourceCache.h>
|
||||
|
||||
class NetworkShader : public Resource {
|
||||
public:
|
||||
NetworkShader(const QUrl& url, bool delayLoad);
|
||||
virtual void downloadFinished(QNetworkReply* reply) override;
|
||||
|
||||
QByteArray _source;
|
||||
};
|
||||
|
||||
using NetworkShaderPointer = QSharedPointer<NetworkShader>;
|
||||
|
||||
class ShaderCache : public ResourceCache {
|
||||
public:
|
||||
static ShaderCache& instance();
|
||||
|
||||
NetworkShaderPointer getShader(const QUrl& url);
|
||||
|
||||
protected:
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) override;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue