Merge branch 'master' of github.com:highfidelity/hifi into MS16480_disableOverlays
34
README.md
|
@ -1,8 +1,8 @@
|
|||
High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR.
|
||||
|
||||
In this repository you'll find the source to many of the components in our
|
||||
alpha-stage virtual world. The project embraces distributed development
|
||||
and if you'd like to help, we'll pay you -- find out more at [Worklist.net](https://worklist.net).
|
||||
This repository contains the source to many of the components in our
|
||||
alpha-stage virtual world. The project embraces distributed development.
|
||||
If you'd like to help, we'll pay you -- find out more at [Worklist.net](https://worklist.net).
|
||||
If you find a small bug and have a fix, pull requests are welcome. If you'd
|
||||
like to get paid for your work, make sure you report the bug via a job on
|
||||
[Worklist.net](https://worklist.net).
|
||||
|
@ -32,9 +32,10 @@ Running Interface
|
|||
When you launch interface, you will automatically connect to our default domain: "root.highfidelity.io".
|
||||
|
||||
If you don't see anything, make sure your preferences are pointing to
|
||||
root.highfidelity.io (set your domain via Cmnd+D/Cntrl+D), if you still have no luck it's possible our servers are
|
||||
simply down; if you're experiencing a major bug, let us know by adding an issue to this repository.
|
||||
Make sure to include details about your computer and how to reproduce the bug.
|
||||
root.highfidelity.io (set your domain via Cmnd+D/Cntrl+D). If you still have no luck,
|
||||
it's possible our servers are down. If you're experiencing a major bug, let us know by
|
||||
adding an issue to this repository. Include details about your computer and how to
|
||||
reproduce the bug in your issue.
|
||||
|
||||
To move around in-world, use the arrow keys (and Shift + up/down to fly up or
|
||||
down) or W A S D, and E or C to fly up/down. All of the other possible options
|
||||
|
@ -48,7 +49,8 @@ you to run the full stack of the virtual world.
|
|||
In order to set up your own virtual world, you need to set up and run your own
|
||||
local "domain".
|
||||
|
||||
The domain-server gives a number different types of assignments to the assignment-client for different features: audio, avatars, voxels, particles, meta-voxels and models.
|
||||
The domain-server gives a number different types of assignments to the assignment-client
|
||||
for different features: audio, avatars, voxels, particles, meta-voxels and models.
|
||||
|
||||
Follow the instructions in the [build guide](BUILD.md) to build the various components.
|
||||
|
||||
|
@ -56,7 +58,8 @@ From the domain-server build directory, launch a domain-server.
|
|||
|
||||
./domain-server
|
||||
|
||||
Then, run an assignment-client. The assignment-client uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
|
||||
Then, run an assignment-client. The assignment-client uses localhost as its assignment-server
|
||||
and talks to it on port 40102 (the default domain-server port).
|
||||
|
||||
In a new Terminal window, run:
|
||||
|
||||
|
@ -64,13 +67,20 @@ In a new Terminal window, run:
|
|||
|
||||
Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal window.
|
||||
|
||||
This assignment-client will grab one assignment from the domain-server. You can tell the assignment-client what type you want it to be with the `-t` option. You can also run an assignment-client that forks off *n* assignment-clients with the `-n` option. The `-min` and `-max` options allow you to set a range of required assignment-clients, this allows you to have flexibility in the number of assignment-clients that are running. See `--help` for more options.
|
||||
This assignment-client will grab one assignment from the domain-server. You can tell the
|
||||
assignment-client what type you want it to be with the `-t` option. You can also run an
|
||||
assignment-client that forks off *n* assignment-clients with the `-n` option. The `-min`
|
||||
and `-max` options allow you to set a range of required assignment-clients. This allows
|
||||
you to have flexibility in the number of assignment-clients that are running.
|
||||
See `--help` for more options.
|
||||
|
||||
./assignment-client --min 6 --max 20
|
||||
|
||||
To test things out you'll want to run the Interface client.
|
||||
To test things out, you'll need to run the Interface client.
|
||||
|
||||
To access your local domain in Interface, open your Preferences -- on OS X this is available in the Interface menu, on Linux you'll find it in the File menu. Enter "localhost" in the "Domain server" field.
|
||||
To access your local domain in Interface, open your Preferences. On OS X, this is available
|
||||
in the Interface menu. On Linux, you'll find it in the File menu. Enter "localhost" in the
|
||||
"Domain server" field.
|
||||
|
||||
If everything worked you should see that you are connected to at least one server.
|
||||
If everything worked, you should see that you are connected to at least one server.
|
||||
Nice work!
|
||||
|
|
|
@ -566,7 +566,7 @@
|
|||
},
|
||||
{
|
||||
"id": "idleToWalkFwd",
|
||||
"interpTarget": 3,
|
||||
"interpTarget": 10,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "idleToWalkFwdOnDone", "state": "walkFwd" },
|
||||
|
@ -603,8 +603,8 @@
|
|||
},
|
||||
{
|
||||
"id": "walkBwd",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"interpTarget": 8,
|
||||
"interpDuration": 2,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
|
@ -621,8 +621,8 @@
|
|||
},
|
||||
{
|
||||
"id": "strafeRight",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"interpTarget": 20,
|
||||
"interpDuration": 1,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
|
@ -639,8 +639,8 @@
|
|||
},
|
||||
{
|
||||
"id": "strafeLeft",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"interpTarget": 20,
|
||||
"interpDuration": 1,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
|
|
777
interface/resources/avatar/bookmarks/avatarbookmarks.json
Normal file
|
@ -0,0 +1,777 @@
|
|||
{
|
||||
"Anime boy": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 6.915350914001465,
|
||||
"ageAsText": "0 hours 0 minutes 6 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.10961885005235672,
|
||||
"y": -0.19444090127944946,
|
||||
"z": -0.15760529041290283
|
||||
},
|
||||
"center": {
|
||||
"x": 2.6226043701171875e-06,
|
||||
"y": -0.13999652862548828,
|
||||
"z": -0.04999971389770508
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.21924294531345367,
|
||||
"y": 0.10888873785734177,
|
||||
"z": 0.2152111530303955
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.10962409526109695,
|
||||
"y": -0.0855521634221077,
|
||||
"z": 0.057605862617492676
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:27:53Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.21924294531345367,
|
||||
"y": 0.07768379896879196,
|
||||
"z": 0.2055898904800415
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 15,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{5d20c775-a0d7-4163-b158-4e0a784a4625}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "Wear these, and others will respect your authoritah.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Aviators",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306178314655,
|
||||
"lastEditedBy": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": 2.6226043701171875e-06,
|
||||
"y": -0.13999652862548828,
|
||||
"z": -0.04999971389770508
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9969173073768616,
|
||||
"x": -0.07845909893512726,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "40d879ec-93f0-4b4a-8c58-dd6349bdb058",
|
||||
"modelURL": "http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"x": 0.1660931408405304,
|
||||
"y": 0.05885136127471924,
|
||||
"z": 0.15574991703033447
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0,
|
||||
"y": 1.6633577346801758,
|
||||
"z": 0.048884183168411255
|
||||
},
|
||||
"originalTextures": "{\n \"aviator:Eyewear2F\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Diffuse.png\",\n \"aviator:Eyewear2F1\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Specular.png\"\n}\n",
|
||||
"owningAvatarID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
|
||||
"parentID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
|
||||
"parentJointIndex": 66,
|
||||
"position": {
|
||||
"x": 2.6226043701171875e-06,
|
||||
"y": -0.13999652862548828,
|
||||
"z": -0.04999971389770508
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.9313028454780579,
|
||||
"x": -1.4091639518737793,
|
||||
"y": -10.133878707885742,
|
||||
"z": 1.9983724355697632
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 1310720,
|
||||
"verticesCount": 982
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9969173073768616,
|
||||
"x": -0.07845909893512726,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Anime girl": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 19.66267967224121,
|
||||
"ageAsText": "0 hours 0 minutes 19 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.10536206513643265,
|
||||
"y": -0.16647332906723022,
|
||||
"z": -0.12632352113723755
|
||||
},
|
||||
"center": {
|
||||
"x": 0,
|
||||
"y": -0.12999999523162842,
|
||||
"z": -0.030000001192092896
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.2107241302728653,
|
||||
"y": 0.07294666767120361,
|
||||
"z": 0.1926470398902893
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.10536206513643265,
|
||||
"y": -0.09352666139602661,
|
||||
"z": 0.06632351875305176
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-05T00:10:37Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.2107241302728653,
|
||||
"y": 0.07294666767120361,
|
||||
"z": 0.1926470398902893
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 5,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{1586b83a-2af7-4532-9bfb-82fe3f5d5ce9}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "moam_00",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "Perfect for side-glancin'.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Blacker Fem Glasses",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528157470041658,
|
||||
"lastEditedBy": "{425df1a8-289b-42fc-819c-c3b2a12d7165}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": 0,
|
||||
"y": -0.12999999523162842,
|
||||
"z": -0.029999999329447746
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 1,
|
||||
"x": -2.2351741790771484e-08,
|
||||
"y": 3.4924596548080444e-10,
|
||||
"z": 3.725290298461914e-09
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "06781d12-9139-48f4-ac2a-417dde090981",
|
||||
"modelURL": "http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx",
|
||||
"name": "Female Glasses 3 by Mario Andrade",
|
||||
"naturalDimensions": {
|
||||
"x": 0.16209548711776733,
|
||||
"y": 0.05611282214522362,
|
||||
"z": 0.14819003641605377
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0,
|
||||
"y": -7.636845111846924e-08,
|
||||
"z": 0
|
||||
},
|
||||
"originalTextures": "{\n \"file49\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Mixed_AO.jpg\",\n \"file81\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Metallic.jpg\",\n \"file84\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Roughness.jpg\",\n \"file86\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Base_Color.jpg\",\n \"file87\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Normal_DirectX.jpg\"\n}\n",
|
||||
"owningAvatarID": "{1277f725-fbb4-478b-ae79-1241fd90e508}",
|
||||
"parentID": "{1277f725-fbb4-478b-ae79-1241fd90e508}",
|
||||
"parentJointIndex": 66,
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": -0.12999999523162842,
|
||||
"z": -0.029999999329447746
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.8840523958206177,
|
||||
"x": -2.6587564945220947,
|
||||
"y": -10.162277221679688,
|
||||
"z": -0.9548344016075134
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 5,
|
||||
"texturesSize": 0,
|
||||
"verticesCount": 1156
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -2.2351741790771484e-08,
|
||||
"y": 3.4924596548080444e-10,
|
||||
"z": 3.725290298461914e-09
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Last Legends: Male": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011327743530273,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.20154684782028198,
|
||||
"y": 0.03644842654466629,
|
||||
"z": -0.2641940414905548
|
||||
},
|
||||
"center": {
|
||||
"x": -0.030000001192092896,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.3430936932563782,
|
||||
"y": 0.18709957599639893,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.1415468454360962,
|
||||
"y": 0.22354799509048462,
|
||||
"z": 0.12419357895851135
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.33466479182243347,
|
||||
"y": 0.16981728374958038,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 19,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{6b0a2b08-e8e3-4d43-95cc-dfc4f7a4b0c9}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Fedora",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032827319,
|
||||
"lastEditedBy": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
|
||||
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"x": 0.2765824794769287,
|
||||
"y": 0.14034485816955566,
|
||||
"z": 0.320981502532959
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0.000143393874168396,
|
||||
"y": 1.7460365295410156,
|
||||
"z": 0.022502630949020386
|
||||
},
|
||||
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentJointIndex": 64,
|
||||
"position": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.6202316284179688,
|
||||
"x": -0.5601736903190613,
|
||||
"y": -10.668098449707031,
|
||||
"z": -0.8933582305908203
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 327680,
|
||||
"verticesCount": 719
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011027336120605,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.04381517320871353,
|
||||
"y": 0.20789726078510284,
|
||||
"z": -0.0394962802529335
|
||||
},
|
||||
"center": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.08762653172016144,
|
||||
"y": 0.04429228603839874,
|
||||
"z": 0.07901687920093536
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.043811358511447906,
|
||||
"y": 0.2521895468235016,
|
||||
"z": 0.03952059894800186
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.03022606298327446,
|
||||
"y": 0.06644226610660553,
|
||||
"z": 0.07229919731616974
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 58,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{d018c6ea-b2f4-441e-85e1-d17373ae6f34}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A cool scifi watch for your avatar!",
|
||||
"itemLicense": "",
|
||||
"itemName": "Scifi Watch",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032505220,
|
||||
"lastEditedBy": "{b46f9c9e-4cd3-4964-96d6-cf3954abb908}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90",
|
||||
"modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx",
|
||||
"name": "Scifi Watch by Jimi",
|
||||
"naturalDimensions": {
|
||||
"x": 0.023250818252563477,
|
||||
"y": 0.0511094331741333,
|
||||
"z": 0.055614765733480453
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0.6493338942527771,
|
||||
"y": 1.4500460624694824,
|
||||
"z": -0.06031447649002075
|
||||
},
|
||||
"originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentJointIndex": 16,
|
||||
"position": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.3082179129123688,
|
||||
"x": -0.19203892350196838,
|
||||
"y": -10.429610252380371,
|
||||
"z": -0.4076632857322693
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 5,
|
||||
"texturesSize": 786432,
|
||||
"verticesCount": 273
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Last legends Female": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/8d823be5-6197-4418-b984-eb94160ed956-v1/LLFemale_Clothes.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Matthew": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Priscilla": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Woody": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/ad348528-de38-420c-82bb-054cb22163f5-v1/mannequin.fst",
|
||||
"version": 3
|
||||
}
|
||||
}
|
|
@ -35,8 +35,6 @@
|
|||
<glyph glyph-name="diclosure-expand" unicode="B" d="M239 187c-3 0-6 1-8 3-5 5-5 12 0 17l42 42-43 44c-5 4-5 12 0 17 4 4 12 4 17 0l60-61-60-59c-2-2-5-3-8-3z"/>
|
||||
<glyph glyph-name="reload-small" unicode="a" d="M334 253c-9 0-18-7-18-16-3-27-25-47-52-47-29 0-52 24-52 52 0 11 2 27 14 38 6 5 14 9 24 12-5-7-4-16 2-22 3-3 8-4 12-4 5 0 9 1 13 5l28 28c1 2 3 4 3 7 3 6 1 13-3 18l-29 29c-3 3-7 5-12 5-5 0-9-2-12-5-4-3-5-8-5-12 0-5 1-9 5-13l0 0c-20-3-37-11-50-23-16-16-25-38-25-63 0-47 39-86 86-86 45 0 82 33 87 78 1 9-6 18-16 19z"/>
|
||||
<glyph glyph-name="close-small" unicode="C" d="M291 259l43 44c8 7 8 19 0 27-7 7-19 7-26 0l-44-44-44 44c-7 7-19 7-26 0-8-8-8-20 0-27l43-44-43-43c-8-8-8-20 0-27 7-7 19-7 26 0l44 44 44-44c7-7 19-7 26 0 8 8 8 19 0 27z"/>
|
||||
<glyph glyph-name="backward" unicode="E" d="M292 349c-5 3-12 3-18-1l-94-71c-4-3-7-8-7-13 0-5 2-10 6-14l95-80c3-2 7-4 11-4 2 0 5 1 7 2 6 3 10 9 10 15l0 151c0 6-4 12-10 15"/>
|
||||
<glyph glyph-name="reload" unicode="F" d="M365 261c-9 1-17-5-18-15-4-45-43-80-89-80-49 0-89 40-89 89 0 19 4 45 25 65 16 15 39 23 68 25l-15-16c-6-6-6-17 0-24 4-3 8-4 12-4 4 0 9 1 12 5l43 44c2 2 3 4 4 6 2 6 1 13-4 18l-44 44c-6 6-17 6-23 0-7-7-7-17 0-24l15-15c-38-2-69-14-91-35-23-21-36-53-36-88 0-68 55-123 123-123 64 0 116 47 122 110 1 9-5 18-15 18"/>
|
||||
<glyph glyph-name="minimize" unicode="I" d="M154 282l198 0c10 0 18-8 18-18 0-10-8-18-18-18l-198 0c-10 0-18 8-18 18 0 10 8 18 18 18"/>
|
||||
<glyph glyph-name="maximize" unicode="J" d="M157 244l77 0 0-75c0-9 8-17 17-17 9 0 17 8 17 17l0 75 75 0c10 0 17 8 17 17 0 10-7 18-17 18l-75 0 0 76c0 10-8 17-17 17-9 0-17-7-17-17l0-76-77 0c-10 0-17-8-17-18 0-9 8-17 17-17z"/>
|
||||
<glyph glyph-name="maximize-inverted" unicode="K" d="M251 434c-96 0-173-78-173-173 0-96 77-173 173-173 95 0 173 77 173 173 0 95-78 173-173 173z m93-190l-77 0 0-76c0-10-7-17-16-17-9 0-16 7-16 17l0 76-77 0c-10 0-17 8-17 17 0 9 7 17 17 17l77 0 0 76c0 9 7 17 16 17 9 0 16-8 16-17l0-76 77 0c9 0 17-8 17-17 0-9-8-17-17-17z"/>
|
||||
|
@ -48,7 +46,6 @@
|
|||
<glyph glyph-name="script-new" unicode="Q" d="M298 80l-145 0c-30 0-55 15-72 43-12 20-16 40-16 41l-3 16 267 0 0-12c0-1 1-15 9-29 10-18 26-27 49-27 13 0 22 4 29 12 16 18 16 54 14 66l0 1 0 206-269 0c-7 0-13 6-13 13 0 7 6 13 13 13l295 0 0-230c1-8 5-57-21-86-12-14-28-21-48-21-41 0-62 23-72 41-5 10-8 19-10 28l-210 0c2-7 5-12 8-18 13-20 29-30 50-30l145 0c7 0 13-7 13-14 0-7-6-13-13-13z m95 260l-180 0c-7 0-14 6-14 14 0 8 7 14 14 14l180 0c7 0 14-6 14-14 0-8-7-14-14-14z m0-59l-135 0c-8 0-14 7-14 15 0 7 6 14 14 14l135 0c7 0 14-7 14-14 0-8-7-15-14-15z m0-58l-180 0c-7 0-14 7-14 14 0 8 7 15 14 15l180 0c7 0 14-7 14-15 0-7-7-14-14-14z m-250 90l0 53c0 9-7 16-16 16-9 0-16-7-16-16l0-53-54 0c-9 0-16-8-16-17 0-9 8-17 16-17l54 0 0-53c0-9 7-16 16-16 9 0 16 8 16 17l0 52 54 0c9 0 16 8 16 17 0 9-8 16-17 16z"/>
|
||||
<glyph glyph-name="hifi-forum" unicode="2" d="M265 410c-83 0-150-68-150-150 0-24 6-47 16-67l-27-79 80 20c23-16 51-25 81-25 83 0 150 68 150 150 0 83-67 151-150 151z m38-248c-9 0-17 7-17 17 0 7 4 14 12 16l0 46-74 33 0-56c7-2 12-8 12-16 0-10-7-18-17-18-10 0-19 8-19 18 0 7 6 13 10 16l0 111c-4 2-10 9-10 16 0 10 9 17 19 17 9 0 17-7 17-17 0-8-5-14-12-16l0-41 74-33 0 51c-8 3-12 9-12 16 0 10 7 18 17 18 10 0 17-8 17-18 0-7-5-14-12-16l0-110c7-3 12-9 12-17 0-10-7-17-17-17z"/>
|
||||
<glyph glyph-name="hifi-logo-small" unicode="S" d="M374 374c-32 32-74 49-119 49-46 0-88-17-120-49-32-32-49-74-49-119 0-45 17-87 49-118 32-32 74-49 119-49 45 0 88 17 119 49 32 32 49 73 49 118 1 45-17 87-48 119z m-17-221c-28-28-65-43-103-43-39 0-75 15-103 43-27 27-42 64-42 102 0 39 15 75 42 103 28 28 64 43 103 43 38 0 75-15 103-43 27-28 42-64 42-103 0-39-15-76-42-102z m-145 47c-5 0-9 3-9 6l0 126c0 3 4 7 9 7 6 0 10-4 10-7l0-125c0-4-4-7-10-7z m0 118c-5 0-10 2-14 6-7 7-7 20 0 27 5 5 9 6 14 6 6 0 11-2 14-6 4-4 5-8 5-14 0-5-2-10-5-13-4-4-8-6-14-6z m0-144c-5 0-10 2-14 5-4 5-5 9-5 14 0 6 2 11 5 14 5 4 9 5 14 5 6 0 11-2 14-5 4-4 5-8 5-14 0-5-2-10-5-14-4-3-8-5-14-5z m85 2c-6 0-10 3-10 7l0 121c0 4 4 7 10 7 5 0 9-3 9-7l0-121c0-5-4-7-9-7z m0 120c-6 0-11 2-14 5-8 8-8 20 0 28 4 4 8 5 14 5 5 0 10-2 14-5 4-4 5-9 5-14 0-5-2-11-5-14-4-3-9-5-14-5z m0-144c-6 0-11 2-14 5-8 7-8 20 0 28 4 4 8 5 14 5 5 0 10-2 14-5 4-5 5-9 5-14 0-5-2-11-5-14-4-3-9-5-14-5z m1 73l-85 40 1 18 86-40z"/>
|
||||
<glyph glyph-name="avatar-1" unicode="T" d="M293 71c-1 0-1 0-1 0-14-1-14-1-16 12-6 43-11 86-16 128-1 2-1 4-1 6-3 0-6 0-9 0-2-10-3-20-4-31-4-36-8-72-12-109-1-7-2-8-9-8-2 0-5 0-7 0 0 74 1 181 1 254-1-1-33 0-44 1-15 0-62 1-79 1-8 0-14 3-18 10-1 2-2 4-4 7 8 0 15 0 22 1 35 1 99 8 100 12 14 11 23 10 36 10 15 0 31 0 46-1 11 0 24 3 37-10 20-10 81-10 123-11 0 0 1 0 1 0-4-12-12-17-25-17-29-1-77-3-127-1 2-73 4-181 6-254z m-32 371c16-6 14-20 13-32 0-5-1-10-1-14-2-11-10-18-20-18-11 0-19 8-20 18-1 6-1 13-2 20-1 7 3 13 11 15 10 3 10 3 19 11z"/>
|
||||
<glyph glyph-name="placemark" unicode="U" d="M134 98c31-32 73-49 119-49l1 0c45 0 86 16 117 47 31 30 48 71 48 114 1 46-16 88-46 120-9 9-20 17-31 24-3-7-6-15-10-22 5-4 11-8 16-13 15-14 28-32 33-46l1-1c0-1 0-2-1-3l-1 0c-12-7-25-10-38-12-2-1-4-1-7-2l-1 0c0 0-1 0-2 1 0 0-1 1-1 1l0 1c-3 15-6 31-12 46l-7-15c4-11 6-21 8-32l0-1c0-1 0-1 0-2-1-1-1-1-2-1l-1 0-55-3-4 0c0 0-1 0-1 0-1 1-1 2-1 2l0 74c-5 9-8 17-11 26 0-1 0-1 0-1l-1 0 0-97c0 0 0-1 0-2 0-1-1-2-2-2l-1 0c0 0-1 0-1 0l-51 3c-1 0-2 0-2 1-1 1-1 2 0 2l0 1c5 27 13 58 35 83 6 6 12 13 20 16 0 0 1 0 1 0-3 7-6 15-9 22-37-4-71-20-99-47-30-30-47-70-48-113-1-46 16-88 47-120z m198 72c0 4 1 7 1 10l0 0c0 7 1 14 1 21 0 9 0 18 0 27 0 3 0 6 0 9l0 1c-1 1-1 6 5 7 11 3 23 5 34 8l1 0c3 1 7 2 11 2l1 1c1 0 3-1 3-2l0-1c0-1 1-1 1-2 1-3 1-5 2-8 3-14 4-27 3-40-2-12-8-21-18-25-8-3-17-5-25-8-3 0-7-1-10-2-1-1-3-1-4-1-1 0-1 0-2 0l-1-1c0 0 0 0 0 0-1 0-2 1-2 1-1 1-1 1-1 2z m2-17c12 4 23 7 35 10l14 4c1 0 2 0 2-1 1 0 1-2 1-2l-1-2c-7-18-17-34-30-47-13-13-28-24-46-31l-2-1c0 0-1 0-1 0-1 0-1 0-2 0 0 1-1 2-1 3l1 1c0 1 0 1 0 1 0 1 0 2 1 3 6 9 15 24 22 55 1 3 2 6 7 7z m-78 83c0 1 1 3 2 3l3 0 41 2 13 1c0 0 1 0 1 0l1 0c0 0 0-1 1-1l1 0c1 0 2-1 2-3 0-1 0-3 0-5 0-2 0-4 0-6l0-5c1-7 1-15 0-22l0-4c0-5 0-11-1-17 0-1 0-3 0-5 0-1 0-3 0-4 0-2-1-7-7-8-10 0-20-1-30-2l-2 0c-3-1-8-1-12-1-5-1-7-1-8-1-1 0-1 0-2 0l-2 1c-1 1-1 2-1 3l0 74z m0-92c0 1 1 3 2 3l3 0 51 3c1 0 2 0 2-1 1 0 1-1 1-2l0-1c0 0 0-1 0-1 0 0 0 0-1-1 0 0 0-1 0-1l0-1c0-1-1-3-1-4-1-2-2-5-3-7l-1-3c-3-9-7-18-11-27-4-8-9-14-14-19-5-6-13-9-23-10l-3 0c-1 0-1 0-2 1 0 0 0 1 0 2l0 69z m-52-59c-1-1-1-2-2-2 0 0-1 1-1 1l-2 1c-27 6-64 40-78 79l-1 1c0 1 0 2 1 3 1 1 2 1 3 1l13-4c12-4 24-7 36-11 3-1 6-2 7-7 5-24 12-42 23-57l1-2c1-1 1-3 0-3z m40-10c0-1-1-1-1-2-1 0-2 0-2 0 0 0-1 0-1 0-1 0-14 4-20 11-15 19-23 41-29 61 0 0 0 0 0 1l0 1c0 1 0 1 1 2 0 1 1 1 2 1l1 0 29-2 17-1c2-1 3-2 3-3l0-69z m-21 165l18-2c2 0 3-1 3-2l0-75c0-1-1-2-1-2-1-1-2-1-2-1 0 0 0 0-1 0 0 0-14 2-21 2-8 1-17 2-26 3-4 0-6 4-6 7-2 18-3 35-3 47 0 5-1 22-1 22 0 1 1 2 1 2 1 1 1 1 2 1z m-98 32l1 1c12 31 42 59 76 71l3 1c1 0 2 0 3-1 0-1 0-2 0-3l-2-3c-18-25-25-53-31-79l0-1c0-1 0-2-1-2 0 0-1-1-1-1-1 0-1 0-1 0l-1 1c-5 1-11 3-17 4-8 2-18 4-27 8l0 1c-2 0-2 2-2 3z m-8-18l1 1c0 1 1 2 3 2l49-13c1 0 2-1 2-3l2-71c0-1 0-2-1-2-1-1-1-1-2-1 0 0 0 0 0 0l-1 0c-1 0-2 1-3 1l0 0c-2 0-4 1-7 1-16 4-29 9-39 17-6 4-9 8-9 15-1 19 1 37 5 53z m178 42c-8 16-14 31-21 46-8 17-17 35-24 52-14 31 3 65 36 71 28 5 56-16 59-44 0-9-1-18-5-26-14-32-29-64-44-97 0 0-1-1-1-2z m27 117c0 15-12 28-27 28-15 0-27-12-28-27 0-16 13-28 28-28 15 0 27 12 27 27z"/>
|
||||
<glyph glyph-name="box" unicode="V" d="M318 74l126 89 15-22-126-88z m-137 101l0-99 27 0 0 96z m145-125c-1 0-1 0-2 0l-262 26c-7 1-12 7-12 13l0 263c0 4 1 7 4 10 3 2 7 4 10 3l263-26c7 0 12-6 12-13l0-262c0-4-2-8-4-10-3-3-6-4-9-4z m-250 51l236-23 0 236-236 23z m377 326l-263 26c-3 1-7-1-10-3-3-3-4-6-4-10l0-21c3 2 7 3 11 3 0 0 0 0 1 0 3 2 7 4 11 4 2 0 3 0 5-1l234-23 0-236c0-7-3-10-10-14 1-2 1-5 1-7 1-2 0-3 0-5l21-2c1 0 1 0 2 0 3 0 6 1 8 4 3 2 5 6 5 10l0 262c0 7-5 13-12 13z m-397-64l125 88 16-22-126-88z m262-26l126 88 15-22-126-88z m-256-123l2 27 263-26-3-26z m146 37l0 91-27 0 0-88c9-1 18-2 27-3z"/>
|
||||
<glyph glyph-name="community" unicode="0" d="M50 175c-4 0-8 2-11 6-4 6-2 14 4 18l24 16 69 48 89-64c6-4 7-13 3-19-5-6-13-7-19-3l-74 53-53-37-24-16c-3-1-5-2-8-2z m130-10l-44 32-47-32-22-14 0-63 135 0 0 60z m120 10c-4 0-9 2-11 6-4 6-3 14 3 18l25 16 68 48 89-64c6-4 7-13 3-19-4-6-13-7-19-3l-73 53-54-37-24-16c-2-1-5-2-7-2z m129-10l-46 32-45-32-22-14 0-63 135 0 0 60z m-256 202c-4 0-9 2-11 5-4 7-3 15 4 19l24 16 68 48 89-65c6-4 7-12 3-18-4-6-12-7-18-3l-74 53-53-37-25-16c-2-2-5-2-7-2z m129-11l-46 32-45-31-22-15 0-62 135 0 0 60z"/>
|
||||
|
@ -103,7 +100,6 @@
|
|||
<glyph glyph-name="lock" unicode="" d="M389 233l0 62c0 68-55 124-123 124-69 0-124-56-124-124l0-62c-24-4-44-26-44-52l0-74c0-29 24-52 52-52l230 0c29 0 53 23 53 52l0 74c0 26-18 48-44 52z m-123 129c37 0 67-30 67-67l0-61-135 0 0 61c0 37 31 67 68 67z"/>
|
||||
<glyph glyph-name="visible" unicode="" d="M258 116c-55 0-106 17-147 51-31 25-47 51-47 52-4 7-4 16 1 23 2 4 66 98 195 96 133-3 192-93 195-97 4-6 4-15 0-22 0-1-15-27-46-53-29-23-79-50-151-50 0 0 0 0 0 0z m-148 113c7-7 17-18 30-29 34-27 73-40 118-40 0 0 0 0 0 0 47 0 88 13 122 40 13 10 23 21 29 29-7 7-16 16-30 26-34 25-74 38-119 38-81 2-130-42-150-64z m-27 1z m227-4c0-25-21-46-47-46-26 0-47 21-47 46 0 26 21 47 47 47 26 0 47-21 47-47z"/>
|
||||
<glyph glyph-name="model" unicode="" d="M494 395c-2 5-8 8-13 7l-90-16 45 72c3 5 2 11-1 15-4 4-10 5-15 3l-213-98c-15 5-72 27-111 43 0 0-1 0-1 0 0 0 0 0 0 0 0 0-1 0-1 1 0 0-1 0-1 0 0 0-1 0-1 0 0 0 0 0 0 0-1 0-1 0-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0 0 0-1-1 0-1 0-2 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0-1 0-1-1 0-2 0-3-1 0 0 0 0 0 0 0-1-1-1-1-1 0 0 0 0 0 0 0 0 0-1 0-1-1 0-1 0-1 0 0 0 0 0 0-1 0 0 0 0-1-1l-27-52-33-40c-3-4-3-10 0-15 2-3 6-5 10-5 1 0 2 0 4 1l50 17 40 2-26-51c-3-4-2-9 1-13 1-1 26-30 52-58 15-17 28-30 38-40 6-6 11-11 15-14l-16-61-46-18c-6-3-9-10-6-16 2-5 6-8 11-8 1 0 3 1 4 1l45 18 17-15c2-3 5-4 8-4 4 0 7 2 9 5 5 5 4 12-1 17l-17 15 16 61 76-90c2-2 6-4 9-4 1 0 2 0 3 0l85 23c5 2 8 6 9 11 0 5-2 9-7 12l-136 72 45 91 178 123c5 3 6 9 4 15z m-200-117l-122 55 41 21 181 83z m-148 73l-24 33c16-6 39-15 54-21z m-59-6l-9 13 15 29 27-38 2-2z m36-77l23 44c18-45 35-91 47-121-19 20-45 49-70 77z m194-194l-57 68 105-55z m-94 101c-5 14-42 104-30 77 0-2-21 49-28 66l121-59-48-95z m108 120l43 63 70 16z"/>
|
||||
<glyph glyph-name="forward" unicode="D" d="M330 278l-95 70c-5 4-12 5-18 2-5-3-9-9-9-16l0-150c0-7 4-13 10-16 2-1 5-2 7-2 4 0 8 2 11 5l95 79c4 4 6 9 6 14-1 5-3 10-7 14"/>
|
||||
<glyph glyph-name="avatar-2" unicode="" d="M256 88c-93 0-169 75-169 168 0 93 76 169 169 169 93 0 169-76 169-169 0-93-76-168-169-168z m0 316c-81 0-148-66-148-148 0-81 67-147 148-147 81 0 148 66 148 147 0 82-67 148-148 148z m97-90l-1 1c-3 3-7 4-10 4-1 0-61-9-86-9-1 0-1 0-2 0-25 0-87 10-87 10-5 0-10-2-13-6l-1-2c-2-3-2-7-1-10 1-4 3-6 6-8 12-5 49-20 60-22 2 0 5 0 6-7 1-8-3-46-7-65-5-17-13-40-13-41-2-6 1-13 7-15l8-3c3-1 6-1 9 1 3 1 5 4 6 7l21 65 20-67c1-3 3-6 6-7 2-1 4-1 5-1 2 0 3 0 5 0l7 3c5 2 8 8 7 14 0 0-6 24-11 44-3 12-4 30-5 45 0 9-1 16-2 22 0 1 0 4 5 5 0 0 1 0 2 0l55 22c4 2 6 5 7 9 1 4 0 8-3 11z m-68 37c0-16-13-29-29-29-16 0-29 13-29 29 0 16 13 29 29 29 16 0 29-13 29-29z"/>
|
||||
<glyph glyph-name="arrow-dn" unicode="5" d="M258 219l-43 55 86 0z"/>
|
||||
<glyph glyph-name="arrow-up" unicode="6" d="M258 283l43-55-86 0z"/>
|
||||
|
@ -154,4 +150,8 @@
|
|||
<glyph glyph-name="uninstall" unicode="" d="M83 227c-7 0-13-6-13-13l0-78c0-19 16-35 35-35l297 0c19 0 35 16 35 35l0 78c0 7-6 13-13 13-7 0-13-6-13-13l0-78c0-5-4-9-9-9l-297 0c-5 0-9 4-9 9l0 78c0 7-6 13-13 13z m191 47l50 50c5 5 5 14 0 19-5 5-13 5-19 0l-50-50-50 50c-5 5-13 5-19 0-5-5-5-14 0-19l50-50-50-50c-5-5-5-14 0-19 5-5 14-5 19 0l50 50 50-50c5-5 14-5 19 0 5 5 5 14 0 19z"/>
|
||||
<glyph glyph-name="install" unicode="" d="M83 227c-7 0-13-6-13-13l0-78c0-19 16-35 35-35l297 0c19 0 35 16 35 35l0 78c0 7-6 13-13 13-7 0-13-6-13-13l0-78c0-5-4-9-9-9l-297 0c-5 0-9 4-9 9l0 78c0 7-6 13-13 13z m170 171l0-155-33 27c-6 5-14 5-19-1-4-5-4-14 2-18l54-48c3-2 5-3 8-3 3 0 7 1 9 3l54 48c5 4 6 13 1 18-4 5-13 6-18 1l-32-27 0 154c0 8-6 13-13 13-7 0-13-5-13-12z"/>
|
||||
<glyph glyph-name="ellipsis-vertical" unicode="" d="M276 178c0-14-11-24-24-24-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24z m0 78c0-14-11-24-24-24-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24z m0 78c0-14-11-24-24-24-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24z"/>
|
||||
<glyph glyph-name="backward" unicode="E" d="M292 349c-5 3-12 3-18-1l-94-71c-4-3-7-8-7-13 0-5 2-10 6-14l95-80c3-2 7-4 11-4 2 0 5 1 7 2 6 3 10 9 10 15l0 151c0 6-4 12-10 15"/>
|
||||
<glyph glyph-name="40-reload" unicode="F" d="M365 261c-9 1-17-5-18-15-4-45-43-80-89-80-49 0-89 40-89 89 0 19 4 45 25 65 16 15 39 23 68 25l-15-16c-6-6-6-17 0-24 4-3 8-4 12-4 4 0 9 1 12 5l43 44c2 2 3 4 4 6 2 6 1 13-4 18l-44 44c-6 6-17 6-23 0-7-7-7-17 0-24l15-15c-38-2-69-14-91-35-23-21-36-53-36-88 0-68 55-123 123-123 64 0 116 47 122 110 1 9-5 18-15 18"/>
|
||||
<glyph glyph-name="forward" unicode="D" d="M330 278l-95 70c-5 4-12 5-18 2-5-3-9-9-9-16l0-150c0-7 4-13 10-16 2-1 5-2 7-2 4 0 8 2 11 5l95 79c4 4 6 9 6 14-1 5-3 10-7 14"/>
|
||||
<glyph glyph-name="avatar-1" unicode="T" d="M396 344l-2 2c-4 4-9 5-15 5-1 0-88-13-124-14-1 0-2 0-3 0-37 0-126 15-127 15-7 1-14-2-18-8l-2-4c-3-4-3-9-2-14 2-5 5-9 10-11 16-7 69-22 85-29 3-1 10-4 10-14 1-11-4-67-10-93-7-26-18-60-19-60-3-9 2-19 11-22l11-4c4-2 9-1 13 1 5 2 8 6 9 10l31 94 28-96c2-5 5-9 9-11 3-1 5-2 8-2 2 0 4 0 7 1l10 4c8 3 12 12 10 20 0 1-8 36-16 65-4 17-6 43-7 64-1 13-1 21-3 30 0 1 2 11 10 14 10 4 81 29 80 28 6 2 10 7 11 13 1 6-1 12-5 16z m-98 54c0-24-19-43-43-43-24 0-43 19-43 43 0 23 19 42 43 42 24 0 43-19 43-42z"/>
|
||||
</font></defs></svg>
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
@ -127,14 +127,6 @@
|
|||
<div class="icon icon-close-small"></div>
|
||||
<input type="text" readonly="readonly" value="close-small">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-backward"></div>
|
||||
<input type="text" readonly="readonly" value="backward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-reload"></div>
|
||||
<input type="text" readonly="readonly" value="reload">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-minimize"></div>
|
||||
<input type="text" readonly="readonly" value="minimize">
|
||||
|
@ -179,10 +171,6 @@
|
|||
<div class="icon icon-hifi-logo-small"></div>
|
||||
<input type="text" readonly="readonly" value="hifi-logo-small">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-avatar-1"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-1">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-placemark"></div>
|
||||
<input type="text" readonly="readonly" value="placemark">
|
||||
|
@ -399,10 +387,6 @@
|
|||
<div class="icon icon-model"></div>
|
||||
<input type="text" readonly="readonly" value="model">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-forward"></div>
|
||||
<input type="text" readonly="readonly" value="forward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-avatar-2"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-2">
|
||||
|
@ -603,6 +587,22 @@
|
|||
<div class="icon icon-ellipsis-vertical"></div>
|
||||
<input type="text" readonly="readonly" value="ellipsis-vertical">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-backward"></div>
|
||||
<input type="text" readonly="readonly" value="backward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-40-reload"></div>
|
||||
<input type="text" readonly="readonly" value="40-reload">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-forward"></div>
|
||||
<input type="text" readonly="readonly" value="forward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-avatar-1"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-1">
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Character mapping</h2>
|
||||
<ul class="glyphs character-mapping">
|
||||
|
@ -718,14 +718,6 @@
|
|||
<div data-icon="C" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="C">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="E" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="E">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="F" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="F">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="I" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="I">
|
||||
|
@ -770,10 +762,6 @@
|
|||
<div data-icon="S" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="S">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="T" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="T">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="U" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="U">
|
||||
|
@ -990,10 +978,6 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe008;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="D" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="D">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe009;">
|
||||
|
@ -1194,6 +1178,22 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe034;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="E" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="E">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="F" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="F">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="D" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="D">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="T" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="T">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>(function() {
|
|
@ -122,12 +122,6 @@
|
|||
.icon-close-small:before {
|
||||
content: "\43";
|
||||
}
|
||||
.icon-backward:before {
|
||||
content: "\45";
|
||||
}
|
||||
.icon-reload:before {
|
||||
content: "\46";
|
||||
}
|
||||
.icon-minimize:before {
|
||||
content: "\49";
|
||||
}
|
||||
|
@ -161,9 +155,6 @@
|
|||
.icon-hifi-logo-small:before {
|
||||
content: "\53";
|
||||
}
|
||||
.icon-avatar-1:before {
|
||||
content: "\54";
|
||||
}
|
||||
.icon-placemark:before {
|
||||
content: "\55";
|
||||
}
|
||||
|
@ -326,9 +317,6 @@
|
|||
.icon-model:before {
|
||||
content: "\e008";
|
||||
}
|
||||
.icon-forward:before {
|
||||
content: "\44";
|
||||
}
|
||||
.icon-avatar-2:before {
|
||||
content: "\e009";
|
||||
}
|
||||
|
@ -479,3 +467,15 @@
|
|||
.icon-ellipsis-vertical:before {
|
||||
content: "\e034";
|
||||
}
|
||||
.icon-backward:before {
|
||||
content: "\45";
|
||||
}
|
||||
.icon-40-reload:before {
|
||||
content: "\46";
|
||||
}
|
||||
.icon-forward:before {
|
||||
content: "\44";
|
||||
}
|
||||
.icon-avatar-1:before {
|
||||
content: "\54";
|
||||
}
|
14
interface/resources/icons/tablet-icons/avatar-a.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M78.3,31.3l-0.4-0.4c-0.8-0.8-2-1.2-3.1-1c-0.2,0-18.6,2.7-26.2,2.8c-0.2,0-0.3,0-0.4,0c-7.8,0-26.6-3-26.8-3.1
|
||||
c-1.5-0.2-3,0.4-3.7,1.7l-0.5,0.8c-0.6,0.9-0.7,2-0.4,3c0.3,1,1.1,1.8,2,2.3c3.4,1.5,14.9,6.3,18.2,6.7c0.6,0.1,1.7,0.2,1.8,2.2
|
||||
c0.2,2.4-0.8,14.1-2.2,19.7c-1.4,5.4-3.8,12.5-3.9,12.6c-0.7,1.9,0.3,4,2.2,4.7l2.3,0.8c0.9,0.3,2,0.3,2.8-0.2
|
||||
c0.9-0.4,1.6-1.2,1.9-2.2l6.4-19.7l6,20.3c0.3,1,1,1.8,1.9,2.3c0.5,0.2,1.1,0.4,1.6,0.4c0.5,0,0.9-0.1,1.3-0.2l2.1-0.8
|
||||
c1.7-0.7,2.7-2.5,2.2-4.3c0-0.1-1.8-7.4-3.3-13.5c-0.9-3.7-1.3-9.2-1.6-13.6c-0.2-2.7-0.3-5-0.6-6.8c-0.1-0.3-0.2-1.1,1.3-1.4
|
||||
c0.3,0,0.4-0.1,0.6-0.2l16.9-6.7c1.2-0.5,2-1.5,2.3-2.7C79.5,33.5,79.2,32.2,78.3,31.3z"/>
|
||||
<ellipse cx="48.7" cy="20" rx="9" ry="9"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
17
interface/resources/icons/tablet-icons/avatar-i.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M78.3,31.3l-0.4-0.4c-0.8-0.8-2-1.2-3.1-1c-0.2,0-18.6,2.7-26.2,2.8c-0.2,0-0.3,0-0.4,0
|
||||
c-7.8,0-26.6-3-26.8-3.1c-1.5-0.2-3,0.4-3.7,1.7l-0.5,0.8c-0.6,0.9-0.7,2-0.4,3c0.3,1,1.1,1.8,2,2.3c3.4,1.5,14.9,6.3,18.2,6.7
|
||||
c0.6,0.1,1.7,0.2,1.8,2.2c0.2,2.4-0.8,14.1-2.2,19.7c-1.4,5.4-3.8,12.5-3.9,12.6c-0.7,1.9,0.3,4,2.2,4.7l2.3,0.8
|
||||
c0.9,0.3,2,0.3,2.8-0.2c0.9-0.4,1.6-1.2,1.9-2.2l6.4-19.7l6,20.3c0.3,1,1,1.8,1.9,2.3c0.5,0.2,1.1,0.4,1.6,0.4
|
||||
c0.5,0,0.9-0.1,1.3-0.2l2.1-0.8c1.7-0.7,2.7-2.5,2.2-4.3c0-0.1-1.8-7.4-3.3-13.5c-0.9-3.7-1.3-9.2-1.6-13.6c-0.2-2.7-0.3-5-0.6-6.8
|
||||
c-0.1-0.3-0.2-1.1,1.3-1.4c0.3,0,0.4-0.1,0.6-0.2l16.9-6.7c1.2-0.5,2-1.5,2.3-2.7C79.5,33.5,79.2,32.2,78.3,31.3z"/>
|
||||
<ellipse class="st0" cx="48.7" cy="20" rx="9" ry="9"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
9
interface/resources/images/FavoriteIconActive.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<svg width="24" height="22" viewBox="0 0 24 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Canvas" fill="none">
|
||||
<g id="Vector">
|
||||
<path d="M 4.29197 19.3019C 4.20267 19.3019 4.11337 19.2697 4.02407 19.2052C 3.96826 19.1622 3.81199 19.0548 3.90129 18.6894L 4.21383 17.4427C 4.55987 16.0778 4.89474 14.7236 5.24078 13.3588C 5.43054 12.6064 5.20729 11.9509 4.60452 11.4672C 3.80082 10.8117 2.9748 10.1453 2.18227 9.51126C 1.54601 8.99539 0.898591 8.46878 0.262332 7.95292C -0.0278912 7.72723 -0.0167287 7.57677 0.0167586 7.41556C 0.0390835 7.32958 0.0948956 7.07165 0.53023 7.05015C 2.65109 6.91044 4.51522 6.78147 6.23423 6.66326C 7.02677 6.60952 7.6407 6.19038 7.93092 5.48106C 8.34393 4.51382 8.75694 3.54657 9.15879 2.56858C 9.47134 1.83777 9.78389 1.09621 10.0964 0.365404C 10.2192 0.0752303 10.3643 0.0107472 10.5987 1.02493e-08L 10.6099 1.02493e-08C 10.8108 1.02493e-08 10.9671 0.193449 11.0452 0.365404L 11.8266 2.20317C 12.2954 3.29939 12.7643 4.3956 13.2219 5.50256C 13.5121 6.17963 14.1038 6.59877 14.8628 6.65251C 15.7558 6.71699 16.6488 6.78147 17.5195 6.83521C 18.0664 6.86745 18.6245 6.91044 19.1715 6.94268C 19.2943 6.95343 19.4171 6.96418 19.5399 6.96418C 19.9752 6.99642 20.377 7.01791 20.79 7.07165C 21.1249 7.11464 21.1584 7.46929 21.1584 7.57677L 21.1584 7.60901C 21.1584 7.71648 20.9686 7.87769 20.8905 7.94217C 20.4217 8.31832 19.9417 8.68373 19.4729 9.05988C 18.5129 9.81218 17.5306 10.586 16.5595 11.3383C 15.8563 11.8864 15.5995 12.6064 15.8116 13.4447C 16.1242 14.6377 16.4255 15.8521 16.7269 17.0343C 16.8609 17.5716 16.9948 18.109 17.1288 18.6464C 17.1734 18.8398 17.1511 19.0225 17.0506 19.1515C 16.9725 19.2375 16.872 19.2912 16.7381 19.2912C 16.7158 19.2912 16.6934 19.2912 16.6711 19.2912C 16.6488 19.2912 16.5818 19.2697 16.4702 19.2052C 14.7958 18.2272 13.1438 17.2492 11.5587 16.3035C 11.2238 16.0993 10.8666 16.0026 10.5094 16.0026C 10.1522 16.0026 9.79505 16.11 9.44901 16.3142C 8.24347 17.0343 7.02677 17.7651 5.84355 18.4744L 4.61568 19.2052C 4.49289 19.2697 4.38127 19.3019 4.29197 19.3019Z" transform="translate(1.5 1)" fill="#FFB017"/>
|
||||
<path d="M 4.29197 19.3019C 4.20267 19.3019 4.11337 19.2697 4.02407 19.2052C 3.96826 19.1622 3.81199 19.0548 3.90129 18.6894L 4.21383 17.4427C 4.55987 16.0778 4.89474 14.7236 5.24078 13.3588C 5.43054 12.6064 5.20729 11.9509 4.60452 11.4672C 3.80082 10.8117 2.9748 10.1453 2.18227 9.51126C 1.54601 8.99539 0.898591 8.46878 0.262332 7.95292C -0.0278912 7.72723 -0.0167287 7.57677 0.0167586 7.41556C 0.0390835 7.32958 0.0948956 7.07165 0.53023 7.05015C 2.65109 6.91044 4.51522 6.78147 6.23423 6.66326C 7.02677 6.60952 7.6407 6.19038 7.93092 5.48106C 8.34393 4.51382 8.75694 3.54657 9.15879 2.56858C 9.47134 1.83777 9.78389 1.09621 10.0964 0.365404C 10.2192 0.0752303 10.3643 0.0107472 10.5987 1.02493e-08L 10.6099 1.02493e-08C 10.8108 1.02493e-08 10.9671 0.193449 11.0452 0.365404L 11.8266 2.20317C 12.2954 3.29939 12.7643 4.3956 13.2219 5.50256C 13.5121 6.17963 14.1038 6.59877 14.8628 6.65251C 15.7558 6.71699 16.6488 6.78147 17.5195 6.83521C 18.0664 6.86745 18.6245 6.91044 19.1715 6.94268C 19.2943 6.95343 19.4171 6.96418 19.5399 6.96418C 19.9752 6.99642 20.377 7.01791 20.79 7.07165C 21.1249 7.11464 21.1584 7.46929 21.1584 7.57677L 21.1584 7.60901C 21.1584 7.71648 20.9686 7.87769 20.8905 7.94217C 20.4217 8.31832 19.9417 8.68373 19.4729 9.05988C 18.5129 9.81218 17.5306 10.586 16.5595 11.3383C 15.8563 11.8864 15.5995 12.6064 15.8116 13.4447C 16.1242 14.6377 16.4255 15.8521 16.7269 17.0343C 16.8609 17.5716 16.9948 18.109 17.1288 18.6464C 17.1734 18.8398 17.1511 19.0225 17.0506 19.1515C 16.9725 19.2375 16.872 19.2912 16.7381 19.2912C 16.7158 19.2912 16.6934 19.2912 16.6711 19.2912C 16.6488 19.2912 16.5818 19.2697 16.4702 19.2052C 14.7958 18.2272 13.1438 17.2492 11.5587 16.3035C 11.2238 16.0993 10.8666 16.0026 10.5094 16.0026C 10.1522 16.0026 9.79505 16.11 9.44901 16.3142C 8.24347 17.0343 7.02677 17.7651 5.84355 18.4744L 4.61568 19.2052C 4.49289 19.2697 4.38127 19.3019 4.29197 19.3019Z" stroke-width="1.75" transform="translate(1.5 1)" stroke="#121212"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
8
interface/resources/images/FavoriteIconInActive.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg width="24" height="22" viewBox="0 0 24 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Canvas" fill="none">
|
||||
<g id="Vector">
|
||||
<path d="M 4.29197 19.3019C 4.20267 19.3019 4.11337 19.2697 4.02407 19.2052C 3.96826 19.1622 3.81199 19.0548 3.90129 18.6894L 4.21383 17.4427C 4.55987 16.0778 4.89474 14.7236 5.24078 13.3588C 5.43054 12.6064 5.20729 11.9509 4.60452 11.4672C 3.80082 10.8117 2.9748 10.1453 2.18227 9.51126C 1.54601 8.99539 0.898591 8.46878 0.262332 7.95292C -0.0278912 7.72723 -0.0167287 7.57677 0.0167586 7.41556C 0.0390835 7.32958 0.0948956 7.07165 0.53023 7.05015C 2.65109 6.91044 4.51522 6.78147 6.23423 6.66326C 7.02677 6.60952 7.6407 6.19038 7.93092 5.48106C 8.34393 4.51382 8.75694 3.54657 9.15879 2.56858C 9.47134 1.83777 9.78389 1.09621 10.0964 0.365404C 10.2192 0.0752303 10.3643 0.0107472 10.5987 1.02493e-08L 10.6099 1.02493e-08C 10.8108 1.02493e-08 10.9671 0.193449 11.0452 0.365404L 11.8266 2.20317C 12.2954 3.29939 12.7643 4.3956 13.2219 5.50256C 13.5121 6.17963 14.1038 6.59877 14.8628 6.65251C 15.7558 6.71699 16.6488 6.78147 17.5195 6.83521C 18.0664 6.86745 18.6245 6.91044 19.1715 6.94268C 19.2943 6.95343 19.4171 6.96418 19.5399 6.96418C 19.9752 6.99642 20.377 7.01791 20.79 7.07165C 21.1249 7.11464 21.1584 7.46929 21.1584 7.57677L 21.1584 7.60901C 21.1584 7.71648 20.9686 7.87769 20.8905 7.94217C 20.4217 8.31832 19.9417 8.68373 19.4729 9.05988C 18.5129 9.81218 17.5306 10.586 16.5595 11.3383C 15.8563 11.8864 15.5995 12.6064 15.8116 13.4447C 16.1242 14.6377 16.4255 15.8521 16.7269 17.0343C 16.8609 17.5716 16.9948 18.109 17.1288 18.6464C 17.1734 18.8398 17.1511 19.0225 17.0506 19.1515C 16.9725 19.2375 16.872 19.2912 16.7381 19.2912C 16.7158 19.2912 16.6934 19.2912 16.6711 19.2912C 16.6488 19.2912 16.5818 19.2697 16.4702 19.2052C 14.7958 18.2272 13.1438 17.2492 11.5587 16.3035C 11.2238 16.0993 10.8666 16.0026 10.5094 16.0026C 10.1522 16.0026 9.79505 16.11 9.44901 16.3142C 8.24347 17.0343 7.02677 17.7651 5.84355 18.4744L 4.61568 19.2052C 4.49289 19.2697 4.38127 19.3019 4.29197 19.3019Z" stroke-width="1.75" transform="translate(1.5 1)" stroke="#121212"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
BIN
interface/resources/images/avatarapp/AvatarIsland.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
interface/resources/images/avatarapp/BodyMart.PNG
Normal file
After Width: | Height: | Size: 126 KiB |
20
interface/resources/images/avatarapp/guy-in-circle.svg
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 134 134" style="enable-background:new 0 0 134 134;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1FC6A6;}
|
||||
</style>
|
||||
<path class="st0" d="M5,0h124c2.8,0,5,2.2,5,5v124c0,2.8-2.2,5-5,5H5c-2.8,0-5-2.2-5-5V5C0,2.2,2.2,0,5,0z"/>
|
||||
<path d="M100.5,99c-9.2,9.2-20.3,13.8-33.5,13.8S42.6,108.2,33.3,99c-9.2-9.2-13.8-20.3-13.8-33.5s4.6-24.3,13.8-33.5
|
||||
C42.6,22.7,53.9,18,67,18s24.3,4.7,33.5,14.1c9.4,9.2,14.1,20.3,14.1,33.5S109.8,89.9,100.5,99z M96.3,36.3
|
||||
C88.2,28.1,78.4,23.9,67,23.9s-21.3,4.1-29.5,12.4c-8.1,8.1-12.1,17.8-12.1,29.3s4,21.2,12.1,29.3c8.3,8.1,18.1,12.1,29.5,12.1
|
||||
s21.2-4,29.3-12.1c8.3-8.1,12.4-17.8,12.4-29.3S104.5,44.4,96.3,36.3z M94.3,49.3L94,49c-0.8-0.8-1.7-1.1-2.8-1.1l-1.7,0.3
|
||||
c-1.3,0.2-3,0.5-5.1,0.8c-1.9,0.2-3.8,0.4-5.9,0.6c-2.1,0.2-4.2,0.4-6.5,0.6c-2.1,0.2-3.8,0.3-5.1,0.3h-0.6L42,47.6
|
||||
c-1.7,0-2.9,0.6-3.7,1.7L38,49.8c-0.6,0.8-0.7,1.7-0.3,2.8c0.2,0.9,0.8,1.7,1.7,2.3c8.8,3.8,14.4,5.8,16.9,6.2c0.9,0,1.5,0.7,1.7,2
|
||||
c0.4,3.8-0.3,9.8-2,18.3c-0.4,1.3-0.9,3-1.7,5.1c-0.6,1.9-1,3.4-1.4,4.5l-0.6,2c-0.8,2.1-0.1,3.5,2,4.2l2.3,0.8
|
||||
c0.9,0.4,1.8,0.3,2.5-0.3c0.8-0.2,1.3-0.8,1.7-2l5.9-18.3l5.6,18.8c0.4,1.1,0.9,1.8,1.7,2c0.4,0.2,0.8,0.3,1.4,0.3h1.4l2-0.8
|
||||
c1.7-0.8,2.3-2.1,2-3.9l-3.1-12.4c-0.6-2.1-1-6.3-1.4-12.7c0-1.9-0.2-3.9-0.6-6.2c0-0.8,0.5-1.2,1.4-1.4h0.6l15.5-6.2
|
||||
c0.9-0.6,1.6-1.4,2-2.5S95.2,50.2,94.3,49.3z M72.6,33.2c1.7,1.5,2.5,3.4,2.5,5.6s-0.8,4.2-2.5,5.9C71.1,46.3,69.3,47,67,47
|
||||
s-4.2-0.8-5.9-2.3c-1.5-1.7-2.3-3.7-2.3-5.9s0.8-4.1,2.3-5.6c1.7-1.7,3.7-2.5,5.9-2.5S71.1,31.5,72.6,33.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -120,12 +120,8 @@ Windows.Window {
|
|||
|
||||
Component.onCompleted: {
|
||||
// Fix for parent loss on OSX:
|
||||
parent.heightChanged.connect(function() {
|
||||
updateContentParent();
|
||||
});
|
||||
parent.widthChanged.connect(function() {
|
||||
updateContentParent();
|
||||
});
|
||||
parent.heightChanged.connect(updateContentParent);
|
||||
parent.widthChanged.connect(updateContentParent);
|
||||
|
||||
x = interactiveWindowPosition.x;
|
||||
y = interactiveWindowPosition.y;
|
||||
|
@ -194,6 +190,11 @@ Windows.Window {
|
|||
initialized = true;
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
parent.heightChanged.disconnect(updateContentParent);
|
||||
parent.widthChanged.disconnect(updateContentParent);
|
||||
}
|
||||
|
||||
// Handle message traffic from the script that launched us to the loaded QML
|
||||
function fromScript(message) {
|
||||
if (root.dynamicContent && root.dynamicContent.fromScript) {
|
||||
|
|
|
@ -19,6 +19,8 @@ Original.Button {
|
|||
|
||||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property int fontSize: hifi.fontSizes.buttonLabel
|
||||
property alias implicitTextWidth: buttonText.implicitWidth
|
||||
property string buttonGlyph: "";
|
||||
|
||||
width: hifi.dimensions.buttonWidth
|
||||
|
@ -108,7 +110,7 @@ Original.Button {
|
|||
font.capitalization: Font.AllUppercase
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||
size: hifi.fontSizes.buttonLabel
|
||||
size: control.fontSize
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
|
|
|
@ -46,6 +46,7 @@ FocusScope {
|
|||
hoverEnabled: true
|
||||
visible: true
|
||||
height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control.
|
||||
textRole: "text"
|
||||
|
||||
function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; }
|
||||
function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; }
|
||||
|
|
|
@ -25,10 +25,16 @@ Original.RadioButton {
|
|||
property int colorScheme: hifi.colorSchemes.light
|
||||
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
|
||||
|
||||
readonly property int boxSize: 14
|
||||
readonly property int boxRadius: 3
|
||||
readonly property int checkSize: 10
|
||||
readonly property int checkRadius: 2
|
||||
property real letterSpacing: 1
|
||||
property int fontSize: hifi.fontSizes.inputLabel
|
||||
property int boxSize: defaultBoxSize
|
||||
property real scaleFactor: boxSize / defaultBoxSize
|
||||
|
||||
readonly property int defaultBoxSize: 14
|
||||
readonly property int boxRadius: 3 * scaleFactor
|
||||
readonly property int checkSize: 10 * scaleFactor
|
||||
readonly property int checkRadius: 2 * scaleFactor
|
||||
readonly property int indicatorRadius: 7 * scaleFactor
|
||||
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
|
@ -44,7 +50,7 @@ Original.RadioButton {
|
|||
id: box
|
||||
width: boxSize
|
||||
height: boxSize
|
||||
radius: 7
|
||||
radius: indicatorRadius
|
||||
x: radioButton.leftPadding
|
||||
y: parent.height / 2 - height / 2
|
||||
gradient: Gradient {
|
||||
|
@ -66,7 +72,7 @@ Original.RadioButton {
|
|||
id: check
|
||||
width: checkSize
|
||||
height: checkSize
|
||||
radius: 7
|
||||
radius: indicatorRadius
|
||||
anchors.centerIn: parent
|
||||
color: "#00B4EF"
|
||||
border.width: 1
|
||||
|
@ -77,7 +83,8 @@ Original.RadioButton {
|
|||
|
||||
contentItem: RalewaySemiBold {
|
||||
text: radioButton.text
|
||||
size: hifi.fontSizes.inputLabel
|
||||
size: radioButton.fontSize
|
||||
font.letterSpacing: letterSpacing
|
||||
color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
|
|
@ -27,6 +27,9 @@ SpinBox {
|
|||
property string suffix: ""
|
||||
property string labelInside: ""
|
||||
property color colorLabelInside: hifi.colors.white
|
||||
property color backgroundColor: isLightColorScheme
|
||||
? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray)
|
||||
: (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
|
||||
property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0)
|
||||
property int decimals: 2;
|
||||
property real factor: Math.pow(10, decimals)
|
||||
|
@ -55,10 +58,14 @@ SpinBox {
|
|||
|
||||
onValueModified: realValue = value/factor
|
||||
onValueChanged: realValue = value/factor
|
||||
onRealValueChanged: {
|
||||
var newValue = Math.round(realValue*factor);
|
||||
if(value != newValue) {
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
stepSize: realStepSize*factor
|
||||
value: Math.round(realValue*factor)
|
||||
|
||||
to : realTo*factor
|
||||
from : realFrom*factor
|
||||
|
||||
|
@ -69,9 +76,7 @@ SpinBox {
|
|||
y: spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0
|
||||
|
||||
background: Rectangle {
|
||||
color: isLightColorScheme
|
||||
? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray)
|
||||
: (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
|
||||
color: backgroundColor
|
||||
border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight
|
||||
border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0
|
||||
}
|
||||
|
|
|
@ -26,11 +26,9 @@ Preference {
|
|||
preference.save();
|
||||
}
|
||||
|
||||
Item {
|
||||
Row {
|
||||
id: control
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: Math.max(spinnerLabel.height, spinner.controlHeight)
|
||||
|
@ -40,15 +38,14 @@ Preference {
|
|||
text: root.label + ":"
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: spinner.left
|
||||
rightMargin: hifi.dimensions.labelPadding
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
spacing: hifi.dimensions.labelPadding
|
||||
|
||||
SpinBox {
|
||||
id: spinner
|
||||
decimals: preference.decimals
|
||||
|
@ -56,7 +53,6 @@ Preference {
|
|||
maximumValue: preference.max
|
||||
width: 100
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
|
840
interface/resources/qml/hifi/AvatarApp.qml
Normal file
|
@ -0,0 +1,840 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQml.Models 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../controls-uit" as HifiControls
|
||||
import "../styles-uit"
|
||||
import "avatarapp"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 480
|
||||
height: 706
|
||||
|
||||
property bool keyboardEnabled: true
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
z: 1000
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
color: style.colors.white
|
||||
property string getAvatarsMethod: 'getAvatars'
|
||||
|
||||
signal sendToScript(var message);
|
||||
function emitSendToScript(message) {
|
||||
sendToScript(message);
|
||||
}
|
||||
|
||||
ListModel { // the only purpose of this model is to convert JS object to ListElement
|
||||
id: currentAvatarModel
|
||||
dynamicRoles: true;
|
||||
function makeAvatarEntry(avatarObject) {
|
||||
clear();
|
||||
append(avatarObject);
|
||||
return get(count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
property var jointNames;
|
||||
property var currentAvatarSettings;
|
||||
|
||||
function fetchAvatarModelName(marketId, avatar) {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
var url = "https://highfidelity.com/api/v1/marketplace/items/" + marketId;
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status === 200) {
|
||||
try {
|
||||
var marketResponse = JSON.parse(xmlhttp.responseText.trim())
|
||||
|
||||
if(marketResponse.status === 'success') {
|
||||
avatar.modelName = marketResponse.data.title;
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET", url, true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
|
||||
function getAvatarModelName() {
|
||||
|
||||
if(currentAvatar === null) {
|
||||
return '';
|
||||
}
|
||||
if(currentAvatar.modelName !== undefined) {
|
||||
return currentAvatar.modelName;
|
||||
} else {
|
||||
var marketId = allAvatars.extractMarketId(currentAvatar.avatarUrl);
|
||||
if(marketId !== '') {
|
||||
fetchAvatarModelName(marketId, currentAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
var avatarUrl = currentAvatar.avatarUrl;
|
||||
var splitted = avatarUrl.split('/');
|
||||
|
||||
return splitted[splitted.length - 1];
|
||||
}
|
||||
|
||||
property string avatarName: currentAvatar ? currentAvatar.name : ''
|
||||
property string avatarUrl: currentAvatar ? currentAvatar.thumbnailUrl : null
|
||||
property bool isAvatarInFavorites: currentAvatar ? allAvatars.findAvatar(currentAvatar.name) !== undefined : false
|
||||
property int avatarWearablesCount: currentAvatar ? currentAvatar.wearables.count : 0
|
||||
property var currentAvatar: null;
|
||||
function setCurrentAvatar(avatar, bookmarkName) {
|
||||
var currentAvatarObject = allAvatars.makeAvatarObject(avatar, bookmarkName);
|
||||
currentAvatar = currentAvatarModel.makeAvatarEntry(currentAvatarObject);
|
||||
}
|
||||
|
||||
property url externalAvatarThumbnailUrl: '../../images/avatarapp/guy-in-circle.svg'
|
||||
|
||||
function fromScript(message) {
|
||||
if(message.method === 'initialize') {
|
||||
jointNames = message.data.jointNames;
|
||||
emitSendToScript({'method' : getAvatarsMethod});
|
||||
} else if(message.method === 'wearableUpdated') {
|
||||
adjustWearables.refreshWearable(message.entityID, message.wearableIndex, message.properties, message.updateUI);
|
||||
} else if(message.method === 'wearablesUpdated') {
|
||||
var wearablesModel = currentAvatar.wearables;
|
||||
wearablesModel.clear();
|
||||
message.wearables.forEach(function(wearable) {
|
||||
wearablesModel.append(wearable);
|
||||
});
|
||||
adjustWearables.refresh(currentAvatar);
|
||||
} else if(message.method === 'scaleChanged') {
|
||||
currentAvatar.avatarScale = message.value;
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'externalAvatarApplied') {
|
||||
currentAvatar.avatarUrl = message.avatarURL;
|
||||
currentAvatar.thumbnailUrl = allAvatars.makeThumbnailUrl(message.avatarURL);
|
||||
currentAvatar.entry.avatarUrl = currentAvatar.avatarUrl;
|
||||
currentAvatar.modelName = undefined;
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'settingChanged') {
|
||||
currentAvatarSettings[message.name] = message.value;
|
||||
} else if(message.method === 'changeSettings') {
|
||||
currentAvatarSettings = message.settings;
|
||||
} else if(message.method === 'bookmarkLoaded') {
|
||||
setCurrentAvatar(message.data.currentAvatar, message.data.name);
|
||||
var avatarIndex = allAvatars.findAvatarIndex(currentAvatar.name);
|
||||
allAvatars.move(avatarIndex, 0, 1);
|
||||
view.setPage(0);
|
||||
} else if(message.method === 'bookmarkAdded') {
|
||||
var avatar = allAvatars.findAvatar(message.bookmarkName);
|
||||
if(avatar !== undefined) {
|
||||
var avatarObject = allAvatars.makeAvatarObject(message.bookmark, message.bookmarkName);
|
||||
for(var prop in avatarObject) {
|
||||
avatar[prop] = avatarObject[prop];
|
||||
}
|
||||
if(currentAvatar.name === message.bookmarkName) {
|
||||
currentAvatar = currentAvatarModel.makeAvatarEntry(avatarObject);
|
||||
}
|
||||
} else {
|
||||
allAvatars.addAvatarEntry(message.bookmark, message.bookmarkName);
|
||||
}
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'bookmarkDeleted') {
|
||||
pageOfAvatars.isUpdating = true;
|
||||
|
||||
var index = pageOfAvatars.findAvatarIndex(message.name);
|
||||
var absoluteIndex = view.currentPage * view.itemsPerPage + index
|
||||
|
||||
allAvatars.remove(absoluteIndex)
|
||||
pageOfAvatars.remove(index);
|
||||
|
||||
var itemsOnPage = pageOfAvatars.count;
|
||||
var newItemIndex = view.currentPage * view.itemsPerPage + itemsOnPage;
|
||||
|
||||
if(newItemIndex <= (allAvatars.count - 1)) {
|
||||
pageOfAvatars.append(allAvatars.get(newItemIndex));
|
||||
} else {
|
||||
if(!pageOfAvatars.hasGetAvatars())
|
||||
pageOfAvatars.appendGetAvatars();
|
||||
}
|
||||
|
||||
pageOfAvatars.isUpdating = false;
|
||||
} else if(message.method === getAvatarsMethod) {
|
||||
var getAvatarsData = message.data;
|
||||
allAvatars.populate(getAvatarsData.bookmarks);
|
||||
setCurrentAvatar(getAvatarsData.currentAvatar, '');
|
||||
displayNameInput.text = getAvatarsData.displayName;
|
||||
currentAvatarSettings = getAvatarsData.currentAvatarSettings;
|
||||
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'updateAvatarInBookmarks') {
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'selectAvatarEntity') {
|
||||
adjustWearables.selectWearableByID(message.entityID);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurrentAvatarInBookmarks(avatar) {
|
||||
var bookmarkAvatarIndex = allAvatars.findAvatarIndexByValue(avatar);
|
||||
if(bookmarkAvatarIndex === -1) {
|
||||
avatar.name = '';
|
||||
view.setPage(0);
|
||||
} else {
|
||||
var bookmarkAvatar = allAvatars.get(bookmarkAvatarIndex);
|
||||
avatar.name = bookmarkAvatar.name;
|
||||
view.selectAvatar(bookmarkAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
property bool isInManageState: false
|
||||
|
||||
Component.onCompleted: {
|
||||
}
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
AvatarAppHeader {
|
||||
id: header
|
||||
z: 100
|
||||
|
||||
property string currentPage: "Avatar"
|
||||
property bool mainPageVisible: !settings.visible && !adjustWearables.visible
|
||||
|
||||
Binding on currentPage {
|
||||
when: settings.visible
|
||||
value: "Avatar Settings"
|
||||
}
|
||||
Binding on currentPage {
|
||||
when: adjustWearables.visible
|
||||
value: "Adjust Wearables"
|
||||
}
|
||||
Binding on currentPage {
|
||||
when: header.mainPageVisible
|
||||
value: "Avatar"
|
||||
}
|
||||
|
||||
pageTitle: currentPage
|
||||
avatarIconVisible: mainPageVisible
|
||||
settingsButtonVisible: mainPageVisible
|
||||
onSettingsClicked: {
|
||||
settings.open(currentAvatarSettings, currentAvatar.avatarScale);
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: settings
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
z: 3
|
||||
|
||||
onSaveClicked: function() {
|
||||
var avatarSettings = {
|
||||
dominantHand : settings.dominantHandIsLeft ? 'left' : 'right',
|
||||
collisionsEnabled : settings.avatarCollisionsOn,
|
||||
animGraphUrl : settings.avatarAnimationJSON,
|
||||
collisionSoundUrl : settings.avatarCollisionSoundUrl
|
||||
};
|
||||
|
||||
emitSendToScript({'method' : 'saveSettings', 'settings' : avatarSettings, 'avatarScale': settings.scaleValue})
|
||||
|
||||
close();
|
||||
}
|
||||
onCancelClicked: function() {
|
||||
emitSendToScript({'method' : 'revertScale', 'avatarScale' : avatarScaleBackup});
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
onScaleChanged: {
|
||||
emitSendToScript({'method' : 'setScale', 'avatarScale' : scale})
|
||||
}
|
||||
}
|
||||
|
||||
AdjustWearables {
|
||||
id: adjustWearables
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
jointNames: root.jointNames
|
||||
onWearableUpdated: {
|
||||
emitSendToScript({'method' : 'adjustWearable', 'entityID' : id, 'wearableIndex' : index, 'properties' : properties})
|
||||
}
|
||||
onWearableDeleted: {
|
||||
emitSendToScript({'method' : 'deleteWearable', 'entityID' : id, 'avatarName' : avatarName});
|
||||
}
|
||||
onAdjustWearablesOpened: {
|
||||
emitSendToScript({'method' : 'adjustWearablesOpened', 'avatarName' : avatarName});
|
||||
}
|
||||
onAdjustWearablesClosed: {
|
||||
emitSendToScript({'method' : 'adjustWearablesClosed', 'save' : status, 'avatarName' : avatarName});
|
||||
}
|
||||
onWearableSelected: {
|
||||
emitSendToScript({'method' : 'selectWearable', 'entityID' : id});
|
||||
}
|
||||
|
||||
z: 3
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainBlock
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: favoritesBlock.top
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
size: 24;
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 34
|
||||
}
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: displayNameLabel
|
||||
size: 24;
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 25
|
||||
text: 'Display Name'
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: displayNameInput
|
||||
|
||||
font.family: "Fira Sans"
|
||||
font.pixelSize: 15
|
||||
anchors.left: displayNameLabel.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.verticalCenter: displayNameLabel.verticalCenter
|
||||
anchors.right: parent.right
|
||||
width: 232
|
||||
|
||||
text: 'ThisIsDisplayName'
|
||||
|
||||
onEditingFinished: {
|
||||
emitSendToScript({'method' : 'changeDisplayName', 'displayName' : text})
|
||||
focus = false;
|
||||
}
|
||||
}
|
||||
|
||||
ShadowImage {
|
||||
id: avatarImage
|
||||
width: 134
|
||||
height: 134
|
||||
anchors.top: displayNameLabel.bottom
|
||||
anchors.topMargin: 31
|
||||
Binding on source {
|
||||
when: avatarUrl !== ''
|
||||
value: avatarUrl
|
||||
}
|
||||
|
||||
visible: avatarImage.status !== Image.Loading && avatarImage.status !== Image.Error
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
ShadowImage {
|
||||
id: customAvatarImage
|
||||
anchors.fill: avatarImage;
|
||||
visible: avatarUrl === '' || avatarImage.status === Image.Error
|
||||
source: externalAvatarThumbnailUrl
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
anchors.fill: avatarImage;
|
||||
color: 'white'
|
||||
visible: avatarImage.status === Image.Loading
|
||||
radius: avatarImage.radius
|
||||
|
||||
dropShadowRadius: avatarImage.dropShadowRadius;
|
||||
dropShadowHorizontalOffset: avatarImage.dropShadowHorizontalOffset
|
||||
dropShadowVerticalOffset: avatarImage.dropShadowVerticalOffset
|
||||
|
||||
Spinner {
|
||||
id: spinner
|
||||
visible: parent.visible
|
||||
anchors.fill: parent;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarWearablesIndicator {
|
||||
anchors.right: avatarImage.right
|
||||
anchors.bottom: avatarImage.bottom
|
||||
anchors.rightMargin: -radius
|
||||
anchors.bottomMargin: 6.08
|
||||
wearablesCount: avatarWearablesCount
|
||||
visible: avatarWearablesCount !== 0
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: star
|
||||
anchors.top: avatarImage.top
|
||||
anchors.topMargin: 11
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: 30.5
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 12.3
|
||||
|
||||
Image {
|
||||
width: 21.2
|
||||
height: 19.3
|
||||
source: isAvatarInFavorites ? '../../images/FavoriteIconActive.svg' : '../../images/FavoriteIconInActive.svg'
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
size: 22;
|
||||
Layout.fillWidth: true
|
||||
text: isAvatarInFavorites ? avatarName : "Add to Favorites"
|
||||
elide: Qt.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
enabled: !isAvatarInFavorites
|
||||
anchors.fill: star
|
||||
onClicked: {
|
||||
createFavorite.onSaveClicked = function() {
|
||||
var entry = currentAvatar.entry;
|
||||
|
||||
var wearables = [];
|
||||
for(var i = 0; i < currentAvatar.wearables.count; ++i) {
|
||||
wearables.push(currentAvatar.wearables.get(i));
|
||||
}
|
||||
|
||||
entry.avatarEntites = wearables;
|
||||
currentAvatar.name = createFavorite.favoriteNameText;
|
||||
|
||||
emitSendToScript({'method': 'addAvatar', 'name' : currentAvatar.name});
|
||||
createFavorite.close();
|
||||
}
|
||||
|
||||
var avatarThumbnail = (avatarUrl === '' || avatarImage.status === Image.Error) ?
|
||||
externalAvatarThumbnailUrl : avatarUrl;
|
||||
|
||||
createFavorite.open(root.currentAvatar.wearables.count, avatarThumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
id: avatarNameLabel
|
||||
size: 22;
|
||||
text: getAvatarModelName();
|
||||
elide: Qt.ElideRight
|
||||
|
||||
anchors.right: linkLabel.left
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: star.bottom
|
||||
anchors.topMargin: 11
|
||||
property bool hasMarketId: currentAvatar && allAvatars.extractMarketId(currentAvatar.avatarUrl) !== '';
|
||||
|
||||
MouseArea {
|
||||
enabled: avatarNameLabel.hasMarketId
|
||||
anchors.fill: parent;
|
||||
onClicked: emitSendToScript({'method' : 'navigate', 'url' : allAvatars.makeMarketItemUrl(currentAvatar.avatarUrl)})
|
||||
}
|
||||
color: hasMarketId ? style.colors.blueHighlight : 'black'
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
id: wearablesLabel
|
||||
size: 22;
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: avatarNameLabel.bottom
|
||||
anchors.topMargin: 16
|
||||
color: 'black'
|
||||
text: 'Wearables'
|
||||
}
|
||||
|
||||
SquareLabel {
|
||||
id: linkLabel
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: avatarNameLabel.verticalCenter
|
||||
glyphText: "."
|
||||
glyphSize: 22
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popup.showSpecifyAvatarUrl(function() {
|
||||
var url = popup.inputText.text;
|
||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||
}, function(link) {
|
||||
Qt.openUrlExternally(link);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SquareLabel {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: wearablesLabel.verticalCenter
|
||||
glyphText: "\ue02e"
|
||||
|
||||
visible: avatarWearablesCount !== 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
size: 22;
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: wearablesLabel.verticalCenter
|
||||
font.underline: true
|
||||
text: "Add"
|
||||
color: 'black'
|
||||
visible: avatarWearablesCount === 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popup.showGetWearables(function() {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'})
|
||||
}, function(link) {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : link})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: favoritesBlock
|
||||
height: 407
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
color: style.colors.lightGrayBackground
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: favoritesLabel
|
||||
size: 24;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
text: "Favorites"
|
||||
}
|
||||
|
||||
// TextStyle8
|
||||
RalewaySemiBold {
|
||||
id: manageLabel
|
||||
color: style.colors.blueHighlight
|
||||
size: 20;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 20
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
text: isInManageState ? "Back" : "Manage"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
isInManageState = isInManageState ? false : true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
anchors.top: favoritesLabel.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
GridView {
|
||||
id: view
|
||||
anchors.fill: parent
|
||||
interactive: false;
|
||||
currentIndex: currentAvatarIndexInBookmarksPage();
|
||||
|
||||
function currentAvatarIndexInBookmarksPage() {
|
||||
return (currentAvatar && currentAvatar.name !== '' && !pageOfAvatars.isUpdating) ? pageOfAvatars.findAvatarIndex(currentAvatar.name) : -1;
|
||||
}
|
||||
|
||||
property int horizontalSpacing: 18
|
||||
property int verticalSpacing: 44
|
||||
property int thumbnailWidth: 92
|
||||
property int thumbnailHeight: 92
|
||||
|
||||
function selectAvatar(avatar) {
|
||||
emitSendToScript({'method' : 'selectAvatar', 'name' : avatar.name})
|
||||
}
|
||||
|
||||
function deleteAvatar(avatar) {
|
||||
emitSendToScript({'method' : 'deleteAvatar', 'name' : avatar.name})
|
||||
}
|
||||
|
||||
AvatarsModel {
|
||||
id: allAvatars
|
||||
}
|
||||
|
||||
property int itemsPerPage: 8
|
||||
property int totalPages: Math.ceil((allAvatars.count + 1) / itemsPerPage)
|
||||
property int currentPage: 0;
|
||||
onCurrentPageChanged: {
|
||||
currentIndex = Qt.binding(currentAvatarIndexInBookmarksPage);
|
||||
}
|
||||
|
||||
property bool hasNext: currentPage < (totalPages - 1)
|
||||
property bool hasPrev: currentPage > 0
|
||||
|
||||
function setPage(pageIndex) {
|
||||
pageOfAvatars.isUpdating = true;
|
||||
pageOfAvatars.clear();
|
||||
var start = pageIndex * itemsPerPage;
|
||||
var end = Math.min(start + itemsPerPage, allAvatars.count);
|
||||
|
||||
for(var itemIndex = 0; start < end; ++start, ++itemIndex) {
|
||||
var avatarItem = allAvatars.get(start)
|
||||
pageOfAvatars.append(avatarItem);
|
||||
}
|
||||
|
||||
if(pageOfAvatars.count !== itemsPerPage)
|
||||
pageOfAvatars.appendGetAvatars();
|
||||
|
||||
currentPage = pageIndex;
|
||||
pageOfAvatars.isUpdating = false;
|
||||
}
|
||||
|
||||
model: AvatarsModel {
|
||||
id: pageOfAvatars
|
||||
|
||||
property bool isUpdating: false;
|
||||
property var getMoreAvatarsEntry: {'thumbnailUrl' : '', 'name' : '', 'getMoreAvatars' : true}
|
||||
|
||||
function appendGetAvatars() {
|
||||
append(getMoreAvatarsEntry);
|
||||
}
|
||||
|
||||
function hasGetAvatars() {
|
||||
return count != 0 && get(count - 1).getMoreAvatars
|
||||
}
|
||||
|
||||
function removeGetAvatars() {
|
||||
if(hasGetAvatars()) {
|
||||
remove(count - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flow: GridView.FlowLeftToRight
|
||||
|
||||
cellHeight: thumbnailHeight + verticalSpacing
|
||||
cellWidth: thumbnailWidth + horizontalSpacing
|
||||
|
||||
delegate: Item {
|
||||
id: delegateRoot
|
||||
height: GridView.view.cellHeight
|
||||
width: GridView.view.cellWidth
|
||||
|
||||
Item {
|
||||
id: container
|
||||
width: 92
|
||||
height: 92
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hovered"
|
||||
when: favoriteAvatarMouseArea.containsMouse;
|
||||
PropertyChanges { target: favoriteAvatarMouseArea; anchors.bottomMargin: -5 }
|
||||
PropertyChanges { target: container; y: -5 }
|
||||
PropertyChanges { target: favoriteAvatarImage; dropShadowRadius: 10 }
|
||||
PropertyChanges { target: favoriteAvatarImage; dropShadowVerticalOffset: 6 }
|
||||
}
|
||||
]
|
||||
|
||||
property bool highlighted: delegateRoot.GridView.isCurrentItem
|
||||
|
||||
AvatarThumbnail {
|
||||
id: favoriteAvatarImage
|
||||
externalAvatarThumbnailUrl: root.externalAvatarThumbnailUrl
|
||||
avatarUrl: thumbnailUrl
|
||||
border.color: container.highlighted ? style.colors.blueHighlight : 'transparent'
|
||||
border.width: container.highlighted ? 4 : 0
|
||||
wearablesCount: {
|
||||
return !getMoreAvatars ? wearables.count : 0
|
||||
}
|
||||
|
||||
visible: !getMoreAvatars
|
||||
|
||||
MouseArea {
|
||||
id: favoriteAvatarMouseArea
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
enabled: !container.highlighted
|
||||
hoverEnabled: enabled
|
||||
|
||||
onClicked: {
|
||||
if(isInManageState) {
|
||||
var currentItem = delegateRoot.GridView.view.model.get(index);
|
||||
popup.showDeleteFavorite(currentItem.name, function() {
|
||||
view.deleteAvatar(currentItem);
|
||||
});
|
||||
} else {
|
||||
if(delegateRoot.GridView.view.currentIndex !== index) {
|
||||
var currentItem = delegateRoot.GridView.view.model.get(index);
|
||||
popup.showLoadFavorite(currentItem.name, function() {
|
||||
view.selectAvatar(currentItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: favoriteAvatarImage
|
||||
color: '#AFAFAF'
|
||||
opacity: 0.4
|
||||
radius: 5
|
||||
visible: isInManageState && !container.highlighted && !getMoreAvatars
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.fill: parent
|
||||
text: "{"
|
||||
visible: isInManageState && !container.highlighted && !getMoreAvatars
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
size: 56
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
width: 92
|
||||
height: 92
|
||||
radius: 5
|
||||
color: style.colors.blueHighlight
|
||||
visible: getMoreAvatars && !isInManageState
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: 'white'
|
||||
size: 60
|
||||
text: "K"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
popup.showBuyAvatars(function() {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : 'hifi://BodyMart'})
|
||||
}, function(link) {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : link})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TextStyle7
|
||||
FiraSansRegular {
|
||||
id: text
|
||||
size: 18;
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: 16.9;
|
||||
width: view.thumbnailWidth
|
||||
height: view.verticalSpacing
|
||||
elide: Qt.ElideRight
|
||||
anchors.top: container.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.horizontalCenter: container.horizontalCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: getMoreAvatars ? 'Get More Avatars' : name
|
||||
visible: !getMoreAvatars || !isInManageState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
color: 'transparent'
|
||||
|
||||
PageIndicator {
|
||||
x: 1
|
||||
hasNext: view.hasNext
|
||||
hasPrev: view.hasPrev
|
||||
onClicked: view.setPage(view.currentPage - 1)
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
color: 'transparent'
|
||||
|
||||
PageIndicator {
|
||||
x: -1
|
||||
isPrevious: false
|
||||
hasNext: view.hasNext
|
||||
hasPrev: view.hasPrev
|
||||
onClicked: view.setPage(view.currentPage + 1)
|
||||
}
|
||||
}
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 20
|
||||
}
|
||||
}
|
||||
|
||||
MessageBoxes {
|
||||
id: popup
|
||||
}
|
||||
|
||||
CreateFavoriteDialog {
|
||||
avatars: allAvatars
|
||||
id: createFavorite
|
||||
}
|
||||
}
|
|
@ -765,7 +765,7 @@ Rectangle {
|
|||
TableViewColumn {
|
||||
id: connectionsUserNameHeader;
|
||||
role: "userName";
|
||||
title: connectionsTable.rowCount + (connectionsTable.rowCount === 1 ? " NAME" : " NAMES");
|
||||
title: connectionsUserModel.totalEntries + (connectionsUserModel.totalEntries === 1 ? " NAME" : " NAMES");
|
||||
width: connectionsNameCardWidth;
|
||||
movable: false;
|
||||
resizable: false;
|
||||
|
|
359
interface/resources/qml/hifi/avatarapp/AdjustWearables.qml
Normal file
|
@ -0,0 +1,359 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
visible: false;
|
||||
width: 480
|
||||
height: 706
|
||||
color: 'white'
|
||||
|
||||
signal wearableUpdated(var id, int index, var properties);
|
||||
signal wearableSelected(var id);
|
||||
signal wearableDeleted(string avatarName, var id);
|
||||
|
||||
signal adjustWearablesOpened(var avatarName);
|
||||
signal adjustWearablesClosed(bool status, var avatarName);
|
||||
|
||||
property bool modified: false;
|
||||
Component.onCompleted: {
|
||||
modified = false;
|
||||
}
|
||||
|
||||
property var jointNames;
|
||||
property string avatarName: ''
|
||||
property var wearablesModel;
|
||||
|
||||
function open(avatar) {
|
||||
adjustWearablesOpened(avatar.name);
|
||||
|
||||
visible = true;
|
||||
avatarName = avatar.name;
|
||||
wearablesModel = avatar.wearables;
|
||||
refresh(avatar);
|
||||
}
|
||||
|
||||
function refresh(avatar) {
|
||||
wearablesCombobox.model.clear();
|
||||
for(var i = 0; i < avatar.wearables.count; ++i) {
|
||||
var wearable = avatar.wearables.get(i).properties;
|
||||
for(var j = (wearable.modelURL.length - 1); j >= 0; --j) {
|
||||
if(wearable.modelURL[j] === '/') {
|
||||
wearable.text = wearable.modelURL.substring(j + 1) + ' [%jointIndex%]'.replace('%jointIndex%', jointNames[wearable.parentJointIndex]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wearablesCombobox.model.append(wearable);
|
||||
}
|
||||
|
||||
wearablesCombobox.currentIndex = 0;
|
||||
}
|
||||
|
||||
function refreshWearable(wearableID, wearableIndex, properties, updateUI) {
|
||||
if(wearableIndex === -1) {
|
||||
wearableIndex = wearablesCombobox.model.findIndexById(wearableID);
|
||||
}
|
||||
|
||||
var wearable = wearablesCombobox.model.get(wearableIndex);
|
||||
|
||||
if(!wearable) {
|
||||
return;
|
||||
}
|
||||
|
||||
var wearableModelItemProperties = wearablesModel.get(wearableIndex).properties;
|
||||
|
||||
for(var prop in properties) {
|
||||
wearable[prop] = properties[prop];
|
||||
wearableModelItemProperties[prop] = wearable[prop];
|
||||
|
||||
if(updateUI) {
|
||||
if(prop === 'localPosition') {
|
||||
position.set(wearable[prop]);
|
||||
} else if(prop === 'localRotationAngles') {
|
||||
rotation.set(wearable[prop]);
|
||||
} else if(prop === 'dimensions') {
|
||||
scalespinner.set(wearable[prop].x / wearable.naturalDimensions.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wearablesModel.setProperty(wearableIndex, 'properties', wearableModelItemProperties);
|
||||
}
|
||||
|
||||
function getCurrentWearable() {
|
||||
return wearablesCombobox.model.get(wearablesCombobox.currentIndex)
|
||||
}
|
||||
|
||||
function selectWearableByID(entityID) {
|
||||
for(var i = 0; i < wearablesCombobox.model.count; ++i) {
|
||||
var wearable = wearablesCombobox.model.get(i);
|
||||
if(wearable.id === entityID) {
|
||||
wearablesCombobox.currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close(status) {
|
||||
visible = false;
|
||||
adjustWearablesClosed(status, avatarName);
|
||||
}
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
spacing: 20
|
||||
width: parent.width - 30 * 2
|
||||
|
||||
HifiControlsUit.ComboBox {
|
||||
id: wearablesCombobox
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
comboBox.textRole: "text"
|
||||
|
||||
model: ListModel {
|
||||
function findIndexById(id) {
|
||||
|
||||
for(var i = 0; i < count; ++i) {
|
||||
var wearable = get(i);
|
||||
if(wearable.id === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
comboBox.onCurrentIndexChanged: {
|
||||
var currentWearable = getCurrentWearable();
|
||||
|
||||
if(currentWearable) {
|
||||
position.set(currentWearable.localPosition);
|
||||
rotation.set(currentWearable.localRotationAngles);
|
||||
scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x)
|
||||
|
||||
wearableSelected(currentWearable.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
Row {
|
||||
spacing: 20
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
id: positionLabel
|
||||
size: 22;
|
||||
text: "Position"
|
||||
}
|
||||
|
||||
// TextStyle7
|
||||
FiraSansRegular {
|
||||
size: 18;
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: 16.9;
|
||||
text: "m"
|
||||
anchors.verticalCenter: positionLabel.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 {
|
||||
id: position
|
||||
backgroundColor: "lightgray"
|
||||
|
||||
function set(localPosition) {
|
||||
notify = false;
|
||||
xvalue = localPosition.x
|
||||
yvalue = localPosition.y
|
||||
zvalue = localPosition.z
|
||||
notify = true;
|
||||
}
|
||||
|
||||
function notifyPositionChanged() {
|
||||
modified = true;
|
||||
var properties = {
|
||||
localPosition: { 'x' : xvalue, 'y' : yvalue, 'z' : zvalue }
|
||||
};
|
||||
|
||||
wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
|
||||
property bool notify: false;
|
||||
|
||||
onXvalueChanged: if(notify) notifyPositionChanged();
|
||||
onYvalueChanged: if(notify) notifyPositionChanged();
|
||||
onZvalueChanged: if(notify) notifyPositionChanged();
|
||||
|
||||
decimals: 2
|
||||
realFrom: -10
|
||||
realTo: 10
|
||||
realStepSize: 0.01
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
Row {
|
||||
spacing: 20
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
id: rotationLabel
|
||||
size: 22;
|
||||
text: "Rotation"
|
||||
}
|
||||
|
||||
// TextStyle7
|
||||
FiraSansRegular {
|
||||
size: 18;
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: 16.9;
|
||||
text: "deg"
|
||||
anchors.verticalCenter: rotationLabel.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 {
|
||||
id: rotation
|
||||
backgroundColor: "lightgray"
|
||||
|
||||
function set(localRotationAngles) {
|
||||
notify = false;
|
||||
xvalue = localRotationAngles.x
|
||||
yvalue = localRotationAngles.y
|
||||
zvalue = localRotationAngles.z
|
||||
notify = true;
|
||||
}
|
||||
|
||||
function notifyRotationChanged() {
|
||||
modified = true;
|
||||
var properties = {
|
||||
localRotationAngles: { 'x' : xvalue, 'y' : yvalue, 'z' : zvalue }
|
||||
};
|
||||
|
||||
wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
|
||||
property bool notify: false;
|
||||
|
||||
onXvalueChanged: if(notify) notifyRotationChanged();
|
||||
onYvalueChanged: if(notify) notifyRotationChanged();
|
||||
onZvalueChanged: if(notify) notifyRotationChanged();
|
||||
|
||||
decimals: 0
|
||||
realFrom: -180
|
||||
realTo: 180
|
||||
realStepSize: 1
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
size: 22;
|
||||
text: "Scale"
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: scalespinner
|
||||
decimals: 2
|
||||
realStepSize: 0.1
|
||||
realFrom: 0.1
|
||||
realTo: 3.0
|
||||
realValue: 1.0
|
||||
backgroundColor: "lightgray"
|
||||
width: position.spinboxWidth
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
|
||||
property bool notify: false;
|
||||
onValueChanged: if(notify) notifyScaleChanged();
|
||||
|
||||
function set(value) {
|
||||
notify = false;
|
||||
realValue = value
|
||||
notify = true;
|
||||
}
|
||||
|
||||
function notifyScaleChanged() {
|
||||
modified = true;
|
||||
var currentWearable = getCurrentWearable();
|
||||
var naturalDimensions = currentWearable.naturalDimensions;
|
||||
|
||||
var properties = {
|
||||
dimensions: {
|
||||
'x' : realValue * naturalDimensions.x,
|
||||
'y' : realValue * naturalDimensions.y,
|
||||
'z' : realValue * naturalDimensions.z
|
||||
}
|
||||
};
|
||||
|
||||
wearableUpdated(currentWearable.id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
fontSize: 18
|
||||
height: 40
|
||||
anchors.right: parent.right
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
text: "TAKE IT OFF"
|
||||
onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id);
|
||||
enabled: wearablesCombobox.model.count !== 0
|
||||
anchors.verticalCenter: scalespinner.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
yesText: "SAVE"
|
||||
noText: "CANCEL"
|
||||
|
||||
onYesClicked: function() {
|
||||
root.close(true);
|
||||
}
|
||||
|
||||
onNoClicked: function() {
|
||||
root.close(false);
|
||||
}
|
||||
}
|
||||
}
|
58
interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml
Normal file
|
@ -0,0 +1,58 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
|
||||
ShadowRectangle {
|
||||
id: header
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 60
|
||||
|
||||
property alias pageTitle: title.text
|
||||
property alias avatarIconVisible: avatarIcon.visible
|
||||
property alias settingsButtonVisible: settingsButton.visible
|
||||
|
||||
signal settingsClicked;
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
color: style.colors.lightGrayBackground
|
||||
|
||||
HiFiGlyphs {
|
||||
id: avatarIcon
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 23
|
||||
anchors.verticalCenter: header.verticalCenter
|
||||
|
||||
size: 38
|
||||
text: "<"
|
||||
}
|
||||
|
||||
// TextStyle6
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 22;
|
||||
anchors.left: avatarIcon.visible ? avatarIcon.right : avatarIcon.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: avatarIcon.verticalCenter
|
||||
text: 'Avatar'
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: settingsButton
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: avatarIcon.verticalCenter
|
||||
text: "&"
|
||||
|
||||
MouseArea {
|
||||
id: settingsMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
settingsClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// HiFiConstants.qml
|
||||
//
|
||||
// Created by Alexander Ivash on 17 Apr 2018
|
||||
// Copyright 2018 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
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Window 2.2
|
||||
import "../../styles-uit"
|
||||
|
||||
QtObject {
|
||||
readonly property QtObject colors: QtObject {
|
||||
readonly property color lightGrayBackground: "#f2f2f2"
|
||||
readonly property color black: "#000000"
|
||||
readonly property color white: "#ffffff"
|
||||
readonly property color blueHighlight: "#00b4ef"
|
||||
readonly property color inputFieldBackground: "#d4d4d4"
|
||||
readonly property color yellowishOrange: "#ffb017"
|
||||
readonly property color blueAccent: "#0093c5"
|
||||
readonly property color greenHighlight: "#1fc6a6"
|
||||
readonly property color lightGray: "#afafaf"
|
||||
readonly property color redHighlight: "#ea4c5f"
|
||||
readonly property color orangeAccent: "#ff6309"
|
||||
}
|
||||
}
|
73
interface/resources/qml/hifi/avatarapp/AvatarThumbnail.qml
Normal file
|
@ -0,0 +1,73 @@
|
|||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
width: 92
|
||||
height: 92
|
||||
property alias wearableIndicator: indicator
|
||||
|
||||
property int wearablesCount: 0
|
||||
onWearablesCountChanged: {
|
||||
console.debug('AvatarThumbnail: wearablesCount = ', wearablesCount)
|
||||
}
|
||||
|
||||
property alias dropShadowRadius: avatarImage.dropShadowRadius
|
||||
property alias dropShadowHorizontalOffset: avatarImage.dropShadowHorizontalOffset
|
||||
property alias dropShadowVerticalOffset: avatarImage.dropShadowVerticalOffset
|
||||
|
||||
property url externalAvatarThumbnailUrl;
|
||||
property var avatarUrl;
|
||||
property alias border: avatarImage.border
|
||||
|
||||
ShadowImage {
|
||||
id: avatarImage
|
||||
anchors.fill: parent
|
||||
radius: 5
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
Binding on source {
|
||||
when: avatarUrl !== ''
|
||||
value: avatarUrl
|
||||
}
|
||||
onSourceChanged: {
|
||||
console.debug('avatarImage: source = ', source);
|
||||
}
|
||||
|
||||
visible: avatarImage.status !== Image.Loading && avatarImage.status !== Image.Error
|
||||
}
|
||||
|
||||
ShadowImage {
|
||||
id: customAvatarImage
|
||||
anchors.fill: avatarImage;
|
||||
visible: avatarUrl === '' || avatarImage.status === Image.Error
|
||||
source: externalAvatarThumbnailUrl
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
anchors.fill: parent;
|
||||
color: 'white'
|
||||
visible: avatarImage.status === Image.Loading
|
||||
radius: avatarImage.radius
|
||||
border.width: avatarImage.border.width
|
||||
border.color: avatarImage.border.color
|
||||
|
||||
dropShadowRadius: avatarImage.dropShadowRadius;
|
||||
dropShadowHorizontalOffset: avatarImage.dropShadowHorizontalOffset
|
||||
dropShadowVerticalOffset: avatarImage.dropShadowVerticalOffset
|
||||
|
||||
Spinner {
|
||||
id: spinner
|
||||
visible: parent.visible
|
||||
anchors.fill: parent;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarWearablesIndicator {
|
||||
id: indicator
|
||||
anchors.left: avatarImage.left
|
||||
anchors.bottom: avatarImage.bottom
|
||||
anchors.leftMargin: 57
|
||||
wearablesCount: parent.wearablesCount
|
||||
visible: parent.wearablesCount !== 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import QtQuick 2.9
|
||||
import "../../controls-uit"
|
||||
import "../../styles-uit"
|
||||
|
||||
ShadowRectangle {
|
||||
property int wearablesCount: 0
|
||||
|
||||
dropShadowRadius: 4
|
||||
dropShadowHorizontalOffset: 0
|
||||
dropShadowVerticalOffset: 0
|
||||
|
||||
width: 46.5
|
||||
height: 46.5
|
||||
radius: width / 2
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
color: style.colors.greenHighlight
|
||||
|
||||
HiFiGlyphs {
|
||||
width: 26.5
|
||||
height: 13.8
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "\ue02e"
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 46.57
|
||||
height: 23
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 2.76
|
||||
|
||||
// TextStyle2
|
||||
RalewayBold {
|
||||
size: 15;
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: wearablesCount
|
||||
}
|
||||
}
|
||||
}
|
228
interface/resources/qml/hifi/avatarapp/AvatarsModel.qml
Normal file
|
@ -0,0 +1,228 @@
|
|||
import QtQuick 2.9
|
||||
|
||||
ListModel {
|
||||
id: model
|
||||
function extractMarketId(avatarUrl) {
|
||||
|
||||
var guidRegexp = '([A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12})';
|
||||
|
||||
var regexp = new RegExp(guidRegexp,["i"]);
|
||||
var match = regexp.exec(avatarUrl);
|
||||
if (match !== null) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function makeMarketItemUrl(avatarUrl) {
|
||||
var marketItemUrl = "https://highfidelity.com/marketplace/items/%marketId%"
|
||||
.split('%marketId%').join(extractMarketId(avatarUrl));
|
||||
|
||||
return marketItemUrl;
|
||||
}
|
||||
|
||||
function makeThumbnailUrl(avatarUrl) {
|
||||
var marketId = extractMarketId(avatarUrl);
|
||||
if(marketId === '')
|
||||
return '';
|
||||
|
||||
var avatarThumbnailUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/%marketId%/large/hifi-mp-%marketId%.jpg"
|
||||
.split('%marketId%').join(marketId);
|
||||
|
||||
return avatarThumbnailUrl;
|
||||
}
|
||||
|
||||
function makeAvatarObject(avatar, avatarName) {
|
||||
var avatarThumbnailUrl = makeThumbnailUrl(avatar.avatarUrl);
|
||||
|
||||
return {
|
||||
'name' : avatarName,
|
||||
'avatarScale' : avatar.avatarScale,
|
||||
'thumbnailUrl' : avatarThumbnailUrl,
|
||||
'avatarUrl' : avatar.avatarUrl,
|
||||
'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [],
|
||||
'attachments' : avatar.attachments ? avatar.attachments : [],
|
||||
'entry' : avatar,
|
||||
'getMoreAvatars' : false
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function addAvatarEntry(avatar, avatarName) {
|
||||
var avatarEntry = makeAvatarObject(avatar, avatarName);
|
||||
append(avatarEntry);
|
||||
|
||||
return allAvatars.count - 1;
|
||||
}
|
||||
|
||||
function populate(bookmarks) {
|
||||
clear();
|
||||
for(var avatarName in bookmarks) {
|
||||
var avatar = bookmarks[avatarName];
|
||||
var avatarEntry = makeAvatarObject(avatar, avatarName);
|
||||
|
||||
append(avatarEntry);
|
||||
}
|
||||
}
|
||||
|
||||
function arraysAreEqual(a1, a2, comparer) {
|
||||
if(Array.isArray(a1) && Array.isArray(a2)) {
|
||||
if(a1.length !== a2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(var i = 0; i < a1.length; ++i) {
|
||||
if(!comparer(a1[i], a2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if(Array.isArray(a1)) {
|
||||
return a1.length === 0;
|
||||
} else if(Array.isArray(a2)) {
|
||||
return a2.length === 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function modelsAreEqual(m1, m2, comparer) {
|
||||
if(m1.count !== m2.count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(var i = 0; i < m1.count; ++i) {
|
||||
var e1 = m1.get(i);
|
||||
|
||||
var allDifferent = true;
|
||||
|
||||
// it turns out order of wearables can randomly change so make position-independent comparison here
|
||||
for(var j = 0; j < m2.count; ++j) {
|
||||
var e2 = m2.get(j);
|
||||
|
||||
if(comparer(e1, e2)) {
|
||||
allDifferent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allDifferent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareNumericObjects(o1, o2) {
|
||||
if(o1 === undefined && o2 !== undefined)
|
||||
return false;
|
||||
if(o1 !== undefined && o2 === undefined)
|
||||
return false;
|
||||
|
||||
for(var prop in o1) {
|
||||
if(o1.hasOwnProperty(prop) && o2.hasOwnProperty(prop)) {
|
||||
var v1 = o1[prop];
|
||||
var v2 = o2[prop];
|
||||
|
||||
|
||||
if(v1 !== v2 && Math.round(v1 * 500) != Math.round(v2 * 500)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareObjects(o1, o2, props, arrayProp) {
|
||||
for(var i = 0; i < props.length; ++i) {
|
||||
var prop = props[i];
|
||||
var propertyName = prop.propertyName;
|
||||
var comparer = prop.comparer;
|
||||
|
||||
var o1Value = arrayProp ? o1[arrayProp][propertyName] : o1[propertyName];
|
||||
var o2Value = arrayProp ? o2[arrayProp][propertyName] : o2[propertyName];
|
||||
|
||||
if(comparer) {
|
||||
if(comparer(o1Value, o2Value) === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(JSON.stringify(o1Value) !== JSON.stringify(o2Value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareWearables(w1, w2) {
|
||||
return compareObjects(w1, w2, [{'propertyName' : 'modelURL'},
|
||||
{'propertyName' : 'parentJointIndex'},
|
||||
{'propertyName' : 'marketplaceID'},
|
||||
{'propertyName' : 'itemName'},
|
||||
{'propertyName' : 'script'},
|
||||
{'propertyName' : 'localPosition', 'comparer' : compareNumericObjects},
|
||||
{'propertyName' : 'localRotationAngles', 'comparer' : compareNumericObjects},
|
||||
{'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties')
|
||||
}
|
||||
|
||||
function compareAttachments(a1, a2) {
|
||||
return compareObjects(a1, a2, [{'propertyName' : 'position', 'comparer' : compareNumericObjects},
|
||||
{'propertyName' : 'orientation'},
|
||||
{'propertyName' : 'parentJointIndex'},
|
||||
{'propertyName' : 'modelurl'}])
|
||||
}
|
||||
|
||||
function findAvatarIndexByValue(avatar) {
|
||||
|
||||
var index = -1;
|
||||
|
||||
// 2DO: find better way of determining selected avatar in bookmarks
|
||||
for(var i = 0; i < allAvatars.count; ++i) {
|
||||
var thesame = true;
|
||||
var bookmarkedAvatar = allAvatars.get(i);
|
||||
|
||||
if(bookmarkedAvatar.avatarUrl !== avatar.avatarUrl)
|
||||
continue;
|
||||
|
||||
if(bookmarkedAvatar.avatarScale !== avatar.avatarScale)
|
||||
continue;
|
||||
|
||||
if(!modelsAreEqual(bookmarkedAvatar.attachments, avatar.attachments, compareAttachments)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(thesame) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function findAvatarIndex(avatarName) {
|
||||
for(var i = 0; i < count; ++i) {
|
||||
if(get(i).name === avatarName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function findAvatar(avatarName) {
|
||||
var avatarIndex = findAvatarIndex(avatarName);
|
||||
if(avatarIndex === -1)
|
||||
return undefined;
|
||||
|
||||
return get(avatarIndex);
|
||||
}
|
||||
|
||||
}
|
15
interface/resources/qml/hifi/avatarapp/BlueButton.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
|
||||
HifiControlsUit.Button {
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
width: Math.max(hifi.dimensions.buttonWidth, implicitTextWidth + 20)
|
||||
fontSize: 18
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
height: 40
|
||||
}
|
184
interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml
Normal file
|
@ -0,0 +1,184 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0, 0, 0, 0.5);
|
||||
z: 999;
|
||||
|
||||
property string titleText: 'Create Favorite'
|
||||
property string favoriteNameText: favoriteName.text
|
||||
property string avatarImageUrl: null
|
||||
property int wearablesCount: 0
|
||||
|
||||
property string button1color: hifi.buttons.noneBorderlessGray;
|
||||
property string button1text: 'CANCEL'
|
||||
property string button2color: hifi.buttons.blue;
|
||||
property string button2text: 'CONFIRM'
|
||||
|
||||
property var avatars;
|
||||
property var onSaveClicked;
|
||||
property var onCancelClicked;
|
||||
|
||||
function open(wearables, thumbnail) {
|
||||
favoriteName.text = '';
|
||||
favoriteName.forceActiveFocus();
|
||||
|
||||
avatarImageUrl = thumbnail;
|
||||
wearablesCount = wearables;
|
||||
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
console.debug('closing');
|
||||
visible = false;
|
||||
}
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainContainer;
|
||||
width: Math.max(parent.width * 0.8, 400)
|
||||
property int margin: 30;
|
||||
|
||||
height: childrenRect.height + margin * 2
|
||||
onHeightChanged: {
|
||||
console.debug('mainContainer: height = ', height)
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: "white"
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 24;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 30
|
||||
anchors.leftMargin: 30
|
||||
anchors.rightMargin: 30
|
||||
|
||||
text: root.titleText
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentContainer
|
||||
width: parent.width - 50
|
||||
height: childrenRect.height
|
||||
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
|
||||
AvatarThumbnail {
|
||||
id: avatarThumbnail
|
||||
avatarUrl: avatarImageUrl
|
||||
onAvatarUrlChanged: {
|
||||
console.debug('CreateFavoritesDialog: onAvatarUrlChanged: ', avatarUrl);
|
||||
}
|
||||
|
||||
wearablesCount: avatarWearablesCount
|
||||
}
|
||||
|
||||
InputTextStyle4 {
|
||||
id: favoriteName
|
||||
anchors.right: parent.right
|
||||
height: 40
|
||||
anchors.left: avatarThumbnail.right
|
||||
anchors.leftMargin: 44
|
||||
anchors.verticalCenter: avatarThumbnail.verticalCenter
|
||||
placeholderText: "Enter Favorite Name"
|
||||
|
||||
RalewayRegular {
|
||||
id: wrongName
|
||||
anchors.top: parent.bottom;
|
||||
anchors.topMargin: 2
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: -contentContainer.anchors.rightMargin // allow text to render beyond favorite input text
|
||||
wrapMode: Text.WordWrap
|
||||
text: 'Favorite name exists. Overwrite existing favorite?'
|
||||
size: 15
|
||||
color: 'red'
|
||||
visible: {
|
||||
for(var i = 0; i < avatars.count; ++i) {
|
||||
var avatarName = avatars.get(i).name;
|
||||
if(avatarName === favoriteName.text) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
anchors.top: contentContainer.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
yesButton.enabled: favoriteNameText !== ''
|
||||
yesText: root.button2text
|
||||
noText: root.button1text
|
||||
|
||||
Binding on yesButton.text {
|
||||
when: wrongName.visible
|
||||
value: "OVERWRITE";
|
||||
}
|
||||
|
||||
Binding on yesButton.color {
|
||||
when: wrongName.visible
|
||||
value: hifi.buttons.red;
|
||||
}
|
||||
|
||||
Binding on yesButton.colorScheme {
|
||||
when: wrongName.visible
|
||||
value: hifi.colorSchemes.dark;
|
||||
}
|
||||
|
||||
onYesClicked: function() {
|
||||
if(onSaveClicked) {
|
||||
onSaveClicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
onNoClicked: function() {
|
||||
if(onCancelClicked) {
|
||||
onCancelClicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
interface/resources/qml/hifi/avatarapp/DialogButtons.qml
Normal file
|
@ -0,0 +1,41 @@
|
|||
import QtQuick 2.9
|
||||
|
||||
Row {
|
||||
id: root
|
||||
property string yesText;
|
||||
property string noText;
|
||||
property var onYesClicked;
|
||||
property var onNoClicked;
|
||||
|
||||
property alias yesButton: yesButton
|
||||
property alias noButton: noButton
|
||||
|
||||
height: childrenRect.height
|
||||
layoutDirection: Qt.RightToLeft
|
||||
|
||||
spacing: 30
|
||||
|
||||
BlueButton {
|
||||
id: yesButton;
|
||||
text: yesText;
|
||||
onClicked: {
|
||||
console.debug('bluebutton.clicked', onYesClicked);
|
||||
|
||||
if(onYesClicked) {
|
||||
onYesClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WhiteButton {
|
||||
id: noButton
|
||||
text: noText;
|
||||
onClicked: {
|
||||
console.debug('whitebutton.clicked', onNoClicked);
|
||||
|
||||
if(onNoClicked) {
|
||||
onNoClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
interface/resources/qml/hifi/avatarapp/InputField.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.2
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
|
||||
property bool error: false;
|
||||
text: 'ThisIsDisplayName'
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hovered"
|
||||
when: textField.hovered && !textField.focus && !textField.error;
|
||||
PropertyChanges { target: background; color: '#afafaf' }
|
||||
},
|
||||
State {
|
||||
name: "focused"
|
||||
when: textField.focus && !textField.error
|
||||
PropertyChanges { target: background; color: '#f2f2f2' }
|
||||
PropertyChanges { target: background; border.color: '#00b4ef' }
|
||||
},
|
||||
State {
|
||||
name: "error"
|
||||
when: textField.error
|
||||
PropertyChanges { target: background; color: '#f2f2f2' }
|
||||
PropertyChanges { target: background; border.color: '#e84e62' }
|
||||
}
|
||||
]
|
||||
|
||||
background: Rectangle {
|
||||
id: background
|
||||
implicitWidth: 200
|
||||
implicitHeight: 40
|
||||
color: '#d4d4d4'
|
||||
border.color: '#afafaf'
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 36
|
||||
text: "\ue00d"
|
||||
}
|
||||
}
|
16
interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../styles-uit"
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: control
|
||||
font.family: "Fira Sans"
|
||||
font.pixelSize: 15;
|
||||
implicitHeight: 40
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
}
|
184
interface/resources/qml/hifi/avatarapp/MessageBox.qml
Normal file
|
@ -0,0 +1,184 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0, 0, 0, 0.5);
|
||||
z: 999;
|
||||
|
||||
property string titleText: ''
|
||||
property string bodyText: ''
|
||||
property alias inputText: input;
|
||||
|
||||
property string imageSource: null
|
||||
onImageSourceChanged: {
|
||||
console.debug('imageSource = ', imageSource)
|
||||
}
|
||||
|
||||
property string button1color: hifi.buttons.noneBorderlessGray;
|
||||
property string button1text: ''
|
||||
property string button2color: hifi.buttons.blue;
|
||||
property string button2text: ''
|
||||
|
||||
property var onButton2Clicked;
|
||||
property var onButton1Clicked;
|
||||
property var onLinkClicked;
|
||||
|
||||
function open() {
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible = false;
|
||||
|
||||
onButton1Clicked = null;
|
||||
onButton2Clicked = null;
|
||||
button1text = '';
|
||||
button2text = '';
|
||||
imageSource = null;
|
||||
inputText.visible = false;
|
||||
inputText.placeholderText = '';
|
||||
inputText.text = '';
|
||||
}
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainContainer;
|
||||
width: Math.max(parent.width * 0.8, 400)
|
||||
property int margin: 30;
|
||||
|
||||
height: childrenRect.height + margin * 2
|
||||
onHeightChanged: {
|
||||
console.debug('mainContainer: height = ', height)
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: "white"
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 24;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 30
|
||||
anchors.leftMargin: 30
|
||||
anchors.rightMargin: 30
|
||||
|
||||
text: root.titleText
|
||||
elide: Qt.ElideRight
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentContainer
|
||||
spacing: 15
|
||||
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
|
||||
InputTextStyle4 {
|
||||
id: input
|
||||
visible: false
|
||||
height: visible ? implicitHeight : 0
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
id: body
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
size: 18
|
||||
text: root.bodyText;
|
||||
linkColor: style.colors.blueHighlight
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
wrapMode: Text.WordWrap;
|
||||
|
||||
onLinkActivated: {
|
||||
if(onLinkClicked)
|
||||
onLinkClicked(link);
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
Binding on height {
|
||||
when: imageSource === null
|
||||
value: 0
|
||||
}
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Binding on source {
|
||||
when: imageSource !== null
|
||||
value: imageSource
|
||||
}
|
||||
|
||||
visible: imageSource !== null ? true : false
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
id: buttons
|
||||
|
||||
anchors.top: contentContainer.bottom
|
||||
anchors.topMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
yesButton.enabled: !input.visible || input.text.length !== 0
|
||||
yesText: root.button2text
|
||||
noText: root.button1text
|
||||
|
||||
onYesClicked: function() {
|
||||
if(onButton2Clicked) {
|
||||
onButton2Clicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
onNoClicked: function() {
|
||||
if(onButton1Clicked) {
|
||||
onButton1Clicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
122
interface/resources/qml/hifi/avatarapp/MessageBoxes.qml
Normal file
|
@ -0,0 +1,122 @@
|
|||
import QtQuick 2.5
|
||||
|
||||
MessageBox {
|
||||
id: popup
|
||||
|
||||
function showSpecifyAvatarUrl(callback, linkCallback) {
|
||||
popup.onButton2Clicked = callback;
|
||||
popup.titleText = 'Specify Avatar URL'
|
||||
popup.bodyText = 'This will not overwrite your existing favorite if you are wearing one.<br>' +
|
||||
'<a href="https://docs.highfidelity.com/create-and-explore/avatars/create-avatars">' +
|
||||
'Learn to make a custom avatar by opening this link on your desktop.' +
|
||||
'</a>'
|
||||
popup.inputText.visible = true;
|
||||
popup.inputText.placeholderText = 'Enter Avatar Url';
|
||||
popup.button1text = 'CANCEL';
|
||||
popup.button2text = 'CONFIRM';
|
||||
|
||||
popup.onButton2Clicked = function() {
|
||||
if(callback)
|
||||
callback();
|
||||
|
||||
popup.close();
|
||||
}
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
if(linkCallback)
|
||||
linkCallback(link);
|
||||
}
|
||||
|
||||
popup.open();
|
||||
popup.inputText.forceActiveFocus();
|
||||
}
|
||||
|
||||
property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg'
|
||||
|
||||
function showGetWearables(callback, linkCallback) {
|
||||
popup.button2text = 'AvatarIsland'
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Wearables'
|
||||
popup.bodyText = 'Buy wearables from <a href="app://marketplace">Marketplace</a>' + '<br/>' +
|
||||
'Wear wearables from <a href="app://purchases">My Purchases</a>' + '<br/>' +
|
||||
'You can visit the domain “AvatarIsland” to get wearables'
|
||||
|
||||
popup.imageSource = getWearablesUrl;
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
popup.close();
|
||||
|
||||
if(linkCallback)
|
||||
linkCallback(link);
|
||||
}
|
||||
|
||||
popup.open();
|
||||
}
|
||||
|
||||
function showDeleteFavorite(favoriteName, callback) {
|
||||
popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName)
|
||||
popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.'
|
||||
popup.imageSource = null;
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.button2text = 'DELETE'
|
||||
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
popup.open();
|
||||
}
|
||||
|
||||
function showLoadFavorite(favoriteName, callback) {
|
||||
popup.button2text = 'CONFIRM'
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Load Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName)
|
||||
popup.bodyText = 'This will switch your current avatar and wearables that you are wearing with a new avatar and wearables.'
|
||||
popup.imageSource = null;
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
popup.open();
|
||||
}
|
||||
|
||||
property url getAvatarsUrl: '../../../images/avatarapp/BodyMart.PNG'
|
||||
|
||||
function showBuyAvatars(callback, linkCallback) {
|
||||
popup.button2text = 'BodyMart'
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Avatars'
|
||||
|
||||
popup.bodyText = 'Buy avatars from <a href="app://marketplace">Marketplace</a>' + '<br/>' +
|
||||
'Wear avatars from <a href="app://purchases">My Purchases</a>' + '<br/>' +
|
||||
'You can visit the domain “BodyMart” to get avatars'
|
||||
|
||||
popup.imageSource = getAvatarsUrl;
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
popup.close();
|
||||
|
||||
if(linkCallback)
|
||||
linkCallback(link);
|
||||
}
|
||||
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
|
44
interface/resources/qml/hifi/avatarapp/PageIndicator.qml
Normal file
|
@ -0,0 +1,44 @@
|
|||
import QtQuick 2.9
|
||||
|
||||
ShadowGlyph {
|
||||
id: indicator
|
||||
property bool isPrevious: true;
|
||||
property bool hasNext: false
|
||||
property bool hasPrev: false
|
||||
property bool isEnabled: isPrevious ? hasPrev : hasNext
|
||||
signal clicked;
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hovered"
|
||||
when: pageIndicatorMouseArea.containsMouse;
|
||||
PropertyChanges { target: pageIndicatorMouseArea; anchors.bottomMargin: -5 }
|
||||
PropertyChanges { target: indicator; y: -5 }
|
||||
PropertyChanges { target: indicator; dropShadowVerticalOffset: 9 }
|
||||
}
|
||||
]
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
text: isPrevious ? "E" : "D";
|
||||
width: 40
|
||||
height: 40
|
||||
font.pixelSize: 100
|
||||
color: isEnabled ? 'black' : 'gray'
|
||||
visible: hasNext || hasPrev
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
MouseArea {
|
||||
id: pageIndicatorMouseArea
|
||||
anchors.fill: parent
|
||||
enabled: isEnabled
|
||||
hoverEnabled: enabled
|
||||
onClicked: {
|
||||
parent.clicked();
|
||||
}
|
||||
}
|
||||
}
|
36
interface/resources/qml/hifi/avatarapp/RoundImage.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
|
||||
property alias border: borderRectangle.border
|
||||
property alias source: image.source
|
||||
property alias fillMode: image.fillMode
|
||||
property alias radius: mask.radius
|
||||
property alias status: image.status
|
||||
property alias progress: image.progress
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
anchors.margins: borderRectangle.border.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mask
|
||||
anchors.fill: image
|
||||
}
|
||||
|
||||
TransparencyMask {
|
||||
anchors.fill: image
|
||||
source: image
|
||||
maskSource: mask
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: borderRectangle
|
||||
anchors.fill: parent
|
||||
|
||||
radius: mask.radius
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
353
interface/resources/qml/hifi/avatarapp/Settings.qml
Normal file
|
@ -0,0 +1,353 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: 'white'
|
||||
visible: false;
|
||||
|
||||
signal scaleChanged(real scale);
|
||||
|
||||
property alias onSaveClicked: dialogButtons.onYesClicked
|
||||
property alias onCancelClicked: dialogButtons.onNoClicked
|
||||
|
||||
property real scaleValue: scaleSlider.value / 10
|
||||
property alias dominantHandIsLeft: leftHandRadioButton.checked
|
||||
property alias avatarCollisionsOn: collisionsEnabledRadiobutton.checked
|
||||
property alias avatarAnimationJSON: avatarAnimationUrlInputText.text
|
||||
property alias avatarCollisionSoundUrl: avatarCollisionSoundUrlInputText.text
|
||||
|
||||
property real avatarScaleBackup;
|
||||
function open(settings, avatarScale) {
|
||||
console.debug('Settings.qml: open: ', JSON.stringify(settings, 0, 4));
|
||||
avatarScaleBackup = avatarScale;
|
||||
|
||||
scaleSlider.notify = false;
|
||||
scaleSlider.value = Math.round(avatarScale * 10);
|
||||
scaleSlider.notify = true;;
|
||||
|
||||
if(settings.dominantHand === 'left') {
|
||||
leftHandRadioButton.checked = true;
|
||||
} else {
|
||||
rightHandRadioButton.checked = true;
|
||||
}
|
||||
|
||||
if(settings.collisionsEnabled) {
|
||||
collisionsEnabledRadiobutton.checked = true;
|
||||
} else {
|
||||
collisionsDisabledRadioButton.checked = true;
|
||||
}
|
||||
|
||||
avatarAnimationJSON = settings.animGraphUrl;
|
||||
avatarCollisionSoundUrl = settings.collisionSoundUrl;
|
||||
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible = false
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 27
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 25
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 32
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 57
|
||||
|
||||
RowLayout {
|
||||
id: avatarScaleRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 17
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
text: "Avatar Scale"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
HiFiGlyphs {
|
||||
size: 30
|
||||
text: 'T'
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
HifiControlsUit.Slider {
|
||||
id: scaleSlider
|
||||
property bool notify: false;
|
||||
|
||||
from: 1
|
||||
to: 40
|
||||
|
||||
onValueChanged: {
|
||||
console.debug('value changed: ', value);
|
||||
if(notify) {
|
||||
console.debug('notifying.. ');
|
||||
root.scaleChanged(value / 10);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
anchors.left: scaleSlider.left
|
||||
anchors.leftMargin: 5
|
||||
anchors.top: scaleSlider.bottom
|
||||
anchors.topMargin: 2
|
||||
text: String(scaleSlider.from / 10) + 'x'
|
||||
}
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
anchors.right: scaleSlider.right
|
||||
anchors.rightMargin: 5
|
||||
anchors.top: scaleSlider.bottom
|
||||
anchors.topMargin: 2
|
||||
text: String(scaleSlider.to / 10) + 'x'
|
||||
}
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
size: 40
|
||||
text: 'T'
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
width: 37
|
||||
height: 28
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
||||
}
|
||||
|
||||
radius: 3
|
||||
|
||||
RalewaySemiBold {
|
||||
color: 'white'
|
||||
anchors.centerIn: parent
|
||||
text: "1x"
|
||||
size: 18
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
scaleSlider.value = 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: handAndCollisions
|
||||
anchors.top: avatarScaleRow.bottom
|
||||
anchors.topMargin: 39
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
rows: 2
|
||||
rowSpacing: 25
|
||||
|
||||
columns: 3
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
Layout.row: 0
|
||||
Layout.column: 0
|
||||
|
||||
text: "Dominant Hand"
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: leftRight
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: leftHandRadioButton
|
||||
|
||||
Layout.row: 0
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -40
|
||||
|
||||
ButtonGroup.group: leftRight
|
||||
checked: true
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Left"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: rightHandRadioButton
|
||||
|
||||
Layout.row: 0
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: 20
|
||||
|
||||
ButtonGroup.group: leftRight
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Right"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
Layout.row: 1
|
||||
Layout.column: 0
|
||||
|
||||
text: "Avatar Collisions"
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: onOff
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: collisionsEnabledRadiobutton
|
||||
|
||||
Layout.row: 1
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -40
|
||||
ButtonGroup.group: onOff
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
checked: true
|
||||
|
||||
text: "ON"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: collisionsDisabledRadioButton
|
||||
|
||||
Layout.row: 1
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: 20
|
||||
|
||||
ButtonGroup.group: onOff
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
|
||||
text: "OFF"
|
||||
boxSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: avatarAnimationLayout
|
||||
anchors.top: handAndCollisions.bottom
|
||||
anchors.topMargin: 25
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 4
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
text: "Avatar Animation JSON"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputTextStyle4 {
|
||||
id: avatarAnimationUrlInputText
|
||||
font.pixelSize: 17
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
placeholderText: 'user\\file\\dir'
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: avatarCollisionLayout
|
||||
anchors.top: avatarAnimationLayout.bottom
|
||||
anchors.topMargin: 25
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 4
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
text: "Avatar collision sound URL (optional)"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputTextStyle4 {
|
||||
id: avatarCollisionSoundUrlInputText
|
||||
font.pixelSize: 17
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-'
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
id: dialogButtons
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
yesText: "SAVE"
|
||||
noText: "CANCEL"
|
||||
}
|
||||
}
|
||||
}
|
29
interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias text: glyph.text
|
||||
property alias font: glyph.font
|
||||
property alias color: glyph.color
|
||||
property alias horizontalAlignment: glyph.horizontalAlignment
|
||||
property alias dropShadowRadius: shadow.radius
|
||||
property alias dropShadowHorizontalOffset: shadow.horizontalOffset
|
||||
property alias dropShadowVerticalOffset: shadow.verticalOffset
|
||||
|
||||
HiFiGlyphs {
|
||||
id: glyph
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: glyph
|
||||
radius: 4
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 4
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: glyph
|
||||
}
|
||||
}
|
32
interface/resources/qml/hifi/avatarapp/ShadowImage.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias source: image.source
|
||||
property alias dropShadowRadius: shadow.radius
|
||||
property alias dropShadowHorizontalOffset: shadow.horizontalOffset
|
||||
property alias dropShadowVerticalOffset: shadow.verticalOffset
|
||||
property alias radius: image.radius
|
||||
property alias border: image.border
|
||||
property alias status: image.status
|
||||
property alias progress: image.progress
|
||||
property alias fillMode: image.fillMode
|
||||
|
||||
RoundImage {
|
||||
id: image
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
radius: 6
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: image
|
||||
radius: 6
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 3
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: image
|
||||
}
|
||||
}
|
30
interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias color: rectangle.color
|
||||
property alias gradient: rectangle.gradient
|
||||
property alias border: rectangle.border
|
||||
property alias radius: rectangle.radius
|
||||
property alias dropShadowRadius: shadow.radius
|
||||
property alias dropShadowHorizontalOffset: shadow.horizontalOffset
|
||||
property alias dropShadowVerticalOffset: shadow.verticalOffset
|
||||
property alias dropShadowOpacity: shadow.opacity
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: rectangle
|
||||
radius: 6
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 3
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: rectangle
|
||||
}
|
||||
}
|
7
interface/resources/qml/hifi/avatarapp/Spinner.qml
Normal file
|
@ -0,0 +1,7 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebEngine 1.5
|
||||
|
||||
AnimatedImage {
|
||||
source: "../../../icons/loader-snake-64-w.gif"
|
||||
playing: visible
|
||||
}
|
29
interface/resources/qml/hifi/avatarapp/SquareLabel.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
ShadowRectangle {
|
||||
width: 44
|
||||
height: 28
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
||||
}
|
||||
|
||||
property alias glyphText: glyph.text
|
||||
property alias glyphRotation: glyph.rotation
|
||||
property alias glyphSize: glyph.size
|
||||
|
||||
radius: 3
|
||||
|
||||
HiFiGlyphs {
|
||||
id: glyph
|
||||
color: 'white'
|
||||
anchors.centerIn: parent
|
||||
size: 30
|
||||
}
|
||||
}
|
43
interface/resources/qml/hifi/avatarapp/TransparencyMask.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
property alias source: sourceImage.sourceItem
|
||||
property alias maskSource: sourceMask.sourceItem
|
||||
|
||||
anchors.fill: parent
|
||||
ShaderEffectSource {
|
||||
id: sourceMask
|
||||
smooth: true
|
||||
hideSource: true
|
||||
}
|
||||
ShaderEffectSource {
|
||||
id: sourceImage
|
||||
hideSource: true
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
id: maskEffect
|
||||
anchors.fill: parent
|
||||
|
||||
property variant source: sourceImage
|
||||
property variant mask: sourceMask
|
||||
|
||||
fragmentShader: {
|
||||
"
|
||||
varying highp vec2 qt_TexCoord0;
|
||||
uniform lowp sampler2D source;
|
||||
uniform lowp sampler2D mask;
|
||||
void main() {
|
||||
|
||||
highp vec4 maskColor = texture2D(mask, vec2(qt_TexCoord0.x, qt_TexCoord0.y));
|
||||
highp vec4 sourceColor = texture2D(source, vec2(qt_TexCoord0.x, qt_TexCoord0.y));
|
||||
|
||||
if(maskColor.a > 0.0)
|
||||
gl_FragColor = sourceColor;
|
||||
else
|
||||
gl_FragColor = maskColor;
|
||||
}
|
||||
"
|
||||
}
|
||||
}
|
||||
}
|
65
interface/resources/qml/hifi/avatarapp/Vector3.qml
Normal file
|
@ -0,0 +1,65 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Row {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: xspinner.controlHeight
|
||||
|
||||
property int spinboxSpace: 10
|
||||
property int spinboxWidth: (parent.width - 2 * spinboxSpace) / 3
|
||||
property color backgroundColor: "darkgray"
|
||||
|
||||
property int decimals: 4
|
||||
property real realFrom: 0
|
||||
property real realTo: 100
|
||||
property real realStepSize: 0.0001
|
||||
|
||||
spacing: spinboxSpace
|
||||
|
||||
property alias xvalue: xspinner.realValue
|
||||
property alias yvalue: yspinner.realValue
|
||||
property alias zvalue: zspinner.realValue
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: xspinner
|
||||
width: parent.spinboxWidth
|
||||
labelInside: "X:"
|
||||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.redHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: yspinner
|
||||
width: parent.spinboxWidth
|
||||
labelInside: "Y:"
|
||||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.greenHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: zspinner
|
||||
width: parent.spinboxWidth
|
||||
labelInside: "Z:"
|
||||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.primaryHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
}
|
15
interface/resources/qml/hifi/avatarapp/WhiteButton.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
|
||||
HifiControlsUit.Button {
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
width: Math.max(hifi.dimensions.buttonWidth, implicitTextWidth + 20)
|
||||
fontSize: 18
|
||||
color: hifi.buttons.noneBorderlessGray;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
height: 40
|
||||
}
|
|
@ -51,6 +51,8 @@ ListModel {
|
|||
if (!delayedClear) { root.clear(); }
|
||||
currentPageToRetrieve = 1;
|
||||
retrievedAtLeastOnePage = false;
|
||||
totalPages = 0;
|
||||
totalEntries = 0;
|
||||
}
|
||||
|
||||
// Page processing.
|
||||
|
@ -59,6 +61,8 @@ ListModel {
|
|||
property var processPage: function (data) { return data; }
|
||||
|
||||
property var listView; // Optional. For debugging.
|
||||
property int totalPages: 0;
|
||||
property int totalEntries: 0;
|
||||
// Check consistency and call processPage.
|
||||
function handlePage(error, response) {
|
||||
var processed;
|
||||
|
@ -79,8 +83,10 @@ ListModel {
|
|||
if (response.current_page && response.current_page !== currentPageToRetrieve) { // Not all endpoints specify this property.
|
||||
return fail("Mismatched page, expected:" + currentPageToRetrieve);
|
||||
}
|
||||
totalPages = response.total_pages || 0;
|
||||
totalEntries = response.total_entries || 0;
|
||||
processed = processPage(response.data || response);
|
||||
if (response.total_pages && (response.total_pages === currentPageToRetrieve)) {
|
||||
if (totalPages && (totalPages === currentPageToRetrieve)) {
|
||||
currentPageToRetrieve = -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -300,6 +300,12 @@ Item {
|
|||
id: controllerPrefereneces
|
||||
objectName: "TabletControllerPreferences"
|
||||
showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
|
||||
categoryProperties: {
|
||||
"VR Movement" : {
|
||||
"User real-world height (meters)" : { "anchors.right" : "undefined" },
|
||||
"RESET SENSORS" : { "width" : "180", "anchors.left" : "undefined" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
interface/resources/qml/hifi/tablet/EditEntityList.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtWebChannel 1.0
|
||||
import "../../controls"
|
||||
import "../toolbars"
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../controls-uit" as HifiControls
|
||||
import "../../styles-uit"
|
||||
|
||||
|
||||
WebView {
|
||||
id: entityListToolWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityList.html"
|
||||
enabled: true
|
||||
}
|
|
@ -9,7 +9,6 @@ import "../../styles-uit"
|
|||
|
||||
TabBar {
|
||||
id: editTabView
|
||||
// anchors.fill: parent
|
||||
width: parent.width
|
||||
contentWidth: parent.width
|
||||
padding: 0
|
||||
|
@ -34,7 +33,7 @@ TabBar {
|
|||
width: parent.width
|
||||
clip: true
|
||||
|
||||
contentHeight: createEntitiesFlow.height + importButton.height + assetServerButton.height +
|
||||
contentHeight: createEntitiesFlow.height + importButton.height + assetServerButton.height +
|
||||
header.anchors.topMargin + createEntitiesFlow.anchors.topMargin +
|
||||
assetServerButton.anchors.topMargin + importButton.anchors.topMargin +
|
||||
header.paintedHeight
|
||||
|
@ -77,8 +76,9 @@ TabBar {
|
|||
text: "MODEL"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newModelButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newModelButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -88,8 +88,9 @@ TabBar {
|
|||
text: "CUBE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newCubeButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -99,8 +100,9 @@ TabBar {
|
|||
text: "SPHERE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newSphereButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -110,8 +112,9 @@ TabBar {
|
|||
text: "LIGHT"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newLightButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newLightButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -121,8 +124,9 @@ TabBar {
|
|||
text: "TEXT"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newTextButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newTextButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -132,8 +136,9 @@ TabBar {
|
|||
text: "IMAGE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newImageButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newImageButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -143,8 +148,9 @@ TabBar {
|
|||
text: "WEB"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newWebButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newWebButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -154,8 +160,9 @@ TabBar {
|
|||
text: "ZONE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newZoneButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -165,8 +172,9 @@ TabBar {
|
|||
text: "PARTICLE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newParticleButton" }
|
||||
});
|
||||
editTabView.currentIndex = 4
|
||||
}
|
||||
}
|
||||
|
@ -176,8 +184,9 @@ TabBar {
|
|||
text: "MATERIAL"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "newMaterialButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newMaterialButton" }
|
||||
});
|
||||
editTabView.currentIndex = 2
|
||||
}
|
||||
}
|
||||
|
@ -196,8 +205,9 @@ TabBar {
|
|||
anchors.topMargin: 35
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "openAssetBrowserButton" }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,8 +224,9 @@ TabBar {
|
|||
anchors.topMargin: 20
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" }
|
||||
});
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "importEntitiesButton" }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
58
interface/resources/qml/hifi/tablet/EditTools.qml
Normal file
|
@ -0,0 +1,58 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
// FIXME pretty non-DRY code, should figure out a way to optionally hide one tab from the tab view, keep in sync with Edit.qml
|
||||
StackView {
|
||||
id: editRoot
|
||||
objectName: "stack"
|
||||
|
||||
signal sendToScript(var message);
|
||||
|
||||
topPadding: 40
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property var itemProperties: {"y": editRoot.topPadding,
|
||||
"width": editRoot.availableWidth,
|
||||
"height": editRoot.availableHeight }
|
||||
Component.onCompleted: {
|
||||
tab.currentIndex = 0
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: "#404040" //default background color
|
||||
EditToolsTabView {
|
||||
id: tab
|
||||
anchors.fill: parent
|
||||
currentIndex: -1
|
||||
onCurrentIndexChanged: {
|
||||
editRoot.replace(null, tab.itemAt(currentIndex).visualItem,
|
||||
itemProperties,
|
||||
StackView.Immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pushSource(path) {
|
||||
editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties,
|
||||
StackView.Immediate);
|
||||
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
|
||||
}
|
||||
|
||||
function popSource() {
|
||||
editRoot.pop(StackView.Immediate);
|
||||
}
|
||||
|
||||
// Passes script messages to the item on the top of the stack
|
||||
function fromScript(message) {
|
||||
var currentItem = editRoot.currentItem;
|
||||
if (currentItem && currentItem.fromScript) {
|
||||
currentItem.fromScript(message);
|
||||
} else if (tab.fromScript) {
|
||||
tab.fromScript(message);
|
||||
}
|
||||
}
|
||||
}
|
328
interface/resources/qml/hifi/tablet/EditToolsTabView.qml
Normal file
|
@ -0,0 +1,328 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtWebChannel 1.0
|
||||
import "../../controls"
|
||||
import "../toolbars"
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../controls-uit" as HifiControls
|
||||
import "../../styles-uit"
|
||||
|
||||
TabBar {
|
||||
id: editTabView
|
||||
width: parent.width
|
||||
contentWidth: parent.width
|
||||
padding: 0
|
||||
spacing: 0
|
||||
|
||||
readonly property QtObject tabIndex: QtObject {
|
||||
readonly property int create: 0
|
||||
readonly property int properties: 1
|
||||
readonly property int grid: 2
|
||||
readonly property int particle: 3
|
||||
}
|
||||
|
||||
readonly property HifiConstants hifi: HifiConstants {}
|
||||
|
||||
EditTabButton {
|
||||
title: "CREATE"
|
||||
active: true
|
||||
enabled: true
|
||||
property string originalUrl: ""
|
||||
|
||||
property Component visualItem: Component {
|
||||
|
||||
Rectangle {
|
||||
color: "#404040"
|
||||
id: container
|
||||
|
||||
Flickable {
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
clip: true
|
||||
|
||||
contentHeight: createEntitiesFlow.height + importButton.height + assetServerButton.height +
|
||||
header.anchors.topMargin + createEntitiesFlow.anchors.topMargin +
|
||||
assetServerButton.anchors.topMargin + importButton.anchors.topMargin +
|
||||
header.paintedHeight
|
||||
|
||||
contentWidth: width
|
||||
|
||||
ScrollBar.vertical : ScrollBar {
|
||||
visible: parent.contentHeight > parent.height
|
||||
width: 20
|
||||
background: Rectangle {
|
||||
color: hifi.colors.tableScrollBackgroundDark
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: header
|
||||
color: "#ffffff"
|
||||
text: "Choose an Entity Type to Create:"
|
||||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 28
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 28
|
||||
}
|
||||
|
||||
Flow {
|
||||
id: createEntitiesFlow
|
||||
spacing: 35
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 55
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 55
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 70
|
||||
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/94-model-01.svg"
|
||||
text: "MODEL"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newModelButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/21-cube-01.svg"
|
||||
text: "CUBE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newCubeButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/22-sphere-01.svg"
|
||||
text: "SPHERE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newSphereButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/24-light-01.svg"
|
||||
text: "LIGHT"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newLightButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/20-text-01.svg"
|
||||
text: "TEXT"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newTextButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/image.svg"
|
||||
text: "IMAGE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newImageButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/25-web-1-01.svg"
|
||||
text: "WEB"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newWebButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/23-zone-01.svg"
|
||||
text: "ZONE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newZoneButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/90-particles-01.svg"
|
||||
text: "PARTICLE"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newParticleButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.particle
|
||||
}
|
||||
}
|
||||
|
||||
NewEntityButton {
|
||||
icon: "icons/create-icons/126-material-01.svg"
|
||||
text: "MATERIAL"
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "newMaterialButton" }
|
||||
});
|
||||
editTabView.currentIndex = tabIndex.properties
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: assetServerButton
|
||||
text: "Open This Domain's Asset Server"
|
||||
color: hifi.buttons.black
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 55
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 55
|
||||
anchors.top: createEntitiesFlow.bottom
|
||||
anchors.topMargin: 35
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "openAssetBrowserButton" }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: importButton
|
||||
text: "Import Entities (.json)"
|
||||
color: hifi.buttons.black
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 55
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 55
|
||||
anchors.top: assetServerButton.bottom
|
||||
anchors.topMargin: 20
|
||||
onClicked: {
|
||||
editRoot.sendToScript({
|
||||
method: "newEntityButtonClicked",
|
||||
params: { buttonName: "importEntitiesButton" }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Flickable
|
||||
}
|
||||
}
|
||||
|
||||
EditTabButton {
|
||||
title: "PROPERTIES"
|
||||
active: true
|
||||
enabled: true
|
||||
property string originalUrl: ""
|
||||
|
||||
property Component visualItem: Component {
|
||||
WebView {
|
||||
id: entityPropertiesWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityProperties.html"
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditTabButton {
|
||||
title: "GRID"
|
||||
active: true
|
||||
enabled: true
|
||||
property string originalUrl: ""
|
||||
|
||||
property Component visualItem: Component {
|
||||
WebView {
|
||||
id: gridControlsWebView
|
||||
url: Paths.defaultScripts + "/system/html/gridControls.html"
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditTabButton {
|
||||
title: "P"
|
||||
active: true
|
||||
enabled: true
|
||||
property string originalUrl: ""
|
||||
|
||||
property Component visualItem: Component {
|
||||
WebView {
|
||||
id: particleExplorerWebView
|
||||
url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html"
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
case 'selectTab':
|
||||
selectTab(message.params.id);
|
||||
break;
|
||||
default:
|
||||
console.warn('Unrecognized message:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Changes the current tab based on tab index or title as input
|
||||
function selectTab(id) {
|
||||
if (typeof id === 'number') {
|
||||
if (id >= tabIndex.create && id <= tabIndex.particle) {
|
||||
editTabView.currentIndex = id;
|
||||
} else {
|
||||
console.warn('Attempt to switch to invalid tab:', id);
|
||||
}
|
||||
} else if (typeof id === 'string'){
|
||||
switch (id.toLowerCase()) {
|
||||
case 'create':
|
||||
editTabView.currentIndex = tabIndex.create;
|
||||
break;
|
||||
case 'properties':
|
||||
editTabView.currentIndex = tabIndex.properties;
|
||||
break;
|
||||
case 'grid':
|
||||
editTabView.currentIndex = tabIndex.grid;
|
||||
break;
|
||||
case 'particle':
|
||||
editTabView.currentIndex = tabIndex.particle;
|
||||
break;
|
||||
default:
|
||||
console.warn('Attempt to switch to invalid tab:', id);
|
||||
}
|
||||
} else {
|
||||
console.warn('Attempt to switch tabs with invalid input:', JSON.stringify(id));
|
||||
}
|
||||
}
|
||||
}
|
5
interface/resources/qml/hifi/tablet/EntityList.qml
Normal file
|
@ -0,0 +1,5 @@
|
|||
WebView {
|
||||
id: entityListToolWebView
|
||||
url: Paths.defaultScripts + "/system/html/entityList.html"
|
||||
enabled: true
|
||||
}
|
|
@ -29,12 +29,16 @@ Rectangle {
|
|||
property bool keyboardRasied: false
|
||||
|
||||
function errorMessageBox(message) {
|
||||
return desktop.messageBox({
|
||||
icon: hifi.icons.warning,
|
||||
defaultButton: OriginalDialogs.StandardButton.Ok,
|
||||
title: "Error",
|
||||
text: message
|
||||
});
|
||||
try {
|
||||
return desktop.messageBox({
|
||||
icon: hifi.icons.warning,
|
||||
defaultButton: OriginalDialogs.StandardButton.Ok,
|
||||
title: "Error",
|
||||
text: message
|
||||
});
|
||||
} catch(e) {
|
||||
Window.alert(message);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
|
20
interface/resources/qml/hifi/tablet/NewMaterialWindow.qml
Normal file
|
@ -0,0 +1,20 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
StackView {
|
||||
id: stackView
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.topMargin: 40
|
||||
|
||||
signal sendToScript(var message);
|
||||
|
||||
NewMaterialDialog {
|
||||
id: dialog
|
||||
anchors.fill: parent
|
||||
Component.onCompleted:{
|
||||
dialog.sendToScript.connect(stackView.sendToScript);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,12 +29,16 @@ Rectangle {
|
|||
property bool keyboardRasied: false
|
||||
|
||||
function errorMessageBox(message) {
|
||||
return desktop.messageBox({
|
||||
icon: hifi.icons.warning,
|
||||
defaultButton: OriginalDialogs.StandardButton.Ok,
|
||||
title: "Error",
|
||||
text: message
|
||||
});
|
||||
try {
|
||||
return desktop.messageBox({
|
||||
icon: hifi.icons.warning,
|
||||
defaultButton: OriginalDialogs.StandardButton.Ok,
|
||||
title: "Error",
|
||||
text: message
|
||||
});
|
||||
} catch(e) {
|
||||
Window.alert(message);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
|
20
interface/resources/qml/hifi/tablet/NewModelWindow.qml
Normal file
|
@ -0,0 +1,20 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
StackView {
|
||||
id: stackView
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.topMargin: 40
|
||||
|
||||
signal sendToScript(var message);
|
||||
|
||||
NewModelDialog {
|
||||
id: dialog
|
||||
anchors.fill: parent
|
||||
Component.onCompleted:{
|
||||
dialog.sendToScript.connect(stackView.sendToScript);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ Item {
|
|||
HifiConstants { id: hifi }
|
||||
property var sections: []
|
||||
property var showCategories: []
|
||||
property var categoryProperties: ({})
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
|
@ -100,7 +101,8 @@ Item {
|
|||
// NOTE: the sort order of items in the showCategories array is the same order in the dialog.
|
||||
for (i = 0; i < showCategories.length; i++) {
|
||||
if (categoryMap[showCategories[i]]) {
|
||||
sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i]}));
|
||||
var properties = categoryProperties.hasOwnProperty(showCategories[i]) ? categoryProperties[showCategories[i]] : {};
|
||||
sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i], sectionProperties: properties}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Preference {
|
|||
property bool isLast: false
|
||||
property string name: "Header"
|
||||
property real spacing: 8
|
||||
property var sectionProperties: ({})
|
||||
default property alias preferences: contentContainer.children
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
@ -163,6 +164,28 @@ Preference {
|
|||
|
||||
if (builder) {
|
||||
preferences.push(builder.createObject(contentContainer, { preference: preference, isFirstCheckBox: (checkBoxCount === 1) , z: zpos}));
|
||||
|
||||
var preferenceObject = preferences[preferences.length - 1];
|
||||
var props = sectionProperties.hasOwnProperty(preference.name) ? sectionProperties[preference.name] : {};
|
||||
|
||||
for(var prop in props) {
|
||||
var value = props[prop];
|
||||
if(value.indexOf('.') !== -1) {
|
||||
var splittedValues = value.split('.');
|
||||
if(splittedValues[0] === 'parent') {
|
||||
value = preferenceObject.parent[splittedValues[1]];
|
||||
}
|
||||
} else if(value === 'undefined') {
|
||||
value = undefined;
|
||||
}
|
||||
|
||||
if(prop.indexOf('.') !== -1) {
|
||||
var splittedProps = prop.split('.');
|
||||
preferenceObject[splittedProps[0]][splittedProps[1]] = value;
|
||||
} else {
|
||||
preferenceObject[prop] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7640,7 +7640,6 @@ void Application::toggleEntityScriptServerLogDialog() {
|
|||
|
||||
void Application::loadAddAvatarBookmarkDialog() const {
|
||||
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
|
||||
avatarBookmarks->addBookmark();
|
||||
}
|
||||
|
||||
void Application::loadAvatarBrowser() const {
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#include <QStandardPaths>
|
||||
#include <QQmlContext>
|
||||
#include <QList>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <Application.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
|
@ -28,7 +30,6 @@
|
|||
#include <VariantMapToScriptValue.h>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
|
@ -92,10 +93,96 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
|||
}
|
||||
|
||||
AvatarBookmarks::AvatarBookmarks() {
|
||||
QDir directory(PathUtils::getAppDataPath());
|
||||
if (!directory.exists()) {
|
||||
directory.mkpath(".");
|
||||
}
|
||||
|
||||
_bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
if(!QFile::exists(_bookmarksFilename)) {
|
||||
auto defaultBookmarksFilename = PathUtils::resourcesPath() + QString("avatar/bookmarks") + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
if (!QFile::exists(defaultBookmarksFilename)) {
|
||||
qDebug() << defaultBookmarksFilename << "doesn't exist!";
|
||||
}
|
||||
|
||||
if (!QFile::copy(defaultBookmarksFilename, _bookmarksFilename)) {
|
||||
qDebug() << "failed to copy" << defaultBookmarksFilename << "to" << _bookmarksFilename;
|
||||
}
|
||||
}
|
||||
readFromFile();
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "addBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
QVariantMap bookmark = getAvatarDataToBookmark();
|
||||
insert(bookmarkName, bookmark);
|
||||
|
||||
emit bookmarkAdded(bookmarkName);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::saveBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "saveBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
if (contains(bookmarkName)) {
|
||||
QVariantMap bookmark = getAvatarDataToBookmark();
|
||||
insert(bookmarkName, bookmark);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarBookmarks::removeBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "removeBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
|
||||
remove(bookmarkName);
|
||||
emit bookmarkDeleted(bookmarkName);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
myAvatar->removeAvatarEntities();
|
||||
|
||||
addAvatarEntities(avatarEntities);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "loadBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
|
||||
auto bookmarkEntry = _bookmarks.find(bookmarkName);
|
||||
|
||||
if (bookmarkEntry != _bookmarks.end()) {
|
||||
QVariantMap bookmark = bookmarkEntry.value().toMap();
|
||||
if (!bookmark.empty()) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
myAvatar->removeAvatarEntities();
|
||||
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
|
||||
myAvatar->useFullAvatarURL(avatarUrl);
|
||||
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
|
||||
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
|
||||
|
||||
qCDebug(interfaceapp) << "Attach " << attachments;
|
||||
myAvatar->setAttachmentsVariant(attachments);
|
||||
|
||||
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
|
||||
myAvatar->setAvatarScale(qScale);
|
||||
|
||||
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
|
||||
addAvatarEntities(avatarEntities);
|
||||
|
||||
emit bookmarkLoaded(bookmarkName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarBookmarks::readFromFile() {
|
||||
// migrate old avatarbookmarks.json, used to be in 'local' folder on windows
|
||||
QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
|
@ -115,99 +202,37 @@ void AvatarBookmarks::readFromFile() {
|
|||
Bookmarks::readFromFile();
|
||||
}
|
||||
|
||||
void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
||||
// Add menus/actions
|
||||
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar);
|
||||
QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection);
|
||||
_bookmarksMenu = menu->addMenu(MenuOption::AvatarBookmarks);
|
||||
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark);
|
||||
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
|
||||
|
||||
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
|
||||
addBookmarkToMenu(menubar, it.key(), it.value());
|
||||
QVariantMap AvatarBookmarks::getBookmark(const QString &bookmarkName)
|
||||
{
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariantMap result;
|
||||
BLOCKING_INVOKE_METHOD(this, "getBookmark", Q_RETURN_ARG(QVariantMap, result), Q_ARG(QString, bookmarkName));
|
||||
return result;
|
||||
}
|
||||
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
QVariantMap bookmark;
|
||||
|
||||
auto bookmarkEntry = _bookmarks.find(bookmarkName);
|
||||
|
||||
if (bookmarkEntry != _bookmarks.end()) {
|
||||
bookmark = bookmarkEntry.value().toMap();
|
||||
}
|
||||
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
void AvatarBookmarks::changeToBookmarkedAvatar() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
QVariantMap AvatarBookmarks::getAvatarDataToBookmark() {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
|
||||
if (action->data().type() == QVariant::String) {
|
||||
// TODO: Phase this out eventually.
|
||||
// Legacy avatar bookmark.
|
||||
|
||||
myAvatar->useFullAvatarURL(action->data().toString());
|
||||
qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark ";
|
||||
} else {
|
||||
|
||||
const QMap<QString, QVariant> bookmark = action->data().toMap();
|
||||
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
|
||||
// handle the new one separately.
|
||||
// This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous
|
||||
if (bookmark.value(ENTRY_VERSION) == 3) {
|
||||
myAvatar->removeAvatarEntities();
|
||||
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
|
||||
myAvatar->useFullAvatarURL(avatarUrl);
|
||||
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
|
||||
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
|
||||
|
||||
qCDebug(interfaceapp) << "Attach " << attachments;
|
||||
myAvatar->setAttachmentsVariant(attachments);
|
||||
|
||||
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
|
||||
myAvatar->setAvatarScale(qScale);
|
||||
|
||||
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
|
||||
addAvatarEntities(avatarEntities);
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark() {
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString());
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
auto bookmarkName = response.toString();
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap bookmark;
|
||||
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
|
||||
QAction* changeAction = _bookmarksMenu->newAction();
|
||||
changeAction->setData(bookmark);
|
||||
connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar()));
|
||||
if (!_isMenuSorted) {
|
||||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
|
||||
} else {
|
||||
// TODO: this is aggressive but other alternatives have proved less fruitful so far.
|
||||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap bookmark;
|
||||
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
|
||||
return bookmark;
|
||||
}
|
||||
|
|
|
@ -30,19 +30,50 @@ class AvatarBookmarks: public Bookmarks, public Dependency {
|
|||
|
||||
public:
|
||||
AvatarBookmarks();
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
|
||||
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu) override {};
|
||||
Q_INVOKABLE QVariantMap getBookmark(const QString& bookmarkName);
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Add the current Avatar to your avatar bookmarks.
|
||||
* @function AvatarBookmarks.addBookMark
|
||||
*/
|
||||
void addBookmark();
|
||||
void addBookmark(const QString& bookmarkName);
|
||||
void saveBookmark(const QString& bookmarkName);
|
||||
void loadBookmark(const QString& bookmarkName);
|
||||
void removeBookmark(const QString& bookmarkName);
|
||||
void updateAvatarEntities(const QVariantList& avatarEntities);
|
||||
QVariantMap getBookmarks() { return _bookmarks; }
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* This function gets triggered after avatar loaded from bookmark
|
||||
* @function AvatarBookmarks.bookmarkLoaded
|
||||
* @param {string} bookmarkName
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void bookmarkLoaded(const QString& bookmarkName);
|
||||
|
||||
/**jsdoc
|
||||
* This function gets triggered after avatar bookmark deleted
|
||||
* @function AvatarBookmarks.bookmarkDeleted
|
||||
* @param {string} bookmarkName
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void bookmarkDeleted(const QString& bookmarkName);
|
||||
|
||||
/**jsdoc
|
||||
* This function gets triggered after avatar bookmark added
|
||||
* @function AvatarBookmarks.bookmarkAdded
|
||||
* @param {string} bookmarkName
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void bookmarkAdded(const QString& bookmarkName);
|
||||
|
||||
protected:
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override {};
|
||||
void readFromFile() override;
|
||||
QVariantMap getAvatarDataToBookmark();
|
||||
|
||||
private:
|
||||
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
|
||||
|
@ -53,9 +84,6 @@ private:
|
|||
const QString ENTRY_VERSION = "version";
|
||||
|
||||
const int AVATAR_BOOKMARK_VERSION = 3;
|
||||
|
||||
private slots:
|
||||
void changeToBookmarkedAvatar();
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarBookmarks_h
|
||||
|
|
|
@ -46,6 +46,10 @@ void Bookmarks::deleteBookmark() {
|
|||
return;
|
||||
}
|
||||
|
||||
deleteBookmark(bookmarkName);
|
||||
}
|
||||
|
||||
void Bookmarks::deleteBookmark(const QString& bookmarkName) {
|
||||
removeBookmarkFromMenu(Menu::getInstance(), bookmarkName);
|
||||
remove(bookmarkName);
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
QString addressForBookmark(const QString& name) const;
|
||||
|
||||
protected:
|
||||
void deleteBookmark(const QString& bookmarkName);
|
||||
|
||||
void addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark);
|
||||
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0;
|
||||
void enableMenuItems(bool enabled);
|
||||
|
@ -38,9 +40,10 @@ protected:
|
|||
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
|
||||
void sortActions(Menu* menubar, MenuWrapper* menu);
|
||||
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
|
||||
|
||||
void removeBookmarkFromMenu(Menu* menubar, const QString& name);
|
||||
bool contains(const QString& name) const;
|
||||
|
||||
void remove(const QString& name);
|
||||
|
||||
QVariantMap _bookmarks; // { name: url, ... }
|
||||
QPointer<MenuWrapper> _bookmarksMenu;
|
||||
QPointer<QAction> _deleteBookmarksAction;
|
||||
|
@ -57,12 +60,9 @@ protected slots:
|
|||
void deleteBookmark();
|
||||
|
||||
private:
|
||||
void remove(const QString& name);
|
||||
static bool sortOrder(QAction* a, QAction* b);
|
||||
|
||||
void persistToFile();
|
||||
|
||||
void removeBookmarkFromMenu(Menu* menubar, const QString& name);
|
||||
};
|
||||
|
||||
#endif // hifi_Bookmarks_h
|
||||
|
|
|
@ -158,46 +158,6 @@ Menu::Menu() {
|
|||
// Edit > Reload All Content
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
|
||||
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto avatar = avatarManager->getMyAvatar();
|
||||
|
||||
// Avatar > Size
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
// Avatar > Size > Increase
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar.get(), SLOT(increaseSize()));
|
||||
|
||||
// Avatar > Size > Decrease
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar.get(), SLOT(decreaseSize()));
|
||||
|
||||
// Avatar > Size > Reset
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar.get(), SLOT(resetSize()));
|
||||
|
||||
// Avatar > Reset Sensors
|
||||
addActionToQMenuAndActionHash(avatarMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp, SLOT(resetSensors()));
|
||||
|
||||
// Avatar > Attachments...
|
||||
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
|
||||
QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
});
|
||||
|
||||
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
|
||||
avatarBookmarks->setupMenus(this, avatarMenu);
|
||||
// Display menu ----------------------------------
|
||||
// FIXME - this is not yet matching Alan's spec because it doesn't have
|
||||
// menus for "2D"/"3D" - we need to add support for detecting the appropriate
|
||||
|
@ -315,13 +275,6 @@ Menu::Menu() {
|
|||
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
});
|
||||
|
||||
// Settings > Avatar...
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
QString("hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
});
|
||||
|
||||
// Settings > Developer Menu
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
|
@ -580,6 +533,9 @@ Menu::Menu() {
|
|||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false);
|
||||
connect(action, &QAction::triggered, [this]{ Avatar::setShowOtherLookAtVectors(isOptionChecked(MenuOption::ShowOtherLookAtVectors)); });
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto avatar = avatarManager->getMyAvatar();
|
||||
|
||||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableLookAtSnapping, 0, true);
|
||||
connect(action, &QAction::triggered, [this, avatar]{
|
||||
avatar->setProperty("lookAtSnappingEnabled", isOptionChecked(MenuOption::EnableLookAtSnapping));
|
||||
|
|
|
@ -88,6 +88,10 @@ const float MyAvatar::ZOOM_MIN = 0.5f;
|
|||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
const float MIN_SCALE_CHANGED_DELTA = 0.001f;
|
||||
const int MODE_READINGS_RING_BUFFER_SIZE = 500;
|
||||
const float CENTIMETERS_PER_METER = 100.0f;
|
||||
|
||||
//#define DEBUG_DRAW_HMD_MOVING_AVERAGE
|
||||
|
||||
MyAvatar::MyAvatar(QThread* thread) :
|
||||
Avatar(thread),
|
||||
|
@ -108,6 +112,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_hmdSensorMatrix(),
|
||||
_hmdSensorOrientation(),
|
||||
_hmdSensorPosition(),
|
||||
_recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE),
|
||||
_bodySensorMatrix(),
|
||||
_goToPending(false),
|
||||
_goToPosition(),
|
||||
|
@ -414,7 +419,8 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
|
|||
void MyAvatar::update(float deltaTime) {
|
||||
|
||||
// update moving average of HMD facing in xz plane.
|
||||
const float HMD_FACING_TIMESCALE = 4.0f; // very slow average
|
||||
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
|
||||
|
||||
float tau = deltaTime / HMD_FACING_TIMESCALE;
|
||||
_headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau);
|
||||
|
||||
|
@ -423,6 +429,12 @@ void MyAvatar::update(float deltaTime) {
|
|||
_smoothOrientationTimer += deltaTime;
|
||||
}
|
||||
|
||||
float newHeightReading = getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y;
|
||||
int newHeightReadingInCentimeters = glm::floor(newHeightReading * CENTIMETERS_PER_METER);
|
||||
_recentModeReadings.insert(newHeightReadingInCentimeters);
|
||||
setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
|
||||
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
|
||||
|
||||
#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE
|
||||
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||
glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation());
|
||||
|
@ -1591,18 +1603,26 @@ void MyAvatar::removeAvatarEntities() {
|
|||
QVariantList MyAvatar::getAvatarEntitiesVariant() {
|
||||
QVariantList avatarEntitiesData;
|
||||
QScriptEngine scriptEngine;
|
||||
forEachChild([&](SpatiallyNestablePointer child) {
|
||||
if (child->getNestableType() == NestableType::Entity) {
|
||||
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(child);
|
||||
if (modelEntity) {
|
||||
QVariantMap avatarEntityData;
|
||||
EntityItemProperties entityProperties = modelEntity->getProperties();
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
avatarEntitiesData.append(QVariant(avatarEntityData));
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
if (entityTree) {
|
||||
AvatarEntityMap avatarEntities = getAvatarEntityData();
|
||||
for (auto entityID : avatarEntities.keys()) {
|
||||
auto entity = entityTree->findEntityByID(entityID);
|
||||
if (!entity) {
|
||||
continue;
|
||||
}
|
||||
QVariantMap avatarEntityData;
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = entity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
avatarEntitiesData.append(QVariant(avatarEntityData));
|
||||
}
|
||||
});
|
||||
}
|
||||
return avatarEntitiesData;
|
||||
}
|
||||
|
||||
|
@ -1979,7 +1999,6 @@ QUrl MyAvatar::getAnimGraphUrl() const {
|
|||
}
|
||||
|
||||
void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAnimGraphUrl", Q_ARG(QUrl, url));
|
||||
return;
|
||||
|
@ -1988,6 +2007,9 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
|||
if (_currentAnimGraphUrl.get() == url) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit animGraphUrlChanged(url);
|
||||
|
||||
destroyAnimGraph();
|
||||
_skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render.
|
||||
|
||||
|
@ -2172,6 +2194,15 @@ void MyAvatar::setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement)
|
|||
_headData->setHasAudioEnabledFaceMovement(hasAudioEnabledFaceMovement);
|
||||
}
|
||||
|
||||
void MyAvatar::setRotationRecenterFilterLength(float length) {
|
||||
const float MINIMUM_ROTATION_RECENTER_FILTER_LENGTH = 0.01f;
|
||||
_rotationRecenterFilterLength = std::max(MINIMUM_ROTATION_RECENTER_FILTER_LENGTH, length);
|
||||
}
|
||||
|
||||
void MyAvatar::setRotationThreshold(float angleRadians) {
|
||||
_rotationThreshold = angleRadians;
|
||||
}
|
||||
|
||||
void MyAvatar::updateOrientation(float deltaTime) {
|
||||
|
||||
// Smoothly rotate body with arrow keys
|
||||
|
@ -2812,6 +2843,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
|
|||
}
|
||||
|
||||
_characterController.setCollisionless(!enabled);
|
||||
emit collisionsEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
bool MyAvatar::getCollisionsEnabled() {
|
||||
|
@ -3135,6 +3167,148 @@ glm::mat4 MyAvatar::deriveBodyUsingCgModel() const {
|
|||
return worldToSensorMat * avatarToWorldMat * avatarHipsMat;
|
||||
}
|
||||
|
||||
static bool isInsideLine(const glm::vec3& a, const glm::vec3& b, const glm::vec3& c) {
|
||||
return (((b.x - a.x) * (c.z - a.z) - (b.z - a.z) * (c.x - a.x)) > 0);
|
||||
}
|
||||
|
||||
static bool withinBaseOfSupport(const controller::Pose& head) {
|
||||
float userScale = 1.0f;
|
||||
|
||||
glm::vec3 frontLeft(-DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD, 0.0f, -DEFAULT_AVATAR_ANTERIOR_STEPPING_THRESHOLD);
|
||||
glm::vec3 frontRight(DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD, 0.0f, -DEFAULT_AVATAR_ANTERIOR_STEPPING_THRESHOLD);
|
||||
glm::vec3 backLeft(-DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD, 0.0f, DEFAULT_AVATAR_POSTERIOR_STEPPING_THRESHOLD);
|
||||
glm::vec3 backRight(DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD, 0.0f, DEFAULT_AVATAR_POSTERIOR_STEPPING_THRESHOLD);
|
||||
|
||||
bool isWithinSupport = false;
|
||||
if (head.isValid()) {
|
||||
bool withinFrontBase = isInsideLine(userScale * frontLeft, userScale * frontRight, head.getTranslation());
|
||||
bool withinBackBase = isInsideLine(userScale * backRight, userScale * backLeft, head.getTranslation());
|
||||
bool withinLateralBase = (isInsideLine(userScale * frontRight, userScale * backRight, head.getTranslation()) &&
|
||||
isInsideLine(userScale * backLeft, userScale * frontLeft, head.getTranslation()));
|
||||
isWithinSupport = (withinFrontBase && withinBackBase && withinLateralBase);
|
||||
}
|
||||
return isWithinSupport;
|
||||
}
|
||||
|
||||
static bool headAngularVelocityBelowThreshold(const controller::Pose& head) {
|
||||
glm::vec3 xzPlaneAngularVelocity(0.0f, 0.0f, 0.0f);
|
||||
if (head.isValid()) {
|
||||
xzPlaneAngularVelocity.x = head.getAngularVelocity().x;
|
||||
xzPlaneAngularVelocity.z = head.getAngularVelocity().z;
|
||||
}
|
||||
float magnitudeAngularVelocity = glm::length(xzPlaneAngularVelocity);
|
||||
bool isBelowThreshold = (magnitudeAngularVelocity < DEFAULT_AVATAR_HEAD_ANGULAR_VELOCITY_STEPPING_THRESHOLD);
|
||||
|
||||
return isBelowThreshold;
|
||||
}
|
||||
|
||||
static bool isWithinThresholdHeightMode(const controller::Pose& head,const float& newMode) {
|
||||
bool isWithinThreshold = true;
|
||||
if (head.isValid()) {
|
||||
isWithinThreshold = (head.getTranslation().y - newMode) > DEFAULT_AVATAR_MODE_HEIGHT_STEPPING_THRESHOLD;
|
||||
}
|
||||
return isWithinThreshold;
|
||||
}
|
||||
|
||||
float MyAvatar::computeStandingHeightMode(const controller::Pose& head) {
|
||||
const float MODE_CORRECTION_FACTOR = 0.02f;
|
||||
int greatestFrequency = 0;
|
||||
int mode = 0;
|
||||
// init mode in meters to the current mode
|
||||
float modeInMeters = getCurrentStandingHeight();
|
||||
if (head.isValid()) {
|
||||
std::map<int, int> freq;
|
||||
for(auto recentModeReadingsIterator = _recentModeReadings.begin(); recentModeReadingsIterator != _recentModeReadings.end(); ++recentModeReadingsIterator) {
|
||||
freq[*recentModeReadingsIterator] += 1;
|
||||
if (freq[*recentModeReadingsIterator] > greatestFrequency) {
|
||||
greatestFrequency = freq[*recentModeReadingsIterator];
|
||||
mode = *recentModeReadingsIterator;
|
||||
}
|
||||
}
|
||||
|
||||
modeInMeters = ((float)mode) / CENTIMETERS_PER_METER;
|
||||
if (!(modeInMeters > getCurrentStandingHeight())) {
|
||||
// if not greater check for a reset
|
||||
if (getResetMode() && qApp->isHMDMode()) {
|
||||
setResetMode(false);
|
||||
float resetModeInCentimeters = glm::floor((head.getTranslation().y - MODE_CORRECTION_FACTOR)*CENTIMETERS_PER_METER);
|
||||
modeInMeters = (resetModeInCentimeters / CENTIMETERS_PER_METER);
|
||||
_recentModeReadings.clear();
|
||||
|
||||
} else {
|
||||
// if not greater and no reset, keep the mode as it is
|
||||
modeInMeters = getCurrentStandingHeight();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return modeInMeters;
|
||||
}
|
||||
|
||||
static bool handDirectionMatchesHeadDirection(const controller::Pose& leftHand, const controller::Pose& rightHand, const controller::Pose& head) {
|
||||
const float VELOCITY_EPSILON = 0.02f;
|
||||
bool leftHandDirectionMatchesHead = true;
|
||||
bool rightHandDirectionMatchesHead = true;
|
||||
glm::vec3 xzHeadVelocity(head.velocity.x, 0.0f, head.velocity.z);
|
||||
if (leftHand.isValid() && head.isValid()) {
|
||||
glm::vec3 xzLeftHandVelocity(leftHand.velocity.x, 0.0f, leftHand.velocity.z);
|
||||
if ((glm::length(xzLeftHandVelocity) > VELOCITY_EPSILON) && (glm::length(xzHeadVelocity) > VELOCITY_EPSILON)) {
|
||||
float handDotHeadLeft = glm::dot(glm::normalize(xzLeftHandVelocity), glm::normalize(xzHeadVelocity));
|
||||
leftHandDirectionMatchesHead = ((handDotHeadLeft > DEFAULT_HANDS_VELOCITY_DIRECTION_STEPPING_THRESHOLD));
|
||||
} else {
|
||||
leftHandDirectionMatchesHead = false;
|
||||
}
|
||||
}
|
||||
if (rightHand.isValid() && head.isValid()) {
|
||||
glm::vec3 xzRightHandVelocity(rightHand.velocity.x, 0.0f, rightHand.velocity.z);
|
||||
if ((glm::length(xzRightHandVelocity) > VELOCITY_EPSILON) && (glm::length(xzHeadVelocity) > VELOCITY_EPSILON)) {
|
||||
float handDotHeadRight = glm::dot(glm::normalize(xzRightHandVelocity), glm::normalize(xzHeadVelocity));
|
||||
rightHandDirectionMatchesHead = (handDotHeadRight > DEFAULT_HANDS_VELOCITY_DIRECTION_STEPPING_THRESHOLD);
|
||||
} else {
|
||||
rightHandDirectionMatchesHead = false;
|
||||
}
|
||||
}
|
||||
return leftHandDirectionMatchesHead && rightHandDirectionMatchesHead;
|
||||
}
|
||||
|
||||
static bool handAngularVelocityBelowThreshold(const controller::Pose& leftHand, const controller::Pose& rightHand) {
|
||||
float leftHandXZAngularVelocity = 0.0f;
|
||||
float rightHandXZAngularVelocity = 0.0f;
|
||||
if (leftHand.isValid()) {
|
||||
glm::vec3 xzLeftHandAngularVelocity(leftHand.angularVelocity.x, 0.0f, leftHand.angularVelocity.z);
|
||||
leftHandXZAngularVelocity = glm::length(xzLeftHandAngularVelocity);
|
||||
}
|
||||
if (rightHand.isValid()) {
|
||||
glm::vec3 xzRightHandAngularVelocity(rightHand.angularVelocity.x, 0.0f, rightHand.angularVelocity.z);
|
||||
rightHandXZAngularVelocity = glm::length(xzRightHandAngularVelocity);
|
||||
}
|
||||
return ((leftHandXZAngularVelocity < DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD) &&
|
||||
(rightHandXZAngularVelocity < DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD));
|
||||
}
|
||||
|
||||
static bool headVelocityGreaterThanThreshold(const controller::Pose& head) {
|
||||
float headVelocityMagnitude = 0.0f;
|
||||
if (head.isValid()) {
|
||||
headVelocityMagnitude = glm::length(head.getVelocity());
|
||||
}
|
||||
return headVelocityMagnitude > DEFAULT_HEAD_VELOCITY_STEPPING_THRESHOLD;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::computeAverageHeadRotation(const controller::Pose& head) {
|
||||
const float AVERAGING_RATE = 0.03f;
|
||||
return safeLerp(_averageHeadRotation, head.getRotation(), AVERAGING_RATE);
|
||||
}
|
||||
|
||||
static bool isHeadLevel(const controller::Pose& head, const glm::quat& averageHeadRotation) {
|
||||
glm::vec3 diffFromAverageEulers(0.0f, 0.0f, 0.0f);
|
||||
if (head.isValid()) {
|
||||
glm::vec3 averageHeadEulers = glm::degrees(safeEulerAngles(averageHeadRotation));
|
||||
glm::vec3 currentHeadEulers = glm::degrees(safeEulerAngles(head.getRotation()));
|
||||
diffFromAverageEulers = averageHeadEulers - currentHeadEulers;
|
||||
}
|
||||
return ((fabs(diffFromAverageEulers.x) < DEFAULT_HEAD_PITCH_STEPPING_TOLERANCE) && (fabs(diffFromAverageEulers.z) < DEFAULT_HEAD_ROLL_STEPPING_TOLERANCE));
|
||||
}
|
||||
|
||||
float MyAvatar::getUserHeight() const {
|
||||
return _userHeight.get();
|
||||
}
|
||||
|
@ -3301,7 +3475,7 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
|
|||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
|
||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(myAvatar.getRotationThreshold());
|
||||
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
|
||||
}
|
||||
|
@ -3328,7 +3502,37 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
|||
}
|
||||
|
||||
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const {
|
||||
|
||||
// get the current readings
|
||||
controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
||||
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
||||
|
||||
bool stepDetected = false;
|
||||
if (!withinBaseOfSupport(currentHeadPose) &&
|
||||
headAngularVelocityBelowThreshold(currentHeadPose) &&
|
||||
isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight()) &&
|
||||
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
|
||||
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
|
||||
headVelocityGreaterThanThreshold(currentHeadPose) &&
|
||||
isHeadLevel(currentHeadPose, myAvatar.getAverageHeadRotation())) {
|
||||
// a step is detected
|
||||
stepDetected = true;
|
||||
} else {
|
||||
glm::vec3 defaultHipsPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Hips"));
|
||||
glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head"));
|
||||
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
|
||||
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
|
||||
if (!isActive(Horizontal) &&
|
||||
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) {
|
||||
myAvatar.setResetMode(true);
|
||||
stepDetected = true;
|
||||
}
|
||||
}
|
||||
return stepDetected;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
|
@ -3348,9 +3552,16 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Rotation);
|
||||
}
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Horizontal);
|
||||
if (myAvatar.getCenterOfGravityModelEnabled()) {
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
||||
activate(Horizontal);
|
||||
}
|
||||
} else {
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Vertical);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "AtRestDetector.h"
|
||||
#include "MyCharacterController.h"
|
||||
#include "RingBufferHistory.h"
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
class AvatarActionHold;
|
||||
|
@ -195,6 +196,8 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(bool hasProceduralBlinkFaceMovement READ getHasProceduralBlinkFaceMovement WRITE setHasProceduralBlinkFaceMovement)
|
||||
Q_PROPERTY(bool hasProceduralEyeFaceMovement READ getHasProceduralEyeFaceMovement WRITE setHasProceduralEyeFaceMovement)
|
||||
Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement)
|
||||
Q_PROPERTY(float rotationRecenterFilterLength READ getRotationRecenterFilterLength WRITE setRotationRecenterFilterLength)
|
||||
Q_PROPERTY(float rotationThreshold READ getRotationThreshold WRITE setRotationThreshold)
|
||||
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
|
||||
|
||||
Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition)
|
||||
|
@ -880,6 +883,12 @@ public:
|
|||
virtual void rebuildCollisionShape() override;
|
||||
|
||||
const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; }
|
||||
float getCurrentStandingHeight() const { return _currentStandingHeight; }
|
||||
void setCurrentStandingHeight(float newMode) { _currentStandingHeight = newMode; }
|
||||
const glm::quat getAverageHeadRotation() const { return _averageHeadRotation; }
|
||||
void setAverageHeadRotation(glm::quat rotation) { _averageHeadRotation = rotation; }
|
||||
bool getResetMode() const { return _resetMode; }
|
||||
void setResetMode(bool hasBeenReset) { _resetMode = hasBeenReset; }
|
||||
|
||||
void setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose);
|
||||
controller::Pose getControllerPoseInSensorFrame(controller::Action action) const;
|
||||
|
@ -888,7 +897,12 @@ public:
|
|||
|
||||
bool hasDriveInput() const;
|
||||
|
||||
QVariantList getAvatarEntitiesVariant();
|
||||
/**jsdoc
|
||||
* Function returns list of avatar entities
|
||||
* @function MyAvatar.getAvatarEntitiesVariant()
|
||||
* @returns {object[]}
|
||||
*/
|
||||
Q_INVOKABLE QVariantList getAvatarEntitiesVariant();
|
||||
void removeAvatarEntities();
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1015,6 +1029,9 @@ public:
|
|||
|
||||
bool isReadyForPhysics() const;
|
||||
|
||||
float computeStandingHeightMode(const controller::Pose& head);
|
||||
glm::quat computeAverageHeadRotation(const controller::Pose& head);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1294,6 +1311,22 @@ signals:
|
|||
*/
|
||||
void collisionWithEntity(const Collision& collision);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when collisions with avatar enabled or disabled
|
||||
* @function MyAvatar.collisionsEnabledChanged
|
||||
* @param {boolean} enabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void collisionsEnabledChanged(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when avatar's animation url changes
|
||||
* @function MyAvatar.animGraphUrlChanged
|
||||
* @param {url} url
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void animGraphUrlChanged(const QUrl& url);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.energyChanged
|
||||
* @param {number} energy
|
||||
|
@ -1387,6 +1420,10 @@ private:
|
|||
bool getHasProceduralEyeFaceMovement() const override { return _headData->getHasProceduralEyeFaceMovement(); }
|
||||
void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement);
|
||||
bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); }
|
||||
void setRotationRecenterFilterLength(float length);
|
||||
float getRotationRecenterFilterLength() const { return _rotationRecenterFilterLength; }
|
||||
void setRotationThreshold(float angleRadians);
|
||||
float getRotationThreshold() const { return _rotationThreshold; }
|
||||
bool isMyAvatar() const override { return true; }
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
virtual glm::vec3 getSkeletonPosition() const override;
|
||||
|
@ -1495,6 +1532,8 @@ private:
|
|||
float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT };
|
||||
float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT };
|
||||
std::atomic<bool> _hasScriptedBlendShapes { false };
|
||||
std::atomic<float> _rotationRecenterFilterLength { 4.0f };
|
||||
std::atomic<float> _rotationThreshold { 0.5235f }; // 30 degrees in radians
|
||||
|
||||
// working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
|
||||
glm::mat4 _sensorToWorldMatrix { glm::mat4() };
|
||||
|
@ -1506,6 +1545,11 @@ private:
|
|||
// cache head controller pose in sensor space
|
||||
glm::vec2 _headControllerFacing; // facing vector in xz plane (sensor space)
|
||||
glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space)
|
||||
glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
float _currentStandingHeight { 0.0f };
|
||||
bool _resetMode { true };
|
||||
RingBufferHistory<int> _recentModeReadings;
|
||||
|
||||
// cache of the current body position and orientation of the avatar's body,
|
||||
// in sensor space.
|
||||
|
@ -1533,6 +1577,7 @@ private:
|
|||
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
|
||||
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
|
||||
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
|
||||
bool getForceActivateRotation() const;
|
||||
|
@ -1621,7 +1666,7 @@ private:
|
|||
// load avatar scripts once when rig is ready
|
||||
bool _shouldLoadScripts { false };
|
||||
|
||||
bool _haveReceivedHeightLimitsFromDomain = { false };
|
||||
bool _haveReceivedHeightLimitsFromDomain { false };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -300,6 +300,12 @@ void setupPreferences() {
|
|||
preference->setStep(0.001f);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto preference = new ButtonPreference(MOVEMENT, "RESET SENSORS", [] {
|
||||
qApp->resetSensors();
|
||||
});
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
static const QString AVATAR_CAMERA{ "Mouse Sensitivity" };
|
||||
{
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "scripting/SettingsScriptingInterface.h"
|
||||
#include <Preferences.h>
|
||||
#include <AvatarBookmarks.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include "FileDialogHelper.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
@ -253,6 +254,7 @@ void Web3DOverlay::setupQmlSurface() {
|
|||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
|
|
|
@ -40,7 +40,6 @@ void GameSpaceToRender::run(const workload::WorkloadContextPointer& runContext,
|
|||
auto visible = _showAllProxies || _showAllViews;
|
||||
auto showProxies = _showAllProxies;
|
||||
auto showViews = _showAllViews;
|
||||
auto freezeViews = _freezeViews;
|
||||
|
||||
render::Transaction transaction;
|
||||
auto scene = gameWorkloadContext->_scene;
|
||||
|
@ -71,7 +70,7 @@ void GameSpaceToRender::run(const workload::WorkloadContextPointer& runContext,
|
|||
transaction.resetItem(_spaceRenderItemID, std::make_shared<GameWorkloadRenderItem::Payload>(renderItem));
|
||||
}
|
||||
|
||||
transaction.updateItem<GameWorkloadRenderItem>(_spaceRenderItemID, [visible, showProxies, proxies, freezeViews, showViews, views](GameWorkloadRenderItem& item) {
|
||||
transaction.updateItem<GameWorkloadRenderItem>(_spaceRenderItemID, [visible, showProxies, proxies, showViews, views](GameWorkloadRenderItem& item) {
|
||||
item.setVisible(visible);
|
||||
item.showProxies(showProxies);
|
||||
item.setAllProxies(proxies);
|
||||
|
|
|
@ -212,6 +212,8 @@ void Avatar::setTargetScale(float targetScale) {
|
|||
_targetScale = newValue;
|
||||
_scaleChanged = usecTimestampNow();
|
||||
_isAnimatingScale = true;
|
||||
|
||||
emit targetScaleChanged(targetScale);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -361,6 +361,9 @@ public:
|
|||
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel() override;
|
||||
|
||||
signals:
|
||||
void targetScaleChanged(float targetScale);
|
||||
|
||||
public slots:
|
||||
|
||||
// FIXME - these should be migrated to use Pose data instead
|
||||
|
|
|
@ -1575,13 +1575,12 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string
|
|||
for (auto shapeID : shapeIDs) {
|
||||
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
||||
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
||||
bool visible = isVisible();
|
||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
bool wireframe = isWireframe();
|
||||
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, renderItemsKey,
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey,
|
||||
invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
||||
data.removeMaterial(material);
|
||||
// if the material changed, we might need to update our item key or shape key
|
||||
|
|
|
@ -24,6 +24,17 @@ const float DEFAULT_AVATAR_SUPPORT_BASE_LEFT = -0.25f;
|
|||
const float DEFAULT_AVATAR_SUPPORT_BASE_RIGHT = 0.25f;
|
||||
const float DEFAULT_AVATAR_SUPPORT_BASE_FRONT = -0.20f;
|
||||
const float DEFAULT_AVATAR_SUPPORT_BASE_BACK = 0.10f;
|
||||
const float DEFAULT_AVATAR_LATERAL_STEPPING_THRESHOLD = 0.10f;
|
||||
const float DEFAULT_AVATAR_ANTERIOR_STEPPING_THRESHOLD = 0.04f;
|
||||
const float DEFAULT_AVATAR_POSTERIOR_STEPPING_THRESHOLD = 0.07f;
|
||||
const float DEFAULT_AVATAR_HEAD_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 0.3f;
|
||||
const float DEFAULT_AVATAR_MODE_HEIGHT_STEPPING_THRESHOLD = -0.02f;
|
||||
const float DEFAULT_HANDS_VELOCITY_DIRECTION_STEPPING_THRESHOLD = 0.4f;
|
||||
const float DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 3.3f;
|
||||
const float DEFAULT_HEAD_VELOCITY_STEPPING_THRESHOLD = 0.18f;
|
||||
const float DEFAULT_HEAD_PITCH_STEPPING_TOLERANCE = 7.0f;
|
||||
const float DEFAULT_HEAD_ROLL_STEPPING_TOLERANCE = 7.0f;
|
||||
const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.07f;
|
||||
const float DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR = 0.5f;
|
||||
const float DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR = 2.0f;
|
||||
const float DEFAULT_AVATAR_HIPS_MASS = 40.0f;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariantMap>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
|
@ -80,7 +81,6 @@ public:
|
|||
}
|
||||
|
||||
void setEnabler(BoolPreference* enabler, bool inverse = false);
|
||||
|
||||
virtual Type getType() { return Invalid; };
|
||||
|
||||
Q_INVOKABLE virtual void load() {};
|
||||
|
|
|
@ -48,7 +48,6 @@ const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND;
|
|||
static const char* MENU_PARENT = "Avatar";
|
||||
static const char* MENU_NAME = "Vive Controllers";
|
||||
static const char* MENU_PATH = "Avatar" ">" "Vive Controllers";
|
||||
static const char* RENDER_CONTROLLERS = "Render Hand Controllers";
|
||||
|
||||
static const int MIN_HEAD = 1;
|
||||
static const int MIN_PUCK_COUNT = 2;
|
||||
|
@ -205,11 +204,6 @@ bool ViveControllerManager::activate() {
|
|||
return false;
|
||||
}
|
||||
|
||||
_container->addMenu(MENU_PATH);
|
||||
_container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
|
||||
[this](bool clicked) { this->setRenderControllers(clicked); },
|
||||
true, true);
|
||||
|
||||
enableOpenVrKeyboard(_container);
|
||||
|
||||
// register with UserInputMapper
|
||||
|
@ -224,9 +218,6 @@ void ViveControllerManager::deactivate() {
|
|||
|
||||
disableOpenVrKeyboard();
|
||||
|
||||
_container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);
|
||||
_container->removeMenu(MENU_PATH);
|
||||
|
||||
if (_system) {
|
||||
_container->makeRenderingContextCurrent();
|
||||
releaseOpenVrSystem();
|
||||
|
|
|
@ -57,8 +57,6 @@ public:
|
|||
void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||
|
||||
void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; }
|
||||
|
||||
virtual void saveSettings() const override;
|
||||
virtual void loadSettings() override;
|
||||
|
||||
|
@ -219,7 +217,6 @@ private:
|
|||
int _leftHandRenderID { 0 };
|
||||
int _rightHandRenderID { 0 };
|
||||
|
||||
bool _renderControllers { false };
|
||||
vr::IVRSystem* _system { nullptr };
|
||||
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>(_system) };
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/bubble.js",
|
||||
"system/snapshot.js",
|
||||
"system/pal.js", // "system/mod.js", // older UX, if you prefer
|
||||
"system/avatarapp.js",
|
||||
"system/makeUserConnection.js",
|
||||
"system/tablet-goto.js",
|
||||
"system/marketplaces/marketplaces.js",
|
||||
|
|
550
scripts/system/avatarapp.js
Normal file
|
@ -0,0 +1,550 @@
|
|||
"use strict";
|
||||
/*jslint vars:true, plusplus:true, forin:true*/
|
||||
/*global Tablet, Settings, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, HMD, Controller, Account, UserActivityLogger, Messages, Window, XMLHttpRequest, print, location, getControllerWorldLocation*/
|
||||
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||
//
|
||||
// avatarapp.js
|
||||
//
|
||||
// Created by Alexander Ivash on April 30, 2018
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var request = Script.require('request').request;
|
||||
var AVATARAPP_QML_SOURCE = "hifi/AvatarApp.qml";
|
||||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
// constants from AvatarBookmarks.h
|
||||
var ENTRY_AVATAR_URL = "avatarUrl";
|
||||
var ENTRY_AVATAR_ATTACHMENTS = "attachments";
|
||||
var ENTRY_AVATAR_ENTITIES = "avatarEntites";
|
||||
var ENTRY_AVATAR_SCALE = "avatarScale";
|
||||
var ENTRY_VERSION = "version";
|
||||
|
||||
function executeLater(callback) {
|
||||
Script.setTimeout(callback, 300);
|
||||
}
|
||||
|
||||
function getMyAvatarWearables() {
|
||||
var wearablesArray = MyAvatar.getAvatarEntitiesVariant();
|
||||
|
||||
for(var i = 0; i < wearablesArray.length; ++i) {
|
||||
var wearable = wearablesArray[i];
|
||||
var localRotation = wearable.properties.localRotation;
|
||||
wearable.properties.localRotationAngles = Quat.safeEulerAngles(localRotation)
|
||||
}
|
||||
|
||||
return wearablesArray;
|
||||
}
|
||||
|
||||
function getMyAvatar() {
|
||||
var avatar = {}
|
||||
avatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL;
|
||||
avatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale();
|
||||
avatar[ENTRY_AVATAR_ATTACHMENTS] = MyAvatar.getAttachmentsVariant();
|
||||
avatar[ENTRY_AVATAR_ENTITIES] = getMyAvatarWearables();
|
||||
return avatar;
|
||||
}
|
||||
|
||||
function getMyAvatarSettings() {
|
||||
return {
|
||||
dominantHand: MyAvatar.getDominantHand(),
|
||||
collisionsEnabled : MyAvatar.getCollisionsEnabled(),
|
||||
collisionSoundUrl : MyAvatar.collisionSoundURL,
|
||||
animGraphUrl : MyAvatar.getAnimGraphUrl(),
|
||||
}
|
||||
}
|
||||
|
||||
function updateAvatarWearables(avatar, bookmarkAvatarName) {
|
||||
executeLater(function() {
|
||||
var wearables = getMyAvatarWearables();
|
||||
avatar[ENTRY_AVATAR_ENTITIES] = wearables;
|
||||
|
||||
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables, 'avatarName' : bookmarkAvatarName})
|
||||
});
|
||||
}
|
||||
|
||||
var adjustWearables = {
|
||||
opened : false,
|
||||
cameraMode : '',
|
||||
setOpened : function(value) {
|
||||
if(this.opened !== value) {
|
||||
if(value) {
|
||||
this.cameraMode = Camera.mode;
|
||||
|
||||
if(!HMD.active) {
|
||||
Camera.mode = 'mirror';
|
||||
}
|
||||
} else {
|
||||
Camera.mode = this.cameraMode;
|
||||
}
|
||||
|
||||
this.opened = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentAvatarWearablesBackup = null;
|
||||
var currentAvatar = null;
|
||||
var currentAvatarSettings = getMyAvatarSettings();
|
||||
|
||||
var notifyScaleChanged = true;
|
||||
function onTargetScaleChanged() {
|
||||
if(currentAvatar.scale !== MyAvatar.getAvatarScale()) {
|
||||
currentAvatar.scale = MyAvatar.getAvatarScale();
|
||||
if(notifyScaleChanged) {
|
||||
sendToQml({'method' : 'scaleChanged', 'value' : currentAvatar.scale})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onSkeletonModelURLChanged() {
|
||||
if(currentAvatar || (currentAvatar.skeletonModelURL !== MyAvatar.skeletonModelURL)) {
|
||||
fromQml({'method' : 'getAvatars'});
|
||||
}
|
||||
}
|
||||
|
||||
function onDominantHandChanged(dominantHand) {
|
||||
if(currentAvatarSettings.dominantHand !== dominantHand) {
|
||||
currentAvatarSettings.dominantHand = dominantHand;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'dominantHand', 'value' : dominantHand})
|
||||
}
|
||||
}
|
||||
|
||||
function onCollisionsEnabledChanged(enabled) {
|
||||
if(currentAvatarSettings.collisionsEnabled !== enabled) {
|
||||
currentAvatarSettings.collisionsEnabled = enabled;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionsEnabled', 'value' : enabled})
|
||||
}
|
||||
}
|
||||
|
||||
function onNewCollisionSoundUrl(url) {
|
||||
if(currentAvatarSettings.collisionSoundUrl !== url) {
|
||||
currentAvatarSettings.collisionSoundUrl = url;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionSoundUrl', 'value' : url})
|
||||
}
|
||||
}
|
||||
|
||||
function onAnimGraphUrlChanged(url) {
|
||||
if(currentAvatarSettings.animGraphUrl !== url) {
|
||||
currentAvatarSettings.animGraphUrl = url;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'animGraphUrl', 'value' : url})
|
||||
}
|
||||
}
|
||||
|
||||
var selectedAvatarEntityGrabbable = false;
|
||||
var selectedAvatarEntityID = null;
|
||||
var grabbedAvatarEntityChangeNotifier = null;
|
||||
|
||||
var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml";
|
||||
var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";
|
||||
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js");
|
||||
|
||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
switch (message.method) {
|
||||
case 'getAvatars':
|
||||
currentAvatar = getMyAvatar();
|
||||
currentAvatarSettings = getMyAvatarSettings();
|
||||
|
||||
message.data = {
|
||||
'bookmarks' : AvatarBookmarks.getBookmarks(),
|
||||
'displayName' : MyAvatar.displayName,
|
||||
'currentAvatar' : currentAvatar,
|
||||
'currentAvatarSettings' : currentAvatarSettings
|
||||
};
|
||||
|
||||
for(var bookmarkName in message.data.bookmarks) {
|
||||
var bookmark = message.data.bookmarks[bookmarkName];
|
||||
|
||||
bookmark.avatarEntites.forEach(function(avatarEntity) {
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation)
|
||||
})
|
||||
}
|
||||
|
||||
sendToQml(message)
|
||||
break;
|
||||
case 'selectAvatar':
|
||||
AvatarBookmarks.loadBookmark(message.name);
|
||||
break;
|
||||
case 'deleteAvatar':
|
||||
AvatarBookmarks.removeBookmark(message.name);
|
||||
break;
|
||||
case 'addAvatar':
|
||||
AvatarBookmarks.addBookmark(message.name);
|
||||
break;
|
||||
case 'adjustWearable':
|
||||
if(message.properties.localRotationAngles) {
|
||||
message.properties.localRotation = Quat.fromVec3Degrees(message.properties.localRotationAngles)
|
||||
}
|
||||
|
||||
Entities.editEntity(message.entityID, message.properties);
|
||||
message.properties = Entities.getEntityProperties(message.entityID, Object.keys(message.properties));
|
||||
|
||||
if(message.properties.localRotation) {
|
||||
message.properties.localRotationAngles = Quat.safeEulerAngles(message.properties.localRotation);
|
||||
}
|
||||
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : message.entityID, wearableIndex : message.wearableIndex, properties : message.properties, updateUI : false})
|
||||
break;
|
||||
case 'adjustWearablesOpened':
|
||||
currentAvatarWearablesBackup = getMyAvatarWearables();
|
||||
adjustWearables.setOpened(true);
|
||||
|
||||
Entities.mousePressOnEntity.connect(onSelectedEntity);
|
||||
Messages.subscribe('Hifi-Object-Manipulation');
|
||||
Messages.messageReceived.connect(handleWearableMessages);
|
||||
break;
|
||||
case 'adjustWearablesClosed':
|
||||
if(!message.save) {
|
||||
// revert changes using snapshot of wearables
|
||||
if(currentAvatarWearablesBackup !== null) {
|
||||
AvatarBookmarks.updateAvatarEntities(currentAvatarWearablesBackup);
|
||||
updateAvatarWearables(currentAvatar, message.avatarName);
|
||||
}
|
||||
} else {
|
||||
sendToQml({'method' : 'updateAvatarInBookmarks'});
|
||||
}
|
||||
|
||||
adjustWearables.setOpened(false);
|
||||
ensureWearableSelected(null);
|
||||
Entities.mousePressOnEntity.disconnect(onSelectedEntity);
|
||||
Messages.messageReceived.disconnect(handleWearableMessages);
|
||||
Messages.unsubscribe('Hifi-Object-Manipulation');
|
||||
break;
|
||||
case 'selectWearable':
|
||||
ensureWearableSelected(message.entityID);
|
||||
break;
|
||||
case 'deleteWearable':
|
||||
Entities.deleteEntity(message.entityID);
|
||||
updateAvatarWearables(currentAvatar, message.avatarName);
|
||||
break;
|
||||
case 'changeDisplayName':
|
||||
if (MyAvatar.displayName !== message.displayName) {
|
||||
MyAvatar.displayName = message.displayName;
|
||||
UserActivityLogger.palAction("display_name_change", message.displayName);
|
||||
}
|
||||
break;
|
||||
case 'applyExternalAvatar':
|
||||
var currentAvatarURL = MyAvatar.getFullAvatarURLFromPreferences();
|
||||
if(currentAvatarURL !== message.avatarURL) {
|
||||
MyAvatar.useFullAvatarURL(message.avatarURL);
|
||||
sendToQml({'method' : 'externalAvatarApplied', 'avatarURL' : message.avatarURL})
|
||||
}
|
||||
break;
|
||||
case 'navigate':
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system")
|
||||
if(message.url.indexOf('app://') === 0) {
|
||||
if(message.url === 'app://marketplace') {
|
||||
tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
} else if(message.url === 'app://purchases') {
|
||||
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
|
||||
}
|
||||
|
||||
} else if(message.url.indexOf('hifi://') === 0) {
|
||||
AddressManager.handleLookupString(message.url, false);
|
||||
} else if(message.url.indexOf('https://') === 0 || message.url.indexOf('http://') === 0) {
|
||||
tablet.gotoWebScreen(message.url, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'setScale':
|
||||
notifyScaleChanged = false;
|
||||
MyAvatar.setAvatarScale(message.avatarScale);
|
||||
currentAvatar.avatarScale = message.avatarScale;
|
||||
notifyScaleChanged = true;
|
||||
break;
|
||||
case 'revertScale':
|
||||
MyAvatar.setAvatarScale(message.avatarScale);
|
||||
currentAvatar.avatarScale = message.avatarScale;
|
||||
break;
|
||||
case 'saveSettings':
|
||||
MyAvatar.setAvatarScale(message.avatarScale);
|
||||
currentAvatar.avatarScale = message.avatarScale;
|
||||
|
||||
MyAvatar.setDominantHand(message.settings.dominantHand);
|
||||
MyAvatar.setCollisionsEnabled(message.settings.collisionsEnabled);
|
||||
MyAvatar.collisionSoundURL = message.settings.collisionSoundUrl;
|
||||
MyAvatar.setAnimGraphUrl(message.settings.animGraphUrl);
|
||||
|
||||
settings = getMyAvatarSettings();
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from AvatarApp.qml:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
function isGrabbable(entityID) {
|
||||
if(entityID === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']);
|
||||
if (properties.clientOnly) {
|
||||
var userData;
|
||||
try {
|
||||
userData = JSON.parse(properties.userData);
|
||||
} catch (e) {
|
||||
userData = {};
|
||||
}
|
||||
|
||||
return userData.grabbableKey && userData.grabbableKey.grabbable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setGrabbable(entityID, grabbable) {
|
||||
var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']);
|
||||
if (properties.clientOnly) {
|
||||
var userData;
|
||||
try {
|
||||
userData = JSON.parse(properties.userData);
|
||||
} catch (e) {
|
||||
userData = {};
|
||||
}
|
||||
|
||||
if (userData.grabbableKey === undefined) {
|
||||
userData.grabbableKey = {};
|
||||
}
|
||||
userData.grabbableKey.grabbable = grabbable;
|
||||
Entities.editEntity(entityID, {userData: JSON.stringify(userData)});
|
||||
}
|
||||
}
|
||||
|
||||
function ensureWearableSelected(entityID) {
|
||||
if(selectedAvatarEntityID !== entityID) {
|
||||
if(grabbedAvatarEntityChangeNotifier !== null) {
|
||||
Script.clearInterval(grabbedAvatarEntityChangeNotifier);
|
||||
grabbedAvatarEntityChangeNotifier = null;
|
||||
}
|
||||
|
||||
if(selectedAvatarEntityID !== null) {
|
||||
setGrabbable(selectedAvatarEntityID, selectedAvatarEntityGrabbable);
|
||||
}
|
||||
|
||||
selectedAvatarEntityID = entityID;
|
||||
selectedAvatarEntityGrabbable = isGrabbable(entityID);
|
||||
|
||||
if(selectedAvatarEntityID !== null) {
|
||||
setGrabbable(selectedAvatarEntityID, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isEntityBeingWorn(entityID) {
|
||||
return Entities.getEntityProperties(entityID, 'parentID').parentID === MyAvatar.sessionUUID;
|
||||
};
|
||||
|
||||
function onSelectedEntity(entityID, pointerEvent) {
|
||||
if(selectedAvatarEntityID !== entityID && isEntityBeingWorn(entityID))
|
||||
{
|
||||
if(ensureWearableSelected(entityID)) {
|
||||
sendToQml({'method' : 'selectAvatarEntity', 'entityID' : selectedAvatarEntityID});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWearableMessages(channel, message, sender) {
|
||||
if (channel !== 'Hifi-Object-Manipulation') {
|
||||
return;
|
||||
}
|
||||
|
||||
var parsedMessage = null;
|
||||
|
||||
try {
|
||||
parsedMessage = JSON.parse(message);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var entityID = parsedMessage.grabbedEntity;
|
||||
if(parsedMessage.action === 'grab') {
|
||||
if(selectedAvatarEntityID !== entityID) {
|
||||
ensureWearableSelected(entityID);
|
||||
sendToQml({'method' : 'selectAvatarEntity', 'entityID' : selectedAvatarEntityID});
|
||||
}
|
||||
|
||||
grabbedAvatarEntityChangeNotifier = Script.setInterval(function() {
|
||||
// for some reasons Entities.getEntityProperties returns more than was asked..
|
||||
var propertyNames = ['localPosition', 'localRotation', 'dimensions', 'naturalDimensions'];
|
||||
var entityProperties = Entities.getEntityProperties(selectedAvatarEntityID, propertyNames);
|
||||
var properties = {}
|
||||
|
||||
propertyNames.forEach(function(propertyName) {
|
||||
properties[propertyName] = entityProperties[propertyName];
|
||||
})
|
||||
|
||||
properties.localRotationAngles = Quat.safeEulerAngles(properties.localRotation);
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : selectedAvatarEntityID, 'wearableIndex' : -1, 'properties' : properties, updateUI : true})
|
||||
|
||||
}, 1000);
|
||||
} else if(parsedMessage.action === 'release') {
|
||||
if(grabbedAvatarEntityChangeNotifier !== null) {
|
||||
Script.clearInterval(grabbedAvatarEntityChangeNotifier);
|
||||
grabbedAvatarEntityChangeNotifier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendToQml(message) {
|
||||
tablet.sendToQml(message);
|
||||
}
|
||||
|
||||
function onBookmarkLoaded(bookmarkName) {
|
||||
executeLater(function() {
|
||||
currentAvatar = getMyAvatar();
|
||||
sendToQml({'method' : 'bookmarkLoaded', 'data' : {'name' : bookmarkName, 'currentAvatar' : currentAvatar} });
|
||||
});
|
||||
}
|
||||
|
||||
function onBookmarkDeleted(bookmarkName) {
|
||||
sendToQml({'method' : 'bookmarkDeleted', 'name' : bookmarkName});
|
||||
}
|
||||
|
||||
function onBookmarkAdded(bookmarkName) {
|
||||
var bookmark = AvatarBookmarks.getBookmark(bookmarkName);
|
||||
bookmark.avatarEntites.forEach(function(avatarEntity) {
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation)
|
||||
})
|
||||
|
||||
sendToQml({ 'method': 'bookmarkAdded', 'bookmarkName': bookmarkName, 'bookmark': bookmark });
|
||||
}
|
||||
|
||||
//
|
||||
// Manage the connection between the button and the window.
|
||||
//
|
||||
var button;
|
||||
var buttonName = "AVATAR";
|
||||
var tablet = null;
|
||||
|
||||
function startup() {
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
text: buttonName,
|
||||
icon: "icons/tablet-icons/avatar-i.svg",
|
||||
activeIcon: "icons/tablet-icons/avatar-a.svg",
|
||||
sortOrder: 7
|
||||
});
|
||||
button.clicked.connect(onTabletButtonClicked);
|
||||
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||
}
|
||||
|
||||
startup();
|
||||
|
||||
var isWired = false;
|
||||
function off() {
|
||||
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
||||
isWired = false;
|
||||
}
|
||||
|
||||
if(adjustWearables.opened) {
|
||||
adjustWearables.setOpened(false);
|
||||
ensureWearableSelected(null);
|
||||
Entities.mousePressOnEntity.disconnect(onSelectedEntity);
|
||||
|
||||
Messages.messageReceived.disconnect(handleWearableMessages);
|
||||
Messages.unsubscribe('Hifi-Object-Manipulation');
|
||||
}
|
||||
|
||||
AvatarBookmarks.bookmarkLoaded.disconnect(onBookmarkLoaded);
|
||||
AvatarBookmarks.bookmarkDeleted.disconnect(onBookmarkDeleted);
|
||||
AvatarBookmarks.bookmarkAdded.disconnect(onBookmarkAdded);
|
||||
|
||||
MyAvatar.skeletonModelURLChanged.disconnect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.disconnect(onDominantHandChanged);
|
||||
MyAvatar.collisionsEnabledChanged.disconnect(onCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.disconnect(onNewCollisionSoundUrl);
|
||||
MyAvatar.animGraphUrlChanged.disconnect(onAnimGraphUrlChanged);
|
||||
MyAvatar.targetScaleChanged.disconnect(onTargetScaleChanged);
|
||||
}
|
||||
|
||||
function on() {
|
||||
AvatarBookmarks.bookmarkLoaded.connect(onBookmarkLoaded);
|
||||
AvatarBookmarks.bookmarkDeleted.connect(onBookmarkDeleted);
|
||||
AvatarBookmarks.bookmarkAdded.connect(onBookmarkAdded);
|
||||
|
||||
MyAvatar.skeletonModelURLChanged.connect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.connect(onDominantHandChanged);
|
||||
MyAvatar.collisionsEnabledChanged.connect(onCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.connect(onNewCollisionSoundUrl);
|
||||
MyAvatar.animGraphUrlChanged.connect(onAnimGraphUrlChanged);
|
||||
MyAvatar.targetScaleChanged.connect(onTargetScaleChanged);
|
||||
}
|
||||
|
||||
function onTabletButtonClicked() {
|
||||
if (onAvatarAppScreen) {
|
||||
// for toolbar-mode: go back to home screen, this will close the window.
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
ContextOverlay.enabled = false;
|
||||
tablet.loadQMLSource(AVATARAPP_QML_SOURCE);
|
||||
isWired = true;
|
||||
}
|
||||
}
|
||||
var hasEventBridge = false;
|
||||
function wireEventBridge(on) {
|
||||
if (on) {
|
||||
if (!hasEventBridge) {
|
||||
tablet.fromQml.connect(fromQml);
|
||||
hasEventBridge = true;
|
||||
}
|
||||
} else {
|
||||
if (hasEventBridge) {
|
||||
tablet.fromQml.disconnect(fromQml);
|
||||
hasEventBridge = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var onAvatarAppScreen = false;
|
||||
function onTabletScreenChanged(type, url) {
|
||||
var onAvatarAppScreenNow = (type === "QML" && url === AVATARAPP_QML_SOURCE);
|
||||
wireEventBridge(onAvatarAppScreenNow);
|
||||
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
||||
button.editProperties({isActive: onAvatarAppScreenNow});
|
||||
|
||||
if (!onAvatarAppScreen && onAvatarAppScreenNow) {
|
||||
on();
|
||||
} else if(onAvatarAppScreen && !onAvatarAppScreenNow) {
|
||||
off();
|
||||
}
|
||||
|
||||
onAvatarAppScreen = onAvatarAppScreenNow;
|
||||
|
||||
if(onAvatarAppScreenNow) {
|
||||
var message = {
|
||||
'method' : 'initialize',
|
||||
'data' : {
|
||||
'jointNames' : MyAvatar.getJointNames()
|
||||
}
|
||||
};
|
||||
|
||||
sendToQml(message)
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
if (onAvatarAppScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onTabletButtonClicked);
|
||||
tablet.removeButton(button);
|
||||
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||
|
||||
off();
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
//
|
||||
Script.scriptEnding.connect(shutdown);
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
|
@ -29,7 +29,6 @@ var CONTOLLER_SCRIPTS = [
|
|||
"controllerModules/disableOtherModule.js",
|
||||
"controllerModules/farTrigger.js",
|
||||
"controllerModules/teleport.js",
|
||||
"controllerModules/scaleAvatar.js",
|
||||
"controllerModules/hudOverlayPointer.js",
|
||||
"controllerModules/mouseHMD.js",
|
||||
"controllerModules/scaleEntity.js",
|
||||
|
|
|
@ -10,17 +10,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger,
|
||||
Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */
|
||||
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager,
|
||||
Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera,
|
||||
progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool, OverlaySystemWindow */
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
"use strict";
|
||||
|
||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton";
|
||||
var SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
||||
var EDIT_TOOLBAR = "com.highfidelity.interface.toolbar.edit";
|
||||
|
||||
Script.include([
|
||||
"libraries/stringHelpers.js",
|
||||
|
@ -36,13 +34,43 @@ Script.include([
|
|||
"libraries/entityIconOverlayManager.js"
|
||||
]);
|
||||
|
||||
var CreateWindow = Script.require('./modules/createWindow.js');
|
||||
|
||||
var TITLE_OFFSET = 60;
|
||||
var CREATE_TOOLS_WIDTH = 490;
|
||||
var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942;
|
||||
|
||||
var createToolsWindow = new CreateWindow(
|
||||
Script.resourcesPath() + "qml/hifi/tablet/EditTools.qml",
|
||||
'Create Tools',
|
||||
'com.highfidelity.create.createToolsWindow',
|
||||
function () {
|
||||
var windowHeight = Window.innerHeight - TITLE_OFFSET;
|
||||
if (windowHeight > MAX_DEFAULT_ENTITY_LIST_HEIGHT) {
|
||||
windowHeight = MAX_DEFAULT_ENTITY_LIST_HEIGHT;
|
||||
}
|
||||
return {
|
||||
size: {
|
||||
x: CREATE_TOOLS_WIDTH,
|
||||
y: windowHeight
|
||||
},
|
||||
position: {
|
||||
x: Window.x + Window.innerWidth - CREATE_TOOLS_WIDTH,
|
||||
y: Window.y + TITLE_OFFSET
|
||||
}
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
var selectionDisplay = SelectionDisplay;
|
||||
var selectionManager = SelectionManager;
|
||||
|
||||
var PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg");
|
||||
var POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg");
|
||||
var SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg");
|
||||
entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) {
|
||||
|
||||
var entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleEffect'], function(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID, ['type', 'isSpotlight']);
|
||||
if (properties.type === 'Light') {
|
||||
return {
|
||||
|
@ -59,7 +87,8 @@ var cameraManager = new CameraManager();
|
|||
|
||||
var grid = new Grid();
|
||||
var gridTool = new GridTool({
|
||||
horizontalGrid: grid
|
||||
horizontalGrid: grid,
|
||||
createToolsWindow: createToolsWindow
|
||||
});
|
||||
gridTool.setVisible(false);
|
||||
|
||||
|
@ -207,7 +236,7 @@ function hideMarketplace() {
|
|||
// }
|
||||
|
||||
function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
|
||||
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
|
||||
// Adjust the position such that the bounding box (registration, dimensions and orientation) lies behind the original
|
||||
// position in the given direction.
|
||||
var CORNERS = [
|
||||
{ x: 0, y: 0, z: 0 },
|
||||
|
@ -232,7 +261,6 @@ function adjustPositionPerBoundingBox(position, direction, registration, dimensi
|
|||
return position;
|
||||
}
|
||||
|
||||
var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
|
||||
var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit";
|
||||
|
||||
// Handles any edit mode updates required when domains have switched
|
||||
|
@ -260,6 +288,7 @@ var toolBar = (function () {
|
|||
toolBar,
|
||||
activeButton = null,
|
||||
systemToolbar = null,
|
||||
dialogWindow = null,
|
||||
tablet = null;
|
||||
|
||||
function createNewEntity(properties) {
|
||||
|
@ -356,6 +385,13 @@ var toolBar = (function () {
|
|||
return entityID;
|
||||
}
|
||||
|
||||
function closeExistingDialogWindow() {
|
||||
if (dialogWindow) {
|
||||
dialogWindow.close();
|
||||
dialogWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
that.setActive(false);
|
||||
if (tablet) {
|
||||
|
@ -438,7 +474,7 @@ var toolBar = (function () {
|
|||
if (materialURL.startsWith("materialData")) {
|
||||
materialData = JSON.stringify({
|
||||
"materials": {}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
var DEFAULT_LAYERED_MATERIAL_PRIORITY = 1;
|
||||
|
@ -458,15 +494,23 @@ var toolBar = (function () {
|
|||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.popFromStack();
|
||||
switch (message.method) {
|
||||
case "newModelDialogAdd":
|
||||
handleNewModelDialogResult(message.params);
|
||||
break;
|
||||
case "newEntityButtonClicked":
|
||||
buttonHandlers[message.params.buttonName]();
|
||||
break;
|
||||
case "newMaterialDialogAdd":
|
||||
handleNewMaterialDialogResult(message.params);
|
||||
break;
|
||||
case "newModelDialogAdd":
|
||||
handleNewModelDialogResult(message.params);
|
||||
closeExistingDialogWindow();
|
||||
break;
|
||||
case "newModelDialogCancel":
|
||||
closeExistingDialogWindow();
|
||||
break;
|
||||
case "newEntityButtonClicked":
|
||||
buttonHandlers[message.params.buttonName]();
|
||||
break;
|
||||
case "newMaterialDialogAdd":
|
||||
handleNewMaterialDialogResult(message.params);
|
||||
closeExistingDialogWindow();
|
||||
break;
|
||||
case "newMaterialDialogCancel":
|
||||
closeExistingDialogWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -501,6 +545,13 @@ var toolBar = (function () {
|
|||
checkEditPermissionsAndUpdate();
|
||||
});
|
||||
|
||||
HMD.displayModeChanged.connect(function() {
|
||||
if (isActive) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
that.setActive(false);
|
||||
});
|
||||
|
||||
Entities.canAdjustLocksChanged.connect(function (canAdjustLocks) {
|
||||
if (isActive && !canAdjustLocks) {
|
||||
that.setActive(false);
|
||||
|
@ -527,11 +578,13 @@ var toolBar = (function () {
|
|||
});
|
||||
createButton = activeButton;
|
||||
tablet.screenChanged.connect(function (type, url) {
|
||||
if (isActive && (type !== "QML" || url !== "hifi/tablet/Edit.qml")) {
|
||||
that.setActive(false)
|
||||
var isGoingToHomescreenOnDesktop = (!HMD.active && (url === 'hifi/tablet/TabletHome.qml' || url === ''));
|
||||
if (isActive && (type !== "QML" || url !== "hifi/tablet/Edit.qml") && !isGoingToHomescreenOnDesktop) {
|
||||
that.setActive(false);
|
||||
}
|
||||
});
|
||||
tablet.fromQml.connect(fromQml);
|
||||
createToolsWindow.fromQml.addListener(fromQml);
|
||||
|
||||
createButton.clicked.connect(function() {
|
||||
if ( ! (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()) ) {
|
||||
|
@ -550,12 +603,29 @@ var toolBar = (function () {
|
|||
addButton("openAssetBrowserButton", function() {
|
||||
Window.showAssetServer();
|
||||
});
|
||||
function createNewEntityDialogButtonCallback(entityType) {
|
||||
return function() {
|
||||
if (HMD.active) {
|
||||
// tablet version of new-model dialog
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.pushOntoStack("hifi/tablet/New" + entityType + "Dialog.qml");
|
||||
} else {
|
||||
closeExistingDialogWindow();
|
||||
var qmlPath = Script.resourcesPath() + "qml/hifi/tablet/New" + entityType + "Window.qml";
|
||||
var DIALOG_WINDOW_SIZE = { x: 500, y: 300 };
|
||||
dialogWindow = Desktop.createWindow(qmlPath, {
|
||||
title: "New " + entityType + " Entity",
|
||||
flags: Desktop.ALWAYS_ON_TOP | Desktop.CLOSE_BUTTON_HIDES,
|
||||
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||
size: DIALOG_WINDOW_SIZE,
|
||||
visible: true
|
||||
});
|
||||
dialogWindow.fromQml.connect(fromQml);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
addButton("newModelButton", function () {
|
||||
// tablet version of new-model dialog
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.pushOntoStack("hifi/tablet/NewModelDialog.qml");
|
||||
});
|
||||
addButton("newModelButton", createNewEntityDialogButtonCallback("Model"));
|
||||
|
||||
addButton("newCubeButton", function () {
|
||||
createNewEntity({
|
||||
|
@ -716,11 +786,7 @@ var toolBar = (function () {
|
|||
});
|
||||
});
|
||||
|
||||
addButton("newMaterialButton", function () {
|
||||
// tablet version of new material dialog
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.pushOntoStack("hifi/tablet/NewMaterialDialog.qml");
|
||||
});
|
||||
addButton("newMaterialButton", createNewEntityDialogButtonCallback("Material"));
|
||||
|
||||
that.setActive(false);
|
||||
}
|
||||
|
@ -743,6 +809,8 @@ var toolBar = (function () {
|
|||
Controller.captureEntityClickEvents();
|
||||
} else {
|
||||
Controller.releaseEntityClickEvents();
|
||||
|
||||
closeExistingDialogWindow();
|
||||
}
|
||||
if (active === isActive) {
|
||||
return;
|
||||
|
@ -769,7 +837,12 @@ var toolBar = (function () {
|
|||
selectionDisplay.triggerMapping.disable();
|
||||
tablet.landscape = false;
|
||||
} else {
|
||||
tablet.loadQMLSource("hifi/tablet/Edit.qml", true);
|
||||
if (HMD.active) {
|
||||
tablet.loadQMLSource("hifi/tablet/Edit.qml", true);
|
||||
} else {
|
||||
// make other apps inactive while in desktop mode
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
UserActivityLogger.enabledEdit();
|
||||
entityListTool.setVisible(true);
|
||||
gridTool.setVisible(true);
|
||||
|
@ -790,17 +863,6 @@ var toolBar = (function () {
|
|||
return that;
|
||||
})();
|
||||
|
||||
|
||||
function isLocked(properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
if (location.hostname === "hq.highfidelity.io" &&
|
||||
properties.modelURL === HIFI_PUBLIC_BUCKET + "ozan/Terrain_Reduce_forAlpha.fbx") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var selectedEntityID;
|
||||
var orientation;
|
||||
var intersection;
|
||||
|
@ -1047,68 +1109,62 @@ function mouseClickEvent(event) {
|
|||
return;
|
||||
}
|
||||
properties = Entities.getEntityProperties(foundEntity);
|
||||
if (isLocked(properties)) {
|
||||
if (wantDebug) {
|
||||
print("Model locked " + properties.id);
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
if (wantDebug) {
|
||||
print("Checking properties: " + properties.id + " " + " - Half Diagonal:" + halfDiagonal);
|
||||
}
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
// / | X - base of the perpendicular line
|
||||
// A---X----->B d - distance fom axis
|
||||
// x x - distance from A
|
||||
//
|
||||
// |X-A| = (P-A).B
|
||||
// X === A + ((P-A).B)B
|
||||
// d = |P-X|
|
||||
|
||||
var A = pickRay.origin;
|
||||
var B = Vec3.normalize(pickRay.direction);
|
||||
var P = properties.position;
|
||||
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) *
|
||||
180 / Math.PI;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) &&
|
||||
(allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (0 < x && sizeOK) {
|
||||
selectedEntityID = foundEntity;
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation));
|
||||
|
||||
if (event.isShifted) {
|
||||
particleExplorerTool.destroyWebView();
|
||||
}
|
||||
if (properties.type !== "ParticleEffect") {
|
||||
particleExplorerTool.destroyWebView();
|
||||
}
|
||||
|
||||
if (!event.isShifted) {
|
||||
selectionManager.setSelections([foundEntity]);
|
||||
} else {
|
||||
selectionManager.addEntity(foundEntity, true);
|
||||
}
|
||||
} else {
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
if (wantDebug) {
|
||||
print("Checking properties: " + properties.id + " " + " - Half Diagonal:" + halfDiagonal);
|
||||
print("Model selected: " + foundEntity);
|
||||
}
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
// / | X - base of the perpendicular line
|
||||
// A---X----->B d - distance fom axis
|
||||
// x x - distance from A
|
||||
//
|
||||
// |X-A| = (P-A).B
|
||||
// X === A + ((P-A).B)B
|
||||
// d = |P-X|
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
|
||||
var A = pickRay.origin;
|
||||
var B = Vec3.normalize(pickRay.direction);
|
||||
var P = properties.position;
|
||||
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) *
|
||||
180 / Math.PI;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) &&
|
||||
(allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (0 < x && sizeOK) {
|
||||
selectedEntityID = foundEntity;
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation));
|
||||
|
||||
if (event.isShifted) {
|
||||
particleExplorerTool.destroyWebView();
|
||||
}
|
||||
if (properties.type !== "ParticleEffect") {
|
||||
particleExplorerTool.destroyWebView();
|
||||
}
|
||||
|
||||
if (!event.isShifted) {
|
||||
selectionManager.setSelections([foundEntity]);
|
||||
} else {
|
||||
selectionManager.addEntity(foundEntity, true);
|
||||
}
|
||||
|
||||
if (wantDebug) {
|
||||
print("Model selected: " + foundEntity);
|
||||
}
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
|
||||
if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) {
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
}
|
||||
if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) {
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
}
|
||||
}
|
||||
} else if (event.isRightButton) {
|
||||
|
@ -1368,11 +1424,7 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) {
|
|||
var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation),
|
||||
Vec3.subtract(position,
|
||||
selectionManager.localPosition));
|
||||
return insideBox({
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}, selectionManager.localDimensions, localPosition);
|
||||
return insideBox(Vec3.ZERO, selectionManager.localDimensions, localPosition);
|
||||
};
|
||||
}
|
||||
for (var i = 0; i < entities.length; ++i) {
|
||||
|
@ -1476,7 +1528,7 @@ function parentSelectedEntities() {
|
|||
return;
|
||||
}
|
||||
var parentCheck = false;
|
||||
var lastEntityId = selectedEntities[selectedEntities.length-1];
|
||||
var lastEntityId = selectedEntities[selectedEntities.length - 1];
|
||||
selectedEntities.forEach(function (id, index) {
|
||||
if (lastEntityId !== id) {
|
||||
var parentId = Entities.getEntityProperties(id, ["parentID"]).parentID;
|
||||
|
@ -1489,7 +1541,7 @@ function parentSelectedEntities() {
|
|||
|
||||
if (parentCheck) {
|
||||
Window.notify("Entities parented");
|
||||
}else {
|
||||
} else {
|
||||
Window.notify("Entities are already parented to last");
|
||||
}
|
||||
} else {
|
||||
|
@ -1902,8 +1954,6 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) {
|
|||
UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData);
|
||||
}
|
||||
|
||||
var ENTITY_PROPERTIES_URL = Script.resolvePath('html/entityProperties.html');
|
||||
|
||||
var ServerScriptStatusMonitor = function(entityID, statusCallback) {
|
||||
var self = this;
|
||||
|
||||
|
@ -1947,13 +1997,14 @@ var PropertiesTool = function (opts) {
|
|||
var currentSelectedEntityID = null;
|
||||
var statusMonitor = null;
|
||||
|
||||
webView.setVisible(visible);
|
||||
|
||||
that.setVisible = function (newVisible) {
|
||||
visible = newVisible;
|
||||
webView.setVisible(visible);
|
||||
webView.setVisible(HMD.active && visible);
|
||||
createToolsWindow.setVisible(!HMD.active && visible);
|
||||
};
|
||||
|
||||
that.setVisible(false);
|
||||
|
||||
function updateScriptStatus(info) {
|
||||
info.type = "server_script_status";
|
||||
webView.emitScriptEvent(JSON.stringify(info));
|
||||
|
@ -1982,7 +2033,7 @@ var PropertiesTool = function (opts) {
|
|||
statusMonitor = null;
|
||||
}
|
||||
currentSelectedEntityID = null;
|
||||
} else if (currentSelectedEntityID != selectionManager.selections[0]) {
|
||||
} else if (currentSelectedEntityID !== selectionManager.selections[0]) {
|
||||
if (statusMonitor !== null) {
|
||||
statusMonitor.stop();
|
||||
}
|
||||
|
@ -2008,11 +2059,14 @@ var PropertiesTool = function (opts) {
|
|||
selections.push(entity);
|
||||
}
|
||||
data.selections = selections;
|
||||
|
||||
webView.emitScriptEvent(JSON.stringify(data));
|
||||
createToolsWindow.emitScriptEvent(JSON.stringify(data));
|
||||
}
|
||||
selectionManager.addEventListener(updateSelections);
|
||||
|
||||
webView.webEventReceived.connect(function (data) {
|
||||
|
||||
var onWebEventReceived = function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
@ -2034,16 +2088,8 @@ var PropertiesTool = function (opts) {
|
|||
} else if (data.properties) {
|
||||
if (data.properties.dynamic === false) {
|
||||
// this object is leaving dynamic, so we zero its velocities
|
||||
data.properties.velocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
data.properties.angularVelocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
data.properties.velocity = Vec3.ZERO;
|
||||
data.properties.angularVelocity = Vec3.ZERO;
|
||||
}
|
||||
if (data.properties.rotation !== undefined) {
|
||||
var rotation = data.properties.rotation;
|
||||
|
@ -2171,7 +2217,11 @@ var PropertiesTool = function (opts) {
|
|||
} else if (data.type === "propertiesPageReady") {
|
||||
updateSelections(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
createToolsWindow.webEventReceived.addListener(this, onWebEventReceived);
|
||||
|
||||
webView.webEventReceived.connect(onWebEventReceived);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
@ -2186,6 +2236,8 @@ var PopupMenu = function () {
|
|||
var overlays = [];
|
||||
var overlayInfo = {};
|
||||
|
||||
var visible = false;
|
||||
|
||||
var upColor = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
|
@ -2303,8 +2355,6 @@ var PopupMenu = function () {
|
|||
}
|
||||
};
|
||||
|
||||
var visible = false;
|
||||
|
||||
self.setVisible = function (newVisible) {
|
||||
if (newVisible !== visible) {
|
||||
visible = newVisible;
|
||||
|
@ -2358,7 +2408,7 @@ propertyMenu.onSelectMenuItem = function (name) {
|
|||
var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace");
|
||||
|
||||
var propertiesTool = new PropertiesTool();
|
||||
var particleExplorerTool = new ParticleExplorerTool();
|
||||
var particleExplorerTool = new ParticleExplorerTool(createToolsWindow);
|
||||
var selectedParticleEntityID = null;
|
||||
|
||||
function selectParticleEntity(entityID) {
|
||||
|
@ -2375,11 +2425,16 @@ function selectParticleEntity(entityID) {
|
|||
particleExplorerTool.setActiveParticleEntity(entityID);
|
||||
|
||||
// Switch to particle explorer
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.sendToQml({method: 'selectTab', params: {id: 'particle'}});
|
||||
var selectTabMethod = { method: 'selectTab', params: { id: 'particle' } };
|
||||
if (HMD.active) {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.sendToQml(selectTabMethod);
|
||||
} else {
|
||||
createToolsWindow.sendToQml(selectTabMethod);
|
||||
}
|
||||
}
|
||||
|
||||
entityListTool.webView.webEventReceived.connect(function (data) {
|
||||
entityListTool.webView.webEventReceived.connect(function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
|
|
|
@ -11,27 +11,64 @@
|
|||
/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
|
||||
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */
|
||||
|
||||
EntityListTool = function(opts) {
|
||||
EntityListTool = function() {
|
||||
var that = {};
|
||||
|
||||
var CreateWindow = Script.require('../modules/createWindow.js');
|
||||
|
||||
var TITLE_OFFSET = 60;
|
||||
var ENTITY_LIST_WIDTH = 495;
|
||||
var MAX_DEFAULT_CREATE_TOOLS_HEIGHT = 778;
|
||||
var entityListWindow = new CreateWindow(
|
||||
Script.resourcesPath() + "qml/hifi/tablet/EditEntityList.qml",
|
||||
'Entity List',
|
||||
'com.highfidelity.create.entityListWindow',
|
||||
function () {
|
||||
var windowHeight = Window.innerHeight - TITLE_OFFSET;
|
||||
if (windowHeight > MAX_DEFAULT_CREATE_TOOLS_HEIGHT) {
|
||||
windowHeight = MAX_DEFAULT_CREATE_TOOLS_HEIGHT;
|
||||
}
|
||||
return {
|
||||
size: {
|
||||
x: ENTITY_LIST_WIDTH,
|
||||
y: windowHeight
|
||||
},
|
||||
position: {
|
||||
x: Window.x,
|
||||
y: Window.y + TITLE_OFFSET
|
||||
}
|
||||
};
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
var webView = null;
|
||||
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
webView.setVisible = function(value) {};
|
||||
webView.setVisible = function(value){ };
|
||||
|
||||
var filterInView = false;
|
||||
var searchRadius = 100;
|
||||
|
||||
var visible = false;
|
||||
|
||||
webView.setVisible(visible);
|
||||
|
||||
that.webView = webView;
|
||||
|
||||
that.setVisible = function(newVisible) {
|
||||
visible = newVisible;
|
||||
webView.setVisible(visible);
|
||||
webView.setVisible(HMD.active && visible);
|
||||
entityListWindow.setVisible(!HMD.active && visible);
|
||||
};
|
||||
|
||||
that.setVisible(false);
|
||||
|
||||
function emitJSONScriptEvent(data) {
|
||||
var dataString = JSON.stringify(data);
|
||||
webView.emitScriptEvent(dataString);
|
||||
if (entityListWindow.window) {
|
||||
entityListWindow.window.emitScriptEvent(dataString);
|
||||
}
|
||||
}
|
||||
|
||||
that.toggleVisible = function() {
|
||||
that.setVisible(!visible);
|
||||
};
|
||||
|
@ -43,18 +80,16 @@ EntityListTool = function(opts) {
|
|||
selectedIDs.push(selectionManager.selections[i]);
|
||||
}
|
||||
|
||||
var data = {
|
||||
emitJSONScriptEvent({
|
||||
type: 'selectionUpdate',
|
||||
selectedIDs: selectedIDs,
|
||||
};
|
||||
webView.emitScriptEvent(JSON.stringify(data));
|
||||
selectedIDs: selectedIDs
|
||||
});
|
||||
});
|
||||
|
||||
that.clearEntityList = function () {
|
||||
var data = {
|
||||
that.clearEntityList = function() {
|
||||
emitJSONScriptEvent({
|
||||
type: 'clearEntityList'
|
||||
};
|
||||
webView.emitScriptEvent(JSON.stringify(data));
|
||||
});
|
||||
};
|
||||
|
||||
that.removeEntities = function (deletedIDs, selectedIDs) {
|
||||
|
@ -87,9 +122,9 @@ EntityListTool = function(opts) {
|
|||
|
||||
if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) {
|
||||
var url = "";
|
||||
if (properties.type == "Model") {
|
||||
if (properties.type === "Model") {
|
||||
url = properties.modelURL;
|
||||
} else if (properties.type == "Material") {
|
||||
} else if (properties.type === "Material") {
|
||||
url = properties.materialURL;
|
||||
}
|
||||
entities.push({
|
||||
|
@ -107,7 +142,7 @@ EntityListTool = function(opts) {
|
|||
valueIfDefined(properties.renderInfo.texturesSize) : ""),
|
||||
hasTransparent: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.hasTransparent) : ""),
|
||||
isBaked: properties.type == "Model" ? url.toLowerCase().endsWith(".baked.fbx") : false,
|
||||
isBaked: properties.type === "Model" ? url.toLowerCase().endsWith(".baked.fbx") : false,
|
||||
drawCalls: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.drawCalls) : ""),
|
||||
hasScript: properties.script !== ""
|
||||
|
@ -120,12 +155,11 @@ EntityListTool = function(opts) {
|
|||
selectedIDs.push(selectionManager.selections[j]);
|
||||
}
|
||||
|
||||
var data = {
|
||||
emitJSONScriptEvent({
|
||||
type: "update",
|
||||
entities: entities,
|
||||
selectedIDs: selectedIDs,
|
||||
};
|
||||
webView.emitScriptEvent(JSON.stringify(data));
|
||||
});
|
||||
};
|
||||
|
||||
function onFileSaveChanged(filename) {
|
||||
|
@ -138,15 +172,15 @@ EntityListTool = function(opts) {
|
|||
}
|
||||
}
|
||||
|
||||
webView.webEventReceived.connect(function(data) {
|
||||
var onWebEventReceived = function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
print("entityList.js: Error parsing JSON: " + e.name + " data " + data)
|
||||
print("entityList.js: Error parsing JSON: " + e.name + " data " + data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type == "selectionUpdate") {
|
||||
if (data.type === "selectionUpdate") {
|
||||
var ids = data.entityIds;
|
||||
var entityIDs = [];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
|
@ -159,20 +193,20 @@ EntityListTool = function(opts) {
|
|||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
}
|
||||
} else if (data.type == "refresh") {
|
||||
} else if (data.type === "refresh") {
|
||||
that.sendUpdate();
|
||||
} else if (data.type == "teleport") {
|
||||
} else if (data.type === "teleport") {
|
||||
if (selectionManager.hasSelection()) {
|
||||
MyAvatar.position = selectionManager.worldPosition;
|
||||
}
|
||||
} else if (data.type == "export") {
|
||||
} else if (data.type === "export") {
|
||||
if (!selectionManager.hasSelection()) {
|
||||
Window.notifyEditError("No entities have been selected.");
|
||||
} else {
|
||||
Window.saveFileChanged.connect(onFileSaveChanged);
|
||||
Window.saveAsync("Select Where to Save", "", "*.json");
|
||||
}
|
||||
} else if (data.type == "pal") {
|
||||
} else if (data.type === "pal") {
|
||||
var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates.
|
||||
selectionManager.selections.forEach(function (id) {
|
||||
var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy;
|
||||
|
@ -189,24 +223,21 @@ EntityListTool = function(opts) {
|
|||
// No need to subscribe if we're just sending.
|
||||
Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true, false]}), 'local');
|
||||
}
|
||||
} else if (data.type == "delete") {
|
||||
} else if (data.type === "delete") {
|
||||
deleteSelectedEntities();
|
||||
} else if (data.type == "toggleLocked") {
|
||||
} else if (data.type === "toggleLocked") {
|
||||
toggleSelectedEntitiesLocked();
|
||||
} else if (data.type == "toggleVisible") {
|
||||
} else if (data.type === "toggleVisible") {
|
||||
toggleSelectedEntitiesVisible();
|
||||
} else if (data.type === "filterInView") {
|
||||
filterInView = data.filterInView === true;
|
||||
} else if (data.type === "radius") {
|
||||
searchRadius = data.radius;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// webView.visibleChanged.connect(function () {
|
||||
// if (webView.visible) {
|
||||
// that.sendUpdate();
|
||||
// }
|
||||
// });
|
||||
webView.webEventReceived.connect(onWebEventReceived);
|
||||
entityListWindow.webEventReceived.addListener(onWebEventReceived);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/* global SelectionManager, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
|
||||
/* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
|
||||
getMainTabletIDs, getControllerWorldLocation */
|
||||
|
||||
var SPACE_LOCAL = "local";
|
||||
|
@ -72,7 +72,9 @@ SelectionManager = (function() {
|
|||
|
||||
subscribeToUpdateMessages();
|
||||
|
||||
var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 }
|
||||
// disabling this for now as it is causing rendering issues with the other handle overlays
|
||||
/*
|
||||
var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 };
|
||||
var editHandleOutlineStyle = {
|
||||
outlineUnoccludedColor: COLOR_ORANGE_HIGHLIGHT,
|
||||
outlineOccludedColor: COLOR_ORANGE_HIGHLIGHT,
|
||||
|
@ -85,8 +87,8 @@ SelectionManager = (function() {
|
|||
outlineWidth: 3,
|
||||
isOutlineSmooth: true
|
||||
};
|
||||
// disabling this for now as it is causing rendering issues with the other handle overlays
|
||||
//Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle);
|
||||
Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle);
|
||||
*/
|
||||
|
||||
that.savedProperties = {};
|
||||
that.selections = [];
|
||||
|
@ -411,8 +413,6 @@ SelectionDisplay = (function() {
|
|||
|
||||
var spaceMode = SPACE_LOCAL;
|
||||
var overlayNames = [];
|
||||
var lastCameraPosition = Camera.getPosition();
|
||||
var lastCameraOrientation = Camera.getOrientation();
|
||||
var lastControllerPoses = [
|
||||
getControllerWorldLocation(Controller.Standard.LeftHand, true),
|
||||
getControllerWorldLocation(Controller.Standard.RightHand, true)
|
||||
|
@ -432,7 +432,7 @@ SelectionDisplay = (function() {
|
|||
|
||||
var ctrlPressed = false;
|
||||
|
||||
var replaceCollisionsAfterStretch = false;
|
||||
that.replaceCollisionsAfterStretch = false;
|
||||
|
||||
var handlePropertiesTranslateArrowCones = {
|
||||
shape: "Cone",
|
||||
|
@ -1018,7 +1018,7 @@ SelectionDisplay = (function() {
|
|||
if (SelectionManager.hasSelection()) {
|
||||
var controllerPose = getControllerWorldLocation(activeHand, true);
|
||||
var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1;
|
||||
if (controllerPose.valid && lastControllerPoses[hand].valid) {
|
||||
if (controllerPose.valid && lastControllerPoses[hand].valid && that.triggered) {
|
||||
if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) ||
|
||||
!Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) {
|
||||
that.mouseMoveEvent({});
|
||||
|
@ -1068,9 +1068,6 @@ SelectionDisplay = (function() {
|
|||
that.select = function(entityID, event) {
|
||||
var properties = Entities.getEntityProperties(SelectionManager.selections[0]);
|
||||
|
||||
lastCameraPosition = Camera.getPosition();
|
||||
lastCameraOrientation = Camera.getOrientation();
|
||||
|
||||
if (event !== false) {
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
|
@ -1668,7 +1665,7 @@ SelectionDisplay = (function() {
|
|||
|
||||
translateXZTool.pickPlanePosition = pickResult.intersection;
|
||||
translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x,
|
||||
SelectionManager.worldDimensions.y),
|
||||
SelectionManager.worldDimensions.y),
|
||||
SelectionManager.worldDimensions.z);
|
||||
translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position);
|
||||
translateXZTool.startingElevation = translateXZTool.elevation(pickRay.origin, translateXZTool.pickPlanePosition);
|
||||
|
@ -1818,23 +1815,27 @@ SelectionDisplay = (function() {
|
|||
function addHandleTranslateTool(overlay, mode, direction) {
|
||||
var pickNormal = null;
|
||||
var lastPick = null;
|
||||
var initialPosition = null;
|
||||
var projectionVector = null;
|
||||
var previousPickRay = null;
|
||||
addHandleTool(overlay, {
|
||||
mode: mode,
|
||||
onBegin: function(event, pickRay, pickResult) {
|
||||
var axisVector;
|
||||
if (direction === TRANSLATE_DIRECTION.X) {
|
||||
pickNormal = { x: 0, y: 1, z: 1 };
|
||||
axisVector = { x: 1, y: 0, z: 0 };
|
||||
} else if (direction === TRANSLATE_DIRECTION.Y) {
|
||||
pickNormal = { x: 1, y: 0, z: 1 };
|
||||
axisVector = { x: 0, y: 1, z: 0 };
|
||||
} else if (direction === TRANSLATE_DIRECTION.Z) {
|
||||
pickNormal = { x: 1, y: 1, z: 0 };
|
||||
axisVector = { x: 0, y: 0, z: 1 };
|
||||
}
|
||||
|
||||
var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation;
|
||||
pickNormal = Vec3.multiplyQbyV(rotation, pickNormal);
|
||||
axisVector = Vec3.multiplyQbyV(rotation, axisVector);
|
||||
pickNormal = Vec3.cross(Vec3.cross(pickRay.direction, axisVector), axisVector);
|
||||
|
||||
lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
|
||||
initialPosition = SelectionManager.worldPosition;
|
||||
|
||||
SelectionManager.saveProperties();
|
||||
that.resetPreviousHandleColor();
|
||||
|
@ -1869,7 +1870,7 @@ SelectionDisplay = (function() {
|
|||
pickRay = previousPickRay;
|
||||
}
|
||||
|
||||
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
|
||||
var newIntersection = rayPlaneIntersection(pickRay, initialPosition, pickNormal);
|
||||
var vector = Vec3.subtract(newIntersection, lastPick);
|
||||
|
||||
if (direction === TRANSLATE_DIRECTION.X) {
|
||||
|
@ -1885,7 +1886,8 @@ SelectionDisplay = (function() {
|
|||
|
||||
var dotVector = Vec3.dot(vector, projectionVector);
|
||||
vector = Vec3.multiply(dotVector, projectionVector);
|
||||
vector = grid.snapToGrid(vector);
|
||||
var gridOrigin = grid.getOrigin();
|
||||
vector = Vec3.subtract(grid.snapToGrid(Vec3.sum(vector, gridOrigin)), gridOrigin);
|
||||
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
|
@ -2185,8 +2187,8 @@ SelectionDisplay = (function() {
|
|||
newDimensions = Vec3.sum(initialDimensions, changeInDimensions);
|
||||
}
|
||||
|
||||
var minimumDimension = directionEnum === STRETCH_DIRECTION.ALL ? STRETCH_ALL_MINIMUM_DIMENSION :
|
||||
STRETCH_MINIMUM_DIMENSION;
|
||||
var minimumDimension = directionEnum ===
|
||||
STRETCH_DIRECTION.ALL ? STRETCH_ALL_MINIMUM_DIMENSION : STRETCH_MINIMUM_DIMENSION;
|
||||
if (newDimensions.x < minimumDimension) {
|
||||
newDimensions.x = minimumDimension;
|
||||
changeInDimensions.x = minimumDimension - initialDimensions.x;
|
||||
|
@ -2280,8 +2282,7 @@ SelectionDisplay = (function() {
|
|||
selectedHandle = handleScaleRTFCube;
|
||||
}
|
||||
offset = Vec3.multiply(directionVector, NEGATE_VECTOR);
|
||||
var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVector,
|
||||
directionVector, offset, null, selectedHandle);
|
||||
var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVector, directionVector, offset, null, selectedHandle);
|
||||
return addHandleTool(overlay, tool);
|
||||
}
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@ GridTool = function(opts) {
|
|||
|
||||
var horizontalGrid = opts.horizontalGrid;
|
||||
var verticalGrid = opts.verticalGrid;
|
||||
var createToolsWindow = opts.createToolsWindow;
|
||||
var listeners = [];
|
||||
|
||||
var webView = null;
|
||||
|
@ -247,13 +248,15 @@ GridTool = function(opts) {
|
|||
webView.setVisible = function(value) { };
|
||||
|
||||
horizontalGrid.addListener(function(data) {
|
||||
webView.emitScriptEvent(JSON.stringify(data));
|
||||
var dataString = JSON.stringify(data);
|
||||
webView.emitScriptEvent(dataString);
|
||||
createToolsWindow.emitScriptEvent(dataString);
|
||||
if (selectionDisplay) {
|
||||
selectionDisplay.updateHandles();
|
||||
}
|
||||
});
|
||||
|
||||
webView.webEventReceived.connect(function(data) {
|
||||
var webEventReceived = function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
|
@ -282,14 +285,17 @@ GridTool = function(opts) {
|
|||
grid.setPosition(newPosition);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
webView.webEventReceived.connect(webEventReceived);
|
||||
createToolsWindow.webEventReceived.addListener(webEventReceived);
|
||||
|
||||
that.addListener = function(callback) {
|
||||
listeners.push(callback);
|
||||
};
|
||||
|
||||
that.setVisible = function(visible) {
|
||||
webView.setVisible(visible);
|
||||
webView.setVisible(HMD.active && visible);
|
||||
};
|
||||
|
||||
return that;
|
||||
|
|
151
scripts/system/modules/createWindow.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
"use strict";
|
||||
|
||||
// createWindow.js
|
||||
//
|
||||
// Created by Thijs Wenker on 6/1/18
|
||||
//
|
||||
// Copyright 2018 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
|
||||
//
|
||||
|
||||
var getWindowRect = function(settingsKey, defaultRect) {
|
||||
var windowRect = Settings.getValue(settingsKey, defaultRect);
|
||||
return windowRect;
|
||||
};
|
||||
|
||||
var setWindowRect = function(settingsKey, position, size) {
|
||||
Settings.setValue(settingsKey, {
|
||||
position: position,
|
||||
size: size
|
||||
});
|
||||
};
|
||||
|
||||
var CallableEvent = (function() {
|
||||
function CallableEvent() {
|
||||
this.callbacks = [];
|
||||
}
|
||||
|
||||
CallableEvent.prototype = {
|
||||
callbacks: null,
|
||||
call: function () {
|
||||
var callArguments = arguments;
|
||||
this.callbacks.forEach(function(callbackObject) {
|
||||
try {
|
||||
callbackObject.callback.apply(callbackObject.context ? callbackObject.context : this, callArguments);
|
||||
} catch (e) {
|
||||
console.error('Call to CallableEvent callback failed!');
|
||||
}
|
||||
});
|
||||
},
|
||||
addListener: function(contextOrCallback, callback) {
|
||||
if (callback) {
|
||||
this.callbacks.push({
|
||||
context: contextOrCallback,
|
||||
callback: callback
|
||||
});
|
||||
} else {
|
||||
this.callbacks.push({
|
||||
callback: contextOrCallback
|
||||
});
|
||||
}
|
||||
},
|
||||
removeListener: function(callback) {
|
||||
var foundIndex = -1;
|
||||
this.callbacks.forEach(function (callbackObject, index) {
|
||||
if (callbackObject.callback === callback) {
|
||||
foundIndex = index;
|
||||
}
|
||||
});
|
||||
|
||||
if (foundIndex !== -1) {
|
||||
this.callbacks.splice(foundIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return CallableEvent;
|
||||
})();
|
||||
|
||||
module.exports = (function() {
|
||||
function CreateWindow(qmlPath, title, settingsKey, defaultRect, createOnStartup) {
|
||||
this.qmlPath = qmlPath;
|
||||
this.title = title;
|
||||
this.settingsKey = settingsKey;
|
||||
this.defaultRect = defaultRect;
|
||||
this.webEventReceived = new CallableEvent();
|
||||
this.fromQml = new CallableEvent();
|
||||
if (createOnStartup) {
|
||||
this.createWindow();
|
||||
}
|
||||
}
|
||||
|
||||
CreateWindow.prototype = {
|
||||
window: null,
|
||||
createWindow: function() {
|
||||
var defaultRect = this.defaultRect;
|
||||
if (typeof this.defaultRect === "function") {
|
||||
defaultRect = this.defaultRect();
|
||||
}
|
||||
|
||||
var windowRect = getWindowRect(this.settingsKey, defaultRect);
|
||||
this.window = Desktop.createWindow(this.qmlPath, {
|
||||
title: this.title,
|
||||
flags: Desktop.ALWAYS_ON_TOP | Desktop.CLOSE_BUTTON_HIDES,
|
||||
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||
size: windowRect.size,
|
||||
visible: true,
|
||||
position: windowRect.position
|
||||
});
|
||||
|
||||
var windowRectChanged = function () {
|
||||
if (this.window.visible) {
|
||||
setWindowRect(this.settingsKey, this.window.position, this.window.size);
|
||||
}
|
||||
};
|
||||
|
||||
this.window.sizeChanged.connect(this, windowRectChanged);
|
||||
this.window.positionChanged.connect(this, windowRectChanged);
|
||||
|
||||
this.window.webEventReceived.connect(this, function (data) {
|
||||
this.webEventReceived.call(data);
|
||||
});
|
||||
|
||||
this.window.fromQml.connect(this, function (data) {
|
||||
this.fromQml.call(data);
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(this, function() {
|
||||
this.window.close();
|
||||
});
|
||||
},
|
||||
setVisible: function(visible) {
|
||||
if (visible && !this.window) {
|
||||
this.createWindow();
|
||||
}
|
||||
|
||||
if (this.window) {
|
||||
if (visible) {
|
||||
this.window.show();
|
||||
} else {
|
||||
this.window.visible = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
emitScriptEvent: function(data) {
|
||||
if (this.window) {
|
||||
this.window.emitScriptEvent(data);
|
||||
}
|
||||
},
|
||||
sendToQml: function(data) {
|
||||
if (this.window) {
|
||||
this.window.sendToQml(data);
|
||||
}
|
||||
},
|
||||
webEventReceived: null,
|
||||
fromQml: null
|
||||
};
|
||||
|
||||
return CreateWindow;
|
||||
})();
|
|
@ -9,13 +9,12 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/* global window, alert, ParticleExplorerTool, EventBridge, dat, listenForSettingsUpdates, createVec3Folder,
|
||||
createQuatFolder, writeVec3ToInterface, writeDataToInterface */
|
||||
/* global ParticleExplorerTool */
|
||||
|
||||
|
||||
var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html');
|
||||
|
||||
ParticleExplorerTool = function() {
|
||||
ParticleExplorerTool = function(createToolsWindow) {
|
||||
var that = {};
|
||||
that.activeParticleEntity = 0;
|
||||
that.updatedActiveParticleProperties = {};
|
||||
|
@ -24,8 +23,15 @@ ParticleExplorerTool = function() {
|
|||
that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
that.webView.setVisible = function(value) {};
|
||||
that.webView.webEventReceived.connect(that.webEventReceived);
|
||||
createToolsWindow.webEventReceived.addListener(this, that.webEventReceived);
|
||||
};
|
||||
|
||||
function emitScriptEvent(data) {
|
||||
var messageData = JSON.stringify(data);
|
||||
that.webView.emitScriptEvent(messageData);
|
||||
createToolsWindow.emitScriptEvent(messageData);
|
||||
}
|
||||
|
||||
that.destroyWebView = function() {
|
||||
if (!that.webView) {
|
||||
return;
|
||||
|
@ -33,17 +39,16 @@ ParticleExplorerTool = function() {
|
|||
that.activeParticleEntity = 0;
|
||||
that.updatedActiveParticleProperties = {};
|
||||
|
||||
var messageData = {
|
||||
emitScriptEvent({
|
||||
messageType: "particle_close"
|
||||
};
|
||||
that.webView.emitScriptEvent(JSON.stringify(messageData));
|
||||
});
|
||||
};
|
||||
|
||||
function sendParticleProperties(properties) {
|
||||
that.webView.emitScriptEvent(JSON.stringify({
|
||||
emitScriptEvent({
|
||||
messageType: "particle_settings",
|
||||
currentProperties: properties
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function sendActiveParticleProperties() {
|
||||
|
|