90 Commits

Author SHA1 Message Date
JR-Morgan 096e840098 Experimental changes for deserilisation 2022-01-31 00:57:12 +00:00
JR-Morgan 7601863e3c Refactor mostly complete, needs bug testing and blocks reimplementing 2022-01-11 01:27:18 +00:00
JR-Morgan 89b6b2bdfc Fixing converison to work with new deserialisation refactor 2022-01-10 23:32:25 +00:00
JR-Morgan c598aa787b Conversion refactor 2022-01-10 21:04:56 +00:00
JR-Morgan 4a451fec3b Deserialiser logic 2022-01-07 17:46:36 +00:00
JR-Morgan 1ff924eea4 Implementing SpeckleConverterComponent 2022-01-06 16:43:02 +00:00
JR-Morgan 9ce2117a0a Moved all ConverterFactory classes into the same header file 2022-01-03 23:56:46 +00:00
JR-Morgan 2ac94215ee Reverted temporary change to texture coordinate scaling for webinar 2022-01-03 02:31:13 +00:00
JR-Morgan 2dac706ca5 Asset Factory classes for Conversion classes 2022-01-03 02:27:34 +00:00
JR-Morgan af950a569e Refactor of conversion functions to seperate conversion from native actors 2021-12-31 03:56:19 +00:00
JR-Morgan 6e900af1fa Temp changes for webinar 2021-12-08 14:30:16 +00:00
JR-Morgan 66741730e0 ditto 2021-12-07 17:00:04 +00:00
JR-Morgan a595b773ba Added explicit Category specifier for Objects member properties. 2021-12-07 16:56:13 +00:00
Jedd Morgan da1989d676 Merge pull request #40 from specklesystems/material-asset-fix
Fixed issue with materials assets not being properly created.
2021-12-01 20:55:03 +00:00
JR-Morgan a89655f7e0 Fixed issue with materials assets not being properly created. 2021-12-01 19:40:20 +00:00
Jedd Morgan a1c53ff063 Merge pull request #39 from specklesystems/ue5-build-test
UE5 Static Mesh Fix
2021-11-29 21:09:54 +00:00
JR-Morgan edafa74bac Ensured UE4 compatiblity 2021-11-29 20:59:52 +00:00
JR-Morgan 8ca9f46c8a Testing building with UE5 preview 2 2021-11-29 20:19:07 +00:00
Jedd Morgan f3d100bbcb Merge pull request #36 from specklesystems/point-cloud-fix
Point cloud fix
2021-11-23 15:34:57 +00:00
JR-Morgan bbe0a8eead Re-added inverted X for point cloud parsing 2021-11-23 15:32:39 +00:00
JR-Morgan cfead600a9 Merge remote-tracking branch 'origin/point-cloud-fix' 2021-11-23 15:24:55 +00:00
JR-Morgan 5a1c145cf2 Fixed issue with point clouds scaling incorrectly 2021-11-23 15:23:14 +00:00
Jedd Morgan c34f39d44c Merge pull request #35 from specklesystems/asset-PIE-fix
Asset registry on PIE fix
2021-11-23 15:22:53 +00:00
JR-Morgan 9982d55cfb Asset registry only in PIE fix 2021-11-23 13:16:18 +00:00
JR-Morgan 2f2bbd06e6 Scaling poing cloud size. 2021-11-23 13:06:20 +00:00
JR-Morgan bdbe31b429 Tided up class dependency declerationa 2021-11-21 23:54:56 +00:00
JR-Morgan 7a96022ad3 Merge branch 'main' of https://github.com/specklesystems/speckle-unreal into master 2021-11-20 17:12:47 +00:00
JR-Morgan 67b0a961a5 standalone build hotfix 2021-11-20 17:11:11 +00:00
Jedd Morgan 99d7a42c89 Merge pull request #30 from specklesystems/point-clouds2
Re-implemented pointcloud support
2021-11-19 21:31:56 +00:00
JR-Morgan 70b092050d Re-implemented pointcloud support 2021-11-19 21:25:07 +00:00
Jedd Morgan e4994a2612 Merge pull request #24 from specklesystems/lighting-performance2
Improved lighting performace by allowing meshes to import as `StaticMeshComponent`
2021-11-19 17:20:04 +00:00
JR-Morgan 9c87d944ea Merge remote-tracking branch 'origin' into lighting-performance2 2021-11-19 17:12:20 +00:00
Jedd Morgan 801b91b33f Merge pull request #28 from specklesystems/repo-restructure
Restructure repo to be uplugin rather than uproject
2021-11-19 17:00:30 +00:00
JR-Morgan 051fbd589f Fixed issues with non-editor builds 2021-11-18 23:01:39 +00:00
JR-Morgan 20a93d1c10 Implemented StaticMesh importing, slight issue with material reloading 2021-11-18 22:17:49 +00:00
JR-Morgan 877b9b7ecb Now using FEditorScriptExecutionGuard to allow editor calling of interface methods 2021-11-18 14:01:07 +00:00
JR-Morgan 02929d6904 Fixed issue with calling interface functions in editor 2021-11-18 13:58:17 +00:00
JR-Morgan 26706c6cbd Started refactor of project to allow for both Procedural and Static mesh conversion 2021-11-18 01:04:54 +00:00
JR-Morgan 746dadcc71 Restructured repo so uplugin is in root dir 2021-11-17 13:22:35 +00:00
Jedd Morgan fa3eec6f10 Merge pull request #27 from specklesystems/build-fix
Fixed issue with non-editor builds caused by call to SetActorLabel
2021-11-03 14:45:36 +00:00
JR-Morgan ad6cb36915 Fixed issue with non-editor builds caused by call to SetActorLabel 2021-11-03 14:38:11 +00:00
JR-Morgan c5ce7d37a5 Fixed asset package path not being unique for each mesh. 2021-10-31 21:16:51 +00:00
JR-Morgan 611470db41 Experimenting with Asset build options 2021-10-24 18:57:46 +01:00
JR-Morgan ce9980e408 Added local transformations to meshes 2021-10-22 21:05:13 +01:00
JR-Morgan 5f7a6573aa Added option to use full editor build process when creating meshes 2021-10-22 17:24:14 +01:00
JR-Morgan a1843dfb50 ASpeckleUnrealManager will now correctly handle edge case where its World is null when trying to Receive, for example, on scene reload 2021-10-20 15:26:29 +01:00
JR-Morgan 1f2b77c41c Fixed issue with runtime crashes due to incorrectly registered mesh assets 2021-10-20 14:49:40 +01:00
JR-Morgan 39ef6a70cf Experimentation with asset registery 2021-10-18 13:04:18 +01:00
JR-Morgan 5212528156 Merge branch 'master' into lighting-performance2 2021-10-15 17:24:34 +01:00
JR-Morgan 510ddd1c01 Added check for recieving invalid polygons to prevent assertion fail 2021-10-15 17:22:03 +01:00
JR-Morgan 8119083cab Added backwards compatibity for non-chunked mesh lists 2021-10-14 20:50:39 +01:00
Jedd Morgan 307993e648 Merge pull request #23 from specklesystems/analytics
Matomo tracking
2021-10-13 22:04:34 +01:00
JR-Morgan 8230ec2540 Added Matomo Analytics 2021-10-13 21:48:07 +01:00
JR-Morgan 0d38a533cc Added support for converting texture coordinates that are index by vertex index rather than sequentially 2021-10-13 20:21:55 +01:00
JR-Morgan f2fdf86e62 Fixed inverted normals 2021-10-13 13:07:17 +01:00
JR-Morgan 4c62bc3005 Added support for n-gon faces and fixed conversion of texture coordinates. 2021-10-13 12:34:19 +01:00
JR-Morgan 5d9bfaa19f User agent and IDs 2021-10-12 18:11:38 +01:00
JR-Morgan cebdca009c Static mesh building now works however, lighmap UVs are overlapping 2021-10-12 11:21:24 +01:00
JR-Morgan 7549927e67 Experimentation with Static Mesh generation 2021-10-11 00:50:16 +01:00
Jedd Morgan 80293fe975 Merge pull request #21 from specklesystems/texture-coords
Added support for Texture Coordinate conversion and Block Instances
2021-10-08 12:23:44 +01:00
JR-Morgan 2ba59cbfcc Fixed bug with actor initialisation order 2021-10-07 11:42:36 +01:00
JR-Morgan 9ed3d1713a Added WorldToMeter conversion allowing for custom world Units
- Also tidied up commented out code.
2021-10-06 20:30:03 +01:00
JR-Morgan 76444ced3c Tempoarly dissabled object caching due to regression caused by block instances 2021-10-06 14:47:48 +01:00
JR-Morgan 8d07def00f Fixed incorrect Normal conversion caused by left to right handed coordinate systems 2021-10-05 12:21:44 +01:00
JR-Morgan 3efc7a4c49 Block definitions with block instance children will now correctly be converted
- Fixed transform issues with left -> right handed coordinate system
 - Fixed issue with null texture-coordinate arrays not being handled.
2021-10-05 11:57:24 +01:00
JR-Morgan 267fafb5a4 Block Nesting 2021-10-04 13:37:41 +01:00
Matteo Cominetti 8b15e39ff7 Create close-issue.yml 2021-10-02 17:15:58 +01:00
Matteo Cominetti 75f4575813 Create open-issue.yml 2021-10-02 17:15:45 +01:00
JR-Morgan 5e491c622f Adding support for conversion of Block instances 2021-10-01 21:02:24 +01:00
JR-Morgan fdcc06c025 Added Support for UV texture-coordinate conversion 2021-09-30 21:21:59 +01:00
JR-Morgan 7ff7d66c5a Small refactor of object convertion to allow for other types of conversion other than mesh. 2021-09-19 07:36:36 +01:00
JR-Morgan fbdcf6a419 Updated default material opacity from 0 to 0.1 2021-09-16 13:23:08 +01:00
JR-Morgan adc64ac2b5 Updated default materials to include PBR properties 2021-09-16 13:19:30 +01:00
Jedd Morgan dbc36e5553 Merge pull request #16 from specklesystems/materials
Support for PBR Material conversion from Blender, Revit, and Rhino
2021-09-16 12:37:28 +01:00
JR-Morgan d1114770f7 Actor naming and removed unnessary null check for agent parenting. 2021-09-15 16:42:16 +01:00
Jedd Morgan 438a5990b1 Merge pull request #15 from specklesystems/object-chunking
Fix: Chunked object lists will now be correctly handled
2021-09-14 15:55:04 +01:00
JR-Morgan 5a70dda7bf Added material overriding by name, and identical converted materials will be reused. 2021-09-13 12:27:11 +01:00
JR-Morgan 51fcef121e Meshes with no RenderMaterial will take the RenderMaterial of their parent
- Allows support for materials from Revit
2021-09-10 12:44:18 +01:00
JR-Morgan dde7f0d08c Added support for overriding materials by name.
- Materials can be added to `SpeckleManager.MaterialOverrides : TMap` and will take priority over the Speckle `RenderMaterial` material.
2021-09-09 11:32:36 +01:00
JR-Morgan a73414f22e Material properties are now correctly converted.
- Fixed an issue caused by converting ARGB values to FColor
2021-09-08 18:07:17 +01:00
JR-Morgan 7e98e1aa28 Experimentation with material conversion 2021-09-08 16:52:11 +01:00
JR-Morgan c1924dce25 Chunked object lists will now be correctly handled
Added protected ASpeckleUnrealManager::CombineChunks function to combine chunked objects
2021-09-06 17:13:23 +01:00
Cristian Balas c9e978351d Merge pull request #1 from PanMig/RootActor
Spawning speckle meshes under a root actor
2021-05-06 12:59:57 +03:00
Panagiotis Migkotzidis 6a35f502b8 refactoring and BP_SpeckleManager
- BP SpeckleManager added in contents browser

- Speckle Manager code refactoring:

Spawning meshes under root is performed directly under the mesh creation process, rather that a separate method.

- Minor updates in the project solution
2021-05-05 13:49:18 +03:00
Panagiotis Migkotzidis ce1b5a5fe1 Root actor update
Places each imported speckle mesh under a root base actor, for easier transformation actions
2021-04-28 15:09:21 +03:00
Cristian Balas 295d0e67c6 Added a screencast in readme 2021-04-14 16:43:22 +03:00
Cristian Balas 789fba6a12 Added .vs to gitignore 2021-04-14 15:34:31 +03:00
Dimitrie Stefanescu d3c0e0699f Update LICENSE 2021-04-14 13:22:35 +01:00
Dimitrie Stefanescu d9afb5574f Update README.md 2021-04-14 13:21:22 +01:00
Cristian Balas 52a2a153ca Speckle v2 integration 2021-04-14 15:16:04 +03:00
80 changed files with 3137 additions and 1134 deletions
+78
View File
@@ -0,0 +1,78 @@
name: Update issue Status
on:
issues:
types: [closed]
jobs:
update_issue:
runs-on: ubuntu-latest
steps:
- name: Get project data
env:
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
ORGANIZATION: specklesystems
PROJECT_NUMBER: 9
run: |
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
query($org: String!, $number: Int!) {
organization(login: $org){
projectNext(number: $number) {
id
fields(first:20) {
nodes {
id
name
settings
}
}
}
}
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo "$PROJECT_ID"
echo "$STATUS_FIELD_ID"
echo 'DONE_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .settings | fromjson | .options[] | select(.name== "Done") | .id' project_data.json) >> $GITHUB_ENV
echo "$DONE_ID"
- name: Add Issue to project #it's already in the project, but we do this to get its node id!
env:
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
ISSUE_ID: ${{ github.event.issue.node_id }}
run: |
item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
mutation($project:ID!, $id:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $id}) {
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
- name: Update Status
env:
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
ISSUE_ID: ${{ github.event.issue.node_id }}
run: |
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
mutation($project:ID!, $status:ID!, $id:ID!, $value:String!) {
set_status: updateProjectNextItemField(
input: {
projectId: $project
itemId: $id
fieldId: $status
value: $value
}
) {
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f status=$STATUS_FIELD_ID -f id=$ITEM_ID -f value=${{ env.DONE_ID }}
+50
View File
@@ -0,0 +1,50 @@
name: Move new issues into Project
on:
issues:
types: [opened]
jobs:
track_issue:
runs-on: ubuntu-latest
steps:
- name: Get project data
env:
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
ORGANIZATION: specklesystems
PROJECT_NUMBER: 9
run: |
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
query($org: String!, $number: Int!) {
organization(login: $org){
projectNext(number: $number) {
id
fields(first:20) {
nodes {
id
name
settings
}
}
}
}
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
- name: Add Issue to project
env:
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
ISSUE_ID: ${{ github.event.issue.node_id }}
run: |
item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
mutation($project:ID!, $id:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $id}) {
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
+7 -6
View File
@@ -31,9 +31,10 @@
*.out
*.app
*.umap
SpeckleUnrealProject/Binaries/
SpeckleUnrealProject/Intermediate/
SpeckleUnrealProject/Plugins/SpeckleUnreal/Binaries/
SpeckleUnrealProject/Plugins/SpeckleUnreal/Intermediate/
SpeckleUnrealProject/Saved/
SpeckleUnrealProject/.vs/
Binaries/
Intermediate/
Saved/
.idea/
.vs/
-3
View File
@@ -1,3 +0,0 @@
{
"CurrentProjectSetting": "No Configurations"
}
Binary file not shown.
Binary file not shown.
-8
View File
@@ -1,8 +0,0 @@
{
"ExpandedNodes": [
"",
"\\SpeckleUnreal",
"\\SpeckleUnreal\\Source"
],
"PreviewInSolutionExplorer": false
}
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+197 -17
View File
@@ -1,21 +1,201 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2020 mobiusnode
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 AEC Systems
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+8 -31
View File
@@ -1,29 +1,18 @@
# SpeckleUnreal
# Speckle Unreal
[![Version](https://img.shields.io/badge/Version-v0.1.0-orange)](https://github.com/mobiusnode/SpeckleUnreal) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](http://makeapullrequest.com)
[![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white)](https://speckle.community) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](https://speckle.guide/dev/)
Our Team is developing a Speckle plugin and interoperability transport schema for UE4. Our goal is to enable Revit/Dynamo and Rhino/Grasshopper to send + receive geometry to UE4 for visualization. Our current priority is to establish and release a data sender (TO UE4). Were also working on receiver methods, however our initial focus is on Rhino/Grasshopper to UE4 translation and the attachment of UE4-specific metadata to the core JSON blobs in transport.
Plugin for Unreal Engine 4 to import objects from Speckle v2.
In this repository you will find the source code, assets and project settings of the SpeckleUnreal plugin for Unreal Engine app development (Unreal Engine 4.25.1 or newer recommended).
# Useful Links
Use the following links to access resources related to bug reporting, issues, feature requests, and general questions regarding SpeckleUnreal. Future releases may not contain these links & note.
Screencast of an example: https://user-images.githubusercontent.com/2551138/114720093-61403e00-9d40-11eb-8045-6e8ca656554d.mp4
## Speckle Unreal Server
https://speckle.mobiusnode.io
## Discourse Forums (Bugs, Issues, etc.)
https://discourse.mobiusnode.io
## SpeckleUnreal Slack Workspace
https://speckle-works-unreal.slack.com
## YouTub Demo & Tutorial - Getting Started
https://bit.ly/3ehHQE6
## NOTICE
* Tested on Windows and MacOS and Linux.
* Tested on Windows, Unreal Engine v4.26 and Visual Studio Community 2019
* Only displays meshes. Breps are converted using their display values.
* Does not use the Speckle Kit workflow as conversions all happen in C++.
@@ -37,20 +26,8 @@ https://bit.ly/3ehHQE6
We will eventually look to distributing the plugin officially on the Unreal Engine Marketplace but for now you'll need to install the plugin manually like this.
---
## Credits
Based off the original Unreal integration for Speckle v1 by Mark and Jak which can be found here: [https://github.com/mobiusnode/SpeckleUnreal](https://github.com/mobiusnode/SpeckleUnreal).
## Roadmap
https://user-images.githubusercontent.com/2551138/114720051-571e3f80-9d40-11eb-9099-d3394747a1d3.mp4
> Roadmap is subject to change. Last reviewed 10th of July 2020.
| Version | Defining Feature |
| ------- | -------------------------------------------------------------------------------- |
| ~0.1~ | ~First prototype release as Unreal Engine plugin~ |
| 0.2 | New component workflow and custom materials assigned via inspector~ |
| 0.3 | Spawn geometry in transform heirarchy based on layer data |
| 0.4 | User login API, get Stream API and no dependency on a local install of Speckle |
| 0.5 | Rendering Rule API |
| 0.6 | Support Lines, Points, Numbers and Text|
| 0.7 | Local caching of Speckle streams |
| 0.8 | Implement Sender API |
| 1.0 | Production ready (out of preview) |
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,69 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Conversion/ConversionUtils.h"
FMatrix UConversionUtils::TransformToNative(const TArray<float>& TransformData)
{
if(TransformData.Num() != 16) return FMatrix::Identity;
FMatrix TransformMatrix;
for(int32 Row = 0; Row < 4; Row++)
for(int32 Col = 0; Col < 4; Col++)
{
TransformMatrix.M[Row][Col] = TransformData[Row * 4 + Col];
}
TransformMatrix = TransformMatrix.GetTransposed();
return TransformMatrix;
}
float UConversionUtils::GetUnitsScaleFactorF(const FString& Units, const float WorldToCentimeters)
{
static const auto ParseUnits = [](const FString& LUnits) -> float
{
if (LUnits == "millimeters" || LUnits == "millimeter" || LUnits == "millimetres" || LUnits == "millimetre" || LUnits == "mm")
return 0.1;
if (LUnits == "centimeters" || LUnits == "centimeter" ||LUnits == "centimetres" || LUnits == "centimetre" || LUnits == "cm")
return 1;
if (LUnits == "meters" || LUnits == "meter" || LUnits == "metres" || LUnits == "metre" || LUnits == "m")
return 100;
if (LUnits == "kilometers" || LUnits == "kilometres" || LUnits == "km")
return 100000;
if (LUnits == "inches" || LUnits == "inch" || LUnits == "in")
return 2.54;
if (LUnits == "feet" || LUnits == "foot" || LUnits == "ft")
return 30.48;
if (LUnits == "yards" || LUnits == "yard"|| LUnits == "yd")
return 91.44;
if (LUnits == "miles" || LUnits == "mile" || LUnits == "mi")
return 160934.4;
return 100;
};
return ParseUnits(Units.ToLower()) * WorldToCentimeters;
}
float UConversionUtils::GetUnitsScaleFactor(const FString& Units, const UWorld* World)
{
int32 WorldToCentimeters;
ensureAlways(TryGetWorldUnits(World, WorldToCentimeters));
return GetUnitsScaleFactorF(Units, WorldToCentimeters);
}
bool UConversionUtils::TryGetWorldUnits(const UWorld* World, int32& OutWorldToCentimeters)
{
OutWorldToCentimeters = 1;
if(!IsValid(World)) return false;
const AWorldSettings* Settings = World->GetWorldSettings();
if(!IsValid(Settings)) return false;
OutWorldToCentimeters = Settings->WorldToMeters / 10.0;
return true;
}
@@ -0,0 +1,87 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Conversion/Converters/PointCloudConverter.h"
#include "LidarPointCloudActor.h"
#include "LidarPointCloudComponent.h"
#include "SpeckleUnrealManager.h"
#include "Objects/PointCloud.h"
UPointCloudConverter::UPointCloudConverter()
{
SpeckleTypes.Add(UPointCloud::StaticClass());
PointCloudActorType = ALidarPointCloudActor::StaticClass();
}
AActor* UPointCloudConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager)
{
const UPointCloud* P = Cast<UPointCloud>(SpeckleBase);
if(P == nullptr) return nullptr;
return PointCloudToNative(P, Manager);
}
ALidarPointCloudActor* UPointCloudConverter::PointCloudToNative(const UPointCloud* SpecklePointCloud, ASpeckleUnrealManager* Manager)
{
TArray<FLidarPointCloudPoint> LidarPoints;
LidarPoints.Reserve(SpecklePointCloud->Points.Num());
for(int i = 0; i < SpecklePointCloud->Points.Num(); i++)
{
FColor c = SpecklePointCloud->Colors.Num() > i? SpecklePointCloud->Colors[i] : FColor::White;
FLidarPointCloudPoint p = FLidarPointCloudPoint(SpecklePointCloud->Points[i], c, true, 0);
LidarPoints.Add(p);
}
ULidarPointCloud* PointCloud = NewObject<ULidarPointCloud>();
PointCloud->Initialize(FBox(SpecklePointCloud->Points));
PointCloud->InsertPoints(LidarPoints, ELidarPointCloudDuplicateHandling::Ignore, false, FVector::ZeroVector);
PointCloud->CenterPoints();
PointCloud->RefreshBounds();
return CreateActor(Manager, PointCloud);
}
ALidarPointCloudActor* UPointCloudConverter::CreateActor(const ASpeckleUnrealManager* Manager, ULidarPointCloud* PointCloudData)
{
ALidarPointCloudActor* Actor = Manager->GetWorld()->SpawnActor<ALidarPointCloudActor>(PointCloudActorType);
Actor->SetPointCloud(PointCloudData);
return Actor;
}
UBase* UPointCloudConverter::ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager)
{
const ULidarPointCloudComponent* P = Cast<ULidarPointCloudComponent>(Object);
if(P == nullptr)
{
const AActor* A = Cast<AActor>(Object);
if(A != nullptr)
{
P = A->FindComponentByClass<ULidarPointCloudComponent>();
}
}
if(P == nullptr) return nullptr;
return PointCloudToSpeckle(P, Manager);
}
UPointCloud* UPointCloudConverter::PointCloudToSpeckle(const ULidarPointCloudComponent* Object, ASpeckleUnrealManager* Manager)
{
return nullptr; //TODO implement ToSpeckle function
}
@@ -0,0 +1,150 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Conversion/Converters/ProceduralMeshConverter.h"
#include "ProceduralMeshComponent.h"
#include "StaticMeshDescription.h"
#include "SpeckleUnrealManager.h"
#include "Objects/Mesh.h"
#include "Objects/RenderMaterial.h"
UProceduralMeshConverter::UProceduralMeshConverter()
{
SpeckleTypes.Add(UMesh::StaticClass());
MeshActorType = AActor::StaticClass();
bCreateCollisions = true;
}
AActor* UProceduralMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager)
{
const UMesh* P = Cast<UMesh>(SpeckleBase);
if(P == nullptr) return nullptr;
//No existing mesh was found, try and convert SpeckleMesh
UMesh* ScaledMesh = DuplicateObject(P, P->GetOuter(), P->GetFName());
ScaledMesh->ApplyUnits(Manager->GetWorld());
ScaledMesh->AlignVerticesWithTexCoordsByIndex();
return MeshToNative(ScaledMesh, Manager);
}
AActor* UProceduralMeshConverter::MeshToNative(const UMesh* SpeckleMesh, ASpeckleUnrealManager* Manager)
{
AActor* MeshActor = CreateActor(Manager, FTransform(SpeckleMesh->GetTransform()));
UProceduralMeshComponent* MeshComponent = NewObject<UProceduralMeshComponent>(MeshActor, FName("SpeckleMeshComponent"));
MeshComponent->SetupAttachment(MeshActor->GetRootComponent());
MeshComponent->RegisterComponent();
TArray<int32> Faces;
int32 i = 0;
while (i < SpeckleMesh->Faces.Num())
{
int32 n = SpeckleMesh->Faces[i];
if(n < 3) n += 3; // 0 -> 3, 1 -> 4
if (n == 3) //Triangles
{
Faces.Add(SpeckleMesh->Faces[i + 3]);
Faces.Add(SpeckleMesh->Faces[i + 2]);
Faces.Add(SpeckleMesh->Faces[i + 1]);
}
else if(n == 4) // Quads
{
Faces.Add(SpeckleMesh->Faces[i + 4]);
Faces.Add(SpeckleMesh->Faces[i + 3]);
Faces.Add(SpeckleMesh->Faces[i + 1]);
Faces.Add(SpeckleMesh->Faces[i + 3]);
Faces.Add(SpeckleMesh->Faces[i + 2]);
Faces.Add(SpeckleMesh->Faces[i + 1]);
}
else
{
// n-gons shall be ignored
}
i += n + 1;
}
TArray<FColor> VertexColors;
VertexColors.Reserve(SpeckleMesh->Colors.Num());
for(const int32& c : SpeckleMesh->Colors) VertexColors.Add(FColor(c));
const TArray<FVector> Normals;
const TArray<FProcMeshTangent> Tangents;
MeshComponent->CreateMeshSection(
0,
SpeckleMesh->GetVerts(),
Faces,
Normals,
SpeckleMesh->GetTextureCoordinates(),
VertexColors,
Tangents,
bCreateCollisions);
MeshComponent->SetMaterial(0, GetMaterial(SpeckleMesh->RenderMaterial, Manager));
return MeshActor;
}
AActor* UProceduralMeshConverter::CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
{
AActor* Actor = Manager->GetWorld()->SpawnActor<AActor>(MeshActorType, Transform, SpawnParameters);
USceneComponent* Scene = NewObject<USceneComponent>(Actor, "Root");
Actor->SetRootComponent(Scene);
Scene->RegisterComponent();
return Actor;
}
UMaterialInterface* UProceduralMeshConverter::GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager)
{
UMaterialInterface* ExistingMaterial;
if(Manager->TryGetMaterial(SpeckleMaterial, true, ExistingMaterial))
return ExistingMaterial; //Return existing material
UMaterialInterface* MaterialBase = SpeckleMaterial->Opacity >= 1
? Manager->BaseMeshOpaqueMaterial
: Manager->BaseMeshTransparentMaterial;
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(MaterialBase, Manager, FName(SpeckleMaterial->Name));
DynMaterial->SetFlags(RF_Public);
DynMaterial->SetScalarParameterValue("Opacity", SpeckleMaterial->Opacity);
DynMaterial->SetScalarParameterValue("Metallic", SpeckleMaterial->Metalness);
DynMaterial->SetScalarParameterValue("Roughness", SpeckleMaterial->Roughness);
DynMaterial->SetVectorParameterValue("BaseColor", FColor(SpeckleMaterial->Diffuse));
DynMaterial->SetVectorParameterValue("EmissiveColor", FColor(SpeckleMaterial->Emissive));
Manager->ConvertedMaterials.Add(SpeckleMaterial->Id, DynMaterial);
return DynMaterial;
}
UBase* UProceduralMeshConverter::ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager)
{
const UProceduralMeshComponent* M = Cast<UProceduralMeshComponent>(Object);
if(M == nullptr)
{
const AActor* A = Cast<AActor>(Object);
if(A != nullptr)
{
M = A->FindComponentByClass<UProceduralMeshComponent>();
}
}
if(M == nullptr) return nullptr;
return MeshToSpeckle(M, Manager);
}
UMesh* UProceduralMeshConverter::MeshToSpeckle(const UProceduralMeshComponent* Object, ASpeckleUnrealManager* Manager)
{
return nullptr; //TODO implement ToSpeckle function
}
@@ -0,0 +1,316 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Conversion/Converters/StaticMeshConverter.h"
#include "MeshDescriptionBase.h"
#include "StaticMeshDescription.h"
#include "MeshTypes.h"
#include "SpeckleUnrealManager.h"
#include "StaticMeshOperations.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/StaticMeshActor.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Objects/Mesh.h"
#include "Objects/RenderMaterial.h"
UStaticMeshConverter::UStaticMeshConverter()
{
Transient = false;
UseFullBuild = true;
BuildSimpleCollision = true;
MeshActorType = AStaticMeshActor::StaticClass();
SpeckleTypes.Add(UMesh::StaticClass());
}
AActor* UStaticMeshConverter::CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters)
{
AActor* Actor = Manager->GetWorld()->SpawnActor<AActor>(AStaticMeshActor::StaticClass(), Transform, SpawnParameters);
return Actor;
}
AActor* UStaticMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager)
{
const FString PackagePath = FPaths::Combine(TEXT("/Game/Speckle"), Manager->StreamID, TEXT("Geometry"), SpeckleBase->Id);
UPackage* Package = CreatePackage(*PackagePath);
const UMesh* SpeckleMesh = Cast<UMesh>(SpeckleBase);
if(SpeckleMesh == nullptr) return nullptr;
//Find existing mesh
UStaticMesh* Mesh = Cast<UStaticMesh>(Package->FindAssetInPackage());
FMatrix ActorTransform = FMatrix::Identity;
if(!IsValid(Mesh))
{
//No existing mesh was found, try and convert SpeckleMesh
UMesh* ScaledMesh = DuplicateObject(SpeckleMesh, SpeckleMesh->GetOuter(), SpeckleMesh->GetFName());
ScaledMesh->ApplyUnits(Manager->GetWorld());
ScaledMesh->AlignVerticesWithTexCoordsByIndex();
Mesh = MeshToNative(Package, ScaledMesh, Manager);
ActorTransform = ScaledMesh->GetTransform();
}
AActor* Actor = CreateActor(Manager, FTransform(ActorTransform));
TInlineComponentArray<UStaticMeshComponent*> Components;
Actor->GetComponents<UStaticMeshComponent>(Components);
UStaticMeshComponent* MeshComponent;
if(Components.Num() > 0) MeshComponent = Components[0];
else
{
// MeshActorType doesn't have a UStaticMeshComponent, so we will add one
MeshComponent = NewObject<UStaticMeshComponent>(Actor, FName("SpeckleMeshComponent"));
MeshComponent->SetupAttachment(Actor->GetRootComponent());
MeshComponent->RegisterComponent();
}
MeshComponent->SetStaticMesh(Mesh);
MeshComponent->SetMaterial(0, GetMaterial(SpeckleMesh->RenderMaterial, Manager));
return Actor;
}
UStaticMesh* UStaticMeshConverter::MeshToNative(UObject* Outer, const UMesh* SpeckleMesh,
ASpeckleUnrealManager* Manager)
{
const EObjectFlags ObjectFags = Transient? RF_Transient | RF_Public : RF_Public;
UStaticMesh* Mesh = NewObject<UStaticMesh>(Outer, FName(SpeckleMesh->Id), ObjectFags);
Mesh->InitResources();
Mesh->SetLightingGuid();
UStaticMeshDescription* StaticMeshDescription = Mesh->CreateStaticMeshDescription(Outer);
FMeshDescription& BaseMeshDescription = StaticMeshDescription->GetMeshDescription();
//Build Settings
#if WITH_EDITOR
{
FStaticMeshSourceModel& SrcModel = Mesh->AddSourceModel();
SrcModel.BuildSettings.bRecomputeNormals = false;
SrcModel.BuildSettings.bRecomputeTangents = false;
SrcModel.BuildSettings.bRemoveDegenerates = false;
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
SrcModel.BuildSettings.bGenerateLightmapUVs = GenerateLightmapUV;
SrcModel.BuildSettings.SrcLightmapIndex = 0;
SrcModel.BuildSettings.DstLightmapIndex = 1;
}
#endif
UStaticMesh::FBuildMeshDescriptionsParams MeshParams;
MeshParams.bBuildSimpleCollision = BuildSimpleCollision;
MeshParams.bCommitMeshDescription = true;
MeshParams.bMarkPackageDirty = true;
MeshParams.bUseHashAsGuid = false;
//Set Mesh Data
UMaterialInterface* Material = GetMaterial(SpeckleMesh->RenderMaterial, Manager);
const FName MaterialSlotName = Mesh->AddMaterial(Material);;
BaseMeshDescription.PolygonGroupAttributes().RegisterAttribute<FName>(MeshAttribute::PolygonGroup::ImportedMaterialSlotName, 1, MaterialSlotName, EMeshAttributeFlags::None);
{
const size_t NumberOfVertices = SpeckleMesh->GetVertexCount();
StaticMeshDescription->ReserveNewVertices(NumberOfVertices);
TArray<FVertexID> Vertices;
Vertices.Reserve(NumberOfVertices);
for(const FVector VertexPosition : SpeckleMesh->GetVerts())
{
const FVertexID VertID = StaticMeshDescription->CreateVertex();
StaticMeshDescription->SetVertexPosition(VertID, VertexPosition);
Vertices.Add(VertID);
}
//Convert Faces
const FPolygonGroupID PolygonGroupID = StaticMeshDescription->CreatePolygonGroup();
StaticMeshDescription->SetPolygonGroupMaterialSlotName(PolygonGroupID, MaterialSlotName);
StaticMeshDescription->VertexInstanceAttributes().RegisterAttribute<FVector2D>(MeshAttribute::VertexInstance::TextureCoordinate, 2, FVector2D::ZeroVector, EMeshAttributeFlags::None);
StaticMeshDescription->ReserveNewTriangles(SpeckleMesh->Faces.Num() * 3); //Reserve space assuming faces will all be triangles
StaticMeshDescription->ReserveNewPolygons(SpeckleMesh->Faces.Num());
StaticMeshDescription->ReserveNewVertexInstances(SpeckleMesh->Faces.Num() * 3); //Reserve space assuming faces will all be triangles
int32 i = 0;
while (i < SpeckleMesh->Faces.Num())
{
int32 n = SpeckleMesh->Faces[i];
if(n < 3) n += 3; // 0 -> 3, 1 -> 4
TArray<FVertexInstanceID> VertexInstances;
VertexInstances.Reserve(n);
TSet<FVertexID> Verts;
Verts.Reserve(n);
for(int j = 0; j < n; j ++)
{
int32 VertIndex = SpeckleMesh->Faces[i + n - j];
FVertexID Vert = Vertices[VertIndex];
bool AlreadyInSet;
Verts.Add(Vert, &AlreadyInSet);
if(AlreadyInSet)
{
UE_LOG(LogTemp, Warning, TEXT("Invalid Polygon while creating mesh %s - vertex at index %d appears more than once in a face, duplicate vertices will be ignored"), *SpeckleMesh->Id, VertIndex);
continue;
}
FVertexInstanceID VertexInstance = StaticMeshDescription->CreateVertexInstance(Vert);
VertexInstances.Add(VertexInstance);
if(SpeckleMesh->GetTexCoordCount() > VertIndex)
StaticMeshDescription->SetVertexInstanceUV(VertexInstance, SpeckleMesh->GetTextureCoordinate(VertIndex));
//if(SpeckleMesh->VertexColors.Num() > VertIndex)
// //TODO set vertex colors
}
i += n + 1;
if(VertexInstances.Num() < 3)
{
UE_LOG(LogTemp, Warning, TEXT("Invalid Polygon while creating mesh %s - face has fewer than 3 verts, this face will be ignored"), *SpeckleMesh->Id);
continue;
}
TArray<FEdgeID> Edges;
Edges.Reserve(n);
const FPolygonID PolygonID = StaticMeshDescription->CreatePolygon(PolygonGroupID, VertexInstances, Edges);
for (const FEdgeID EdgeID : Edges)
{
StaticMeshDescription->GetEdgeHardnesses()[EdgeID] = true;
}
StaticMeshDescription->ComputePolygonTriangulation(PolygonID);
}
#if ENGINE_MAJOR_VERSION <= 4
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Normal, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Tangent, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Binormal, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
BaseMeshDescription.PolygonAttributes().RegisterAttribute<FVector>(MeshAttribute::Polygon::Center, 1, FVector::ZeroVector, EMeshAttributeFlags::Transient);
FStaticMeshOperations::ComputePolygonTangentsAndNormals(BaseMeshDescription);
#else
FStaticMeshOperations::ComputeTriangleTangentsAndNormals(BaseMeshDescription);
#endif
FStaticMeshOperations::ComputeTangentsAndNormals(BaseMeshDescription, EComputeNTBsFlags::Normals | EComputeNTBsFlags::Tangents);
}
//Mesh->PreEditChange(nullptr);
#if ENGINE_MAJOR_VERSION <= 4
Mesh->LightMapCoordinateIndex = 1;
#else
Mesh->SetLightMapCoordinateIndex(1);
#endif
Mesh->BuildFromMeshDescriptions(TArray<const FMeshDescription*>{&BaseMeshDescription}, MeshParams);
#if WITH_EDITOR
if(UseFullBuild) Mesh->Build(true); //This makes conversion time much slower, but is needed for generating lightmap UVs
if (GIsEditor && !GWorld->HasBegunPlay())
{
Mesh->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(Mesh);
}
#endif
//Mesh->PostEditChange(); //This doesn't seem to be required
return Mesh;
}
UMaterialInterface* UStaticMeshConverter::GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager)
{
if(SpeckleMaterial == nullptr || SpeckleMaterial->Id == "") return Manager->DefaultMeshMaterial; //Material is invalid
UMaterialInterface* ExistingMaterial;
if(Manager->TryGetMaterial(SpeckleMaterial, true, ExistingMaterial))
return ExistingMaterial; //Return existing material
UMaterialInterface* MaterialBase = SpeckleMaterial->Opacity >= 1
? Manager->BaseMeshOpaqueMaterial
: Manager->BaseMeshTransparentMaterial;
const FString PackagePath = FPaths::Combine(TEXT("/Game/Speckle"), Manager->StreamID, TEXT("Materials"), SpeckleMaterial->Id);
UPackage* Package = CreatePackage(*PackagePath);
UMaterialInstance* MaterialInstance;
#if WITH_EDITOR
if (GIsEditor && !GWorld->HasBegunPlay())
{
const FName Name = MakeUniqueObjectName(Package, UMaterialInstanceConstant::StaticClass(), FName(SpeckleMaterial->Name));
//TStrongObjectPtr< UMaterialInstanceConstantFactoryNew > MaterialFact( NewObject< UMaterialInstanceConstantFactoryNew >() );
//MaterialFact->InitialParent = MaterialBase;
//UMaterialInstanceConstant* ConstMaterial = Cast< UMaterialInstanceConstant >( MaterialFact->FactoryCreateNew( UMaterialInstanceConstant::StaticClass(), Package, Name, RF_Public, nullptr, GWarn ) );
UMaterialInstanceConstant* ConstMaterial = NewObject<UMaterialInstanceConstant>(Package, Name, RF_Public);
MaterialInstance = ConstMaterial;
ConstMaterial->SetParentEditorOnly(MaterialBase);
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Opacity"), SpeckleMaterial->Opacity);
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Metallic"), SpeckleMaterial->Metalness);
ConstMaterial->SetScalarParameterValueEditorOnly(FMaterialParameterInfo("Roughness"), SpeckleMaterial->Roughness);
ConstMaterial->SetVectorParameterValueEditorOnly(FMaterialParameterInfo("BaseColor"), FColor(SpeckleMaterial->Diffuse));
ConstMaterial->SetVectorParameterValueEditorOnly(FMaterialParameterInfo("EmissiveColor"), FColor(SpeckleMaterial->Emissive));
//ConstMaterial->InitStaticPermutation();
ConstMaterial->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(MaterialInstance);
}
else
#endif
{
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(MaterialBase, Package, FName(SpeckleMaterial->Name));
MaterialInstance = DynMaterial;
DynMaterial->SetScalarParameterValue("Opacity", SpeckleMaterial->Opacity);
DynMaterial->SetScalarParameterValue("Metallic", SpeckleMaterial->Metalness);
DynMaterial->SetScalarParameterValue("Roughness", SpeckleMaterial->Roughness);
DynMaterial->SetVectorParameterValue("BaseColor", FColor(SpeckleMaterial->Diffuse));
DynMaterial->SetVectorParameterValue("EmissiveColor", FColor(SpeckleMaterial->Emissive));
DynMaterial->SetFlags(RF_Public);
}
Manager->ConvertedMaterials.Add(SpeckleMaterial->Id, MaterialInstance);
return MaterialInstance;
}
UBase* UStaticMeshConverter::ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager)
{
const UStaticMeshComponent* M = Cast<UStaticMeshComponent>(Object);
if(M == nullptr)
{
const AActor* A = Cast<AActor>(Object);
if(A != nullptr)
{
M = A->FindComponentByClass<UStaticMeshComponent>();
}
}
if(M == nullptr) return nullptr;
return MeshToSpeckle(M, Manager);
}
UMesh* UStaticMeshConverter::MeshToSpeckle(const UStaticMeshComponent* Object, ASpeckleUnrealManager* Manager)
{
return nullptr; //TODO implement ToSpeckle function
}
@@ -0,0 +1,118 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Conversion/SpeckleConverterComponent.h"
#include "SpeckleUnrealManager.h"
#include "Conversion/Converters/PointCloudConverter.h"
#include "Conversion/Converters/StaticMeshConverter.h"
// Sets default values for this component's properties
USpeckleConverterComponent::USpeckleConverterComponent()
{
PrimaryComponentTick.bCanEverTick = false;
//TODO consider using an object library for default converters
static ConstructorHelpers::FObjectFinder<UStaticMeshConverter> MeshConverter(TEXT("StaticMeshConverter'/SpeckleUnreal/Converters/DefaultStaticMeshConverter.DefaultStaticMeshConverter'"));
static ConstructorHelpers::FObjectFinder<UPointCloudConverter> PointCloudConverter(TEXT("PointCloudConverter'/SpeckleUnreal/Converters/DefaultPointCloudConverter.DefaultPointCloudConverter'"));
//Mesh
//Point
//Light
//BlockDef
//BlockInst
//Wall
//Element
SpeckleConverters.Add(MeshConverter.Object);
SpeckleConverters.Add(PointCloudConverter.Object);
//SpeckleConverter.Add(FLightConverter);
}
void USpeckleConverterComponent::OnConvertersChangeHandler()
{
SpeckleTypeMap.Empty();
for(int i = 0; i < SpeckleConverters.Num(); i++)
{
const UObject* Converter = SpeckleConverters[i];
if(Converter != nullptr && !Converter->GetClass()->ImplementsInterface(USpeckleTypeConverter::StaticClass()))
{
UE_LOG(LogTemp, Warning, TEXT("Converter {%s} is not a valid converter, Expected to implement interface %s"), *Converter->GetClass()->GetName(), *USpeckleTypeConverter::StaticClass()->GetName())
SpeckleConverters.RemoveAt(i);
i--;
}
}
}
#if WITH_EDITOR
void USpeckleConverterComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(USpeckleConverterComponent, SpeckleConverters))
{
OnConvertersChangeHandler();
}
}
#endif
UBase* USpeckleConverterComponent::ConvertToSpeckle(UObject* Object)
{
//TODO implement ToSpeckle
return nullptr;
}
AActor* USpeckleConverterComponent::ConvertToNative(const UBase* Object, ASpeckleUnrealManager* Manager)
{
check(Object != nullptr);
const TSubclassOf<UBase> Type = Object->GetClass();
UObject* Converter = GetConverter(Type).GetObject();
if(Converter == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("Skipping Object %s - No conversion functions exist for %s"), *Object->Id, *Type->GetName());
return nullptr;
}
FEditorScriptExecutionGuard ScriptGuard;
AActor* ReturnObject = ISpeckleTypeConverter::Execute_ConvertToNative(Converter, Object, Manager);
UE_LOG(LogTemp, Log, TEXT("Converted object of type: %s id: %s "), *Object->Id, *Type->GetName());
return ReturnObject;
}
TScriptInterface<ISpeckleTypeConverter> USpeckleConverterComponent::GetConverter(const TSubclassOf<UBase> BaseType)
{
// Check if this SpeckleType has a known converter.
if(SpeckleTypeMap.Contains(BaseType))
{
return SpeckleTypeMap[BaseType];
}
// Try and find one that can convert this SpeckleType.
FEditorScriptExecutionGuard ScriptGuard;
for(UObject* Converter : SpeckleConverters)
{
if(Converter == nullptr) continue;
if(!Converter->GetClass()->ImplementsInterface(USpeckleTypeConverter::StaticClass()))
{
UE_LOG(LogTemp, Warning, TEXT("Converter {%s} is not a valid converter, Expected to implement interface {%s}"), *Converter->GetClass()->GetName(), *USpeckleTypeConverter::StaticClass()->GetName())
continue;
}
if(ISpeckleTypeConverter::Execute_CanConvertToNative(Converter, BaseType))
{
//Found a Converter! Save this mapping for next time.
SpeckleTypeMap.Add(BaseType, Converter);
return Converter;
}
}
// SpeckleType has no conversions.
SpeckleTypeMap.Add(BaseType, nullptr);
return nullptr;
}
@@ -0,0 +1,49 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Objects/BuiltElement.h"
#include "SpeckleUnrealManager.h"
TArray<FString> UBuiltElement::DisplayValueAliasStrings = {
"displayValue",
"@displayValue",
"displayMesh"
"@displayMesh"
};
void UBuiltElement::AddDisplayValue(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
{
UBase* v = Manager->DeserializeBase(Obj);
if(v != nullptr) this->DisplayValue.Add(v);
}
void UBuiltElement::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
{
Super::Parse(Obj, Manager);
//Find display values
for(const FString& Alias : DisplayValueAliasStrings)
{
const TSharedPtr<FJsonObject>* SubObjectPtr;
if (Obj->TryGetObjectField(Alias, SubObjectPtr))
{
AddDisplayValue(*SubObjectPtr, Manager);
}
const TArray<TSharedPtr<FJsonValue>>* SubArrayPtr;
if (Obj->TryGetArrayField(Alias, SubArrayPtr))
{
for (const auto& ArrayElement : *SubArrayPtr)
{
const TSharedPtr<FJsonObject>* ArraySubObjPtr;
if (ArrayElement->TryGetObject(ArraySubObjPtr))
{
AddDisplayValue(*SubObjectPtr, Manager);
}
}
}
}
}
@@ -0,0 +1,184 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Objects/Mesh.h"
#include "SpeckleUnrealManager.h"
#include "Conversion/ConversionUtils.h"
FMatrix UMesh::GetTransform() const
{
if(Transform.Num() != 16) return FMatrix::Identity;
FMatrix TransformMatrix;
for(int32 Row = 0; Row < 4; Row++)
for(int32 Col = 0; Col < 4; Col++)
{
TransformMatrix.M[Row][Col] = Transform[Row * 4 + Col];
}
TransformMatrix = TransformMatrix.GetTransposed();
return TransformMatrix;
}
void UMesh::SetTransform(const FMatrix& T)
{
const FMatrix TransformMatrix = T.GetTransposed();
for(int32 Row = 0; Row < 4; Row++)
for(int32 Col = 0; Col < 4; Col++)
{
Transform[Row * 4 + Col] = TransformMatrix.M[Row][Col];
}
}
FVector UMesh::GetVert(int32 Index) const
{
Index *= 3;
return FVector(
Vertices[Index],
Vertices[Index + 1],
Vertices[Index + 2]
);
}
TArray<FVector> UMesh::GetVerts() const
{
check(Vertices.Num() % 3 == 0);
//TODO - Maybe could just use a blit copy assuming 3 floats -> FVector
TArray<FVector> VertexVectors;
const int32 NumberOfVertices = Vertices.Num() / 3;
VertexVectors.Reserve(NumberOfVertices);
for (size_t i = 0, j = 0; i < NumberOfVertices; i++, j += 3)
{
VertexVectors.Add(FVector
(
Vertices[j],
Vertices[j + 1],
Vertices[j + 2]
));
}
return VertexVectors;
}
FVector2D UMesh::GetTextureCoordinate(int32 Index) const
{
Index *= 2;
return FVector2D(TextureCoordinates[Index], TextureCoordinates[Index + 1]);
}
TArray<FVector2D> UMesh::GetTextureCoordinates() const
{
//TODO - Maybe could just use a blit copy assuming 2 floats -> FVector2D
TArray<FVector2D> TexCoords;
TexCoords.Reserve(TextureCoordinates.Num() / 2);
for (int32 i = 0; i + 1 < TexCoords.Num(); i += 2)
{
TexCoords.Add(FVector2D
(
TextureCoordinates[i],
TextureCoordinates[i + 1]
));
}
return TexCoords;
}
FColor UMesh::GetVertexColor(int32 Index) const
{
return FColor(Colors[Index]);
}
TArray<FColor> UMesh::GetVertexColors() const
{
TArray<FColor> VertexColors;
VertexColors.Reserve(Colors.Num());
for (int32 i = 0; i + 1 < Colors.Num(); i ++)
{
VertexColors.Add(FColor(Colors[i]));
}
return VertexColors;
}
/**
* If not already so, this method will align vertices
* such that a vertex and its corresponding texture coordinates have the same index.
* See "https://github.com/specklesystems/speckle-sharp/blob/main/Objects/Objects/Geometry/Mesh.cs"
*/
void UMesh::AlignVerticesWithTexCoordsByIndex()
{
if(TextureCoordinates.Num() == 0) return;
if(TextureCoordinates.Num() == Vertices.Num()) return; //Tex-coords already aligned as expected
TArray<int32> FacesUnique;
FacesUnique.Reserve(Faces.Num());
TArray<float> VerticesUnique;
VerticesUnique.Reserve(TextureCoordinates.Num() * 3);
const bool HasColor = Colors.Num() > 0;
TArray<int32> ColorsUnique;
if(HasColor) ColorsUnique.Reserve(TextureCoordinates.Num());
int32 NIndex = 0;
while(NIndex < Faces.Num())
{
int32 n = Faces[NIndex];
if (n < 3) n += 3; // 0 -> 3, 1 -> 4
if (NIndex + n >= Faces.Num()) break; //Malformed face list
FacesUnique.Add(n);
for (int32 i = 1; i <= n; i++)
{
const int32 VertIndex = Faces[NIndex + i];
const int32 NewVertIndex = VerticesUnique.Num();
VerticesUnique.Add(Vertices[VertIndex]);
if(HasColor) ColorsUnique.Add(Colors[NewVertIndex]);
FacesUnique.Add(NewVertIndex);
}
NIndex += n + 1;
}
Vertices = VerticesUnique;
Colors = ColorsUnique;
Faces = FacesUnique;
}
void UMesh::ApplyScaleFactor(const float ScaleFactor)
{
for (size_t i = 0; i < Vertices.Num(); i++)
{
Vertices[i] *= ScaleFactor;
}
FMatrix Transform = GetTransform();
Transform.ScaleTranslation(FVector(ScaleFactor));
}
void UMesh::ApplyUnits(const UWorld* World)
{
const UWorld* CheckedWorld = IsValid(World)? World : GetWorld();
const float ScaleFactor = UConversionUtils::GetUnitsScaleFactor(Units, CheckedWorld);
ApplyScaleFactor(ScaleFactor);
Units = "cm";
}
@@ -0,0 +1,54 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Objects/PointCloud.h"
#include "SpeckleUnrealManager.h"
void UPointCloud::Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager)
{
Super::Parse(Obj, Manager);
const float ScaleFactor = Manager->ParseScaleFactor(Units);
//Parse Points
{
TArray<TSharedPtr<FJsonValue>> ObjectPoints = Manager->CombineChunks(Obj->GetArrayField("points"));
Points.Reserve(ObjectPoints.Num() / 3);
for (int32 i = 2; i < ObjectPoints.Num(); i += 3)
{
Points.Add(FVector
(
-ObjectPoints[i - 2].Get()->AsNumber(),
ObjectPoints[i - 1].Get()->AsNumber(),
ObjectPoints[i].Get()->AsNumber()
) * ScaleFactor);
}
}
//Parse Colors
{
TArray<TSharedPtr<FJsonValue>> ObjectColors = Manager->CombineChunks(Obj->GetArrayField("colors"));
Colors.Reserve(ObjectColors.Num());
for (int32 i = 0; i < ObjectColors.Num(); i += 1)
{
Colors.Add( FColor(ObjectColors[i].Get()->AsNumber()) );
}
}
//Parse Sizes
{
TArray<TSharedPtr<FJsonValue>> ObjectSizes = Manager->CombineChunks(Obj->GetArrayField("sizes"));
Sizes.Reserve(ObjectSizes.Num());
for (int32 i = 0; i < ObjectSizes.Num(); i += 1)
{
Sizes.Add( ObjectSizes[i].Get()->AsNumber() * ScaleFactor);
}
}
}
@@ -0,0 +1,74 @@
#include "Objects/RegisteringBase.h"
#include "Objects/Base.h"
//TOptional<TMap<FString, TSubclassOf<UBase>>> URegisteringBase::TypeRegistry;
TMap<FString, TSubclassOf<UBase>> URegisteringBase::TypeRegistry;
void URegisteringBase::GenerateTypeRegistry()
{
//TypeRegistry.Reset();
TypeRegistry.Empty();
//TypeRegistry = TMap<FString, TSubclassOf<UBase>>();
//check(TypeRegistry.IsSet());
//Find every class : UBase and add to Registry
for (TObjectIterator<UClass> It; It; ++It)
{
const UClass* Class = *It;
if (Class->IsChildOf(UBase::StaticClass()) &&
!Class->HasAnyClassFlags(CLASS_Abstract))
{
const FString& SpeckleType = Class->GetDefaultObject<UBase>()->SpeckleType;;
ensureAlwaysMsgf(!TypeRegistry.Contains(SpeckleType),
TEXT("Base class: %s conflicts with: %s for SpeckleType: %s"),
*Class->GetName(),
*TypeRegistry[SpeckleType]->GetName(),
*SpeckleType);
TypeRegistry.Add(SpeckleType, *It);
}
}
}
TSubclassOf<UBase> URegisteringBase::FindClosestType(const FString& SpeckleType)
{
FString TypeString(SpeckleType);
TSubclassOf<UBase> Type = nullptr;
while(!TryGetRegisteredType(TypeString, Type))
{
int32 SplitIndex;
if(TypeString.FindLastChar('.', SplitIndex))
{
TypeString = TypeString.Left(SplitIndex);
}
else return nullptr;
}
return Type;
}
TSubclassOf<UBase> URegisteringBase::GetRegisteredType(const FString& SpeckleType)
{
TSubclassOf<UBase> Type = nullptr;
TryGetRegisteredType(SpeckleType, Type);
return Type;
}
bool URegisteringBase::TryGetRegisteredType(const FString& SpeckleType, TSubclassOf<UBase>& OutType)
{
if(TypeRegistry.Num() == 0) GenerateTypeRegistry();
const bool Contains = TypeRegistry.Contains(SpeckleType);
if(Contains)
{
OutType = *TypeRegistry.Find(SpeckleType);
}
return Contains;
}
@@ -0,0 +1,16 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "SpeckleUnrealActor.h"
// Sets default values
ASpeckleUnrealActor::ASpeckleUnrealActor()
{
PrimaryActorTick.bCanEverTick = false;
Scene = CreateDefaultSubobject<USceneComponent>("Root");
RootComponent = Scene;
Scene->SetMobility(EComponentMobility::Static);
}
@@ -3,16 +3,13 @@
#include "SpeckleUnrealLayer.h"
ASpeckleUnrealLayer::ASpeckleUnrealLayer()
USpeckleUnrealLayer::USpeckleUnrealLayer()
{
Scene = CreateDefaultSubobject<USceneComponent>("Root");
RootComponent = Scene;
}
void ASpeckleUnrealLayer::Init(FString NewLayerName, int32 NewStartIndex, int32 NewObjectCount)
void USpeckleUnrealLayer::Init(FString NewLayerName, int32 NewStartIndex, int32 NewObjectCount)
{
SetActorLabel(NewLayerName);
LayerName = NewLayerName;
LayerColor = FLinearColor(FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), 1);
StartIndex = NewStartIndex;
@@ -0,0 +1,426 @@
#include "JsonObjectConverter.h"
#include "SpeckleUnrealActor.h"
#include "SpeckleUnrealManager.h"
#include "Objects/Mesh.h"
#include "Objects/RenderMaterial.h"
void ASpeckleUnrealManager::ImportObjectFromCache(AActor* AOwner, const TSharedPtr<FJsonObject> SpeckleObject)
{
{ // Handle Detached Objects
TSharedPtr<FJsonObject> DetachedObject;
if(ResolveReference(SpeckleObject, DetachedObject))
{
return ImportObjectFromCache(AOwner, DetachedObject);
}
}
const UBase* Base = DeserializeBase(SpeckleObject);
if(Base == nullptr)
return;
AActor* Native = Converter->ConvertToNative(Base, this);
if(IsValid(Native))
{
#if WITH_EDITOR
Native->SetActorLabel(FString::Printf(TEXT("%s - %s"), *Base->SpeckleType, *Base->Id));
#endif
Native->AttachToActor(AOwner, FAttachmentTransformRules::KeepRelativeTransform);
Native->SetOwner(AOwner);
InProgressObjectsCache.Add(Native);
}
else
{
Native = AOwner;
}
//Convert Children
for (const auto& Kv : SpeckleObject->Values)
{
const TSharedPtr<FJsonObject>* SubObjectPtr;
if (Kv.Value->TryGetObject(SubObjectPtr))
{
ImportObjectFromCache(Native, *SubObjectPtr);
continue;
}
const TArray<TSharedPtr<FJsonValue>>* SubArrayPtr;
if (Kv.Value->TryGetArray(SubArrayPtr))
{
for (const auto ArrayElement : *SubArrayPtr)
{
const TSharedPtr<FJsonObject>* ArraySubObjPtr;
if (!ArrayElement->TryGetObject(ArraySubObjPtr))
continue;
ImportObjectFromCache(Native, *ArraySubObjPtr);
}
}
}
}
bool ASpeckleUnrealManager::TryGetMaterial(const URenderMaterial* SpeckleMaterial, const bool AcceptMaterialOverride, UMaterialInterface*& OutMaterial)
{
const auto MaterialID = SpeckleMaterial->Id;
if(AcceptMaterialOverride)
{
//Override by id
if(MaterialOverridesById.Contains(MaterialID))
{
OutMaterial = MaterialOverridesById[MaterialID];
return true;
}
//Override by name
const FString Name = SpeckleMaterial->Name;
for (const UMaterialInterface* Mat : MaterialOverridesByName)
{
if(Mat->GetName() == Name)
{
OutMaterial = MaterialOverridesById[MaterialID];
return true;
}
}
}
if(ConvertedMaterials.Contains(MaterialID))
{
OutMaterial = ConvertedMaterials[MaterialID];
return true;
}
return false;
}
UBase* ASpeckleUnrealManager::DeserializeBase(const TSharedPtr<FJsonObject> Obj) const
{
{ // Handle Detached Objects
TSharedPtr<FJsonObject> DetachedObject;
if(ResolveReference(Obj, DetachedObject))
{
return DeserializeBase(DetachedObject);
}
}
FString SpeckleType;
if (!Obj->TryGetStringField("speckle_type", SpeckleType)) return nullptr;
FString ObjectId = "";
Obj->TryGetStringField("id", ObjectId);
// Get the registered type from Base register
const TSubclassOf<UBase> ObjectType = UBase::FindClosestType(SpeckleType);
if(ObjectType == nullptr)
{
UE_LOG(LogTemp, Verbose, TEXT("SpeckleType: %s is unknown,%t object: %s will be ignored"), *SpeckleType, *ObjectId );
return nullptr; //BaseType = UBase::StaticClass();
}
//TODO before we create and deserialised a new object, first check if we have already deserialised this object
//Create instance of the ObjectType
UBase* Base = NewObject<UBase>(GetTransientPackage(), ObjectType);
//Map of all properties with no explicit UProperty to set.
//For now we add all values to this map, then remove the ones we find explicit UProperties for.
auto DynamicProperties = TMap<FString, TSharedPtr<FJsonValue>>(Obj->Values);
//Loop through each UProperty in the UBase and try and set its value from the JSON obj
for (TFieldIterator<UProperty> It(ObjectType); It; ++It)
{
FProperty* Property = *It;
void* PropertyValueAddress = Property->ContainerPtrToValuePtr<uint8>(Base);
// Find a json value matching this property name
const FString Key = Property->GetName();
const TSharedPtr<FJsonValue>* JsonValuePtr = Obj->Values.Find(Key);
//Ensure value is valid
if (!JsonValuePtr) continue;
TSharedPtr<FJsonValue> JsonValue = *JsonValuePtr;
if ( (!JsonValue.IsValid()) || JsonValue->IsNull() ) continue;
//Handle chunked values!!!!!
if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
const TSharedPtr<FJsonObject>* ChunkedObject;
FString ChunkedType;
if(JsonValue->TryGetObject(ChunkedObject)
&& ChunkedObject->operator->()->TryGetStringField("", ChunkedType)
&& ChunkedType == "Speckle.Core.Models.DataChunk")
{
const TArray<TSharedPtr<FJsonValue>>* ChunkedArray;
if(JsonValue->TryGetArray(ChunkedArray))
{
auto Arr = CombineChunks(*ChunkedArray);
if()
{
}
}
}
}
//Check if property is a Speckle Object
if (const FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))
{
UBase* SpeckleObject;
if(TryParseSpeckleObjectFromJsonProperty(JsonValue, SpeckleObject))
{
ObjectProperty->SetObjectPropertyValue(PropertyValueAddress, SpeckleObject);
DynamicProperties.Remove(Key);
continue;
}
}
//Handle primitive types
if(FJsonObjectConverter::JsonValueToUProperty(JsonValue, Property, PropertyValueAddress, 0, 0))
{
DynamicProperties.Remove(Key);
continue;
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Failed to deserialise a value, object id: %s, property key: %s"), *ObjectId, *Key)
}
}
//Find any remaining Speckle objects and add them as children
TMap<FString, UBase*> ChildBases;
{
const auto DynamicPropertiesCopy = TMap<FString, TSharedPtr<FJsonValue>>(DynamicProperties);
for(const auto& Kvp : DynamicPropertiesCopy)
{
UBase* ChildObject;
if(TryParseSpeckleObjectFromJsonProperty(Kvp.Value, ChildObject))
{
DynamicProperties.Remove(Kvp.Key);
ChildBases.Add(ChildObject->Id, ChildObject);
}
}
}
Base->DynamicProperties = DynamicProperties;
Base->Children = ChildBases;
return Base;
}
bool ASpeckleUnrealManager::TryParseSpeckleObjectFromJsonProperty(const TSharedPtr<FJsonValue> JsonValue, UBase*& OutBase) const
{
const TSharedPtr<FJsonObject>* JsonObjectPtr;
if(JsonValue->TryGetObject(JsonObjectPtr))
{
TSharedPtr<FJsonObject> JsonObject;
if(!ResolveReference(*JsonObjectPtr, JsonObject))
JsonObject = *JsonObjectPtr;
if(JsonObject.IsValid())
{
//Handle Speckle object types
OutBase = DeserializeBase(JsonObject);
return IsValid(OutBase);
}
}
return false;
}
void Test(const TSharedPtr<FJsonValue> JsonValue, const FProperty* Property, UObject* Base)
{
if (const FNumericProperty* NumericProperty = CastField<FNumericProperty>(Property))
{
if (NumericProperty->IsFloatingPoint())
{
NumericProperty->SetFloatingPointPropertyValue(Base, JsonValue->AsNumber());
}
else if (NumericProperty->IsInteger())
{
NumericProperty->SetIntPropertyValue(Base, (int64)JsonValue->AsNumber());
}
}
else if (const FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
{
// Export bools as bools
BoolProperty->SetPropertyValue(Base, JsonValue->AsBool());
}
else if (const FStrProperty* StringProperty = CastField<FStrProperty>(Property))
{
StringProperty->SetPropertyValue(Base, JsonValue->AsString());
}
else if (const FMapProperty* MapProperty = CastField<FMapProperty>(Property))
{
}
else if (const FSetProperty* SetProperty = CastField<FSetProperty>(Property))
{
}
else if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
if (JsonValue->Type != EJson::Array) return;
const TArray< TSharedPtr<FJsonValue> > ArrayValue = JsonValue->AsArray();
int32 ArrLen = ArrayValue.Num();
// make the output array size match
}
else if (const FStructProperty *StructProperty = CastField<FStructProperty>(Property))
{
}
else if (const FObjectProperty *ObjectProperty = CastField<FObjectProperty>(Property))
{
}
else
{
}
}
bool ASpeckleUnrealManager::HasObject(const FString& Id) const
{
return SpeckleObjects.Contains(Id);
}
TSharedPtr<FJsonObject, ESPMode::Fast> ASpeckleUnrealManager::GetSpeckleObject(const FString& Id) const
{
return *SpeckleObjects.Find(Id);
}
bool ASpeckleUnrealManager::ResolveReference(const TSharedPtr<FJsonObject> Object, TSharedPtr<FJsonObject>& OutObject) const
{
FString SpeckleType;
FString ReferenceID;
if (Object->TryGetStringField("speckle_type", SpeckleType)
&& SpeckleType == "reference"
&& Object->TryGetStringField("referencedId",ReferenceID))
{
OutObject = GetSpeckleObject(ReferenceID);
return true;
}
return false;
}
TArray<TSharedPtr<FJsonValue>> ASpeckleUnrealManager::CombineChunks(const TArray<TSharedPtr<FJsonValue>>& ArrayField) const
{
TArray<TSharedPtr<FJsonValue>> ObjectPoints;
for(int32 i = 0; i < ArrayField.Num(); i++)
{
FString Index;
if(ArrayField[i]->AsObject()->TryGetStringField("referencedId", Index))
{
const auto Chunk = SpeckleObjects[Index]->GetArrayField("data");
ObjectPoints.Append(Chunk);
}
else
{
return ArrayField; //Array was never chunked to begin with
}
}
return ObjectPoints;
}
float ASpeckleUnrealManager::ParseScaleFactor(const FString& Units) const
{
static const auto ParseUnits = [](const FString& LUnits) -> float
{
if (LUnits == "millimeters" || LUnits == "millimeter" || LUnits == "millimetres" || LUnits == "millimetre" || LUnits == "mm")
return 0.1;
if (LUnits == "centimeters" || LUnits == "centimeter" ||LUnits == "centimetres" || LUnits == "centimetre" || LUnits == "cm")
return 1;
if (LUnits == "meters" || LUnits == "meter" || LUnits == "metres" || LUnits == "metre" || LUnits == "m")
return 100;
if (LUnits == "kilometers" || LUnits == "kilometres" || LUnits == "km")
return 100000;
if (LUnits == "inches" || LUnits == "inch" || LUnits == "in")
return 2.54;
if (LUnits == "feet" || LUnits == "foot" || LUnits == "ft")
return 30.48;
if (LUnits == "yards" || LUnits == "yard"|| LUnits == "yd")
return 91.44;
if (LUnits == "miles" || LUnits == "mile" || LUnits == "mi")
return 160934.4;
return 100;
};
return ParseUnits(Units.ToLower()) * WorldToCentimeters;
}
AActor* ASpeckleUnrealManager::CreateBlockInstance(const TSharedPtr<FJsonObject> Obj)
{
//Transform
const FString Units = Obj->GetStringField("units");
const float ScaleFactor = ParseScaleFactor(Units);
const TArray<TSharedPtr<FJsonValue>>* TransformData;
if(!Obj->TryGetArrayField("transform", TransformData)) return nullptr;
FMatrix TransformMatrix;
for(int32 Row = 0; Row < 4; Row++)
for(int32 Col = 0; Col < 4; Col++)
{
TransformMatrix.M[Row][Col] = TransformData->operator[](Row * 4 + Col)->AsNumber();
}
TransformMatrix = TransformMatrix.GetTransposed();
TransformMatrix.ScaleTranslation(FVector(ScaleFactor));
//Block Instance
const FString ObjectId = Obj->GetStringField("id"), SpeckleType = Obj->GetStringField("speckle_type");
ASpeckleUnrealActor* BlockInstance = World->SpawnActor<ASpeckleUnrealActor>(ASpeckleUnrealActor::StaticClass(), FTransform(TransformMatrix));
//Block Definition
const TSharedPtr<FJsonObject>* BlockDefinitionReference;
if(!Obj->TryGetObjectField("blockDefinition", BlockDefinitionReference)) return nullptr;
const FString RefID = BlockDefinitionReference->operator->()->GetStringField("referencedId");
const TSharedPtr<FJsonObject> BlockDefinition = SpeckleObjects[RefID];
//For now just recreate mesh, eventually we should use instanced static mesh
const auto Geometries = BlockDefinition->GetArrayField("geometry");
for(const auto Child : Geometries)
{
const TSharedPtr<FJsonObject> MeshReference = Child->AsObject();
const FString MeshID = MeshReference->GetStringField("referencedId");
//It is important that ParentObject is the BlockInstance not the BlockDefinition to keep NativeIDs of meshes unique between Block Instances
ImportObjectFromCache(BlockInstance, SpeckleObjects[MeshID]);
}
return BlockInstance;
}
@@ -0,0 +1,196 @@
#include "SpeckleUnrealManager.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
ASpeckleUnrealManager::ASpeckleUnrealManager()
{
static ConstructorHelpers::FObjectFinder<UMaterial> SpeckleMaterial(TEXT("Material'/SpeckleUnreal/SpeckleMaterial.SpeckleMaterial'"));
static ConstructorHelpers::FObjectFinder<UMaterial> SpeckleGlassMaterial(TEXT("Material'/SpeckleUnreal/SpeckleGlassMaterial.SpeckleGlassMaterial'"));
//When the object is constructed, Get the HTTP module
Http = &FHttpModule::Get();
SetRootComponent(CreateDefaultSubobject<USceneComponent>("Root"));
RootComponent->SetRelativeScale3D(FVector(-1,1,1));
RootComponent->SetMobility(EComponentMobility::Static);
Converter = CreateDefaultSubobject<USpeckleConverterComponent>(FName("Converter"));
DefaultMeshMaterial = SpeckleMaterial.Object;
BaseMeshOpaqueMaterial = SpeckleMaterial.Object;
BaseMeshTransparentMaterial = SpeckleGlassMaterial.Object;
}
// Called when the game starts or when spawned
void ASpeckleUnrealManager::BeginPlay()
{
Super::BeginPlay();
if (ImportAtRuntime)
ImportSpeckleObject();
}
/*Import the Speckle object*/
void ASpeckleUnrealManager::ImportSpeckleObject()
{
const FString UserAgent = FString::Printf(TEXT("Unreal Engine (%s) / %d.%d.%d"), *UGameplayStatics::GetPlatformName(), ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ENGINE_PATCH_VERSION);
#if !SUPPRESS_SPECKLE_ANALYTICS
const FString HostApplication = FString::Printf(TEXT("Unreal%%20Engine%%20%d"), ENGINE_MAJOR_VERSION);
const FString Action = "receive/manual";
FString SpeckleUserID = "No%20SUUID";
#if PLATFORM_WINDOWS
const FString UserPath = UKismetSystemLibrary::GetPlatformUserDir().LeftChop(10); //remove "Documents/"
const FString Dir = FString::Printf(TEXT("%sAppData/Roaming/Speckle/suuid"), *UserPath);
FFileHelper::LoadFileToString(SpeckleUserID, *Dir);
#endif
//TODO MACOS
//Track page view
const FString ViewURL = FString::Printf(
TEXT("https://speckle.matomo.cloud/matomo.php?idsite=2&rec=1&apiv=1&uid=%s&action_name=%s&url=http://connectors/%s/%s&urlref=http://connectors/%s/%s&_cvar=%%7B%%22hostApplication%%22:%%20%%22%s%%22%%7D"),
*SpeckleUserID,
*Action,
*HostApplication,
*Action,
*HostApplication,
*Action,
*HostApplication
);
const FHttpRequestRef ViewTrackingRequest = Http->CreateRequest();
ViewTrackingRequest->SetVerb("POST");
ViewTrackingRequest->SetURL(ViewURL);
ViewTrackingRequest->SetHeader("User-Agent", UserAgent);
ViewTrackingRequest->ProcessRequest();
//Track receive action
const FString EventURL = FString::Printf(
TEXT("https://speckle.matomo.cloud/matomo.php?idsite=2&rec=1&apiv=1&uid=%s&_cvar=%%7B%%22hostApplication%%22:%%20%%22%s%%22%%7D&e_c=%s&e_a=%s"),
*SpeckleUserID,
*HostApplication,
*HostApplication,
*Action
);
const FHttpRequestRef EventTrackingRequest = Http->CreateRequest();
EventTrackingRequest->SetVerb("POST");
EventTrackingRequest->SetURL(EventURL);
EventTrackingRequest->SetHeader("User-Agent", UserAgent);
EventTrackingRequest->ProcessRequest();
#endif
const FString url = ServerUrl + "/objects/" + StreamID + "/" + ObjectID;
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "[Speckle] Downloading: " + url);
const FHttpRequestRef Request = Http->CreateRequest();
Request->SetVerb("GET");
Request->SetHeader("Accept", TEXT("text/plain"));
Request->SetHeader("Authorization", "Bearer " + AuthToken);
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamTextResponseReceived);
Request->SetURL(url);
Request->SetHeader("User-Agent", UserAgent);
Request->ProcessRequest();
}
void ASpeckleUnrealManager::OnStreamTextResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Stream Request failed: " + Response->GetContentAsString());
return;
}
auto responseCode = Response->GetResponseCode();
if (responseCode != 200)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, FString::Printf(TEXT("Error response. Response code %d"), responseCode));
return;
}
FString response = Response->GetContentAsString();
// ParseIntoArray is very inneficient for large strings.
// https://docs.unrealengine.com/en-US/API/Runtime/Core/Containers/FString/ParseIntoArrayLines/index.html
// https://answers.unrealengine.com/questions/81697/reading-text-file-line-by-line.html
// Can be fixed by setting the size of the array
int lineCount = 0;
for (const TCHAR* ptr = *response; *ptr; ptr++)
if (*ptr == '\n')
lineCount++;
TArray<FString> lines;
lines.Reserve(lineCount);
response.ParseIntoArray(lines, TEXT("\n"), true);
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Parsing %d downloaded objects..."), lineCount));
for (const auto& line : lines)
{
FString objectId, objectJson;
if (!line.Split("\t", &objectId, &objectJson))
continue;
TSharedPtr<FJsonObject> jsonObject;
TSharedRef<TJsonReader<>> jsonReader = TJsonReaderFactory<>::Create(objectJson);
if (!FJsonSerializer::Deserialize(jsonReader, jsonObject))
continue;
SpeckleObjects.Add(objectId, jsonObject);
}
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Converting %d objects..."), lineCount));
//World Units setup
WorldToCentimeters = 1; //Default value of 1uu = 1cm
AWorldSettings* WorldSettings;
if(IsValid(World = GetWorld() )
&& IsValid(WorldSettings = World->GetWorldSettings()) )
{
WorldToCentimeters = WorldSettings->WorldToMeters / 100;
}
ImportObjectFromCache(this, SpeckleObjects[ObjectID]);
for (const auto& m : CreatedObjectsCache)
{
if(AActor* a = Cast<AActor>(m))
a->Destroy();
else
m->ConditionalBeginDestroy();
}
CreatedObjectsCache = InProgressObjectsCache;
InProgressObjectsCache.Empty();
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, FString::Printf(TEXT("[Speckle] Objects imported successfully. Created %d Actors"), CreatedObjectsCache.Num()));
}
void ASpeckleUnrealManager::DeleteObjects()
{
ConvertedMaterials.Empty();
for (const auto& m : CreatedObjectsCache)
{
if(AActor* a = Cast<AActor>(m))
a->Destroy();
else
m->ConditionalBeginDestroy();
}
CreatedObjectsCache.Empty();
}
@@ -0,0 +1,31 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "ConversionUtils.generated.h"
/**
*
*/
UCLASS()
class SPECKLEUNREAL_API UConversionUtils : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Speckle|Conversion Utilities")
static FMatrix TransformToNative(const TArray<float>& TransformData);
//Parses units string into
UFUNCTION(BlueprintCallable, BlueprintPure)
static float GetUnitsScaleFactorF(const FString& Units, const float WorldToCentimeters = 1);
UFUNCTION(BlueprintCallable, BlueprintPure)
static float GetUnitsScaleFactor(const FString& Units, const UWorld* World = nullptr);
//Safely try and get the WorldToCentimeters unit scale factor from the given world.
UFUNCTION(BlueprintCallable, BlueprintPure)
static bool TryGetWorldUnits(const UWorld* World, int32& OutWorldToCentimeters);
};
@@ -0,0 +1,40 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Conversion/SpeckleTypeConverter.h"
#include "PointCloudConverter.generated.h"
class ULidarPointCloudComponent;
class ALidarPointCloudActor;
class ULidarPointCloud;
class UPointCloud;
UCLASS()
class SPECKLEUNREAL_API UPointCloudConverter : public UObject, public ISpeckleTypeConverter
{
GENERATED_BODY()
CONVERTS_SPECKLE_TYPES()
protected:
UFUNCTION(BlueprintCallable)
virtual ALidarPointCloudActor* CreateActor(const ASpeckleUnrealManager* Manager, ULidarPointCloud* PointCloudData);
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<ALidarPointCloudActor> PointCloudActorType;
UPointCloudConverter();
virtual AActor* ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager) override;
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager) override;
UFUNCTION(BlueprintCallable)
virtual ALidarPointCloudActor* PointCloudToNative(const UPointCloud* SpecklePointCloud, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintCallable)
virtual UPointCloud* PointCloudToSpeckle(const ULidarPointCloudComponent* Object, ASpeckleUnrealManager* Manager);
};
@@ -0,0 +1,49 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Conversion/SpeckleTypeConverter.h"
#include "ProceduralMeshConverter.generated.h"
class UProceduralMeshComponent;
class UMesh;
class URenderMaterial;
UCLASS()
class SPECKLEUNREAL_API UProceduralMeshConverter : public UObject, public ISpeckleTypeConverter
{
GENERATED_BODY()
CONVERTS_SPECKLE_TYPES()
protected:
virtual AActor* CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> MeshActorType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bCreateCollisions;
// Sets default values for this actor's properties
UProceduralMeshConverter();
virtual AActor* ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager) override;
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager) override;
UFUNCTION(BlueprintCallable)
virtual AActor* MeshToNative(const UMesh* SpeckleMesh, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintCallable)
virtual UMaterialInterface* GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintCallable)
virtual UMesh* MeshToSpeckle(const UProceduralMeshComponent* Object, ASpeckleUnrealManager* Manager);
};
@@ -0,0 +1,59 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Conversion/SpeckleTypeConverter.h"
#include "StaticMeshConverter.generated.h"
class AStaticMeshActor;
class UMesh;
class URenderMaterial;
UCLASS()
class SPECKLEUNREAL_API UStaticMeshConverter : public UObject, public ISpeckleTypeConverter
{
GENERATED_BODY()
CONVERTS_SPECKLE_TYPES()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> MeshActorType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseFullBuild;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool GenerateLightmapUV;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool BuildSimpleCollision;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool Transient;
protected:
virtual AActor* CreateActor(const ASpeckleUnrealManager* Manager, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
public:
// Sets default values for this actor's properties
UStaticMeshConverter();
virtual AActor* ConvertToNative_Implementation(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager) override;
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object, ASpeckleUnrealManager* Manager) override;
UFUNCTION(BlueprintCallable)
virtual UStaticMesh* MeshToNative(UObject* Outer, const UMesh* SpeckleMesh, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintCallable)
virtual UMesh* MeshToSpeckle(const UStaticMeshComponent* Object, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintCallable)
virtual UMaterialInterface* GetMaterial(const URenderMaterial* SpeckleMaterial, ASpeckleUnrealManager* Manager);
};
@@ -0,0 +1,54 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SpeckleConverterComponent.generated.h"
class USpeckleTypeConverter;
class ASpeckleUnrealManager;
class UBase;
class ISpeckleTypeConverter;
/**
* This component contains modular conversion functions for converting Speckle Objects <--> Native Unreal Objects.
*/
UCLASS(ClassGroup=(Speckle), meta=(BlueprintSpawnableComponent))
class SPECKLEUNREAL_API USpeckleConverterComponent : public UActorComponent
{
GENERATED_BODY()
protected:
// A lazily initialised mapping of SpeckleType -> converters.
TMap<TSubclassOf<UBase>, TScriptInterface<ISpeckleTypeConverter>> SpeckleTypeMap;
public:
// Array of converters
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle|Conversion")
TArray<UObject*> SpeckleConverters;
// Sets default values for this component's properties
USpeckleConverterComponent();
// Validates changes to SpeckleConverters, Should be called after modifying SpeckleConverters
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
virtual void OnConvertersChangeHandler();
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) override;
#endif
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
UBase* ConvertToSpeckle(UObject* Object);
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
AActor* ConvertToNative(const UBase* Object, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
TScriptInterface<ISpeckleTypeConverter> GetConverter(const TSubclassOf<UBase> BaseType);
};
@@ -0,0 +1,56 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Objects/Base.h"
#include "UObject/Interface.h"
#include "SpeckleTypeConverter.generated.h"
class UBase;
class ASpeckleUnrealManager;
// This class does not need to be modified.
UINTERFACE()
class USpeckleTypeConverter : public UInterface
{
GENERATED_BODY()
};
/**
* Interfaces for object conversion functions (ToSpeckle and ToNative) of a specific (most likely single) native type.
*
* Classes implementing this function are responsible for converting one or more UBase types
* to a native AActor. (ToNative)
* And/Or
* Converting one or more AActor types to a UBase type.
*
* Note: This interface is not equivalent to ISpeckleConverter in the .NET SDK.
*/
class SPECKLEUNREAL_API ISpeckleTypeConverter
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent)
bool CanConvertToNative(TSubclassOf<UBase> BaseType);
/// Returns the type of Base expected for a given SpeckleType
UFUNCTION(BlueprintNativeEvent)
TSubclassOf<UBase> ToNativeBase(const FString& SpeckleType);
UFUNCTION(BlueprintNativeEvent)
AActor* ConvertToNative(const UBase* SpeckleBase, ASpeckleUnrealManager* Manager);
UFUNCTION(BlueprintNativeEvent)
UBase* ConvertToSpeckle(const UObject* Object, ASpeckleUnrealManager* Manager);
};
#define CONVERTS_SPECKLE_TYPES() \
protected: \
UPROPERTY(EditAnywhere, BlueprintReadWrite) \
TSet<TSubclassOf<UBase>> SpeckleTypes; \
public: \
virtual bool CanConvertToNative_Implementation(TSubclassOf<UBase> BaseType) override { return SpeckleTypes.Contains(BaseType); } \
private:
@@ -0,0 +1,58 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Objects/RegisteringBase.h"
#include "Dom/JsonObject.h"
#include "Base.generated.h"
class ASpeckleUnrealManager;
/**
*
*/
UCLASS(BlueprintType)
class SPECKLEUNREAL_API UBase : public URegisteringBase
{
public:
GENERATED_BODY()
protected:
explicit UBase(const wchar_t* SpeckleType): SpeckleType(SpeckleType) {}
explicit UBase(const FString& SpeckleType) : SpeckleType(SpeckleType) {}
public:
UBase() : SpeckleType("Base") {}
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
FString Id;
//UPROPERTY(BlueprintReadOnly)
//TMap<FString, FString> Properties; //TODO figure out how I'm going to do custom properties
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
FString Units;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
FString SpeckleType;
//TODO this won't be serialised
TMap<FString, TSharedPtr<FJsonValue>> DynamicProperties;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Speckle|Objects")
TMap<FString, UBase*> Children;
/// Callback called right before serialization of this object
virtual void OnBeforeSerialize();
/// Callback called right after deserialization of this object
virtual void OnAfterDeserialize();
};
@@ -0,0 +1,28 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Base.h"
#include "BuiltElement.generated.h"
/**
*
*/
UCLASS()
class SPECKLEUNREAL_API UBuiltElement : public UBase
{
GENERATED_BODY()
protected:
static TArray<FString> DisplayValueAliasStrings;
void AddDisplayValue(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager);
public:
UBuiltElement() : UBase(TEXT("Objects.Elements")) {}
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<UBase*> DisplayValue;
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override;
};
@@ -0,0 +1,77 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Base.h"
#include "Mesh.generated.h"
class URenderMaterial;
class ASpeckleUnrealManager;
/**
*
*/
UCLASS()
class SPECKLEUNREAL_API UMesh : public UBase
{
GENERATED_BODY()
public:
UMesh() : UBase(TEXT("Objects.Geometry.Mesh")) {}
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<float> Vertices;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<int32> Faces;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<float> TextureCoordinates;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<int32> Colors;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<float> Transform;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
URenderMaterial* RenderMaterial;
public:
UFUNCTION(BlueprintPure)
FVector GetVert(int32 Index) const;
UFUNCTION(BlueprintPure)
TArray<FVector> GetVerts() const;
UFUNCTION(BlueprintPure)
int32 GetVertexCount() const { return Vertices.Num() / 3; }
UFUNCTION(BlueprintPure)
FVector2D GetTextureCoordinate(int32 Index) const;
UFUNCTION(BlueprintPure)
TArray<FVector2D> GetTextureCoordinates() const;
UFUNCTION(BlueprintPure)
int32 GetTexCoordCount() const { return TextureCoordinates.Num() / 2; }
UFUNCTION(BlueprintPure)
FORCEINLINE FColor GetVertexColor(int32 Index) const;
UFUNCTION(BlueprintPure)
TArray<FColor> GetVertexColors() const;
UFUNCTION(BlueprintPure)
FMatrix GetTransform() const;
UFUNCTION(BlueprintCallable)
void SetTransform(const FMatrix& T);
UFUNCTION()
virtual void AlignVerticesWithTexCoordsByIndex();
UFUNCTION()
virtual void ApplyScaleFactor(const float ScaleFactor);
UFUNCTION()
virtual void ApplyUnits(const UWorld* World);
};
@@ -0,0 +1,31 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Base.h"
#include "PointCloud.generated.h"
/**
*
*/
UCLASS()
class SPECKLEUNREAL_API UPointCloud : public UBase
{
GENERATED_BODY()
public:
UPointCloud() : UBase(TEXT("Objects.Other.Pointcloud")) {}
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<float> Points;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<int32> Colors;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Speckle|Objects")
TArray<float> Sizes;
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override;
};
@@ -0,0 +1,43 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "RegisteringBase.generated.h"
class UBase;
UCLASS(Abstract)
class SPECKLEUNREAL_API URegisteringBase : public UObject
{
GENERATED_BODY()
protected:
//static TOptional<TMap<FString, TSubclassOf<UBase>>> TypeRegistry;
static TMap<FString, TSubclassOf<UBase>> TypeRegistry;
static void GenerateTypeRegistry();
public:
/// Attempts to find the closest registered TSubclassOf<UBase>
/// by recursively stripping away a the most specific name specifier from the given SpeckleType
/// until a converter is found or the FString is exhausted.
///
/// Eg. with an input of "Objects.Elements.Wall"
/// Will first look for a registered type of "Objects.Elements.Wall"
/// If one is not found, will look for "Objects.Elements" etc.
/// Returns nullptr if none found.
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
static TSubclassOf<UBase> FindClosestType(const FString& SpeckleType);
/// Attempts to find a TSubclassOf<UBase> with a UBase::SpeckleType matching the given SpeckleType param
/// Returns nullptr if none found.
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
static TSubclassOf<UBase> GetRegisteredType(const FString& SpeckleType);
UFUNCTION(BlueprintCallable, Category="Speckle|Objects")
static bool TryGetRegisteredType(const FString& SpeckleType, TSubclassOf<UBase>& OutType);
};
@@ -0,0 +1,61 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Objects/Base.h"
#include "RenderMaterial.generated.h"
/**
*
*/
UCLASS()
class SPECKLEUNREAL_API URenderMaterial : public UBase
{
GENERATED_BODY()
public:
URenderMaterial() : UBase(TEXT("Objects.Other.RenderMaterial")) {}
UPROPERTY()
FString Name;
UPROPERTY()
double Opacity = 1;
UPROPERTY()
double Metalness = 0;
UPROPERTY()
double Roughness = 1;
UPROPERTY()
int32 Diffuse = FColor{221,221,221};
UPROPERTY()
int32 Emissive = FLinearColor::Black;
virtual void Parse(const TSharedPtr<FJsonObject> Obj, const ASpeckleUnrealManager* Manager) override
{
Super::Parse(Obj, Manager);
Obj->TryGetStringField("name", Name);
Obj->TryGetNumberField("opacity", Opacity);
Obj->TryGetNumberField("metalness", Metalness);
Obj->TryGetNumberField("roughness", Roughness);
int32 ARGB;
if(Obj->TryGetNumberField("diffuse", ARGB))
Diffuse = FColor(ARGB);
if(Obj->TryGetNumberField("emissive", ARGB))
Emissive = FColor(ARGB);
}
};
@@ -0,0 +1,23 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "BaseObjectSerializer.generated.h"
class UBase;
/**
*
*/
UCLASS()
class SPECKLEUNREAL_API UBaseObjectSerializer : public UObject
{
GENERATED_BODY()
public:
//UFUNCTION()
//UBase* DeserialiseBase();
};
@@ -0,0 +1,23 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SpeckleUnrealActor.generated.h"
class UMeshDescriptionBase;
UCLASS(BlueprintType)
class SPECKLEUNREAL_API ASpeckleUnrealActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere)
USceneComponent* Scene;
// Sets default values for this actor's properties
ASpeckleUnrealActor();
};
@@ -3,21 +3,18 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "UObject/NoExportTypes.h"
#include "SpeckleUnrealLayer.generated.h"
/**
*
*/
UCLASS(BlueprintType)
class SPECKLEUNREAL_API ASpeckleUnrealLayer : public AActor
class SPECKLEUNREAL_API USpeckleUnrealLayer : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere)
USceneComponent* Scene;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle")
FString LayerName;
@@ -30,7 +27,7 @@ public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle")
int32 ObjectCount;
ASpeckleUnrealLayer();
USpeckleUnrealLayer();
void Init(FString NewLayerName, int32 NewStartIndex, int32 NewObjectCount);
};
@@ -0,0 +1,137 @@
#pragma once
// json manipulation
#include "Dom/JsonObject.h"
#include "Dom/JsonValue.h"
// web requests
#include "Runtime/Online/HTTP/Public/Http.h"
#include "SpeckleUnrealLayer.h"
#include "Conversion/SpeckleConverterComponent.h"
#include "GameFramework/Actor.h"
#include "SpeckleUnrealManager.generated.h"
class URenderMaterial;
UCLASS(ClassGroup=(Speckle), BlueprintType)
class SPECKLEUNREAL_API ASpeckleUnrealManager : public AActor
{
GENERATED_BODY()
public:
FHttpModule* Http;
/* The actual HTTP call */
UFUNCTION(CallInEditor, Category = "Speckle")
void ImportSpeckleObject();
UFUNCTION(CallInEditor, Category = "Speckle")
void DeleteObjects();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
USpeckleConverterComponent* Converter;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString ServerUrl {
"https://speckle.xyz"
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString StreamID {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString ObjectID {
""
};
/** A Personal Access Token can be created from your Speckle Profile page (Treat tokens like passwords, do not share publicly) */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString AuthToken {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
bool ImportAtRuntime;
/** Material to be applied to meshes when no RenderMaterial can be converted */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials")
UMaterialInterface* DefaultMeshMaterial;
/** Material Parent for converted opaque materials*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials")
UMaterialInterface* BaseMeshOpaqueMaterial;
/** Material Parent for converted materials with an opacity less than one */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials")
UMaterialInterface* BaseMeshTransparentMaterial;
/** When generating meshes, materials in this TMap will be used instead of converted ones if the key matches the ID of the Object's RenderMaterial. (Takes priority over name matching)*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials|Overrides", DisplayName = "By Speckle ID")
TMap<FString, UMaterialInterface*> MaterialOverridesById;
/** When generating meshes, materials in this TSet will be used instead of converted ones if the material name matches the name of the Object's RenderMaterial. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle|Materials|Overrides", DisplayName = "By Name")
TSet<UMaterialInterface*> MaterialOverridesByName;
/** Materials converted from stream RenderMaterial objects */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Speckle|Materials")
TMap<FString, UMaterialInterface*> ConvertedMaterials;
void OnStreamTextResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
// Sets default values for this actor's properties
ASpeckleUnrealManager();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
TArray<TSharedPtr<FJsonValue>> CombineChunks(const TArray<TSharedPtr<FJsonValue>>& ArrayField) const;
UFUNCTION(BlueprintCallable, BlueprintPure)
float ParseScaleFactor(const FString& Units) const;
//TODO move to conversion functions
UFUNCTION(BlueprintCallable)
bool TryGetMaterial(const URenderMaterial* SpeckleMaterial, bool AcceptMaterialOverride,
UMaterialInterface*& OutMaterial);
UFUNCTION(BlueprintCallable, BlueprintPure)
bool HasObject(const FString& Id) const;
UBase* DeserializeBase(const TSharedPtr<FJsonObject> Obj) const;
bool ResolveReference(const TSharedPtr<FJsonObject> Object, TSharedPtr<FJsonObject>& OutObject) const;
TSharedPtr<FJsonObject, ESPMode::Fast> GetSpeckleObject(const FString& Id) const;
protected:
UWorld* World;
float WorldToCentimeters;
bool TryParseSpeckleObjectFromJsonProperty(const TSharedPtr<FJsonValue> JsonValue, UBase*& OutBase) const;
TMap<FString, TSharedPtr<FJsonObject>> SpeckleObjects;
UPROPERTY()
TArray<UObject*> CreatedObjectsCache;
TArray<UObject*> InProgressObjectsCache;
void ImportObjectFromCache(AActor* AOwner, const TSharedPtr<FJsonObject> SpeckleObject);
//AActor* CreateMesh(const TSharedPtr<FJsonObject> Obj, const TSharedPtr<FJsonObject> Parent = nullptr);
AActor* CreateBlockInstance(const TSharedPtr<FJsonObject> Obj);
//AActor* CreatePointCloud(const TSharedPtr<FJsonObject> Obj);
};
@@ -1,5 +1,3 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class SpeckleUnreal : ModuleRules
@@ -8,18 +6,20 @@ public class SpeckleUnreal : ModuleRules
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDefinitions.Add("SUPPRESS_SPECKLE_ANALYTICS=0");
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
);
PublicDependencyModuleNames.AddRange(
@@ -29,10 +29,13 @@ public class SpeckleUnreal : ModuleRules
"Http",
"Json",
"JsonUtilities",
"ProceduralMeshComponent"
"ProceduralMeshComponent",
"MeshDescription",
"StaticMeshDescription",
"LidarPointCloudRuntime",
// ... add other public dependencies that you statically link with here ...
}
);
);
PrivateDependencyModuleNames.AddRange(
@@ -44,7 +47,7 @@ public class SpeckleUnreal : ModuleRules
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
);
DynamicallyLoadedModuleNames.AddRange(
@@ -52,6 +55,6 @@ public class SpeckleUnreal : ModuleRules
{
// ... add any modules that your module loads dynamically here ...
}
);
);
}
}
@@ -0,0 +1,29 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Conversion/ConverterFactory.h"
#include "SpeckleUnrealEditorModule.h"
UConverterFactory::UConverterFactory(UClass* SupportedClass)
{
bCreateNew = true;
bEditAfterNew = true;
this->SupportedClass = SupportedClass;
}
uint32 UConverterFactory::GetMenuCategories() const
{
static const uint32 SpeckleCategory = FModuleManager::LoadModuleChecked<FSpeckleUnrealEditorModule>("SpeckleUnrealEditor").GetSpeckleAssetCategory();
return SpeckleCategory;
}
UObject* UConverterFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags,
UObject* Context, FFeedbackContext* Warn)
{
UObject* NewObjectAsset = NewObject<UObject>(InParent, Class, Name, Flags);
return NewObjectAsset;
}
@@ -0,0 +1,60 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SpeckleUnrealEditorModule.h"
#include "AssetToolsModule.h"
#include "IAssetTools.h"
#include "Conversion/ConverterActions.h"
#include "Conversion/ConverterFactory.h"
#define LOCTEXT_NAMESPACE "FSpeckleUnrealEditorModule"
uint32 FSpeckleUnrealEditorModule::GetSpeckleAssetCategory() const
{
return SpeckleAssetCategoryBit;
}
void FSpeckleUnrealEditorModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
#if WITH_EDITOR
if (GIsEditor)
{
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
// Register Speckle Category
SpeckleAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Speckle")), LOCTEXT("SpeckleCategoryText","Speckle"));
// Finds all class definitions : UConverterFactory and creates a FConverterActions for their supported class.
// See UAssetToolsImpl::GetNewAssetFactories() for reference
for (TObjectIterator<UClass> It; It; ++It)
{
const UClass* Class = *It;
if (Class->IsChildOf(UConverterFactory::StaticClass()) &&
!Class->HasAnyClassFlags(CLASS_Abstract))
{
const UConverterFactory* Factory = Class->GetDefaultObject<UConverterFactory>();
if (Factory->ShouldShowInNewMenu() &&
ensure(!Factory->GetDisplayName().IsEmpty()))
{
AssetTools.RegisterAssetTypeActions(MakeShareable(new FConverterActions(Factory->SupportedClass, SpeckleAssetCategoryBit)));
}
}
}
}
#endif
}
void FSpeckleUnrealEditorModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FSpeckleUnrealEditorModule, SpeckleUnrealEditor)
@@ -0,0 +1,31 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "AssetTypeActions_Base.h"
/**
*
*/
class FConverterActions : public FAssetTypeActions_Base
{
private:
UClass* SupportedClass;
uint32 Category;
public:
explicit FConverterActions(UClass* SupportedClass, uint32 Category)
{
this->SupportedClass = SupportedClass;
this->Category = Category;
}
// IAssetTypeActions Implementation
virtual FText GetName() const override { return SupportedClass->GetDisplayNameText(); }
virtual UClass* GetSupportedClass() const override { return SupportedClass; }
virtual FColor GetTypeColor() const override { return FColor(59, 130, 246); }
virtual bool CanLocalize() const override { return false; }
virtual uint32 GetCategories() override { return Category; }
};
@@ -0,0 +1,62 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "Conversion/Converters/ProceduralMeshConverter.h"
#include "Conversion/Converters/StaticMeshConverter.h"
#include "Conversion/Converters/PointCloudConverter.h"
#include "ConverterFactory.generated.h"
/**
* This class is designed to reduce the boiler plate required to define a UFactory for ISpeckleTypeConverter types.
*
* For each ISpeckleTypeConverter we want to appear in the "Create Advanced Asset" context menu
* A class definition : UConverterFactory should be created (see below examples)
*
* A FConverterAction : FAssetTypeActions_Base instance will automatically be created by
* FSpeckleUnrealEditorModule::StartupModule for each class definition : UConverterFactory.
* So we don't need to worry about manually registering the SupportedClass types with FAssetToolsModule.
*/
UCLASS(abstract)
class SPECKLEUNREALEDITOR_API UConverterFactory : public UFactory
{
GENERATED_BODY()
protected:
explicit UConverterFactory(UClass* SupportedClass);
public:
UConverterFactory() : Super() {}
virtual uint32 GetMenuCategories() const override;
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
};
UCLASS()
class SPECKLEUNREALEDITOR_API UStaticMeshConverterFactory : public UConverterFactory
{
GENERATED_BODY()
public:
UStaticMeshConverterFactory() : Super( UStaticMeshConverter::StaticClass() ) { }
};
UCLASS()
class SPECKLEUNREALEDITOR_API UProceduralMeshConverterFactory : public UConverterFactory
{
GENERATED_BODY()
public:
UProceduralMeshConverterFactory() : Super( UProceduralMeshConverter::StaticClass() ) { }
};
UCLASS()
class SPECKLEUNREALEDITOR_API UPointCloudConverterFactory : public UConverterFactory
{
GENERATED_BODY()
public:
UPointCloudConverterFactory() : Super( UPointCloudConverter::StaticClass() ) { }
};
@@ -0,0 +1,21 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
class FSpeckleUnrealEditorModule : public IModuleInterface
{
#if WITH_EDITOR
protected:
uint32 SpeckleAssetCategoryBit = 0;
public:
virtual uint32 GetSpeckleAssetCategory() const;
#endif
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
@@ -0,0 +1,56 @@
using UnrealBuildTool;
public class SpeckleUnrealEditor : ModuleRules
{
public SpeckleUnrealEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Http",
"Json",
"JsonUtilities",
"UnrealEd",
"SpeckleUnreal",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
@@ -5,9 +5,9 @@
"FriendlyName": "SpeckleUnreal",
"Description": "Speckle is an open source data platform for Architecture, Engineering, and Construction. It has been open sourced under the MIT license, is customizable, and available in the cloud or via a self-hosted server. It allows users to exchange data between various AEC modelling and content creation platforms.",
"Category": "AEC",
"CreatedBy": "Mobius Node",
"CreatedByURL": "https://www.mobiusnode.io/",
"DocsURL": "https://github.com/mobiusnode/SpeckleUnreal",
"CreatedBy": "Speckle",
"CreatedByURL": "https://speckle.systems/",
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
@@ -19,12 +19,21 @@
"Name": "SpeckleUnreal",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "SpeckleUnrealEditor",
"Type": "Editor",
"LoadingPhase": "PostEngineInit"
}
],
"Plugins": [
{
"Name": "ProceduralMeshComponent",
"Enabled": true
},
{
"Name": "LidarPointCloud",
"Enabled": true
}
]
}
@@ -1,16 +0,0 @@
[/Script/HardwareTargeting.HardwareTargetingSettings]
TargetedHardwareClass=Desktop
AppliedTargetedHardwareClass=Desktop
DefaultGraphicsPerformance=Maximum
AppliedDefaultGraphicsPerformance=Maximum
[/Script/Engine.Engine]
+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/SpeckleUnrealProject")
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/SpeckleUnrealProject")
+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="SpeckleUnrealProjectGameModeBase")
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
RemoteServerName=
@@ -1,3 +0,0 @@
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=F126DB004A50E31B2A69D284041A10E1
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

@@ -1,256 +0,0 @@
#include "SpeckleUnrealManager.h"
// Sets default values
ASpeckleUnrealManager::ASpeckleUnrealManager()
{
//When the object is constructed, Get the HTTP module
Http = &FHttpModule::Get();
// default conversion is millimeters to centimeters because streams tend to be in ml and unreal is in cm by defaults
ScaleFactor = 0.1;
}
// Called when the game starts or when spawned
void ASpeckleUnrealManager::BeginPlay()
{
Super::BeginPlay();
World = GetWorld();
GetStream();
}
void ASpeckleUnrealManager::SetUpGetRequest(TSharedRef<IHttpRequest> Request)
{
Request->SetVerb("GET");
Request->SetHeader("Content-Type", TEXT("application/json"));
Request->SetHeader("Authorization", AuthToken);
}
/*Http call*/
void ASpeckleUnrealManager::GetStream()
{
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "Downloading: " + StreamID);
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
SetUpGetRequest(Request);
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamResponseReceived);
//This is the url on which to process the request
Request->SetURL(ServerUrl + "streams/" + StreamID);
Request->ProcessRequest();
}
/*Assigned function on successfull http call*/
void ASpeckleUnrealManager::OnStreamResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Stream Request failed");
return;
}
//Create a pointer to hold the json serialized data
TSharedPtr<FJsonObject> ResponseJsonObject;
//Create a reader pointer to read the json data
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
//Deserialize the json data given Reader and the actual object to deserialize
if (FJsonSerializer::Deserialize(Reader, ResponseJsonObject))
{
//Get the value of the json object by field name
FString ResponseMessage = ResponseJsonObject->GetStringField("message");
TSharedPtr<FJsonObject> Stream = ResponseJsonObject->GetObjectField("resource");
FString StreamName = Stream->GetStringField("name");
FString StreamDescription = Stream->GetStringField("description");
FString Units = ResponseJsonObject->GetObjectField("baseProperties")->GetStringField("units").ToLower();
// unreal engine units are in cm by default but the conversion is editable by users so
// this needs to be accounted for later.
if (Units == "meters" || Units == "metres")
ScaleFactor = 100;
if (Units == "centimeters" || Units == "centimetres")
ScaleFactor = 1;
if (Units == "millimeters" || Units == "millimetres")
ScaleFactor = 0.1;
if (Units == "yards")
ScaleFactor = 91.4402757;
if (Units == "feet")
ScaleFactor = 30.4799990;
if (Units == "inches")
ScaleFactor = 2.5399986;
TArray<TSharedPtr<FJsonValue>> LayersInStream = Stream->GetArrayField("layers");
SpeckleUnrealLayers = TArray<ASpeckleUnrealLayer*>();
for (size_t i = 0; i < LayersInStream.Num(); i++)
{
TSharedPtr<FJsonObject> LayerObject = LayersInStream[i]->AsObject();
FString LayerName = LayerObject->GetStringField("name");
int32 StartIndex = LayerObject->GetIntegerField("startIndex");
int32 ObjectCount = LayerObject->GetIntegerField("objectCount");
//USpeckleUnrealLayer NewLayer = USpeckleUnrealLayer(LayerName, StartIndex, ObjectCount);
ASpeckleUnrealLayer* NewLayer = (ASpeckleUnrealLayer*)World->SpawnActor(ASpeckleUnrealLayer::StaticClass());
NewLayer->Init(LayerName, StartIndex, ObjectCount);
SpeckleUnrealLayers.Add(NewLayer);
}
//Output it to the engine
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "Units: " + FString::SanitizeFloat(ScaleFactor));
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Green, "Status: " + ResponseMessage);
GEngine->AddOnScreenDebugMessage(2, 5.0f, FColor::Green, "Name: " + StreamName);
GEngine->AddOnScreenDebugMessage(3, 5.0f, FColor::Green, "Description: " + StreamDescription);
TArray<TSharedPtr<FJsonValue>> ObjectPlaceholderArray = Stream->GetArrayField("objects");
GetStreamObjects(ObjectPlaceholderArray.Num());
}
else
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Couldn't deserialize Json from stream response");
GEngine->AddOnScreenDebugMessage(2, 10.0f, FColor::Red, Response->GetContentAsString());
}
}
void ASpeckleUnrealManager::GetStreamObjects(int32 objectCount)
{
int32 RequestLimit = 1;
CurrentObjectIndex = 0;
LayerIndex = 0;
for (size_t i = 0; i < objectCount; i += RequestLimit)
{
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
SetUpGetRequest(Request);
Request->OnProcessRequestComplete().BindUObject(this, &ASpeckleUnrealManager::OnStreamObjectResponseReceived);
//This is the url on which to process the request
Request->SetURL(ServerUrl + "streams/" + StreamID + "/objects?limit=" + FString::FromInt(RequestLimit) + "&offset=" + FString::FromInt(i));
Request->ProcessRequest();
}
}
void ASpeckleUnrealManager::OnStreamObjectResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Object Request failed");
return;
}
//Create a pointer to hold the json serialized data
TSharedPtr<FJsonObject> ResponseJsonObject;
//Create a reader pointer to read the json data
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
//Deserialize the json data given Reader and the actual object to deserialize
if (FJsonSerializer::Deserialize(Reader, ResponseJsonObject))
{
int32 Offset = FCString::Atoi (*Request->GetURLParameter("offset"));
//Get the value of the json object by field name
TArray<TSharedPtr<FJsonValue>> StreamObjects = ResponseJsonObject->GetArrayField("resources");
for (size_t i = 0; i < SpeckleUnrealLayers.Num(); i++)
{
if (Offset >= SpeckleUnrealLayers[i]->StartIndex)
{
if (Offset < (SpeckleUnrealLayers[i]->StartIndex + SpeckleUnrealLayers[i]->ObjectCount))
LayerIndex = i;
}
}
for (size_t i = 0; i < StreamObjects.Num(); i++)
{
TSharedPtr<FJsonObject> StreamObject = StreamObjects[i].Get()->AsObject();
TSharedPtr<FJsonObject> ObjectToConvert = StreamObject;
FString objectType = ObjectToConvert->GetStringField("type");
if (objectType.ToLower().Contains("brep"))
{
ObjectToConvert = StreamObject->GetObjectField("displayValue");
objectType = ObjectToConvert->GetStringField("type");
}
if (objectType.ToLower().Contains("mesh"))
{
AActor* ActorInstance = World->SpawnActor(MeshActor);
ASpeckleUnrealMesh* MeshInstance = (ASpeckleUnrealMesh*)ActorInstance;
TArray<TSharedPtr<FJsonValue>> ObjectVertices = ObjectToConvert->GetArrayField("vertices");
TArray<TSharedPtr<FJsonValue>> ObjectFaces = ObjectToConvert->GetArrayField("faces");
TArray<FVector> ParsedVerticies;
for (size_t j = 0; j < ObjectVertices.Num(); j += 3)
{
ParsedVerticies.Add(FVector
(
(float)(ObjectVertices[j].Get()->AsNumber()) * -1,
(float)(ObjectVertices[j + 1].Get()->AsNumber()),
(float)(ObjectVertices[j + 2].Get()->AsNumber())
) * ScaleFactor);
}
//convert mesh faces into triangle array regardless of whether or not they are quads
TArray<int32> ParsedTriangles;
int32 j = 0;
while (j < ObjectFaces.Num())
{
if (ObjectFaces[j].Get()->AsNumber() == 0)
{
//Triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
j += 4;
}
else
{
//Quads to triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 4].Get()->AsNumber());
j += 5;
}
}
if (RandomColorsPerLayer)
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, DefaultMeshMaterial, SpeckleUnrealLayers[LayerIndex]->LayerColor);
else
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, DefaultMeshMaterial, FLinearColor::White);
UE_LOG(LogTemp, Warning, TEXT("%d"), Offset);
UE_LOG(LogTemp, Warning, TEXT("%s"), *SpeckleUnrealLayers[LayerIndex]->LayerName);
}
}
}
else
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Couldn't deserialize Json from object response");
GEngine->AddOnScreenDebugMessage(2, 10.0f, FColor::Red, Response->GetContentAsString());
}
}
@@ -1,66 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "SpeckleUnrealMesh.h"
// Sets default values
ASpeckleUnrealMesh::ASpeckleUnrealMesh()
{
Scene = CreateDefaultSubobject<USceneComponent>("Root");
RootComponent = Scene;
ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>("Mesh");
ProceduralMesh->SetupAttachment(RootComponent);
}
void ASpeckleUnrealMesh::SetMesh(TArray<FVector> Vertices, TArray<int32> Triangles, UMaterialInterface* Material, FLinearColor Color)
{
ProceduralMesh->ClearAllMeshSections();
TArray<FVector> Normals;
TArray<FProcMeshTangent> Tangents;
TArray<FVector2D> UVs;
TArray<FColor> Colors;
// for each triangle
for (size_t i = 0; i < Triangles.Num(); i += 3)
{
// get a normal direction for this triangle
FVector Normal = FVector::CrossProduct(Vertices[Triangles[i]], Vertices[Triangles[i + 2]]).GetSafeNormal();
// get a tangent direction perpendicular to the normal
FVector TangentVector = FVector::CrossProduct(Vertices[Triangles[i]], Normal);
FProcMeshTangent Tangent = FProcMeshTangent(TangentVector.X, TangentVector.Y, TangentVector.Z);
// for each vertex in a triangle
for (size_t j = 0; j < 3; j++)
{
Normals.Add(Normal);
Tangents.Add(Tangent);
Colors.Add(FColor::White);
}
}
ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, Colors, Tangents, true);
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(Material, this);
DynMaterial->SetVectorParameterValue("BaseColor", Color);
ProceduralMesh->SetMaterial(0, DynMaterial);
}
// Called when the game starts or when spawned
void ASpeckleUnrealMesh::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ASpeckleUnrealMesh::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
@@ -1,304 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "SpeckleUnrealReceiver.h"
// Sets default values
USpeckleUnrealReceiver::USpeckleUnrealReceiver()
{
//When the object is constructed, Get the HTTP module
Http = &FHttpModule::Get();
// default conversion is millimeters to centimeters because streams tend to be in ml and unreal is in cm by defaults
ScaleFactor = 0.1;
}
void USpeckleUnrealReceiver::SetUpGetRequest(TSharedRef<IHttpRequest> Request)
{
Request->SetVerb("GET");
Request->SetHeader("Content-Type", TEXT("application/json"));
Request->SetHeader("Authorization", AuthToken);
}
/*Http call*/
void USpeckleUnrealReceiver::ReceiveStream()
{
World = GetWorld();
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "Downloading: " + StreamID);
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
SetUpGetRequest(Request);
Request->OnProcessRequestComplete().BindUObject(this, &USpeckleUnrealReceiver::OnStreamResponseReceived);
//This is the url on which to process the request
Request->SetURL(ServerUrl + "streams/" + StreamID);
Request->ProcessRequest();
}
/*Assigned function on successfull http call*/
void USpeckleUnrealReceiver::OnStreamResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Stream Request failed");
return;
}
//Create a pointer to hold the json serialized data
TSharedPtr<FJsonObject> ResponseJsonObject;
//Create a reader pointer to read the json data
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
//Deserialize the json data given Reader and the actual object to deserialize
if (FJsonSerializer::Deserialize(Reader, ResponseJsonObject))
{
//Get the value of the json object by field name
FString ResponseMessage = ResponseJsonObject->GetStringField("message");
TSharedPtr<FJsonObject> Stream = ResponseJsonObject->GetObjectField("resource");
FString StreamName = Stream->GetStringField("name");
FString StreamDescription = Stream->GetStringField("description");
FString Units = ResponseJsonObject->GetObjectField("baseProperties")->GetStringField("units").ToLower();
// unreal engine units are in cm by default but the conversion is editable by users so
// this needs to be accounted for later.
if (Units == "meters" || Units == "metres")
ScaleFactor = 100;
if (Units == "centimeters" || Units == "centimetres")
ScaleFactor = 1;
if (Units == "millimeters" || Units == "millimetres")
ScaleFactor = 0.1;
if (Units == "yards")
ScaleFactor = 91.4402757;
if (Units == "feet")
ScaleFactor = 30.4799990;
if (Units == "inches")
ScaleFactor = 2.5399986;
TArray<TSharedPtr<FJsonValue>> LayersInStream = Stream->GetArrayField("layers");
SpeckleUnrealLayers = TArray<ASpeckleUnrealLayer*>();
for (size_t i = 0; i < LayersInStream.Num(); i++)
{
TSharedPtr<FJsonObject> LayerObject = LayersInStream[i]->AsObject();
ASpeckleUnrealLayer* NewLayer = (ASpeckleUnrealLayer*)World->SpawnActor(ASpeckleUnrealLayer::StaticClass());
FString LayerName = LayerObject->GetStringField("name");
if (LayerName.Contains("::"))
{
TArray<FString> Parsed = TArray<FString> ();
int32 heirarchySize = LayerName.ParseIntoArray(Parsed, TEXT("::"), true);
LayerName = Parsed[heirarchySize - 1];
FString ParentLayerName = Parsed[heirarchySize - 2];
for (size_t j = 0; j < SpeckleUnrealLayers.Num(); j++)
{
if (SpeckleUnrealLayers[j]->LayerName == ParentLayerName)
{
EAttachmentRule rule = EAttachmentRule();
FAttachmentTransformRules rules = FAttachmentTransformRules(rule, false);
NewLayer->AttachToActor(SpeckleUnrealLayers[j], rules);
break;
}
}
}
int32 StartIndex = LayerObject->GetIntegerField("startIndex");
int32 ObjectCount = LayerObject->GetIntegerField("objectCount");
NewLayer->Init(LayerName, StartIndex, ObjectCount);
SpeckleUnrealLayers.Add(NewLayer);
}
//Output it to the engine
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, "Units: " + FString::SanitizeFloat(ScaleFactor));
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Green, "Status: " + ResponseMessage);
GEngine->AddOnScreenDebugMessage(2, 5.0f, FColor::Green, "Name: " + StreamName);
GEngine->AddOnScreenDebugMessage(3, 5.0f, FColor::Green, "Description: " + StreamDescription);
TArray<TSharedPtr<FJsonValue>> ObjectPlaceholderArray = Stream->GetArrayField("objects");
GetStreamObjects(ObjectPlaceholderArray.Num());
}
else
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Couldn't deserialize Json from stream response");
GEngine->AddOnScreenDebugMessage(2, 10.0f, FColor::Red, Response->GetContentAsString());
}
}
void USpeckleUnrealReceiver::GetStreamObjects(int32 objectCount)
{
ObjectsReceived = TArray<bool>();
int32 RequestLimit = 1;
CurrentObjectIndex = 0;
LayerIndex = 0;
for (size_t i = 0; i < objectCount; i += RequestLimit)
{
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
SetUpGetRequest(Request);
Request->OnProcessRequestComplete().BindUObject(this, &USpeckleUnrealReceiver::OnStreamObjectResponseReceived);
//This is the url on which to process the request
Request->SetURL(ServerUrl + "streams/" + StreamID + "/objects?limit=" + FString::FromInt(RequestLimit) + "&offset=" + FString::FromInt(i));
ObjectsReceived.Add(false);
Request->ProcessRequest();
}
}
void USpeckleUnrealReceiver::OnStreamObjectResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
int32 Offset = FCString::Atoi(*Request->GetURLParameter("offset"));
if (!bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Object Request failed");
return;
}
//Create a pointer to hold the json serialized data
TSharedPtr<FJsonObject> ResponseJsonObject;
//Create a reader pointer to read the json data
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
//Deserialize the json data given Reader and the actual object to deserialize
if (FJsonSerializer::Deserialize(Reader, ResponseJsonObject))
{
//Get the value of the json object by field name
TArray<TSharedPtr<FJsonValue>> StreamObjects = ResponseJsonObject->GetArrayField("resources");
for (size_t i = 0; i < SpeckleUnrealLayers.Num(); i++)
{
if (Offset >= SpeckleUnrealLayers[i]->StartIndex)
{
if (Offset < (SpeckleUnrealLayers[i]->StartIndex + SpeckleUnrealLayers[i]->ObjectCount))
LayerIndex = i;
}
}
for (size_t i = 0; i < StreamObjects.Num(); i++)
{
TSharedPtr<FJsonObject> StreamObject = StreamObjects[i].Get()->AsObject();
TSharedPtr<FJsonObject> ObjectToConvert = StreamObject;
FString objectType = ObjectToConvert->GetStringField("type");
if (objectType.ToLower().Contains("brep"))
{
ObjectToConvert = StreamObject->GetObjectField("displayValue");
objectType = ObjectToConvert->GetStringField("type");
}
if (objectType.ToLower().Contains("mesh"))
{
EAttachmentRule rule = EAttachmentRule();
FAttachmentTransformRules rules = FAttachmentTransformRules(rule, false);
ASpeckleUnrealMesh* MeshInstance = World->SpawnActor<ASpeckleUnrealMesh>(ASpeckleUnrealMesh::StaticClass());
TArray<TSharedPtr<FJsonValue>> ObjectVertices = ObjectToConvert->GetArrayField("vertices");
TArray<TSharedPtr<FJsonValue>> ObjectFaces = ObjectToConvert->GetArrayField("faces");
TArray<FVector> ParsedVerticies;
for (size_t j = 0; j < ObjectVertices.Num(); j += 3)
{
ParsedVerticies.Add(FVector
(
(float)(ObjectVertices[j].Get()->AsNumber()) * -1,
(float)(ObjectVertices[j + 1].Get()->AsNumber()),
(float)(ObjectVertices[j + 2].Get()->AsNumber())
) * ScaleFactor);
}
//convert mesh faces into triangle array regardless of whether or not they are quads
TArray<int32> ParsedTriangles;
int32 j = 0;
while (j < ObjectFaces.Num())
{
if (ObjectFaces[j].Get()->AsNumber() == 0)
{
//Triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
j += 4;
}
else
{
//Quads to triangles
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 2].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 3].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 1].Get()->AsNumber());
ParsedTriangles.Add(ObjectFaces[j + 4].Get()->AsNumber());
j += 5;
}
}
if (RandomColorsPerLayer)
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, DefaultMeshMaterial, SpeckleUnrealLayers[LayerIndex]->LayerColor);
else
MeshInstance->SetMesh(ParsedVerticies, ParsedTriangles, DefaultMeshMaterial, FLinearColor::White);
MeshInstance->AttachToActor(SpeckleUnrealLayers[LayerIndex], rules);
UE_LOG(LogTemp, Warning, TEXT("%d"), Offset);
UE_LOG(LogTemp, Warning, TEXT("%s"), *SpeckleUnrealLayers[LayerIndex]->LayerName);
}
}
}
else
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red, "Couldn't deserialize Json from object response");
GEngine->AddOnScreenDebugMessage(2, 10.0f, FColor::Red, Response->GetContentAsString());
}
ObjectsReceived[Offset] = true;
}
float USpeckleUnrealReceiver::GetStreamProgress()
{
if (ObjectsReceived.Num() == 0)
return 0;
float ProgressMade = 0;
for (size_t i = 0; i < ObjectsReceived.Num(); i++)
{
if (ObjectsReceived[i])
ProgressMade++;
}
return ProgressMade / ObjectsReceived.Num();
}
@@ -1,81 +0,0 @@
#pragma once
// logs
#include "Engine/Engine.h"
// json manipulation
#include "Dom/JsonObject.h"
#include "Dom/JsonValue.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
// web requests
#include "Runtime/Online/HTTP/Public/Http.h"
#include "SpeckleUnrealMesh.h"
#include "SpeckleUnrealLayer.h"
#include "GameFramework/Actor.h"
#include "SpeckleUnrealManager.generated.h"
UCLASS(BlueprintType)
class SPECKLEUNREAL_API ASpeckleUnrealManager : public AActor
{
GENERATED_BODY()
public:
FHttpModule* Http;
/* The actual HTTP call */
UFUNCTION()
void GetStream();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString ServerUrl {
"https://hestia.speckle.works/api/"
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString StreamID {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString AuthToken {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
TSubclassOf<ASpeckleUnrealMesh> MeshActor;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
UMaterialInterface* DefaultMeshMaterial;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
bool RandomColorsPerLayer;
TArray<ASpeckleUnrealLayer*> SpeckleUnrealLayers;
/*Assign this function to call when the GET request processes sucessfully*/
void OnStreamResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
// Sets default values for this actor's properties
ASpeckleUnrealManager();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
protected:
UWorld* World;
float ScaleFactor;
int32 LayerIndex;
int32 CurrentObjectIndex;
void SetUpGetRequest(TSharedRef<IHttpRequest> Request);
void GetStreamObjects(int32 objectCount);
void OnStreamObjectResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
};
@@ -1,35 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "SpeckleUnrealMesh.generated.h"
UCLASS(BlueprintType)
class SPECKLEUNREAL_API ASpeckleUnrealMesh : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere)
USceneComponent* Scene;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UProceduralMeshComponent* ProceduralMesh;
// Sets default values for this actor's properties
ASpeckleUnrealMesh();
virtual void SetMesh(TArray<FVector> Vertices, TArray<int32> Triangles, UMaterialInterface* Material, FLinearColor Color);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
@@ -1,91 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
// logs
#include "Engine/Engine.h"
// json manipulation
#include "Dom/JsonObject.h"
#include "Dom/JsonValue.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
// web requests
#include "Runtime/Online/HTTP/Public/Http.h"
#include "SpeckleUnrealMesh.h"
#include "SpeckleUnrealLayer.h"
#include "GameFramework/Actor.h"
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "SpeckleUnrealReceiver.generated.h"
/**
*
*/
UCLASS(BlueprintType)
class SPECKLEUNREAL_API USpeckleUnrealReceiver : public UObject
{
GENERATED_BODY()
public:
FHttpModule* Http;
/* The actual HTTP call */
UFUNCTION(BlueprintCallable, Category = "Speckle")
void ReceiveStream();
UFUNCTION(BlueprintCallable, Category = "Speckle")
float GetStreamProgress();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString ServerUrl {
"https://hestia.speckle.works/api/"
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString StreamID {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
FString AuthToken {
""
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
TSubclassOf<ASpeckleUnrealMesh> MeshActor;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
UMaterialInterface* DefaultMeshMaterial;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Speckle")
bool RandomColorsPerLayer;
TArray<ASpeckleUnrealLayer*> SpeckleUnrealLayers;
/*Assign this function to call when the GET request processes sucessfully*/
void OnStreamResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
// Sets default values for this actor's properties
USpeckleUnrealReceiver();
protected:
UWorld* World;
float ScaleFactor;
TArray<bool> ObjectsReceived;
int32 LayerIndex;
int32 CurrentObjectIndex;
void SetUpGetRequest(TSharedRef<IHttpRequest> Request);
void GetStreamObjects(int32 objectCount);
void OnStreamObjectResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
};
@@ -1,14 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class SpeckleUnrealProjectTarget : TargetRules
{
public SpeckleUnrealProjectTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "SpeckleUnrealProject" } );
}
}
@@ -1,23 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class SpeckleUnrealProject : ModuleRules
{
public SpeckleUnrealProject(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}
@@ -1,6 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SpeckleUnrealProject.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, SpeckleUnrealProject, "SpeckleUnrealProject" );
@@ -1,6 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
@@ -1,5 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SpeckleUnrealProjectGameModeBase.h"
@@ -1,17 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "SpeckleUnrealProjectGameModeBase.generated.h"
/**
*
*/
UCLASS()
class SPECKLEUNREALPROJECT_API ASpeckleUnrealProjectGameModeBase : public AGameModeBase
{
GENERATED_BODY()
};
@@ -1,14 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class SpeckleUnrealProjectEditorTarget : TargetRules
{
public SpeckleUnrealProjectEditorTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "SpeckleUnrealProject" } );
}
}
@@ -1,87 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28315.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{94A6C6F3-99B3-346E-9557-ABF9D4064DBD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Games", "Games", "{8E2F6A87-1826-34F4-940C-CC23A48F9FE4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UE4", "Intermediate\ProjectFiles\UE4.vcxproj", "{DE17FD13-E94F-4875-9509-F6023C8B5747}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpeckleUnrealProject", "Intermediate\ProjectFiles\SpeckleUnrealProject.vcxproj", "{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Visualizers", "Visualizers", "{1CCEC849-CC72-4C59-8C36-2F7C38706D4C}"
ProjectSection(SolutionItems) = preProject
..\..\..\Epic Games\UE_4.25\Engine\Extras\VisualStudioDebugging\UE4.natvis = ..\..\..\Epic Games\UE_4.25\Engine\Extras\VisualStudioDebugging\UE4.natvis
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
DebugGame Editor|Android = DebugGame Editor|Android
DebugGame Editor|Win32 = DebugGame Editor|Win32
DebugGame Editor|Win64 = DebugGame Editor|Win64
DebugGame|Android = DebugGame|Android
DebugGame|Win32 = DebugGame|Win32
DebugGame|Win64 = DebugGame|Win64
Development Editor|Android = Development Editor|Android
Development Editor|Win32 = Development Editor|Win32
Development Editor|Win64 = Development Editor|Win64
Development|Android = Development|Android
Development|Win32 = Development|Win32
Development|Win64 = Development|Win64
Shipping|Android = Shipping|Android
Shipping|Win32 = Shipping|Win32
Shipping|Win64 = Shipping|Win64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame Editor|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.DebugGame|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development Editor|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Development|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Shipping|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Shipping|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{DE17FD13-E94F-4875-9509-F6023C8B5747}.Shipping|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Android.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Win32.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame Editor|Win64.Build.0 = DebugGame_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Android.ActiveCfg = Android_DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Android.Build.0 = Android_DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win32.ActiveCfg = DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win32.Build.0 = DebugGame|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win64.ActiveCfg = DebugGame|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.DebugGame|Win64.Build.0 = DebugGame|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Android.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Win32.ActiveCfg = Invalid|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Win64.ActiveCfg = Development_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development Editor|Win64.Build.0 = Development_Editor|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Android.ActiveCfg = Android_Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Android.Build.0 = Android_Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win32.ActiveCfg = Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win32.Build.0 = Development|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win64.ActiveCfg = Development|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Development|Win64.Build.0 = Development|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Android.ActiveCfg = Android_Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Android.Build.0 = Android_Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win32.ActiveCfg = Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win32.Build.0 = Shipping|Win32
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win64.ActiveCfg = Shipping|x64
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9}.Shipping|Win64.Build.0 = Shipping|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{DE17FD13-E94F-4875-9509-F6023C8B5747} = {94A6C6F3-99B3-346E-9557-ABF9D4064DBD}
{A1185D9A-4D4D-4C9C-A9CD-D130F0A23FB9} = {8E2F6A87-1826-34F4-940C-CC23A48F9FE4}
EndGlobalSection
EndGlobal
@@ -1,22 +0,0 @@
{
"FileVersion": 3,
"EngineAssociation": "4.25",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "SpeckleUnrealProject",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"TargetPlatforms": [
"Android",
"AllDesktop",
"HoloLens",
"LinuxNoEditor",
"LinuxAArch64NoEditor",
"Lumin",
"WindowsNoEditor"
]
}