From 36a3a38ab542cb586e5ac2750ef00d569b9b7c9c Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 11:58:56 -0800 Subject: [PATCH 01/31] Create css file that removes top bar and drawer in modelviewpage. --- src/App.tsx | 4 ++- src/components/pages/ModelViewPage.tsx | 19 ++++++----- src/gui.css | 44 ++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 src/gui.css diff --git a/src/App.tsx b/src/App.tsx index 5ac6095..26b87ae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -71,7 +71,9 @@ function App({ signOut, user }: WithAuthenticatorProps) {
- +
+ +
} /> diff --git a/src/components/pages/ModelViewPage.tsx b/src/components/pages/ModelViewPage.tsx index e04e7cc..930467e 100644 --- a/src/components/pages/ModelViewPage.tsx +++ b/src/components/pages/ModelViewPage.tsx @@ -104,20 +104,23 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) {
- +
+ +
Date: Thu, 29 Feb 2024 12:37:53 -0800 Subject: [PATCH 02/31] Modifying lambda function. --- src/backend/lambda_function.py | 9 +++- src/components/Components/DropFile.tsx | 58 ++++++++++++++++---------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index 6f129eb..f85b6cf 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -27,6 +27,7 @@ def handler(event, context): # download the file from url into local tmp folder response = requests.get(source_url) print("response.status_code=", response.status_code) + user_uuid = None if response.status_code == 200: file_name = os.path.join('/tmp', filename) print("file_name =", file_name) @@ -39,12 +40,14 @@ def handler(event, context): source_bucket = event["s3"] object_key = event["key"] file_name = '/tmp/' + object_key.split('/')[-1] + user_uuid = object_key.split('/')[-2] print("Attempting to download") s3.download_file(source_bucket, object_key, file_name) else : #invoked using s3 upload directly source_bucket = event['Records'][0]['s3']['bucket']['name'] object_key = event['Records'][0]['s3']['object']['key'] file_name = '/tmp/' + object_key.split('/')[-1] + user_uuid = object_key.split('/')[-2] print("Attempting to download") s3.download_file(source_bucket, object_key, file_name) @@ -62,7 +65,11 @@ def handler(event, context): gltfJson.save(destinationFile) print("Gltf file saved") destinationFileName = Path(file_name).with_suffix('.gltf') - strDestinationFileName = str(destinationFileName).split('/')[-1] + if user_uuid: + user_uuid = user_uuid + "/" + else: + user_uuid = "" + strDestinationFileName = user_uuid + str(destinationFileName).split('/')[-1] # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) # print("File upload launched") diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index 69aa155..b0c5c1a 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -113,30 +113,42 @@ const FileDropArea = observer(() => { axios.post(api_url, data).then(response => { const gltf_url = response.data['url']; .replace(/\.\w+$/, '.gltf') appState.setCurrentModelPath(gltf_url); */ - const params: AWS.Lambda.InvocationRequest = { - FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name - Payload: JSON.stringify({ - s3: 'opensimviewer-input-bucket101047-dev', - key: 'public/'+file.name - }) - }; - lambda.invoke(params, (err: any, data: any) => { - if (err) { - console.error(err); - } else { - const key = file.name.replace(/\.\w+$/, '.gltf') - const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/"+key - /* appState.setCurrentModelPath(gltf_url); */ - navigate("/viewer/"+encodeURIComponent(gltf_url)) - console.log('Lambda function invoked successfully:', data); - closeSnackbar() - } + const storedDataString = localStorage.getItem('CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.albertocasas.userData'); + if (storedDataString != null) { + let storedData = JSON.parse(storedDataString); + let user_uuid = "" + storedData["UserAttributes"].forEach((element:any) => { + if (element["Name"] === "sub") { + user_uuid = element["Value"]; + } }); - if (location.pathname !== '/viewer') - navigate('/viewer'); - - // File uploaded to S3, so it is not a local upload. - viewerState.isLocalUpload = true + const params: AWS.Lambda.InvocationRequest = { + FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name + Payload: JSON.stringify({ + s3: 'opensimviewer-input-bucket101047-dev', + key: 'public/' + user_uuid + "/" +file.name + }) + }; + lambda.invoke(params, (err: any, data: any) => { + if (err) { + console.error(err); + } else { + const key = file.name.replace(/\.\w+$/, '.gltf') + const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/"+user_uuid+"/"+key + /* appState.setCurrentModelPath(gltf_url); */ + navigate("/viewer/"+encodeURIComponent(gltf_url)) + console.log('Lambda function invoked successfully:', data); + closeSnackbar() + } + }); + if (location.pathname !== '/viewer') + navigate('/viewer'); + + // File uploaded to S3, so it is not a local upload. + viewerState.isLocalUpload = true + } else { + console.log("ERROR") + } }) .catch(error => { console.error('Error:', error); From bab86ddf0487be7c121bd1f2f05326b22dd679cc Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 14:50:38 -0800 Subject: [PATCH 03/31] Checking folder exist and creating it before uploading gltf file. --- src/backend/lambda_function.py | 7 +++ src/components/Components/DropFile.tsx | 60 +++++++++++++------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index f85b6cf..ed611d7 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -70,6 +70,13 @@ def handler(event, context): else: user_uuid = "" strDestinationFileName = user_uuid + str(destinationFileName).split('/')[-1] + # Create user folder if it does not exist + user_folder_exists = s3.list_objects_v2(Bucket=target_bucket, Prefix=user_uuid).get('Contents') + + if not user_folder_exists: + # If the folder doesn't exist, create it + s3.put_object(Bucket=target_bucket, Key=user_uuid) + # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) # print("File upload launched") diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index b0c5c1a..6e18515 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -114,41 +114,43 @@ const FileDropArea = observer(() => { const gltf_url = response.data['url']; .replace(/\.\w+$/, '.gltf') appState.setCurrentModelPath(gltf_url); */ const storedDataString = localStorage.getItem('CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.albertocasas.userData'); + + let user_uuid = "" if (storedDataString != null) { let storedData = JSON.parse(storedDataString); - let user_uuid = "" storedData["UserAttributes"].forEach((element:any) => { if (element["Name"] === "sub") { - user_uuid = element["Value"]; + user_uuid = element["Value"] + "/"; } }); - const params: AWS.Lambda.InvocationRequest = { - FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name - Payload: JSON.stringify({ - s3: 'opensimviewer-input-bucket101047-dev', - key: 'public/' + user_uuid + "/" +file.name - }) - }; - lambda.invoke(params, (err: any, data: any) => { - if (err) { - console.error(err); - } else { - const key = file.name.replace(/\.\w+$/, '.gltf') - const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/"+user_uuid+"/"+key - /* appState.setCurrentModelPath(gltf_url); */ - navigate("/viewer/"+encodeURIComponent(gltf_url)) - console.log('Lambda function invoked successfully:', data); - closeSnackbar() - } - }); - if (location.pathname !== '/viewer') - navigate('/viewer'); - - // File uploaded to S3, so it is not a local upload. - viewerState.isLocalUpload = true - } else { - console.log("ERROR") - } + } else { + console.log("User not authenticated.") + } + + const params: AWS.Lambda.InvocationRequest = { + FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name + Payload: JSON.stringify({ + s3: 'opensimviewer-input-bucket101047-dev', + key: 'public/' + user_uuid + file.name + }) + }; + lambda.invoke(params, (err: any, data: any) => { + if (err) { + console.error(err); + } else { + const key = file.name.replace(/\.\w+$/, '.gltf') + const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/"+key + /* appState.setCurrentModelPath(gltf_url); */ + navigate("/viewer/"+encodeURIComponent(gltf_url)) + console.log('Lambda function invoked successfully:', data); + closeSnackbar() + } + }); + if (location.pathname !== '/viewer') + navigate('/viewer'); + + // File uploaded to S3, so it is not a local upload. + viewerState.isLocalUpload = true }) .catch(error => { console.error('Error:', error); From 46be5b243b9ed36fbbeed6d2b9c24ad46d6f9cc4 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 15:03:55 -0800 Subject: [PATCH 04/31] Checking folder exist and creating it before uploading gltf file. --- src/backend/lambda_function.py | 5 ++++- src/components/Components/DropFile.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index ed611d7..ee11b8e 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -70,12 +70,15 @@ def handler(event, context): else: user_uuid = "" strDestinationFileName = user_uuid + str(destinationFileName).split('/')[-1] + print("Destination File Name: " + strDestinationFileName) # Create user folder if it does not exist user_folder_exists = s3.list_objects_v2(Bucket=target_bucket, Prefix=user_uuid).get('Contents') - + print("User folder exists: " + user_folder_exists) if not user_folder_exists: # If the folder doesn't exist, create it s3.put_object(Bucket=target_bucket, Key=user_uuid) + user_folder_exists = s3.list_objects_v2(Bucket=target_bucket, Prefix=user_uuid).get('Contents') + print("User folder exists after creation: " + user_folder_exists) # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index 6e18515..7543009 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -139,7 +139,7 @@ const FileDropArea = observer(() => { console.error(err); } else { const key = file.name.replace(/\.\w+$/, '.gltf') - const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/"+key + const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/" + user_uuid + key /* appState.setCurrentModelPath(gltf_url); */ navigate("/viewer/"+encodeURIComponent(gltf_url)) console.log('Lambda function invoked successfully:', data); From cfa23ac068f1c8fa6c04a2028f99887a690f3e17 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 15:17:47 -0800 Subject: [PATCH 05/31] Testing in amplify. --- src/backend/lambda_function.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index ee11b8e..008de36 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -39,7 +39,9 @@ def handler(event, context): elif ("s3" in event): source_bucket = event["s3"] object_key = event["key"] + print("Object Key: " + object_key) file_name = '/tmp/' + object_key.split('/')[-1] + print("File Name: " + file_name) user_uuid = object_key.split('/')[-2] print("Attempting to download") s3.download_file(source_bucket, object_key, file_name) From 94e790204a79216794c041333bd8e5ff4d888589 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 15:33:28 -0800 Subject: [PATCH 06/31] Testing in amplify. --- src/backend/lambda_function.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index 008de36..7142aed 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -27,7 +27,6 @@ def handler(event, context): # download the file from url into local tmp folder response = requests.get(source_url) print("response.status_code=", response.status_code) - user_uuid = None if response.status_code == 200: file_name = os.path.join('/tmp', filename) print("file_name =", file_name) @@ -39,20 +38,18 @@ def handler(event, context): elif ("s3" in event): source_bucket = event["s3"] object_key = event["key"] - print("Object Key: " + object_key) file_name = '/tmp/' + object_key.split('/')[-1] - print("File Name: " + file_name) - user_uuid = object_key.split('/')[-2] print("Attempting to download") s3.download_file(source_bucket, object_key, file_name) else : #invoked using s3 upload directly source_bucket = event['Records'][0]['s3']['bucket']['name'] object_key = event['Records'][0]['s3']['object']['key'] file_name = '/tmp/' + object_key.split('/')[-1] - user_uuid = object_key.split('/')[-2] print("Attempting to download") s3.download_file(source_bucket, object_key, file_name) + print("Object key: " + object_key) + print("setup conversion function") target_bucket = 'opensim-viewer-public-download' print("file_name", file_name) @@ -67,21 +64,7 @@ def handler(event, context): gltfJson.save(destinationFile) print("Gltf file saved") destinationFileName = Path(file_name).with_suffix('.gltf') - if user_uuid: - user_uuid = user_uuid + "/" - else: - user_uuid = "" - strDestinationFileName = user_uuid + str(destinationFileName).split('/')[-1] - print("Destination File Name: " + strDestinationFileName) - # Create user folder if it does not exist - user_folder_exists = s3.list_objects_v2(Bucket=target_bucket, Prefix=user_uuid).get('Contents') - print("User folder exists: " + user_folder_exists) - if not user_folder_exists: - # If the folder doesn't exist, create it - s3.put_object(Bucket=target_bucket, Key=user_uuid) - user_folder_exists = s3.list_objects_v2(Bucket=target_bucket, Prefix=user_uuid).get('Contents') - print("User folder exists after creation: " + user_folder_exists) - + strDestinationFileName = str(destinationFileName).split('/')[-1] # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) # print("File upload launched") From 1049beb5c0f75f67bcb33a1eeb7b061ea2e9f579 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 15:40:48 -0800 Subject: [PATCH 07/31] Testing in amplify. --- src/backend/lambda_function.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index 7142aed..567a17e 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -63,7 +63,10 @@ def handler(event, context): print ("output file", destinationFile) gltfJson.save(destinationFile) print("Gltf file saved") - destinationFileName = Path(file_name).with_suffix('.gltf') + # Extract user_uuid from the key + user_uuid = object_key.split('/')[1] + "/" + print("UUID: " + user_uuid) + destinationFileName = user_uuid + Path(file_name).with_suffix('.gltf') strDestinationFileName = str(destinationFileName).split('/')[-1] # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) From 31f7dd28a3f73b9058c2cde634029ad309985c11 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 15:52:58 -0800 Subject: [PATCH 08/31] Testing in amplify. --- src/components/Components/DropFile.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index 7543009..9b34251 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -131,7 +131,7 @@ const FileDropArea = observer(() => { FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name Payload: JSON.stringify({ s3: 'opensimviewer-input-bucket101047-dev', - key: 'public/' + user_uuid + file.name + key: 'public/' + file.name }) }; lambda.invoke(params, (err: any, data: any) => { @@ -139,7 +139,7 @@ const FileDropArea = observer(() => { console.error(err); } else { const key = file.name.replace(/\.\w+$/, '.gltf') - const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/" + user_uuid + key + const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/" + key /* appState.setCurrentModelPath(gltf_url); */ navigate("/viewer/"+encodeURIComponent(gltf_url)) console.log('Lambda function invoked successfully:', data); From a152722bd06f695d0b7c859a8fdfeade3baf33f9 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 15:56:46 -0800 Subject: [PATCH 09/31] Testing in amplify. --- src/backend/lambda_function.py | 13 +++++++------ src/components/Components/DropFile.tsx | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index 567a17e..7af5d20 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -48,7 +48,11 @@ def handler(event, context): print("Attempting to download") s3.download_file(source_bucket, object_key, file_name) - print("Object key: " + object_key) + user_uuid = "" + if "user_uuid" in event: + user_uuid = event["user_uuid"] + "/" + + print("user_uuid: " + user_uuid) print("setup conversion function") target_bucket = 'opensim-viewer-public-download' @@ -63,11 +67,8 @@ def handler(event, context): print ("output file", destinationFile) gltfJson.save(destinationFile) print("Gltf file saved") - # Extract user_uuid from the key - user_uuid = object_key.split('/')[1] + "/" - print("UUID: " + user_uuid) - destinationFileName = user_uuid + Path(file_name).with_suffix('.gltf') - strDestinationFileName = str(destinationFileName).split('/')[-1] + destinationFileName = Path(file_name).with_suffix('.gltf') + strDestinationFileName = user_uuid + str(destinationFileName).split('/')[-1] # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) # print("File upload launched") diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index 9b34251..dbdd451 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -131,7 +131,8 @@ const FileDropArea = observer(() => { FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name Payload: JSON.stringify({ s3: 'opensimviewer-input-bucket101047-dev', - key: 'public/' + file.name + key: 'public/' + file.name, + user_uuid: user_uuid }) }; lambda.invoke(params, (err: any, data: any) => { From c950493e46e25ed68d62533fa9ac2e8724680ada Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 16:00:05 -0800 Subject: [PATCH 10/31] Testing in amplify. --- src/backend/lambda_function.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/lambda_function.py b/src/backend/lambda_function.py index 7af5d20..1109f44 100644 --- a/src/backend/lambda_function.py +++ b/src/backend/lambda_function.py @@ -69,6 +69,7 @@ def handler(event, context): print("Gltf file saved") destinationFileName = Path(file_name).with_suffix('.gltf') strDestinationFileName = user_uuid + str(destinationFileName).split('/')[-1] + print("Destination File Name: " + strDestinationFileName) # print("DestinationFile string", strDestinationFileName) s3.upload_file(destinationFile, target_bucket, strDestinationFileName) # print("File upload launched") From 37fb4d5f2c9efd125770757fa16b50b61ceec740 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 29 Feb 2024 16:15:26 -0800 Subject: [PATCH 11/31] Testing in amplify. --- src/components/Components/DropFile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index dbdd451..329c7c3 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -120,7 +120,7 @@ const FileDropArea = observer(() => { let storedData = JSON.parse(storedDataString); storedData["UserAttributes"].forEach((element:any) => { if (element["Name"] === "sub") { - user_uuid = element["Value"] + "/"; + user_uuid = element["Value"]; } }); } else { From 925d2d7d4a6ecc2d604c80cd19c93da44e89000e Mon Sep 17 00:00:00 2001 From: aymanhab Date: Wed, 3 Apr 2024 21:52:35 -0700 Subject: [PATCH 12/31] Cache user_uuid and use it in request sent to back end and to specify output file to download. This change should go along with accompanying update to backend done manually on aws --- src/App.tsx | 4 -- src/backend/README.md | 68 ++++++-------------------- src/backend/readme.md | 68 ++++++-------------------- src/components/Components/DropFile.tsx | 20 ++------ src/components/pages/ModelViewPage.tsx | 2 +- src/state/ViewerState.tsx | 15 ++++++ 6 files changed, 49 insertions(+), 128 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 26b87ae..a1fa1b3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -86,10 +86,6 @@ function App({ signOut, user }: WithAuthenticatorProps) { path="/viewer/:urlParam?" element={} /> - } - /> } diff --git a/src/backend/README.md b/src/backend/README.md index b2e4949..fcc75fc 100644 --- a/src/backend/README.md +++ b/src/backend/README.md @@ -1,59 +1,19 @@ *Requisites:** Conda and python installed. -1. Create environment using the `environment.yml` file: - - `conda env create -f environment.yml` - -2. Activate environment: - - `conda activate opensim-viewer-bend` - -3. Start server: - - `python manage.py runserver` - -### Instructions for database migration - - 1. Create migration files: - - `python manage.py makemigrations` - - 2. Migrate the database (warning: data can be lost) - - `python manage.py migrate` - -### Instructions for recreating ERD diagram - -Instructions in this [Link](https://www.wplogout.com/export-database-diagrams-erd-from-django/). - -### Instructions for localization - -Instructions in this [Link](https://docs.djangoproject.com/en/4.2/topics/i18n/translation/). - -Inside of backend app folder: - -1. Create files for a language: - - `django-admin makemessages -l ` - -2. Compile messages: - - `django-admin compilemessages` - -### Instruction for testing - -- Execute all tests: - - `python manage.py test --verbosity=0` - - -General -------- -- This folder contains scripts to convert OpenSim based data files into gltf format -- We use third party library pygltflib to manipulate the gltf structure thus avoiding low level json file manipulation and encoding/decoding whenever possible. - -Description of specific python files: -------------------------------------- +1. To develop/update backend you have to have aws permissions/access so you can build a docker image and uploaded on aws. Typically the steps to make changes and push them are as follows: +1. Modify/test the python code in osimConverters folder (these are the utilities that take OpenSim native formats and convert to gltf format) +2. Build docker image using the command + "docker build -t opensim-viewer/converter ." +3. Push the image to aws using the push commands on this page: + https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 +4. Wire the lambda function that does the work to use the latest/uploaded image, ECR repository is here + https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 + and you select the image to run (for the lambda function opensim-viewer-func) on this page + https://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2#/functions/opensim-viewer-func?tab=image + select image -> Deploy new image -> select repository opensim-viewer/converter then select from drop down + +Description of specific python files in osimConverters: +------------------------------------------------------ - openSimData2Gltf.py: gneric utilities that take columns of data and time stamps and create the corresponding Accessors in the passed in GLTF2 structure - convert{xxx}2Gltf.py: Utilities for converting files with extension {xxx} to gltf, the convention is to produce a file with the same name but with different extension, unless an output file is specified \ No newline at end of file diff --git a/src/backend/readme.md b/src/backend/readme.md index b2e4949..fcc75fc 100644 --- a/src/backend/readme.md +++ b/src/backend/readme.md @@ -1,59 +1,19 @@ *Requisites:** Conda and python installed. -1. Create environment using the `environment.yml` file: - - `conda env create -f environment.yml` - -2. Activate environment: - - `conda activate opensim-viewer-bend` - -3. Start server: - - `python manage.py runserver` - -### Instructions for database migration - - 1. Create migration files: - - `python manage.py makemigrations` - - 2. Migrate the database (warning: data can be lost) - - `python manage.py migrate` - -### Instructions for recreating ERD diagram - -Instructions in this [Link](https://www.wplogout.com/export-database-diagrams-erd-from-django/). - -### Instructions for localization - -Instructions in this [Link](https://docs.djangoproject.com/en/4.2/topics/i18n/translation/). - -Inside of backend app folder: - -1. Create files for a language: - - `django-admin makemessages -l ` - -2. Compile messages: - - `django-admin compilemessages` - -### Instruction for testing - -- Execute all tests: - - `python manage.py test --verbosity=0` - - -General -------- -- This folder contains scripts to convert OpenSim based data files into gltf format -- We use third party library pygltflib to manipulate the gltf structure thus avoiding low level json file manipulation and encoding/decoding whenever possible. - -Description of specific python files: -------------------------------------- +1. To develop/update backend you have to have aws permissions/access so you can build a docker image and uploaded on aws. Typically the steps to make changes and push them are as follows: +1. Modify/test the python code in osimConverters folder (these are the utilities that take OpenSim native formats and convert to gltf format) +2. Build docker image using the command + "docker build -t opensim-viewer/converter ." +3. Push the image to aws using the push commands on this page: + https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 +4. Wire the lambda function that does the work to use the latest/uploaded image, ECR repository is here + https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 + and you select the image to run (for the lambda function opensim-viewer-func) on this page + https://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2#/functions/opensim-viewer-func?tab=image + select image -> Deploy new image -> select repository opensim-viewer/converter then select from drop down + +Description of specific python files in osimConverters: +------------------------------------------------------ - openSimData2Gltf.py: gneric utilities that take columns of data and time stamps and create the corresponding Accessors in the passed in GLTF2 structure - convert{xxx}2Gltf.py: Utilities for converting files with extension {xxx} to gltf, the convention is to produce a file with the same name but with different extension, unless an output file is specified \ No newline at end of file diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index 329c7c3..3875081 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -112,20 +112,10 @@ const FileDropArea = observer(() => { const api_url = 'https://eudfxg3a9l.execute-api.us-west-2.amazonaws.com/dev/' axios.post(api_url, data).then(response => { const gltf_url = response.data['url']; .replace(/\.\w+$/, '.gltf') - appState.setCurrentModelPath(gltf_url); */ - const storedDataString = localStorage.getItem('CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.albertocasas.userData'); - - let user_uuid = "" - if (storedDataString != null) { - let storedData = JSON.parse(storedDataString); - storedData["UserAttributes"].forEach((element:any) => { - if (element["Name"] === "sub") { - user_uuid = element["Value"]; - } - }); - } else { - console.log("User not authenticated.") - } + appState.setCurrentModelPath(gltf_url); + CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.ayman1234.userData*/ + + let user_uuid = viewerState.user_uuid; const params: AWS.Lambda.InvocationRequest = { FunctionName: 'opensim-viewer-func', // replace with your Lambda function's name @@ -140,7 +130,7 @@ const FileDropArea = observer(() => { console.error(err); } else { const key = file.name.replace(/\.\w+$/, '.gltf') - const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/" + key + const gltf_url = "https://s3.us-west-2.amazonaws.com/opensim-viewer-public-download/" + user_uuid + "/"+key /* appState.setCurrentModelPath(gltf_url); */ navigate("/viewer/"+encodeURIComponent(gltf_url)) console.log('Lambda function invoked successfully:', data); diff --git a/src/components/pages/ModelViewPage.tsx b/src/components/pages/ModelViewPage.tsx index 930467e..576b056 100644 --- a/src/components/pages/ModelViewPage.tsx +++ b/src/components/pages/ModelViewPage.tsx @@ -76,7 +76,7 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { viewerState.setCurrentModelPath(decodedUrl); curState.setCurrentModelPath(viewerState.currentModelPath); // If urlParam is not undefined, this means it is getting the model from S3 and not from local. - viewerState.isLocalUpload = false; + viewerState.setIsLocalUpload(false); } else curState.setCurrentModelPath(viewerState.currentModelPath); diff --git a/src/state/ViewerState.tsx b/src/state/ViewerState.tsx index 41c2533..e51471e 100644 --- a/src/state/ViewerState.tsx +++ b/src/state/ViewerState.tsx @@ -13,6 +13,7 @@ class ViewerState { recordedVideoFormat: string isRecordingVideo: boolean isProcessingVideo: boolean + user_uuid: string constructor( currentModelPathState: string, @@ -40,6 +41,7 @@ class ViewerState { this.recordedVideoFormat = recordedVideoFormat this.isRecordingVideo = isRecordingVideo this.isProcessingVideo = isProcessingVideo + this.user_uuid = '' makeObservable(this, { currentModelPath: observable, featuredModelsFilePath: observable, @@ -78,6 +80,19 @@ class ViewerState { } setIsLoggedIn(newState: boolean) { this.isLoggedIn = newState + if (this.isLoggedIn){ + // Cache user_uuid until logout + const userName = localStorage.getItem('CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.LastAuthUser'); + const storedDataString = localStorage.getItem('CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.'+userName+'.userData'); + if (storedDataString != null) { + let storedData = JSON.parse(storedDataString); + storedData["UserAttributes"].forEach((element:any) => { + if (element["Name"] === "sub") { + this.user_uuid = element["Value"]; + } + }); + } + } } setIsFullScreen(newState: boolean) { this.isFullScreen = newState From c3573be4296924af2c83f98c184d55ab3ba43c9e Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 9 May 2024 11:14:28 -0700 Subject: [PATCH 13/31] CSS defining gui mode loaded dinamically through url param. --- src/App.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index a1fa1b3..a21117f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -53,6 +53,21 @@ function App({ signOut, user }: WithAuthenticatorProps) { alert(t('app.switch_landscape')); } }, [isSmallScreen, isPortrait, t]); + + React.useEffect(() => { + // Parse URL parameters + const urlParams = new URLSearchParams(window.location.search); + const cssParam = urlParams.get('css'); // Assuming 'css' is the parameter name + + // Dynamically import CSS file based on the parameter value + if (cssParam === 'gui') { + require('./gui.css') + console.log('CSS for gui mode loaded'); + } else { + console.log('No specific CSS to load'); + } + }, []); + // On file system we'll have a folder per model containing cached/versioned gltf, possibly .osim file, data files, display // preferences // urls could be something like: From 7b1605b0f93842e122419ddcc2b2b785eb3f12dc Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 9 May 2024 11:41:31 -0700 Subject: [PATCH 14/31] Added require App.css --- src/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.tsx b/src/App.tsx index a21117f..4304c9f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -64,6 +64,7 @@ function App({ signOut, user }: WithAuthenticatorProps) { require('./gui.css') console.log('CSS for gui mode loaded'); } else { + require('./App.css') console.log('No specific CSS to load'); } }, []); From f1f3fa4978193d9beafc046e46e94ca7384eaf5d Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 9 May 2024 11:43:31 -0700 Subject: [PATCH 15/31] Added display to app.css --- src/App.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/App.css b/src/App.css index 43d0262..a2a6828 100644 --- a/src/App.css +++ b/src/App.css @@ -1,3 +1,7 @@ +#opensim-appbar-visibility { + display: inherit; +} + .App { text-align: center; height: 100vh; From 08c187ce4d6331c2c3ee02fb0d993cecc6483097 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 9 May 2024 12:00:31 -0700 Subject: [PATCH 16/31] Removed important, loading only needed css. --- src/App.css | 4 ++++ src/App.tsx | 1 - src/gui.css | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/App.css b/src/App.css index a2a6828..b5de0d9 100644 --- a/src/App.css +++ b/src/App.css @@ -2,6 +2,10 @@ display: inherit; } +#opensim-modelview-sidebar { + display: inherit; +} + .App { text-align: center; height: 100vh; diff --git a/src/App.tsx b/src/App.tsx index 4304c9f..1896421 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,6 @@ import LogoutPage from './components/pages/LogoutPage' import RegisterPage from './components/pages/RegisterPage' import Chart from './components/pages/Chart' import { BrowserRouter, Routes, Route } from 'react-router-dom' -import './App.css' import { observer } from 'mobx-react' import { ThemeProvider, CssBaseline } from '@mui/material' import appTheme from './Theme' diff --git a/src/gui.css b/src/gui.css index 7ac956e..ba980f1 100644 --- a/src/gui.css +++ b/src/gui.css @@ -1,9 +1,9 @@ #opensim-appbar-visibility { - display: none !important; + display: none; } #opensim-modelview-sidebar { - display: none !important; + display: none; } #canvas-element { From a460023633bf3a3e3e8ac14025d748e3488ec7ae Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 9 May 2024 15:41:57 -0700 Subject: [PATCH 17/31] Using typescript instead of css file to switch between app and gui mode. --- src/App.css | 8 ---- src/App.tsx | 12 +++--- src/components/pages/ModelViewPage.tsx | 53 +++++++++++++++++--------- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/App.css b/src/App.css index b5de0d9..43d0262 100644 --- a/src/App.css +++ b/src/App.css @@ -1,11 +1,3 @@ -#opensim-appbar-visibility { - display: inherit; -} - -#opensim-modelview-sidebar { - display: inherit; -} - .App { text-align: center; height: 100vh; diff --git a/src/App.tsx b/src/App.tsx index 1896421..75241cd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,8 +20,9 @@ import '@aws-amplify/ui-react/styles.css'; import { useMediaQuery } from '@mui/material'; import { useMediaQuery as useResponsiveQuery } from 'react-responsive'; import screenfull from 'screenfull'; -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import './App.css' import awsconfig from './aws-exports'; Amplify.configure(awsconfig); @@ -36,6 +37,7 @@ function App({ signOut, user }: WithAuthenticatorProps) { const isPortrait = useDeviceOrientation(); const isSmallScreen = useMediaQuery('(max-width:600px)'); const elementRef = useRef(null); + const [ displayAppBar, setDisplayAppBar ] = useState('inherit'); const toggleFullscreen = () => { if (screenfull.isEnabled) { @@ -60,11 +62,7 @@ function App({ signOut, user }: WithAuthenticatorProps) { // Dynamically import CSS file based on the parameter value if (cssParam === 'gui') { - require('./gui.css') - console.log('CSS for gui mode loaded'); - } else { - require('./App.css') - console.log('No specific CSS to load'); + setDisplayAppBar('none') } }, []); @@ -86,7 +84,7 @@ function App({ signOut, user }: WithAuthenticatorProps) {
-
+
diff --git a/src/components/pages/ModelViewPage.tsx b/src/components/pages/ModelViewPage.tsx index 576b056..ca1240f 100644 --- a/src/components/pages/ModelViewPage.tsx +++ b/src/components/pages/ModelViewPage.tsx @@ -53,12 +53,27 @@ interface ViewerProps { export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { const bottomBarRef = useRef(null); + const videoRecorderRef = useRef(null); + + + // TODO: Move to a general styles file? + const leftMenuWidth = 60; + const drawerContentWidth = 250; + + const [heightBottomBar, setHeightBottomBar] = useState(0); const theme = useTheme(); const curState = useModelContext(); let { urlParam } = useParams(); - const [heightBottomBar, setHeightBottomBar] = useState(0); + const [uiState] = React.useState(curState); + const [menuOpen, setMenuOpen] = React.useState(false); + const [selectedTabName, setSelectedTabName] = React.useState("File"); + + const [ displaySideBar, setDisplaySideBar ] = useState('inherit'); + const [canvasWidth, setCanvasWidth] = useState("calc(100vw - " + (leftMenuWidth + (menuOpen ? drawerContentWidth : 0)) + "px)") + const [canvasHeight, setCanvasHeight] = useState("calc(100vh - 68px - " + heightBottomBar + "px)") + const [canvasLeft, setCanvasLeft] = useState(leftMenuWidth + (menuOpen ? drawerContentWidth : 0)) useEffect(() => { if (bottomBarRef.current) { @@ -70,6 +85,20 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { } }, []); + React.useEffect(() => { + // Parse URL parameters + const urlParams = new URLSearchParams(window.location.search); + const cssParam = urlParams.get('css'); // Assuming 'css' is the parameter name + + // Dynamically import CSS file based on the parameter value + if (cssParam === 'gui') { + setDisplaySideBar('none'); + setCanvasWidth('100% !important'); + setCanvasHeight('calc(100vh - 68px) !important'); + setCanvasLeft(0); + } + }, []); + //console.log(urlParam); if (urlParam!== undefined) { var decodedUrl = decodeURIComponent(urlParam); @@ -80,12 +109,6 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { } else curState.setCurrentModelPath(viewerState.currentModelPath); - const [uiState] = React.useState(curState); - const [menuOpen, setMenuOpen] = React.useState(false); - const [selectedTabName, setSelectedTabName] = React.useState("File"); - - const videoRecorderRef = useRef(null); - function toggleOpenMenu(name: string = "") { // If same name, or empty just toggle. if (name === selectedTabName || name === "") setMenuOpen(!menuOpen); @@ -94,17 +117,12 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { // Always store same name. setSelectedTabName(name); } - - // TODO: Move to a general styles file? - const leftMenuWidth = 60; - const drawerContentWidth = 250; - return (
-
+
Date: Wed, 15 May 2024 14:51:39 -0700 Subject: [PATCH 18/31] GUI mode as state in viewer state. --- src/App.tsx | 5 +++- .../Components/FloatingControlsPanel.css | 1 - .../Components/FloatingControlsPanel.tsx | 3 ++- src/components/pages/ModelViewPage.tsx | 24 +++++++++---------- src/state/ViewerState.tsx | 9 ++++++- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 75241cd..4ceb055 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -60,12 +60,15 @@ function App({ signOut, user }: WithAuthenticatorProps) { const urlParams = new URLSearchParams(window.location.search); const cssParam = urlParams.get('css'); // Assuming 'css' is the parameter name - // Dynamically import CSS file based on the parameter value + // Set gui mode if parameter is present. if (cssParam === 'gui') { + viewerState.setIsGuiMode(true) setDisplayAppBar('none') } }, []); + console.log("GUI: " + viewerState.isGuiMode) + // On file system we'll have a folder per model containing cached/versioned gltf, possibly .osim file, data files, display // preferences // urls could be something like: diff --git a/src/components/Components/FloatingControlsPanel.css b/src/components/Components/FloatingControlsPanel.css index 0588b63..c59e2ae 100644 --- a/src/components/Components/FloatingControlsPanel.css +++ b/src/components/Components/FloatingControlsPanel.css @@ -1,7 +1,6 @@ /* FloatingButton.css */ .floating-buttons-container { position: absolute; - top: 80px; right: 25px; z-index: 999; } diff --git a/src/components/Components/FloatingControlsPanel.tsx b/src/components/Components/FloatingControlsPanel.tsx index 70ebce6..6df288f 100644 --- a/src/components/Components/FloatingControlsPanel.tsx +++ b/src/components/Components/FloatingControlsPanel.tsx @@ -16,6 +16,7 @@ import { ModelInfo } from '../../state/ModelUIState'; interface FloatingControlsPanelProps { videoRecorderRef: any; info: ModelInfo; + top: string; } function FloatingControlsPanel(props :FloatingControlsPanelProps) { @@ -29,7 +30,7 @@ function FloatingControlsPanel(props :FloatingControlsPanelProps) { }; return ( -
+
diff --git a/src/components/pages/ModelViewPage.tsx b/src/components/pages/ModelViewPage.tsx index ca1240f..dbfc4d6 100644 --- a/src/components/pages/ModelViewPage.tsx +++ b/src/components/pages/ModelViewPage.tsx @@ -71,9 +71,10 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { const [selectedTabName, setSelectedTabName] = React.useState("File"); const [ displaySideBar, setDisplaySideBar ] = useState('inherit'); - const [canvasWidth, setCanvasWidth] = useState("calc(100vw - " + (leftMenuWidth + (menuOpen ? drawerContentWidth : 0)) + "px)") - const [canvasHeight, setCanvasHeight] = useState("calc(100vh - 68px - " + heightBottomBar + "px)") - const [canvasLeft, setCanvasLeft] = useState(leftMenuWidth + (menuOpen ? drawerContentWidth : 0)) + const [canvasWidth, setCanvasWidth] = useState("calc(100vw - " + (leftMenuWidth + (menuOpen ? drawerContentWidth : 0)) + "px)"); + const [canvasHeight, setCanvasHeight] = useState("calc(100vh - 68px - " + heightBottomBar + "px)"); + const [canvasLeft, setCanvasLeft] = useState(leftMenuWidth + (menuOpen ? drawerContentWidth : 0)); + const [floatingButtonsContainerTop, setFloatingButtonsContainerTop] = useState("80px"); useEffect(() => { if (bottomBarRef.current) { @@ -86,18 +87,16 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { }, []); React.useEffect(() => { - // Parse URL parameters - const urlParams = new URLSearchParams(window.location.search); - const cssParam = urlParams.get('css'); // Assuming 'css' is the parameter name - - // Dynamically import CSS file based on the parameter value - if (cssParam === 'gui') { + // Change interface if we are in GUI mode. + if (viewerState.isGuiMode) { setDisplaySideBar('none'); - setCanvasWidth('100% !important'); - setCanvasHeight('calc(100vh - 68px) !important'); + setCanvasWidth('100%'); + setCanvasHeight('calc(100vh - 68px)'); setCanvasLeft(0); + setFloatingButtonsContainerTop("12px") } }, []); + console.log("GUI mvp: " + viewerState.isGuiMode) //console.log(urlParam); if (urlParam!== undefined) { @@ -136,7 +135,8 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { + info={new ModelInfo(uiState.modelInfo.model_name, uiState.modelInfo.desc, uiState.modelInfo.authors)} + top={floatingButtonsContainerTop}/> Date: Wed, 15 May 2024 14:53:02 -0700 Subject: [PATCH 19/31] Removed logs prints. --- src/App.tsx | 2 -- src/components/pages/ModelViewPage.tsx | 1 - 2 files changed, 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4ceb055..08e976a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -67,8 +67,6 @@ function App({ signOut, user }: WithAuthenticatorProps) { } }, []); - console.log("GUI: " + viewerState.isGuiMode) - // On file system we'll have a folder per model containing cached/versioned gltf, possibly .osim file, data files, display // preferences // urls could be something like: diff --git a/src/components/pages/ModelViewPage.tsx b/src/components/pages/ModelViewPage.tsx index dbfc4d6..2fab707 100644 --- a/src/components/pages/ModelViewPage.tsx +++ b/src/components/pages/ModelViewPage.tsx @@ -96,7 +96,6 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { setFloatingButtonsContainerTop("12px") } }, []); - console.log("GUI mvp: " + viewerState.isGuiMode) //console.log(urlParam); if (urlParam!== undefined) { From 4a1ffa59a0765e5ff51c04f1dafcc495ee4a6778 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Tue, 4 Jun 2024 11:37:44 -0700 Subject: [PATCH 20/31] Fixed canvas height in regular mode. --- src/components/pages/ModelViewPage.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/pages/ModelViewPage.tsx b/src/components/pages/ModelViewPage.tsx index 2fab707..3b885af 100644 --- a/src/components/pages/ModelViewPage.tsx +++ b/src/components/pages/ModelViewPage.tsx @@ -81,6 +81,8 @@ export function ModelViewPage({url, embedded, noFloor}:ViewerProps) { const heightBottomBar = bottomBarRef.current.offsetHeight; setHeightBottomBar(bottomBarRef.current.offsetHeight); + setCanvasHeight("calc(100vh - 68px - " + heightBottomBar + "px)"); + // Do something with heightBottomBar if needed console.log('Height of BottomBar:', heightBottomBar); } From fc17d7ab3ead1c0b03c5ee057bdb2fab23b3f9ce Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 13 Jun 2024 16:06:55 -0700 Subject: [PATCH 21/31] Added camera selector. mobx bugs fixed. --- src/components/pages/BottomBar.tsx | 50 +++++++++++++++++++++++++++ src/components/pages/OpenSimScene.tsx | 46 +++++++++++++++++++++--- src/state/ModelUIState.tsx | 21 +++++++++-- src/state/ViewerState.tsx | 1 + 4 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/components/pages/BottomBar.tsx b/src/components/pages/BottomBar.tsx index 78ffc73..9b0df4d 100644 --- a/src/components/pages/BottomBar.tsx +++ b/src/components/pages/BottomBar.tsx @@ -10,6 +10,7 @@ import { observer } from 'mobx-react' import { AnimationClip } from 'three'; import { useTranslation } from 'react-i18next'; import { useModelContext } from '../../state/ModelUIStateContext'; +import { Camera } from 'three/src/cameras/Camera' import React, { useCallback, useRef } from 'react'; const NonAnimatedSlider = styled(Slider)(({ theme } : {theme:any}) => ({ @@ -39,6 +40,7 @@ const BottomBar = React.forwardRef(function CustomContent( const [speed, setSpeed] = useState(1.0); const [play, setPlay] = useState(false); const [selectedAnim, setSelectedAnim] = useState(""); + const [selectedCam, setSelectedCam] = useState(""); const isExtraSmallScreen = useMediaQuery((theme:any) => theme.breakpoints.only('xs')); const isSmallScreen = useMediaQuery((theme:any) => theme.breakpoints.only('sm')); @@ -47,6 +49,7 @@ const BottomBar = React.forwardRef(function CustomContent( const minWidthSlider = isExtraSmallScreen ? 150 : isSmallScreen ? 175 : isMediumScreen ? 250 : 300; // Adjust values as needed const maxWidthTime = 45; + const handleAnimationChange = useCallback((animationName: string, animate: boolean) => { const targetName = animationName setSelectedAnim(animationName); @@ -63,11 +66,29 @@ const BottomBar = React.forwardRef(function CustomContent( //setAge(event.target.value as string); }, [curState]); + const handleCameraChange = useCallback((cameraName: string) => { + const targetName = cameraName + setSelectedCam(cameraName); + + const idx = curState.cameras.findIndex((value: Camera, index: number)=>{return (value.name === targetName)}) + if (idx !== -1) { + curState.setCurrentCameraIndex(idx) + } + + curState.setCurrentFrame(0); + //setAge(event.target.value as string); + }, [curState]); + const handleAnimationChangeEvent = (event: SelectChangeEvent) => { const targetName = event.target.value as string handleAnimationChange(targetName, true) }; + const handleCameraChangeEvent = (event: SelectChangeEvent) => { + const targetName = event.target.value as string + handleCameraChange(targetName) + }; + function togglePlayAnimation() { curState.setAnimating(!curState.animating); setPlay(!play); @@ -101,11 +122,19 @@ const BottomBar = React.forwardRef(function CustomContent( } }, [curState.animations, handleAnimationChange]); + useEffect(() => { + if (curState.cameras.length > 0) { + setSelectedCam(curState.cameras[0].name) + handleCameraChange(curState.cameras[0].name) + } + }, [curState.cameras]); + return ( + { curState.animations.length < 1 ? null : ( + {curState.cameras.map(cam => ( + + {cam.name} + + ))} + visibility={false} + + + + )} diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index 08d7162..ced69d9 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -1,11 +1,14 @@ import { useGLTF } from '@react-three/drei' -import { useFrame } from '@react-three/fiber' +import { useFrame, useThree } from '@react-three/fiber' import { useEffect, useState } from 'react' import { AnimationMixer, BoxHelper, Group, Object3D } from 'three' +import { observer } from 'mobx-react' import SceneTreeModel from '../../helpers/SceneTreeModel' import { useModelContext } from '../../state/ModelUIStateContext' +import { Camera } from 'three/src/cameras/Camera' +import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera' interface OpenSimSceneProps { currentModelPath: string, @@ -16,6 +19,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo // useGLTF suspends the component, it literally stops processing const { scene, animations } = useGLTF(currentModelPath); + const { set, camera, gl } = useThree(); const no_face_cull = (scene: Group)=>{ if (scene) { scene.traverse((o)=>{ @@ -23,10 +27,11 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo o.frustumCulled = false; } mapObjectToLayer(o) - + }) } }; + const LayerMap = new Map([ ["Mesh", 1], ["Force", 2], @@ -66,6 +71,40 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo let curState = useModelContext(); curState.scene = scene; + const [currentCamera, setCurrentCamera] = useState() + + + // This useEffect loads the cameras and assign them to its respective states. + useEffect(() => { + const cameras = scene.getObjectsByProperty( 'isPerspectiveCamera', true ) + if (cameras.length > 0) { + // Get the canvas element from the gl + var canvas = gl.domElement; + // Calculate the aspect ratio + var aspectRatio = canvas.clientWidth / canvas.clientHeight; + // Set aspectRatio to cameras + cameras.forEach(function(camera) { + const cameraPers = camera as PerspectiveCamera + cameraPers.aspect = aspectRatio; + cameraPers.updateProjectionMatrix(); + }); + // Update cameras list. + curState.setCamerasList(cameras.map(obj => obj as PerspectiveCamera)) + // Set current camera and current index as 0 + setCurrentCamera(cameras.length > 0 ? cameras[0] as PerspectiveCamera : new PerspectiveCamera()) + curState.setCurrentCameraIndex(0) + set({ camera: cameras[0] as PerspectiveCamera}); + } + }, [curState, scene]); + + // This useEffect sets the current selected camera. + useEffect(() => { + if (curState.cameras.length > 0 && currentCamera) { + setCurrentCamera(curState.cameras[curState.currentCameraIndex] as PerspectiveCamera) + set({ camera: currentCamera as PerspectiveCamera}); + } + }, [currentCamera, set, curState.currentCameraIndex, curState.cameras]); + if (supportControls) { scene.traverse((o) => { sceneObjectMap.set(o.uuid, o) @@ -153,7 +192,6 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo mixers[curState.currentAnimationIndex].clipAction(animations[curState.currentAnimationIndex]).time = currentTime; setStartTime(curState.currentFrame) mixers[curState.currentAnimationIndex].update(delta * curState.animationSpeed) - } } } @@ -196,4 +234,4 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } -export default OpenSimScene +export default observer(OpenSimScene) diff --git a/src/state/ModelUIState.tsx b/src/state/ModelUIState.tsx index f3e5358..6d0d9f4 100644 --- a/src/state/ModelUIState.tsx +++ b/src/state/ModelUIState.tsx @@ -1,6 +1,7 @@ import { makeObservable, observable, action } from 'mobx' import SceneTreeModel from '../helpers/SceneTreeModel' import { AnimationClip } from 'three/src/animation/AnimationClip' +import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera' import { Group } from 'three' export class ModelInfo { @@ -26,6 +27,8 @@ export class ModelUIState { animationSpeed: number animations: AnimationClip[] currentAnimationIndex: number + cameras: PerspectiveCamera[] + currentCameraIndex: number selected: string deSelected: string cameraLayersMask: number @@ -47,6 +50,8 @@ export class ModelUIState { this.animationSpeed = 1.0 this.animations = [] this.currentAnimationIndex = -1 + this.cameras = [] + this.currentCameraIndex = -1 this.selected = "" this.deSelected = "" this.cameraLayersMask = -1 @@ -64,13 +69,17 @@ export class ModelUIState { setAnimationList: observable, setAnimationSpeed: action, animations: observable, + cameras: observable, + setCamerasList: action, selected: observable, setSelected: action, sceneTree: observable, setSceneTree: action, cameraLayersMask: observable, currentFrame: observable, - setCurrentFrame: action + setCurrentFrame: action, + currentCameraIndex: observable, + setCurrentCameraIndex: action }) console.log("Created ModelUIState instance ", currentModelPathState) } @@ -83,8 +92,10 @@ export class ModelUIState { this.cameraLayersMask = -1 this.animating = false this.animationSpeed = 1 - this.animations = [] + this.animations = [] this.currentAnimationIndex = -1 + this.cameras = [] + this.currentCameraIndex = -1 } } setRotating(newState: boolean) { @@ -105,6 +116,9 @@ export class ModelUIState { setCurrentAnimationIndex(newIndex: number) { this.currentAnimationIndex = newIndex } + setCurrentCameraIndex(newIndex: number) { + this.currentCameraIndex = newIndex + } setShowGlobalFrame(newState: boolean) { this.showGlobalFrame = newState } @@ -114,6 +128,9 @@ export class ModelUIState { setAnimationList(animations: AnimationClip[]) { this.animations=animations } + setCamerasList(cameras: PerspectiveCamera[]) { + this.cameras=cameras + } setAnimationSpeed(newSpeed: number) { this.animationSpeed = newSpeed } diff --git a/src/state/ViewerState.tsx b/src/state/ViewerState.tsx index 0d17874..2e2fd73 100644 --- a/src/state/ViewerState.tsx +++ b/src/state/ViewerState.tsx @@ -58,6 +58,7 @@ class ViewerState { setSnapshotFormat: action, setRecordedVideoName: action, setRecordedVideoFormat: action, + setIsLoggedIn: action, snapshotName: observable, snapshotFormat: observable, recordedVideoName: observable, From 463d4ebb36f3bf3a90da7d67dc38ba830eb058de Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 13 Jun 2024 16:09:41 -0700 Subject: [PATCH 22/31] Clean up warnings. --- src/components/pages/BottomBar.tsx | 2 +- src/components/pages/OpenSimScene.tsx | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/pages/BottomBar.tsx b/src/components/pages/BottomBar.tsx index 9b0df4d..269d32c 100644 --- a/src/components/pages/BottomBar.tsx +++ b/src/components/pages/BottomBar.tsx @@ -127,7 +127,7 @@ const BottomBar = React.forwardRef(function CustomContent( setSelectedCam(curState.cameras[0].name) handleCameraChange(curState.cameras[0].name) } - }, [curState.cameras]); + }, [curState.cameras, handleCameraChange]); return ( diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index ced69d9..01325fd 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -7,7 +7,6 @@ import { observer } from 'mobx-react' import SceneTreeModel from '../../helpers/SceneTreeModel' import { useModelContext } from '../../state/ModelUIStateContext' -import { Camera } from 'three/src/cameras/Camera' import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera' interface OpenSimSceneProps { @@ -19,7 +18,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo // useGLTF suspends the component, it literally stops processing const { scene, animations } = useGLTF(currentModelPath); - const { set, camera, gl } = useThree(); + const { set, gl } = useThree(); const no_face_cull = (scene: Group)=>{ if (scene) { scene.traverse((o)=>{ @@ -95,7 +94,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo curState.setCurrentCameraIndex(0) set({ camera: cameras[0] as PerspectiveCamera}); } - }, [curState, scene]); + }, [curState, scene, gl.domElement, set]); // This useEffect sets the current selected camera. useEffect(() => { From 22ff22256e429df91347930e77d76153232fd152 Mon Sep 17 00:00:00 2001 From: aymanhab Date: Wed, 24 Jul 2024 12:07:42 -0700 Subject: [PATCH 23/31] Fix strings in tooltips --- src/internationalization/i18n.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internationalization/i18n.js b/src/internationalization/i18n.js index 3553de2..5f1471b 100644 --- a/src/internationalization/i18n.js +++ b/src/internationalization/i18n.js @@ -57,8 +57,8 @@ i18next zoomOut: "Zoom Out", measure: "Measure", annotate: "Annotate", - snapshoot: "Snapshoot", - record: "record", + snapshoot: "Snapshot", + record: "Record", }, login: { title: "Login", From 7912e465419b82ff709cb2f54c4498358a8b6f56 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 25 Jul 2024 00:47:08 -0700 Subject: [PATCH 24/31] Fixing camera default pos error. Added v padding to dropfile. --- src/components/Components/DropFile.tsx | 8 +++- src/components/pages/HomePage.tsx | 2 +- src/components/pages/OpenSimScene.tsx | 57 +++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index 3875081..b017f43 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -20,7 +20,11 @@ const lambda = new AWS.Lambda({ region: 'us-west-2', // replace with your region }); -const FileDropArea = observer(() => { +interface FileDropAreaProps { + paddingY?: number; +} + +const FileDropArea: React.FC =observer(({ paddingY = 16}) => { const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); @@ -157,7 +161,7 @@ const FileDropArea = observer(() => { sx={{ border: '1px dashed gray', borderRadius: '4px', - padding: '16px', + padding: `${paddingY}px`, textAlign: 'center', cursor: 'pointer', }} diff --git a/src/components/pages/HomePage.tsx b/src/components/pages/HomePage.tsx index 6b92d1e..62168c4 100644 --- a/src/components/pages/HomePage.tsx +++ b/src/components/pages/HomePage.tsx @@ -8,7 +8,7 @@ const HomePage = () => { {t('welcome_title')} - + ) diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index 01325fd..de72e28 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -1,6 +1,8 @@ import { useGLTF } from '@react-three/drei' import { useFrame, useThree } from '@react-three/fiber' +import * as THREE from 'three'; + import { useEffect, useState } from 'react' import { AnimationMixer, BoxHelper, Group, Object3D } from 'three' import { observer } from 'mobx-react' @@ -18,7 +20,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo // useGLTF suspends the component, it literally stops processing const { scene, animations } = useGLTF(currentModelPath); - const { set, gl } = useThree(); + const { set, gl, camera } = useThree(); const no_face_cull = (scene: Group)=>{ if (scene) { scene.traverse((o)=>{ @@ -92,17 +94,57 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo // Set current camera and current index as 0 setCurrentCamera(cameras.length > 0 ? cameras[0] as PerspectiveCamera : new PerspectiveCamera()) curState.setCurrentCameraIndex(0) - set({ camera: cameras[0] as PerspectiveCamera}); } }, [curState, scene, gl.domElement, set]); // This useEffect sets the current selected camera. useEffect(() => { if (curState.cameras.length > 0 && currentCamera) { - setCurrentCamera(curState.cameras[curState.currentCameraIndex] as PerspectiveCamera) - set({ camera: currentCamera as PerspectiveCamera}); + const selectedCamera = curState.cameras[curState.currentCameraIndex] as PerspectiveCamera; + setCurrentCamera(selectedCamera); + set({ camera: selectedCamera }); + + animations.forEach((clip) => { + clip.tracks.forEach((track) => { + if (track.name.includes(selectedCamera.name)) { + console.log(`Camera animation found in clip: ${clip.name}`); + + if (track.name.endsWith('.position')) { + // Extract initial position + const initialPosition = new THREE.Vector3( + track.values[0], + track.values[1], + track.values[2] + ); + selectedCamera.position.copy(initialPosition); + } + + if (track.name.endsWith('.quaternion')) { + // Extract initial rotation (quaternion) + const initialRotation = new THREE.Quaternion( + track.values[0], + track.values[1], + track.values[2], + track.values[3] + ); + selectedCamera.quaternion.copy(initialRotation); + } + + if (track.name.endsWith('.rotation')) { + // Extract initial rotation (Euler) + const initialRotation = new THREE.Euler( + track.values[0], + track.values[1], + track.values[2] + ); + selectedCamera.rotation.copy(initialRotation); + } + } + }); + }); } - }, [currentCamera, set, curState.currentCameraIndex, curState.cameras]); + }, [currentCamera, set, curState.currentCameraIndex, curState.cameras, animations]); + if (supportControls) { scene.traverse((o) => { @@ -139,6 +181,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } useFrame((state, delta) => { + console.log(camera.position) + console.log(camera.rotation) if (!useEffectRunning) { if (curState !== undefined) { if (supportControls ) { @@ -166,6 +210,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } setAnimationIndex(newAnimationIndex) mixers[curState.currentAnimationIndex]?.clipAction(animations[curState.currentAnimationIndex]).play() + camera.updateProjectionMatrix(); } if (supportControls && curState.animating){ if (curState.currentAnimationIndex!==-1) { @@ -181,6 +226,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo console.log(duration) curState.setCurrentFrame(Math.trunc((currentTime / duration) * 100)) setStartTime(Math.trunc((currentTime / duration) * 100)) + camera.updateProjectionMatrix(); } } else if (supportControls) { if (curState.currentAnimationIndex!==-1) { @@ -191,6 +237,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo mixers[curState.currentAnimationIndex].clipAction(animations[curState.currentAnimationIndex]).time = currentTime; setStartTime(curState.currentFrame) mixers[curState.currentAnimationIndex].update(delta * curState.animationSpeed) + camera.updateProjectionMatrix(); } } } From f488b999fd93a571e43baf8f10c3f8b86ee69ef1 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 25 Jul 2024 00:49:46 -0700 Subject: [PATCH 25/31] Removed logs. --- src/components/pages/OpenSimScene.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index de72e28..9092257 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -107,8 +107,6 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo animations.forEach((clip) => { clip.tracks.forEach((track) => { if (track.name.includes(selectedCamera.name)) { - console.log(`Camera animation found in clip: ${clip.name}`); - if (track.name.endsWith('.position')) { // Extract initial position const initialPosition = new THREE.Vector3( @@ -181,8 +179,6 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } useFrame((state, delta) => { - console.log(camera.position) - console.log(camera.rotation) if (!useEffectRunning) { if (curState !== undefined) { if (supportControls ) { From 60393b016400f4e9a596393d82a2552165ff3e6d Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 25 Jul 2024 01:10:35 -0700 Subject: [PATCH 26/31] Removed logs. --- src/components/pages/OpenSimScene.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index 9092257..2b26f18 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -179,6 +179,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } useFrame((state, delta) => { + console.log(camera.position) + console.log(camera.rotation) if (!useEffectRunning) { if (curState !== undefined) { if (supportControls ) { From 693f1db915e99f4b8d8dd4888b40a1d8b0816db4 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 25 Jul 2024 01:53:34 -0700 Subject: [PATCH 27/31] Fixing cameras. --- src/components/pages/OpenSimScene.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index 2b26f18..6daddad 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -95,7 +95,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo setCurrentCamera(cameras.length > 0 ? cameras[0] as PerspectiveCamera : new PerspectiveCamera()) curState.setCurrentCameraIndex(0) } - }, [curState, scene, gl.domElement, set]); + }, [curState, scene, gl.domElement.clientWidth, gl.domElement, set]); // This useEffect sets the current selected camera. useEffect(() => { @@ -114,6 +114,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo track.values[1], track.values[2] ); + console.log("INITIAL") + console.log(initialPosition) selectedCamera.position.copy(initialPosition); } @@ -125,6 +127,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo track.values[2], track.values[3] ); + console.log("INITIAL") + console.log(initialRotation) selectedCamera.quaternion.copy(initialRotation); } @@ -135,6 +139,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo track.values[1], track.values[2] ); + console.log("INITIAL") + console.log(initialRotation) selectedCamera.rotation.copy(initialRotation); } } @@ -166,6 +172,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo curState.setModelInfo(modelData.name, desc, authors) } } + // Make sure mixers match animations if ((animations.length > 0 && mixers.length !==animations.length) || (animations.length > 0 && mixers.length > 0 && mixers[0].getRoot() !== scene)) { @@ -208,7 +215,6 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } setAnimationIndex(newAnimationIndex) mixers[curState.currentAnimationIndex]?.clipAction(animations[curState.currentAnimationIndex]).play() - camera.updateProjectionMatrix(); } if (supportControls && curState.animating){ if (curState.currentAnimationIndex!==-1) { @@ -224,7 +230,6 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo console.log(duration) curState.setCurrentFrame(Math.trunc((currentTime / duration) * 100)) setStartTime(Math.trunc((currentTime / duration) * 100)) - camera.updateProjectionMatrix(); } } else if (supportControls) { if (curState.currentAnimationIndex!==-1) { @@ -235,7 +240,6 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo mixers[curState.currentAnimationIndex].clipAction(animations[curState.currentAnimationIndex]).time = currentTime; setStartTime(curState.currentFrame) mixers[curState.currentAnimationIndex].update(delta * curState.animationSpeed) - camera.updateProjectionMatrix(); } } } From 4a02c7a0a4f768c28486c34cde1def8c9938d2f5 Mon Sep 17 00:00:00 2001 From: aymanhab Date: Mon, 9 Sep 2024 15:46:42 -0700 Subject: [PATCH 28/31] Utilize ColorNodes to pass and interpolate colors on the viewer side --- src/components/pages/OpenSimScene.tsx | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/pages/OpenSimScene.tsx b/src/components/pages/OpenSimScene.tsx index 01325fd..1e33cdd 100644 --- a/src/components/pages/OpenSimScene.tsx +++ b/src/components/pages/OpenSimScene.tsx @@ -2,7 +2,7 @@ import { useGLTF } from '@react-three/drei' import { useFrame, useThree } from '@react-three/fiber' import { useEffect, useState } from 'react' -import { AnimationMixer, BoxHelper, Group, Object3D } from 'three' +import { AnimationMixer, BoxHelper, Color, Group, Mesh, Object3D } from 'three' import { observer } from 'mobx-react' import SceneTreeModel from '../../helpers/SceneTreeModel' @@ -17,7 +17,7 @@ interface OpenSimSceneProps { const OpenSimScene: React.FC = ({ currentModelPath, supportControls }) => { // useGLTF suspends the component, it literally stops processing - const { scene, animations } = useGLTF(currentModelPath); + const { scene, animations} = useGLTF(currentModelPath); const { set, gl } = useThree(); const no_face_cull = (scene: Group)=>{ if (scene) { @@ -60,6 +60,17 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo } } no_face_cull(scene); + + const applyAnimationColors = ()=>{ + colorNodeMap.forEach((node)=>{ + if (node instanceof Mesh){ + //console.log(node.material.color); + //console.log(node); + const newColor = new Color(node.position.x, node.position.y, node.position.z); + node.material.color = newColor + } + }) + } // eslint-disable-next-line no-mixed-operators const [sceneObjectMap] = useState>(new Map()); const [objectSelectionBox, setObjectSelectionBox] = useState(new BoxHelper(scene)); @@ -67,6 +78,7 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo const [animationIndex, setAnimationIndex] = useState(-1) const [startTime, setStartTime] = useState(0) const [mixers, ] = useState([]) + const [colorNodeMap] = useState>(new Map()); let curState = useModelContext(); curState.scene = scene; @@ -106,9 +118,13 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo if (supportControls) { scene.traverse((o) => { - sceneObjectMap.set(o.uuid, o) + sceneObjectMap.set(o.uuid, o); + if (o.name.startsWith("ColorNode")) { + colorNodeMap.set(o.uuid, o); + } } ) + if (objectSelectionBox !== null) { objectSelectionBox.visible = false; scene.add(objectSelectionBox!); @@ -179,6 +195,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo const currentTime = mixers[curState.currentAnimationIndex].clipAction(animations[curState.currentAnimationIndex]).time mixers[curState.currentAnimationIndex].update(delta * curState.animationSpeed) console.log(duration) + // For material at index "key" setColor to nodes["value"].translation + applyAnimationColors(); curState.setCurrentFrame(Math.trunc((currentTime / duration) * 100)) setStartTime(Math.trunc((currentTime / duration) * 100)) } @@ -188,6 +206,8 @@ const OpenSimScene: React.FC = ({ currentModelPath, supportCo let duration = mixers[curState.currentAnimationIndex]?.clipAction(animations[curState.currentAnimationIndex]).getClip().duration; const framePercentage = curState.currentFrame / 100; const currentTime = duration * framePercentage; + // For material at index "key" setColor to nodes["value"].translation + applyAnimationColors(); mixers[curState.currentAnimationIndex].clipAction(animations[curState.currentAnimationIndex]).time = currentTime; setStartTime(curState.currentFrame) mixers[curState.currentAnimationIndex].update(delta * curState.animationSpeed) From 001b2c0182857cc27ba30e5e0b1235a830d6e86f Mon Sep 17 00:00:00 2001 From: Ayman Habib Date: Thu, 12 Sep 2024 11:35:24 -0700 Subject: [PATCH 29/31] Delete src/backend/readme.md Duplicate file Signed-off-by: Ayman Habib --- src/backend/readme.md | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/backend/readme.md diff --git a/src/backend/readme.md b/src/backend/readme.md deleted file mode 100644 index fcc75fc..0000000 --- a/src/backend/readme.md +++ /dev/null @@ -1,19 +0,0 @@ -*Requisites:** Conda and python installed. - -1. To develop/update backend you have to have aws permissions/access so you can build a docker image and uploaded on aws. Typically the steps to make changes and push them are as follows: -1. Modify/test the python code in osimConverters folder (these are the utilities that take OpenSim native formats and convert to gltf format) -2. Build docker image using the command - "docker build -t opensim-viewer/converter ." -3. Push the image to aws using the push commands on this page: - https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 -4. Wire the lambda function that does the work to use the latest/uploaded image, ECR repository is here - https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 - and you select the image to run (for the lambda function opensim-viewer-func) on this page - https://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2#/functions/opensim-viewer-func?tab=image - select image -> Deploy new image -> select repository opensim-viewer/converter then select from drop down - -Description of specific python files in osimConverters: ------------------------------------------------------- -- openSimData2Gltf.py: gneric utilities that take columns of data and time stamps and create the corresponding Accessors in the passed in GLTF2 structure -- convert{xxx}2Gltf.py: Utilities for converting files with extension {xxx} to gltf, the convention is to produce a file with the same name but with different extension, unless an output file is specified - \ No newline at end of file From 3996a82f042fa1d3aa8bb126a1c7a7d024f31df3 Mon Sep 17 00:00:00 2001 From: aymanhab Date: Thu, 12 Sep 2024 11:42:45 -0700 Subject: [PATCH 30/31] Remove potentially user specific info from source --- src/backend/README.md | 19 ------------------- src/components/Components/DropFile.tsx | 6 ------ 2 files changed, 25 deletions(-) delete mode 100644 src/backend/README.md diff --git a/src/backend/README.md b/src/backend/README.md deleted file mode 100644 index fcc75fc..0000000 --- a/src/backend/README.md +++ /dev/null @@ -1,19 +0,0 @@ -*Requisites:** Conda and python installed. - -1. To develop/update backend you have to have aws permissions/access so you can build a docker image and uploaded on aws. Typically the steps to make changes and push them are as follows: -1. Modify/test the python code in osimConverters folder (these are the utilities that take OpenSim native formats and convert to gltf format) -2. Build docker image using the command - "docker build -t opensim-viewer/converter ." -3. Push the image to aws using the push commands on this page: - https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 -4. Wire the lambda function that does the work to use the latest/uploaded image, ECR repository is here - https://us-west-2.console.aws.amazon.com/ecr/repositories/private/660440363484/opensim-viewer/converter?region=us-west-2 - and you select the image to run (for the lambda function opensim-viewer-func) on this page - https://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2#/functions/opensim-viewer-func?tab=image - select image -> Deploy new image -> select repository opensim-viewer/converter then select from drop down - -Description of specific python files in osimConverters: ------------------------------------------------------- -- openSimData2Gltf.py: gneric utilities that take columns of data and time stamps and create the corresponding Accessors in the passed in GLTF2 structure -- convert{xxx}2Gltf.py: Utilities for converting files with extension {xxx} to gltf, the convention is to produce a file with the same name but with different extension, unless an output file is specified - \ No newline at end of file diff --git a/src/components/Components/DropFile.tsx b/src/components/Components/DropFile.tsx index b017f43..9c4300d 100644 --- a/src/components/Components/DropFile.tsx +++ b/src/components/Components/DropFile.tsx @@ -112,12 +112,6 @@ const FileDropArea: React.FC =observer(({ paddingY = 16}) => viewerState.isLocalUpload = true } else { Storage.put(file.name, file).then(()=>{ - /* - const api_url = 'https://eudfxg3a9l.execute-api.us-west-2.amazonaws.com/dev/' - axios.post(api_url, data).then(response => { - const gltf_url = response.data['url']; .replace(/\.\w+$/, '.gltf') - appState.setCurrentModelPath(gltf_url); - CognitoIdentityServiceProvider.6jlm2jeibh9aqb0dg34q2uf8pu.ayman1234.userData*/ let user_uuid = viewerState.user_uuid; From 80821dd5514a0ac10e4ae37fdffa89b4ecaa4fd9 Mon Sep 17 00:00:00 2001 From: Alberto Casas Ortiz Date: Thu, 12 Sep 2024 11:48:41 -0700 Subject: [PATCH 31/31] Renamed README file. --- src/backend/{README_1.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/backend/{README_1.md => README.md} (100%) diff --git a/src/backend/README_1.md b/src/backend/README.md similarity index 100% rename from src/backend/README_1.md rename to src/backend/README.md