├── flexChop.png ├── CHOP ├── Toe │ ├── demo.toe │ ├── demo.24.toe │ ├── demo_torusCollision.6.toe │ └── demo_torusCollision.toe └── Source │ ├── GL_Extensions.h │ ├── FlexCHOP.sln │ ├── FlexCHOP.h │ ├── FlexCHOP.vcxproj.filters │ ├── FlexSystem.h │ ├── FlexCHOP.vcxproj │ ├── CHOP_CPlusPlusBase.h │ ├── FlexSystem.cpp │ ├── FlexCHOP.cpp │ └── CPlusPlus_Common.h ├── .gitattributes ├── LICENSE ├── .gitignore └── README.md /flexChop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinz9/FlexCHOP/HEAD/flexChop.png -------------------------------------------------------------------------------- /CHOP/Toe/demo.toe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinz9/FlexCHOP/HEAD/CHOP/Toe/demo.toe -------------------------------------------------------------------------------- /CHOP/Toe/demo.24.toe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinz9/FlexCHOP/HEAD/CHOP/Toe/demo.24.toe -------------------------------------------------------------------------------- /CHOP/Source/GL_Extensions.h: -------------------------------------------------------------------------------- 1 | // Stub file for simpler CHOP usage than an OpenGLTOP 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /CHOP/Toe/demo_torusCollision.6.toe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinz9/FlexCHOP/HEAD/CHOP/Toe/demo_torusCollision.6.toe -------------------------------------------------------------------------------- /CHOP/Toe/demo_torusCollision.toe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinz9/FlexCHOP/HEAD/CHOP/Toe/demo_torusCollision.toe -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /CHOP/Source/FlexCHOP.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlexCHOP", "FlexCHOP.vcxproj", "{24522312-1689-424A-A3D4-9989E676A274}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {24522312-1689-424A-A3D4-9989E676A274}.Debug|x64.ActiveCfg = Debug|x64 15 | {24522312-1689-424A-A3D4-9989E676A274}.Debug|x64.Build.0 = Debug|x64 16 | {24522312-1689-424A-A3D4-9989E676A274}.Release|x64.ActiveCfg = Release|x64 17 | {24522312-1689-424A-A3D4-9989E676A274}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vincent Houze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | flex/ 2 | 3 | *.opendb 4 | *.obj 5 | *.idb 6 | *.pdb 7 | *.ncb 8 | *.dep 9 | *.manifest 10 | *.suo 11 | *.ilk 12 | 13 | *.bak 14 | *.exp 15 | *.db 16 | *.exe 17 | VH*.lib 18 | BuildLog.htm 19 | 20 | ################# 21 | ## Eclipse 22 | ################# 23 | 24 | *.pydevproject 25 | .project 26 | .metadata 27 | bin/ 28 | tmp/ 29 | *.tmp 30 | *.bak 31 | *.swp 32 | *~.nib 33 | local.properties 34 | .classpath 35 | .settings/ 36 | .loadpath 37 | 38 | # External tool builders 39 | .externalToolBuilders/ 40 | 41 | # Locally stored "Eclipse launch configurations" 42 | *.launch 43 | 44 | # CDT-specific 45 | .cproject 46 | 47 | # PDT-specific 48 | .buildpath 49 | 50 | 51 | ################# 52 | ## Visual Studio 53 | ################# 54 | 55 | ## Ignore Visual Studio temporary files, build results, and 56 | ## files generated by popular Visual Studio add-ons. 57 | 58 | # User-specific files 59 | *.suo 60 | *.user 61 | *.sln.docstates 62 | 63 | # Build results 64 | [Dd]ebug/ 65 | [Rr]elease/ 66 | *_i.c 67 | *_p.c 68 | *.ilk 69 | *.meta 70 | *.obj 71 | *.pch 72 | *.pdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.vspscc 82 | .builds 83 | *.dotCover 84 | 85 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 86 | #packages/ 87 | 88 | # Visual C++ cache files 89 | ipch/ 90 | *.aps 91 | *.ncb 92 | *.opensdf 93 | *.sdf 94 | 95 | # Visual Studio profiler 96 | *.psess 97 | *.vsp 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper* 101 | 102 | # Installshield output folder 103 | [Ee]xpress 104 | 105 | # DocProject is a documentation generator add-in 106 | DocProject/buildhelp/ 107 | DocProject/Help/*.HxT 108 | DocProject/Help/*.HxC 109 | DocProject/Help/*.hhc 110 | DocProject/Help/*.hhk 111 | DocProject/Help/*.hhp 112 | DocProject/Help/Html2 113 | DocProject/Help/html 114 | 115 | # Click-Once directory 116 | publish 117 | 118 | # Others 119 | [Bb]in 120 | [Oo]bj 121 | sql 122 | TestResults 123 | *.Cache 124 | ClientBin 125 | stylecop.* 126 | ~$* 127 | *.dbmdl 128 | Generated_Code #added for RIA/Silverlight projects 129 | 130 | # Backup & report files from converting an old project file to a newer 131 | # Visual Studio version. Backup files are not needed, because we have git ;-) 132 | _UpgradeReport_Files/ 133 | Backup*/ 134 | UpgradeLog*.XML 135 | 136 | 137 | 138 | ############ 139 | ## Windows 140 | ############ 141 | 142 | # Windows image file caches 143 | Thumbs.db 144 | 145 | # Folder config file 146 | Desktop.ini 147 | 148 | 149 | ############# 150 | ## Python 151 | ############# 152 | 153 | *.py[co] 154 | 155 | # Packages 156 | *.egg 157 | *.egg-info 158 | dist 159 | build 160 | eggs 161 | parts 162 | bin 163 | var 164 | sdist 165 | develop-eggs 166 | .installed.cfg 167 | 168 | # Installer logs 169 | pip-log.txt 170 | 171 | # Unit test / coverage reports 172 | .coverage 173 | .tox 174 | 175 | #Translations 176 | *.mo 177 | 178 | #Mr Developer 179 | .mr.developer.cfg 180 | 181 | # Mac crap 182 | .DS_Store 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlexCHOP 2 | 3 | Look at the Releases page https://github.com/vinz9/FlexCHOP/releases for the compiled dlls for Windows. 4 | 5 | NVIDIA FleX 1.2 solver (https://developer.nvidia.com/flex) integration in TouchDesigner as a CHOP with a limited feature set : 6 | * liquid particles only 7 | * collisions against primitives : planes, boxes and spheres 8 | * collisions against a single animated and deforming triangle mesh 9 | * simple disc and rectangle emitters 10 | 11 | A NVIDIA graphics card is required since this uses the CUDA version of the library. Geforce 1070 or better recommended 12 | 13 | 14 | 15 | ## Usage 16 | 17 | Sample Toe files for TouchDesigner 099 are provided. 18 | demo.toe : emitters and collisions against primitives 19 | demo_torusCollision.toe : collisions against an animated and deforming triangle mesh 20 | Shift-R will reset the timeline to frame 1 which is set to reset the simulation in those examples (Reset parameter on the Chop). 21 | 22 | Most parameters on the FlexCHOP are parameters of the solver, please refer to the official FleX documentation at https://gameworksdocs.nvidia.com/FleX/1.2/lib_docs/index.html for more information. 23 | FPS determines the timestep of the simulation, higher FPS than TD FPS means the simulation will run in slow motion. 24 | There is a fixed maximum number of particles in the simulation, after the number is reached, older particles are recycled in new ones. 25 | 26 | Emission and primitive Collisions are handled using CHOPs, with one emitter/collider per chop sample. 27 | Refer to demo.toe to see which channels are required (channel order is important) 28 | 29 | ## Compilation 30 | To compile with Visual Studio 2015, download the FleX 1.2 library from https://github.com/NVIDIAGameWorks/FleX (you need to get access here first https://developer.nvidia.com/gameworks-source-github) and put the directories in a flex folder at the root of the repository (you should have flex/bin, flex/core, ... alongside CHOP/Source). 31 | You also need to manually copy the flex .dlls (NvFlexDeviceRelease_x64.dll, NvFlexReleaseCUDA_x64.dll) from flex\bin to CHOP\Source\Release or the FlexChop dll will fail to load. 32 | 33 | 34 | ## Disclaimer 35 | This is provided as is, mainly as a starting point for people interested in extending TouchDesigner with external c++ libraries. 36 | I have an ongoing TOP implementation in TouchDesigner, more optimized and with a more complete feature set which was used on a few projects of mine, such as Fluid Structure (https://vimeo.com/218695680) and lull, with AV&C (https://vimeo.com/154879680) 37 | 38 | ## Change Log 39 | 40 | 11/13/2019 0.4 : added support for collisions against a single animated and deforming triangle mesh. For TD 2019.10000+. To get the plugin to load with Experimental, copy cudart64_92.dll from TD 2019.10000+ alongside the other dlls. There is currently an issue with the GLSL shader for the instancing. 41 | 09/10/2019 0.3 : Upgrade to flex 1.2 42 | 09/10/2019 0.2 : Upgraded to work with 2019.14650 and later series of TouchDesigner (Spring 2019 Release), and be usable as a Custom OP. Thanks to Malcolm Bechard for the upgrade. 43 | 09/29/2017 0.1 : initial release, flex 1.1 library 44 | 45 | Vincent Houzé 46 | https://vincenthouze.com -------------------------------------------------------------------------------- /CHOP/Source/FlexCHOP.h: -------------------------------------------------------------------------------- 1 | #include "CHOP_CPlusPlusBase.h" 2 | 3 | #include "FlexSystem.h" 4 | 5 | using namespace std; 6 | 7 | /* 8 | 9 | This example file implements a class that does 2 different things depending on 10 | if a CHOP is connected to the CPlusPlus CHOPs input or not. 11 | The example is timesliced, which is the more complex way of working. 12 | 13 | If an input is connected the node will output the same number of channels as the 14 | input and divide the first 'N' samples in the input channel by 2. 'N' being the current 15 | timeslice size. This is noteworthy because if the input isn't changing then the output 16 | will look wierd since depending on the timeslice size some number of the first samples 17 | of the input will get used. 18 | 19 | If no input is connected then the node will output a smooth sine wave at 120hz. 20 | */ 21 | 22 | 23 | // To get more help about these functions, look at CHOP_CPlusPlusBase.h 24 | class FlexCHOP : public CHOP_CPlusPlusBase 25 | { 26 | public: 27 | FlexCHOP(const OP_NodeInfo *info); 28 | virtual ~FlexCHOP(); 29 | 30 | virtual void getGeneralInfo(CHOP_GeneralInfo*, const OP_Inputs *inputs, void* reserved1) override; 31 | virtual bool getOutputInfo(CHOP_OutputInfo*, const OP_Inputs *inputs, void *reserved1)override; 32 | virtual void getChannelName(int32_t index, OP_String *name, 33 | const OP_Inputs *inputs, void* reserved1) override; 34 | 35 | virtual void execute(CHOP_Output* outputs, 36 | const OP_Inputs* inputs, 37 | void* reserved1) override; 38 | 39 | 40 | virtual int32_t getNumInfoCHOPChans(void *reserved1) override; 41 | virtual void getInfoCHOPChan(int32_t index, OP_InfoCHOPChan* chan, void* reserved1) override; 42 | 43 | virtual bool getInfoDATSize(OP_InfoDATSize* infoSize, void *reserved1) override; 44 | virtual void getInfoDATEntries(int32_t index, int32_t nEntries, 45 | OP_InfoDATEntries* entries, 46 | void *reserved1) override; 47 | 48 | void updateParams(const OP_Inputs* inputs); 49 | 50 | virtual void setupParameters(OP_ParameterManager* manager, void* reserved1) override; 51 | //virtual void pulsePressed(const char* name) override; 52 | 53 | 54 | private: 55 | 56 | // We don't need to store this pointer, but we do for the example. 57 | // The CHOP_NodeInfo class store information about the node that's using 58 | // this instance of the class (like its name). 59 | const OP_NodeInfo *myNodeInfo; 60 | 61 | // In this example this value will be incremented each time the execute() 62 | // function is called, then passes back to the CHOP 63 | int myExecuteCount; 64 | 65 | float maxVel; 66 | 67 | int maxParticles; 68 | int activeIndicesSize; 69 | int inactiveIndicesSize; 70 | 71 | float timer; 72 | 73 | void updateTriangleMesh(const OP_Inputs* inputs); 74 | void initTriangleMesh(const OP_Inputs* inputs); 75 | 76 | void initVolumeBoxes(const OP_Inputs* inputs); 77 | void initEmitters(const OP_Inputs* inputs); 78 | 79 | void updatePlanes(const OP_Inputs* inputs); 80 | void updateEmitters(const OP_Inputs* inputs); 81 | void updateSpheresCols(const OP_Inputs* inputs); 82 | void updateBoxesCols(const OP_Inputs* inputs); 83 | 84 | void setupParamsCustom(OP_ParameterManager* manager); 85 | void setupParamsSolver(OP_ParameterManager* manager); 86 | void setupParamsParts(OP_ParameterManager* manager); 87 | void setupParamsEmission(OP_ParameterManager* manager); 88 | void setupParamsCollisions(OP_ParameterManager* manager); 89 | 90 | FlexSystem* FlexSys; 91 | 92 | enum { TX, TY, TZ, VX, VY, VZ, Q1X, Q1Y, Q1Z, Q1W, Q2X, Q2Y, Q2Z, Q2W, Q3X, Q3Y, Q3Z, Q3W }; 93 | 94 | }; 95 | -------------------------------------------------------------------------------- /CHOP/Source/FlexCHOP.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FlexCore 7 | 8 | 9 | FlexCore 10 | 11 | 12 | FlexCore 13 | 14 | 15 | FlexCore 16 | 17 | 18 | FlexCore 19 | 20 | 21 | FlexCore 22 | 23 | 24 | FlexCore 25 | 26 | 27 | FlexCore 28 | 29 | 30 | FlexCore 31 | 32 | 33 | FlexCore 34 | 35 | 36 | FlexCore 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Include 45 | 46 | 47 | Include 48 | 49 | 50 | Include 51 | 52 | 53 | FlexCore 54 | 55 | 56 | FlexCore 57 | 58 | 59 | FlexCore 60 | 61 | 62 | FlexCore 63 | 64 | 65 | FlexCore 66 | 67 | 68 | FlexCore 69 | 70 | 71 | FlexCore 72 | 73 | 74 | FlexCore 75 | 76 | 77 | FlexCore 78 | 79 | 80 | FlexCore 81 | 82 | 83 | FlexCore 84 | 85 | 86 | FlexCore 87 | 88 | 89 | FlexCore 90 | 91 | 92 | FlexCore 93 | 94 | 95 | FlexCore 96 | 97 | 98 | FlexCore 99 | 100 | 101 | FlexCore 102 | 103 | 104 | FlexCore 105 | 106 | 107 | FlexCore 108 | 109 | 110 | FlexCore 111 | 112 | 113 | FlexCore 114 | 115 | 116 | FlexCore 117 | 118 | 119 | FlexCore 120 | 121 | 122 | FlexCore 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | {4d575cfb-c9e1-46b3-8875-4c8193172647} 131 | 132 | 133 | {16536796-9b8b-4f92-8dd3-e9e5eeae4bca} 134 | 135 | 136 | -------------------------------------------------------------------------------- /CHOP/Source/FlexSystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | 12 | #include 13 | //#include 14 | 15 | #include 16 | 17 | 18 | #include 19 | #include 20 | 21 | 22 | using namespace std; 23 | 24 | inline float sqr(float x) { return x*x; } 25 | 26 | struct VMesh 27 | { 28 | 29 | uint32_t GetNumVertices() const { return uint32_t(m_positions.size()); } 30 | uint32_t GetNumFaces() const { return uint32_t(m_indices.size()) / 3; } 31 | 32 | void GetBounds(Vector3& minExtents, Vector3& maxExtents) const; 33 | 34 | Vector3 minExtents; 35 | Vector3 maxExtents; 36 | 37 | std::vector m_positions; 38 | std::vector m_indices; 39 | 40 | }; 41 | 42 | struct Emitter 43 | { 44 | Emitter() : mSpeed(0.0f), mEnabled(false), mLeftOver(0.0f), mWidth(8) {} 45 | 46 | Vec3 mPos; 47 | Vec3 mDir; 48 | Vec3 mRight; 49 | float mSpeed; 50 | bool mEnabled; 51 | float mLeftOver; 52 | int mWidth; 53 | }; 54 | 55 | struct RectEmitter 56 | { 57 | RectEmitter() : mSpeed(0.0f), mEnabled(false), mLeftOver(0.0f), mDisc(0), mNoise(0) {} 58 | 59 | Point3 mPos; 60 | Point3 mSize; 61 | Vec3 mRot; 62 | 63 | float mSpeed; 64 | bool mEnabled; 65 | float mLeftOver; 66 | 67 | int mDisc; 68 | int mNoise; 69 | float mNoiseThreshold; 70 | float mNoiseFreq; 71 | float mNoiseOffset; 72 | 73 | }; 74 | 75 | struct VolumeBox 76 | { 77 | VolumeBox() {} 78 | 79 | Point3 mPos; 80 | Point3 mSize; 81 | Vec3 mRot; 82 | 83 | 84 | }; 85 | 86 | struct GpuTimers 87 | { 88 | /*unsigned long long renderBegin; 89 | unsigned long long renderEnd; 90 | unsigned long long renderFreq;*/ 91 | unsigned long long computeBegin; 92 | unsigned long long computeEnd; 93 | unsigned long long computeFreq; 94 | 95 | /*static const int maxTimerCount = 4; 96 | double timers[benchmarkEndFrame][maxTimerCount]; 97 | int timerCount[benchmarkEndFrame];*/ 98 | }; 99 | 100 | struct SimBuffers 101 | { 102 | NvFlexVector positions; 103 | NvFlexVector restPositions; 104 | NvFlexVector velocities; 105 | NvFlexVector phases; 106 | NvFlexVector densities; 107 | NvFlexVector anisotropy1; 108 | NvFlexVector anisotropy2; 109 | NvFlexVector anisotropy3; 110 | NvFlexVector normals; 111 | NvFlexVector smoothPositions; 112 | NvFlexVector diffusePositions; 113 | NvFlexVector diffuseVelocities; 114 | NvFlexVector diffuseIndices; 115 | NvFlexVector activeIndices; 116 | 117 | // convexes 118 | NvFlexVector shapeGeometry; 119 | NvFlexVector shapePositions; 120 | NvFlexVector shapeRotations; 121 | NvFlexVector shapePrevPositions; 122 | NvFlexVector shapePrevRotations; 123 | NvFlexVector shapeFlags; 124 | 125 | // rigids 126 | NvFlexVector rigidOffsets; 127 | NvFlexVector rigidIndices; 128 | NvFlexVector rigidMeshSize; 129 | NvFlexVector rigidCoefficients; 130 | NvFlexVector rigidRotations; 131 | NvFlexVector rigidTranslations; 132 | NvFlexVector rigidLocalPositions; 133 | NvFlexVector rigidLocalNormals; 134 | 135 | // inflatables 136 | NvFlexVector inflatableTriOffsets; 137 | NvFlexVector inflatableTriCounts; 138 | NvFlexVector inflatableVolumes; 139 | NvFlexVector inflatableCoefficients; 140 | NvFlexVector inflatablePressures; 141 | 142 | // springs 143 | NvFlexVector springIndices; 144 | NvFlexVector springLengths; 145 | NvFlexVector springStiffness; 146 | 147 | NvFlexVector triangles; 148 | NvFlexVector triangleNormals; 149 | NvFlexVector uvs; 150 | 151 | 152 | SimBuffers(NvFlexLibrary* l); 153 | ~SimBuffers(); 154 | 155 | void MapBuffers(); 156 | void UnmapBuffers(); 157 | void InitBuffers(); 158 | 159 | }; 160 | 161 | 162 | 163 | class FlexSystem { 164 | 165 | public: 166 | 167 | FlexSystem(); 168 | ~FlexSystem(); 169 | 170 | void initSystem(); 171 | void initParams(); 172 | 173 | void getSimTimers(); 174 | 175 | void GetParticleBounds(Vec3& lower, Vec3& upper); 176 | void CreateParticleGrid(Vec3 lower, int dimx, int dimy, int dimz, float radius, Vec3 velocity, float invMass, bool rigid, float rigidStiffness, int phase, float jitter); 177 | void CreateCenteredParticleGrid(Point3 position, Vec3 rotation, Point3 size, float restDistance, Vec3 velocity, float invMass, bool rigid, int phase, float jitter=0.005f); 178 | 179 | void ClearShapes(); 180 | void setShapes(); 181 | 182 | void AddSphere(float radius, Vec3 position, Quat rotation); 183 | 184 | void AddBox(Vec3 halfEdge = Vec3(2.0f), Vec3 center = Vec3(0.0f), Quat quat = Quat(), bool dynamic = false); 185 | 186 | NvFlexTriangleMeshId CreateTriangleMesh(VMesh* m); 187 | void UpdateTriangleMesh(VMesh* m, NvFlexTriangleMeshId flexMeshId); 188 | void AddTriangleMesh(NvFlexTriangleMeshId mesh, Vec3 translation, Quat rotation, Vec3 prevTrans, Quat prevRot, Vec3 scale); 189 | 190 | 191 | void emission(); 192 | void update(); 193 | 194 | void initScene(); 195 | void postInitScene(); 196 | 197 | int deformingMesh; 198 | 199 | Vec3 curMeshTrans; 200 | Quat curMeshRot; 201 | 202 | Vec3 previousMeshTrans; 203 | Quat previousMeshRot; 204 | 205 | VMesh* g_triangleCollisionMesh; 206 | NvFlexTriangleMeshId triangleCollisionMeshId; 207 | 208 | NvFlexSolver* g_solver; 209 | NvFlexLibrary* g_flexLib; 210 | 211 | NvFlexParams g_params; 212 | NvFlexParams g_defaultParams; 213 | 214 | SimBuffers* g_buffers; 215 | 216 | vector g_inactiveIndices; 217 | 218 | bool g_profile; 219 | 220 | int activeParticles; 221 | int maxParticles; 222 | int numDiffuse; 223 | 224 | 225 | vector g_rectEmitters; 226 | vector g_volumeBoxes; 227 | 228 | 229 | int g_numSubsteps; 230 | float g_dt; // the time delta used for simulation 231 | 232 | Vec3 g_sceneLower; 233 | Vec3 g_sceneUpper; 234 | 235 | 236 | int g_maxDiffuseParticles; 237 | int g_maxContactsPerParticle; 238 | unsigned char g_maxNeighborsPerParticle; 239 | 240 | 241 | int nEmitter; 242 | int nVolumeBoxes; 243 | 244 | double time1; 245 | double time2; 246 | double time3; 247 | double time4; 248 | 249 | NvFlexTimers g_timers; 250 | 251 | float simLatency; 252 | 253 | int cursor; 254 | 255 | NvFlexSolverDesc g_solverDesc; 256 | 257 | GpuTimers g_GpuTimers; 258 | 259 | 260 | }; 261 | -------------------------------------------------------------------------------- /CHOP/Source/FlexCHOP.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {24522312-1689-424A-A3D4-9989E676A274} 23 | CPlusPlusCHOPExample 24 | Win32Proj 25 | FlexCHOP 26 | 27 | 28 | 29 | DynamicLibrary 30 | NotSet 31 | true 32 | v140 33 | 34 | 35 | DynamicLibrary 36 | NotSet 37 | true 38 | v140 39 | 40 | 41 | DynamicLibrary 42 | NotSet 43 | v140 44 | 45 | 46 | DynamicLibrary 47 | NotSet 48 | v140 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | <_ProjectFileVersion>10.0.40219.1 69 | $(SolutionDir)$(Configuration)\ 70 | $(SolutionDir)$(Configuration)\ 71 | $(Configuration)\ 72 | $(Configuration)\ 73 | false 74 | false 75 | $(SolutionDir)$(Configuration)\ 76 | $(SolutionDir)$(Configuration)\ 77 | $(Configuration)\ 78 | $(Configuration)\ 79 | false 80 | false 81 | 82 | 83 | 84 | Disabled 85 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPLUSPLUSCHOPEXAMPLE_EXPORTS;%(PreprocessorDefinitions) 86 | true 87 | EnableFastChecks 88 | MultiThreadedDebugDLL 89 | 90 | 91 | Level3 92 | ProgramDatabase 93 | ../flex/include 94 | 95 | 96 | true 97 | Windows 98 | MachineX86 99 | ../flex/lib/x64 100 | flexRelease_x64.lib;%(AdditionalDependencies) 101 | 102 | 103 | 104 | 105 | Disabled 106 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPLUSPLUSCHOPEXAMPLE_EXPORTS;%(PreprocessorDefinitions) 107 | EnableFastChecks 108 | MultiThreadedDebugDLL 109 | 110 | 111 | Level3 112 | ProgramDatabase 113 | ../../flex/include;../../flex/;$(CUDA_PATH_V8_0)/include;../../TOP/Source 114 | 115 | 116 | true 117 | Windows 118 | NvFlexReleaseCUDA_x64.lib;NvFlexExtReleaseCUDA_x64.lib;NvFlexDeviceRelease_x64.lib;cuda.lib;cudart.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 119 | ../../flex/lib/win64;$(CUDA_PATH_V9_2)\lib\x64 120 | 121 | 122 | 123 | 124 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPLUSPLUSCHOPEXAMPLE_EXPORTS;%(PreprocessorDefinitions) 125 | MultiThreadedDLL 126 | 127 | 128 | Level3 129 | ProgramDatabase 130 | 131 | 132 | true 133 | Windows 134 | true 135 | true 136 | MachineX86 137 | 138 | 139 | 140 | 141 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPLUSPLUSCHOPEXAMPLE_EXPORTS;%(PreprocessorDefinitions) 142 | MultiThreadedDLL 143 | 144 | 145 | Level3 146 | ProgramDatabase 147 | ../../flex/include;../../flex/;$(CUDA_PATH_V9_2)/include; 148 | 4244;4267 149 | 150 | 151 | true 152 | Windows 153 | true 154 | true 155 | NvFlexReleaseCUDA_x64.lib;NvFlexExtReleaseCUDA_x64.lib;NvFlexDeviceRelease_x64.lib;cuda.lib;cudart.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 156 | ../../flex/lib/win64;$(CUDA_PATH_V9_2)\lib\x64 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /CHOP/Source/CHOP_CPlusPlusBase.h: -------------------------------------------------------------------------------- 1 | /* Shared Use License: This file is owned by Derivative Inc. (Derivative) and 2 | * can only be used, and/or modified for use, in conjunction with 3 | * Derivative's TouchDesigner software, and only if you are a licensee who has 4 | * accepted Derivative's TouchDesigner license or assignment agreement (which 5 | * also govern the use of this file). You may share a modified version of this 6 | * file with another authorized licensee of Derivative's TouchDesigner software. 7 | * Otherwise, no redistribution or sharing of this file, with or without 8 | * modification, is permitted. 9 | */ 10 | 11 | /* 12 | * Produced by: 13 | * 14 | * Derivative Inc 15 | * 401 Richmond Street West, Unit 386 16 | * Toronto, Ontario 17 | * Canada M5V 3A8 18 | * 416-591-3555 19 | * 20 | * NAME: CHOP_CPlusPlusBase.h 21 | * 22 | * 23 | * Do not edit this file directly! 24 | * Make a subclass of CHOP_CPlusPlusBase instead, and add your own 25 | * data/functions. 26 | 27 | * Derivative Developers:: Make sure the virtual function order 28 | * stays the same, otherwise changes won't be backwards compatible 29 | */ 30 | 31 | #ifndef __CHOP_CPlusPlusBase__ 32 | #define __CHOP_CPlusPlusBase__ 33 | 34 | #include "CPlusPlus_Common.h" 35 | 36 | 37 | class CHOP_CPlusPlusBase; 38 | 39 | // Define for the current API version that this sample code is made for. 40 | // To upgrade to a newer version, replace the files 41 | // CHOP_CPlusPlusBase.h 42 | // CPlusPlus_Common.h 43 | // from the samples folder in a newer TouchDesigner installation. 44 | // You may need to upgrade your plugin code in that case, to match 45 | // the new API requirements 46 | const int CHOPCPlusPlusAPIVersion = 8; 47 | 48 | struct CHOP_PluginInfo 49 | { 50 | public: 51 | 52 | // Must be set to CHOPCPlusPlusAPIVersion in FillCHOPPluginInfo 53 | int32_t apiVersion = 0; 54 | 55 | int32_t reserved[100]; 56 | 57 | 58 | // Information used to describe this plugin as a custom OP. 59 | OP_CustomOPInfo customOPInfo; 60 | 61 | 62 | int32_t reserved2[20]; 63 | 64 | }; 65 | 66 | 67 | 68 | // These are the definitions for the C-functions that are used to 69 | // load the library and create instances of the object you define 70 | typedef void (__cdecl *FILLCHOPPLUGININFO)(CHOP_PluginInfo *info); 71 | typedef CHOP_CPlusPlusBase* (__cdecl *CREATECHOPINSTANCE)(const OP_NodeInfo*); 72 | typedef void (__cdecl *DESTROYCHOPINSTANCE)(CHOP_CPlusPlusBase*); 73 | 74 | 75 | class CHOP_GeneralInfo 76 | { 77 | public: 78 | // Set this to true if you want the CHOP to cook every frame, even 79 | // if none of it's inputs/parameters are changing 80 | // DEFAULT: false 81 | 82 | bool cookEveryFrame; 83 | 84 | // Set this to true if you want the CHOP to cook every frame, but only 85 | // if someone asks for it to cook. So if nobody is using the output from 86 | // the CHOP, it won't cook. This is difereent from 'cookEveryFrame' 87 | // since that will cause it to cook every frame no matter what. 88 | 89 | bool cookEveryFrameIfAsked; 90 | 91 | // Set this to true if you will be outputting a timeslice 92 | // Outputting a timeslice means the number of samples in the CHOP will 93 | // be determined by the number of frames that have elapsed since the last 94 | // time TouchDesigner cooked (it will be more than one in cases where it's 95 | // running slower than the target cook rate), the playbar framerate and 96 | // the sample rate of the CHOP. 97 | // For example if you are outputting the CHOP 120hz sample rate, 98 | // TouchDesigner is running at 60 hz cookrate, and you missed a frame last cook 99 | // then on this cook the number of sampels of the output of this CHOP will 100 | // be 4 samples. I.e (120 / 60) * number of playbar frames to output. 101 | // If this isn't set then you specify the number of sample in the CHOP using 102 | // the getOutputInfo() function 103 | // DEFAULT: false 104 | 105 | bool timeslice; 106 | 107 | // If you are returning 'false' from getOutputInfo, this index will 108 | // specify the CHOP input whos attribues you will match 109 | // (channel names, length, sample rate etc.) 110 | // DEFAULT : 0 111 | 112 | int32_t inputMatchIndex; 113 | 114 | 115 | int32_t reserved[20]; 116 | }; 117 | 118 | 119 | 120 | class CHOP_OutputInfo 121 | { 122 | public: 123 | 124 | // The number of channels you want to output 125 | 126 | int32_t numChannels; 127 | 128 | 129 | // If you arn't outputting a timeslice, specify the number of samples here 130 | 131 | int32_t numSamples; 132 | 133 | 134 | // if you arn't outputting a timeslice, specify the start index 135 | // of the channels here. This is the 'Start' you see when you 136 | // middle click on a CHOP 137 | 138 | uint32_t startIndex; 139 | 140 | 141 | // Specify the sample rate of the channel data 142 | // DEFAULT : whatever the timeline FPS is ($FPS) 143 | 144 | float sampleRate; 145 | 146 | 147 | void* reserved1; 148 | 149 | 150 | int32_t reserved[20]; 151 | 152 | }; 153 | 154 | 155 | 156 | 157 | 158 | class CHOP_Output 159 | { 160 | public: 161 | CHOP_Output(int32_t nc, int32_t l, float s, uint32_t st, 162 | float **cs, const char** ns): 163 | numChannels(nc), 164 | numSamples(l), 165 | sampleRate(s), 166 | startIndex(st), 167 | channels(cs), 168 | names(ns) 169 | { 170 | } 171 | 172 | // Info about what you are expected to output 173 | const int32_t numChannels; 174 | const int32_t numSamples; 175 | const float sampleRate; 176 | const uint32_t startIndex; 177 | 178 | // This is an array of const char* that tells you the channel names 179 | // of the channels you are providing values for. It's 'numChannels' long. 180 | // E.g names[3] is the name of the 4th channel 181 | const char** const names; 182 | 183 | // This is an array of float arrays that is already allocated for you. 184 | // Fill it with the data you want outputted for this CHOP. 185 | // The length of the array is 'numChannels', 186 | // While the length of each of the array entries is 'numSamples'. 187 | // For example channels[1][10] will point to the 11th sample in the 2nd 188 | // channel 189 | float** const channels; 190 | 191 | 192 | 193 | int32_t reserved[20]; 194 | }; 195 | 196 | 197 | 198 | /***** FUNCTION CALL ORDER DURING INITIALIZATION ******/ 199 | /* 200 | When the TOP loads the dll the functions will be called in this order 201 | 202 | setupParameters(OP_ParameterManager* m); 203 | 204 | */ 205 | 206 | /***** FUNCTION CALL ORDER DURING A COOK ******/ 207 | /* 208 | 209 | When the CHOP cooks the functions will be called in this order 210 | 211 | getGeneralInfo() 212 | getOutputInfo() 213 | if getOutputInfo() returns true 214 | { 215 | getChannelName() once for each channel needed 216 | } 217 | execute() 218 | getNumInfoCHOPChans() 219 | for the number of chans returned getNumInfoCHOPChans() 220 | { 221 | getInfoCHOPChan() 222 | } 223 | getInfoDATSize() 224 | for the number of rows/cols returned by getInfoDATSize() 225 | { 226 | getInfoDATEntries() 227 | } 228 | getInfoPopupString() 229 | getWarningString() 230 | getErrorString() 231 | */ 232 | 233 | /*** DO NOT EDIT THIS CLASS, MAKE A SUBCLASS OF IT INSTEAD ***/ 234 | class CHOP_CPlusPlusBase 235 | { 236 | protected: 237 | CHOP_CPlusPlusBase() 238 | { 239 | } 240 | 241 | virtual ~CHOP_CPlusPlusBase() 242 | { 243 | } 244 | 245 | public: 246 | 247 | 248 | // BEGIN PUBLIC INTERFACE 249 | 250 | // Some general settings can be assigned here (if you override it) 251 | virtual void 252 | getGeneralInfo(CHOP_GeneralInfo*, const OP_Inputs *inputs, void* reserved1) 253 | { 254 | } 255 | 256 | 257 | // This function is called so the class can tell the CHOP how many 258 | // channels it wants to output, how many samples etc. 259 | // Return true if you specify the output here. 260 | // Return false if you want the output to be set by matching 261 | // the channel names, numSamples, sample rate etc. of one of your inputs 262 | // The input that is used is chosen by setting the 'inputMatchIndex' 263 | // memeber in CHOP_OutputInfo 264 | // The CHOP_OutputInfo class is pre-filled with what the CHOP would 265 | // output if you return false, so you can just tweak a few settings 266 | // and return true if you want 267 | virtual bool 268 | getOutputInfo(CHOP_OutputInfo*, const OP_Inputs *inputs, void *reserved1) 269 | { 270 | return false; 271 | } 272 | 273 | 274 | // This function will be called after getOutputInfo() asking for 275 | // the channel names. It will get called once for each channel name 276 | // you need to specify. If you returned 'false' from getOutputInfo() 277 | // it won't be called. 278 | virtual void 279 | getChannelName(int32_t index, OP_String *name, 280 | const OP_Inputs *inputs, void* reserved1) 281 | { 282 | name->setString("chan1"); 283 | } 284 | 285 | 286 | // In this function you do whatever you want to fill the output channels 287 | // which are already allocated for you in 'outputs' 288 | virtual void execute(CHOP_Output* outputs, 289 | const OP_Inputs* inputs, 290 | void* reserved1) = 0; 291 | 292 | 293 | // Override these methods if you want to output values to the Info CHOP/DAT 294 | // returning 0 means you dont plan to output any Info CHOP channels 295 | virtual int32_t 296 | getNumInfoCHOPChans(void *reserved1) 297 | { 298 | return 0; 299 | } 300 | 301 | // Specify the name and value for Info CHOP channel 'index', 302 | // by assigning something to 'name' and 'value' members of the 303 | // OP_InfoCHOPChan class pointer that is passed in. 304 | virtual void 305 | getInfoCHOPChan(int32_t index, OP_InfoCHOPChan* chan, void* reserved1) 306 | { 307 | } 308 | 309 | 310 | // Return false if you arn't returning data for an Info DAT 311 | // Return true if you are. 312 | // Set the members of the CHOP_InfoDATSize class to specify 313 | // the dimensions of the Info DAT 314 | virtual bool 315 | getInfoDATSize(OP_InfoDATSize* infoSize, void *reserved1) 316 | { 317 | return false; 318 | } 319 | 320 | // You are asked to assign values to the Info DAT 1 row or column at a time 321 | // The 'byColumn' variable in 'getInfoDATSize' is how you specify 322 | // if it is by column or by row. 323 | // 'index' is the row/column index 324 | // 'nEntries' is the number of entries in the row/column 325 | // Strings should be UTF-8 encoded. 326 | virtual void 327 | getInfoDATEntries(int32_t index, int32_t nEntries, 328 | OP_InfoDATEntries* entries, 329 | void *reserved1) 330 | { 331 | } 332 | 333 | // You can use this function to put the node into a warning state 334 | // by calling setSting() on 'warning' with a non empty string. 335 | // Leave 'warning' unchanged to not go into warning state. 336 | virtual void 337 | getWarningString(OP_String *warning, void *reserved1) 338 | { 339 | } 340 | 341 | // You can use this function to put the node into a error state 342 | // by calling setSting() on 'error' with a non empty string. 343 | // Leave 'error' unchanged to not go into error state. 344 | virtual void 345 | getErrorString(OP_String *error, void *reserved1) 346 | { 347 | } 348 | 349 | // Use this function to return some text that will show up in the 350 | // info popup (when you middle click on a node) 351 | // call setString() on info and give it some info if desired. 352 | virtual void 353 | getInfoPopupString(OP_String *info, void *reserved1) 354 | { 355 | } 356 | 357 | 358 | // Override these methods if you want to define specfic parameters 359 | virtual void 360 | setupParameters(OP_ParameterManager* manager, void* reserved1) 361 | { 362 | } 363 | 364 | 365 | // This is called whenever a pulse parameter is pressed 366 | virtual void 367 | pulsePressed(const char* name, void* reserved1) 368 | { 369 | } 370 | 371 | // END PUBLIC INTERFACE 372 | 373 | 374 | private: 375 | 376 | // Reserved for future features 377 | virtual int32_t reservedFunc6() { return 0; } 378 | virtual int32_t reservedFunc7() { return 0; } 379 | virtual int32_t reservedFunc8() { return 0; } 380 | virtual int32_t reservedFunc9() { return 0; } 381 | virtual int32_t reservedFunc10() { return 0; } 382 | virtual int32_t reservedFunc11() { return 0; } 383 | virtual int32_t reservedFunc12() { return 0; } 384 | virtual int32_t reservedFunc13() { return 0; } 385 | virtual int32_t reservedFunc14() { return 0; } 386 | virtual int32_t reservedFunc15() { return 0; } 387 | virtual int32_t reservedFunc16() { return 0; } 388 | virtual int32_t reservedFunc17() { return 0; } 389 | virtual int32_t reservedFunc18() { return 0; } 390 | virtual int32_t reservedFunc19() { return 0; } 391 | virtual int32_t reservedFunc20() { return 0; } 392 | 393 | int32_t reserved[400]; 394 | 395 | }; 396 | 397 | static_assert(offsetof(CHOP_PluginInfo, apiVersion) == 0, "Incorrect Alignment"); 398 | static_assert(offsetof(CHOP_PluginInfo, customOPInfo) == 408, "Incorrect Alignment"); 399 | static_assert(sizeof(CHOP_PluginInfo) == 944, "Incorrect Size"); 400 | 401 | static_assert(offsetof(CHOP_GeneralInfo, cookEveryFrame) == 0, "Incorrect Alignment"); 402 | static_assert(offsetof(CHOP_GeneralInfo, cookEveryFrameIfAsked) == 1, "Incorrect Alignment"); 403 | static_assert(offsetof(CHOP_GeneralInfo, timeslice) == 2, "Incorrect Alignment"); 404 | static_assert(offsetof(CHOP_GeneralInfo, inputMatchIndex) == 4, "Incorrect Alignment"); 405 | static_assert(sizeof(CHOP_GeneralInfo) == 88, "Incorrect Size"); 406 | 407 | static_assert(offsetof(CHOP_OutputInfo, numChannels) == 0, "Incorrect Alignment"); 408 | static_assert(offsetof(CHOP_OutputInfo, numSamples) == 4, "Incorrect Alignment"); 409 | static_assert(offsetof(CHOP_OutputInfo, startIndex) == 8, "Incorrect Alignment"); 410 | static_assert(offsetof(CHOP_OutputInfo, sampleRate) == 12, "Incorrect Alignment"); 411 | static_assert(offsetof(CHOP_OutputInfo, reserved1) == 16, "Incorrect Alignment"); 412 | static_assert(sizeof(CHOP_OutputInfo) == 104, "Incorrect Size"); 413 | 414 | static_assert(offsetof(CHOP_Output, numChannels) == 0, "Incorrect Alignment"); 415 | static_assert(offsetof(CHOP_Output, numSamples) == 4, "Incorrect Alignment"); 416 | static_assert(offsetof(CHOP_Output, sampleRate) == 8, "Incorrect Alignment"); 417 | static_assert(offsetof(CHOP_Output, startIndex) == 12, "Incorrect Alignment"); 418 | static_assert(offsetof(CHOP_Output, names) == 16, "Incorrect Alignment"); 419 | static_assert(offsetof(CHOP_Output, channels) == 24, "Incorrect Alignment"); 420 | static_assert(sizeof(CHOP_Output) == 112, "Incorrect Size"); 421 | #endif 422 | -------------------------------------------------------------------------------- /CHOP/Source/FlexSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "FlexSystem.h" 2 | 3 | 4 | void ErrorCallback(NvFlexErrorSeverity, const char* msg, const char* file, int line) 5 | { 6 | printf("Flex: %s - %s:%d\n", msg, file, line); 7 | //g_Error = true; 8 | //assert(0); asserts are bad for TeamCity 9 | } 10 | 11 | 12 | void VMesh::GetBounds(Vector3& outMinExtents, Vector3& outMaxExtents) const 13 | { 14 | Point3 minExtents(FLT_MAX); 15 | Point3 maxExtents(-FLT_MAX); 16 | 17 | // calculate face bounds 18 | for (uint32_t i = 0; i < m_positions.size(); ++i) 19 | { 20 | const Point3& a = Point3(m_positions[i].x, m_positions[i].y, m_positions[i].z); 21 | 22 | minExtents = Min(a, minExtents); 23 | maxExtents = Max(a, maxExtents); 24 | } 25 | 26 | outMinExtents = Vector3(minExtents); 27 | outMaxExtents = Vector3(maxExtents); 28 | } 29 | 30 | SimBuffers::SimBuffers(NvFlexLibrary* l) : 31 | positions(l), restPositions(l), velocities(l), phases(l), densities(l), 32 | anisotropy1(l), anisotropy2(l), anisotropy3(l), normals(l), smoothPositions(l), 33 | diffusePositions(l), diffuseVelocities(l), diffuseIndices(l), activeIndices(l), 34 | shapeGeometry(l), shapePositions(l), shapeRotations(l), shapePrevPositions(l), 35 | shapePrevRotations(l), shapeFlags(l), rigidOffsets(l), rigidIndices(l), rigidMeshSize(l), 36 | rigidCoefficients(l), rigidRotations(l), rigidTranslations(l), 37 | rigidLocalPositions(l), rigidLocalNormals(l), inflatableTriOffsets(l), 38 | inflatableTriCounts(l), inflatableVolumes(l), inflatableCoefficients(l), 39 | inflatablePressures(l), springIndices(l), springLengths(l), 40 | springStiffness(l), triangles(l), triangleNormals(l), uvs(l){ 41 | 42 | 43 | } 44 | 45 | SimBuffers::~SimBuffers() { 46 | 47 | positions.destroy(); 48 | restPositions.destroy(); 49 | velocities.destroy(); 50 | phases.destroy(); 51 | densities.destroy(); 52 | anisotropy1.destroy(); 53 | anisotropy2.destroy(); 54 | anisotropy3.destroy(); 55 | normals.destroy(); 56 | diffusePositions.destroy(); 57 | diffuseVelocities.destroy(); 58 | diffuseIndices.destroy(); 59 | smoothPositions.destroy(); 60 | activeIndices.destroy(); 61 | 62 | // convexes 63 | shapeGeometry.destroy(); 64 | shapePositions.destroy(); 65 | shapeRotations.destroy(); 66 | shapePrevPositions.destroy(); 67 | shapePrevRotations.destroy(); 68 | shapeFlags.destroy(); 69 | 70 | // rigids 71 | rigidOffsets.destroy(); 72 | rigidIndices.destroy(); 73 | rigidMeshSize.destroy(); 74 | rigidCoefficients.destroy(); 75 | rigidRotations.destroy(); 76 | rigidTranslations.destroy(); 77 | rigidLocalPositions.destroy(); 78 | rigidLocalNormals.destroy(); 79 | 80 | // springs 81 | springIndices.destroy(); 82 | springLengths.destroy(); 83 | springStiffness.destroy(); 84 | 85 | // inflatables 86 | inflatableTriOffsets.destroy(); 87 | inflatableTriCounts.destroy(); 88 | inflatableVolumes.destroy(); 89 | inflatableCoefficients.destroy(); 90 | inflatablePressures.destroy(); 91 | 92 | // triangles 93 | triangles.destroy(); 94 | triangleNormals.destroy(); 95 | uvs.destroy(); 96 | 97 | 98 | } 99 | 100 | void SimBuffers::MapBuffers() { 101 | 102 | positions.map(); 103 | restPositions.map(); 104 | velocities.map(); 105 | phases.map(); 106 | densities.map(); 107 | anisotropy1.map(); 108 | anisotropy2.map(); 109 | anisotropy3.map(); 110 | normals.map(); 111 | diffusePositions.map(); 112 | diffuseVelocities.map(); 113 | diffuseIndices.map(); 114 | smoothPositions.map(); 115 | activeIndices.map(); 116 | 117 | // convexes 118 | shapeGeometry.map(); 119 | shapePositions.map(); 120 | shapeRotations.map(); 121 | shapePrevPositions.map(); 122 | shapePrevRotations.map(); 123 | shapeFlags.map(); 124 | 125 | rigidOffsets.map(); 126 | rigidIndices.map(); 127 | rigidMeshSize.map(); 128 | rigidCoefficients.map(); 129 | rigidRotations.map(); 130 | rigidTranslations.map(); 131 | rigidLocalPositions.map(); 132 | rigidLocalNormals.map(); 133 | 134 | springIndices.map(); 135 | springLengths.map(); 136 | springStiffness.map(); 137 | 138 | // inflatables 139 | inflatableTriOffsets.map(); 140 | inflatableTriCounts.map(); 141 | inflatableVolumes.map(); 142 | inflatableCoefficients.map(); 143 | inflatablePressures.map(); 144 | 145 | triangles.map(); 146 | triangleNormals.map(); 147 | uvs.map(); 148 | 149 | 150 | } 151 | 152 | void SimBuffers::UnmapBuffers() { 153 | 154 | // particles 155 | positions.unmap(); 156 | restPositions.unmap(); 157 | velocities.unmap(); 158 | phases.unmap(); 159 | densities.unmap(); 160 | anisotropy1.unmap(); 161 | anisotropy2.unmap(); 162 | anisotropy3.unmap(); 163 | normals.unmap(); 164 | diffusePositions.unmap(); 165 | diffuseVelocities.unmap(); 166 | diffuseIndices.unmap(); 167 | smoothPositions.unmap(); 168 | activeIndices.unmap(); 169 | 170 | // convexes 171 | shapeGeometry.unmap(); 172 | shapePositions.unmap(); 173 | shapeRotations.unmap(); 174 | shapePrevPositions.unmap(); 175 | shapePrevRotations.unmap(); 176 | shapeFlags.unmap(); 177 | 178 | // rigids 179 | rigidOffsets.unmap(); 180 | rigidIndices.unmap(); 181 | rigidMeshSize.unmap(); 182 | rigidCoefficients.unmap(); 183 | rigidRotations.unmap(); 184 | rigidTranslations.unmap(); 185 | rigidLocalPositions.unmap(); 186 | rigidLocalNormals.unmap(); 187 | 188 | // springs 189 | springIndices.unmap(); 190 | springLengths.unmap(); 191 | springStiffness.unmap(); 192 | 193 | // inflatables 194 | inflatableTriOffsets.unmap(); 195 | inflatableTriCounts.unmap(); 196 | inflatableVolumes.unmap(); 197 | inflatableCoefficients.unmap(); 198 | inflatablePressures.unmap(); 199 | 200 | // triangles 201 | triangles.unmap(); 202 | triangleNormals.unmap(); 203 | uvs.unmap(); 204 | 205 | } 206 | 207 | void SimBuffers::InitBuffers() { 208 | 209 | positions.resize(0); 210 | velocities.resize(0); 211 | phases.resize(0); 212 | 213 | /*rigidOffsets.resize(0); 214 | rigidIndices.resize(0); 215 | rigidMeshSize.resize(0); 216 | rigidRotations.resize(0); 217 | rigidTranslations.resize(0); 218 | rigidCoefficients.resize(0); 219 | rigidLocalPositions.resize(0); 220 | rigidLocalNormals.resize(0); 221 | 222 | springIndices.resize(0); 223 | springLengths.resize(0); 224 | springStiffness.resize(0); 225 | triangles.resize(0); 226 | triangleNormals.resize(0); 227 | uvs.resize(0);*/ 228 | 229 | shapeGeometry.resize(0); 230 | shapePositions.resize(0); 231 | shapeRotations.resize(0); 232 | shapePrevPositions.resize(0); 233 | shapePrevRotations.resize(0); 234 | shapeFlags.resize(0); 235 | 236 | } 237 | 238 | FlexSystem::FlexSystem() 239 | { 240 | 241 | g_profile = false; 242 | 243 | nEmitter = 0; 244 | nVolumeBoxes = 0; 245 | 246 | maxParticles = 9; 247 | g_maxDiffuseParticles=0; 248 | numDiffuse = 0; 249 | 250 | g_solver = NULL; 251 | 252 | deformingMesh = 0; 253 | g_triangleCollisionMesh = NULL; 254 | triangleCollisionMeshId = NULL; 255 | 256 | time1 = 0; 257 | time2 = 0; 258 | time3 = 0; 259 | time4 = 0; 260 | 261 | memset(&g_timers, 0, sizeof(g_timers)); 262 | 263 | cursor = 0; 264 | 265 | } 266 | 267 | FlexSystem::~FlexSystem(){ 268 | 269 | if (g_triangleCollisionMesh) { 270 | delete g_triangleCollisionMesh; 271 | g_triangleCollisionMesh = NULL; 272 | } 273 | 274 | if (triangleCollisionMeshId) { 275 | NvFlexDestroyTriangleMesh(g_flexLib, triangleCollisionMeshId); 276 | triangleCollisionMeshId = NULL; 277 | } 278 | 279 | if (g_solver) 280 | { 281 | if(g_buffers) 282 | delete g_buffers; 283 | 284 | NvFlexDestroySolver(g_solver); 285 | NvFlexShutdown(g_flexLib); 286 | } 287 | 288 | 289 | 290 | NvFlexDeviceDestroyCudaContext(); 291 | 292 | } 293 | 294 | void FlexSystem::getSimTimers() { 295 | 296 | if (g_profile) { 297 | memset(&g_timers, 0, sizeof(g_timers)); 298 | NvFlexGetTimers(g_solver, &g_timers); 299 | simLatency = g_timers.total; 300 | } 301 | else 302 | //simLatency = NvFlexGetDeviceLatency(g_flex); 303 | simLatency = NvFlexGetDeviceLatency(g_solver, &g_GpuTimers.computeBegin, &g_GpuTimers.computeEnd, &g_GpuTimers.computeFreq); 304 | //simLatency = 0; 305 | } 306 | 307 | void FlexSystem::initSystem() { 308 | 309 | int g_device = -1; 310 | g_device = NvFlexDeviceGetSuggestedOrdinal(); 311 | 312 | // Create an optimized CUDA context for Flex and set it on the 313 | // calling thread. This is an optional call, it is fine to use 314 | // a regular CUDA context, although creating one through this API 315 | // is recommended for best performance. 316 | bool success = NvFlexDeviceCreateCudaContext(g_device); 317 | 318 | if (!success) 319 | { 320 | printf("Error creating CUDA context.\n"); 321 | } 322 | 323 | NvFlexInitDesc desc; 324 | desc.deviceIndex = g_device; 325 | desc.enableExtensions = true; 326 | desc.renderDevice = 0; 327 | desc.renderContext = 0; 328 | desc.computeType = eNvFlexCUDA; 329 | 330 | g_flexLib = NvFlexInit(NV_FLEX_VERSION, ErrorCallback, &desc); 331 | 332 | } 333 | 334 | 335 | void FlexSystem::initParams() { 336 | 337 | // sim params 338 | g_params.gravity[0] = 0.0f; 339 | g_params.gravity[1] = -9.8f; 340 | g_params.gravity[2] = 0.0f; 341 | 342 | g_params.wind[0] = 0.0f; 343 | g_params.wind[1] = 0.0f; 344 | g_params.wind[2] = 0.0f; 345 | 346 | g_params.radius = 0.15f; 347 | g_params.viscosity = 0.0f; 348 | g_params.dynamicFriction = 0.0f; 349 | g_params.staticFriction = 0.0f; 350 | g_params.particleFriction = 0.0f; // scale friction between particles by default 351 | g_params.freeSurfaceDrag = 0.0f; 352 | g_params.drag = 0.0f; 353 | g_params.lift = 0.0f; 354 | g_params.numIterations = 3; 355 | g_params.fluidRestDistance = 0.0f; 356 | g_params.solidRestDistance = 0.0f; 357 | 358 | g_params.anisotropyScale = 1.0f; 359 | g_params.anisotropyMin = 0.1f; 360 | g_params.anisotropyMax = 2.0f; 361 | g_params.smoothing = 1.0f; 362 | 363 | g_params.dissipation = 0.0f; 364 | g_params.damping = 0.0f; 365 | g_params.particleCollisionMargin = 0.0f; 366 | g_params.shapeCollisionMargin = 0.0f; 367 | g_params.collisionDistance = 0.0f; 368 | /*g_params.plasticThreshold = 0.0f; 369 | g_params.plasticCreep = 0.0f; 370 | g_params.fluid = true;*/ 371 | g_params.sleepThreshold = 0.0f; 372 | g_params.shockPropagation = 0.0f; 373 | g_params.restitution = 0.001f; 374 | 375 | g_params.maxSpeed = FLT_MAX; 376 | g_params.maxAcceleration = 100.0f; // approximately 10x gravity 377 | 378 | 379 | g_params.relaxationMode = eNvFlexRelaxationLocal; 380 | g_params.relaxationFactor = 1.0f; 381 | g_params.solidPressure = 1.0f; 382 | g_params.adhesion = 0.0f; 383 | g_params.cohesion = 0.1f; 384 | g_params.surfaceTension = 0.0f; 385 | g_params.vorticityConfinement = 80.0f; 386 | g_params.buoyancy = 1.0f; 387 | g_params.diffuseThreshold = 100.0f; 388 | g_params.diffuseBuoyancy = 1.0f; 389 | g_params.diffuseDrag = 0.8f; 390 | g_params.diffuseBallistic = 16; 391 | /*g_params.diffuseSortAxis[0] = 0.0f; 392 | g_params.diffuseSortAxis[1] = 0.0f; 393 | g_params.diffuseSortAxis[2] = 0.0f;*/ 394 | g_params.diffuseLifetime = 2.0f; 395 | 396 | g_params.numPlanes = 0; 397 | 398 | //g_params.collisionDistance = 0.05f; 399 | //g_params.shapeCollisionMargin = 0.00001f; 400 | 401 | } 402 | 403 | void FlexSystem::initScene(){ 404 | 405 | 406 | RandInit(); 407 | 408 | cursor = 0; 409 | 410 | 411 | if (g_solver) 412 | { 413 | 414 | if (g_buffers) 415 | delete g_buffers; 416 | 417 | NvFlexDestroySolver(g_solver); 418 | g_solver = NULL; 419 | 420 | if (g_triangleCollisionMesh) { 421 | delete g_triangleCollisionMesh; 422 | g_triangleCollisionMesh = NULL; 423 | } 424 | 425 | } 426 | 427 | // alloc buffers 428 | g_buffers = new SimBuffers(g_flexLib); 429 | 430 | // map during initialization 431 | g_buffers->MapBuffers(); 432 | g_buffers->InitBuffers(); 433 | 434 | g_rectEmitters.resize(0); 435 | g_volumeBoxes.resize(0); 436 | 437 | initParams(); 438 | 439 | g_numSubsteps = 2; 440 | 441 | g_sceneLower = FLT_MAX; 442 | g_sceneUpper = -FLT_MAX; 443 | 444 | // initialize solver desc 445 | NvFlexSetSolverDescDefaults(&g_solverDesc); 446 | 447 | ClearShapes(); 448 | 449 | 450 | maxParticles = 64; 451 | 452 | g_maxDiffuseParticles = 0; 453 | g_maxNeighborsPerParticle = 96; 454 | 455 | g_maxContactsPerParticle = 6; 456 | 457 | } 458 | 459 | void FlexSystem::postInitScene(){ 460 | 461 | 462 | g_solverDesc.featureMode = eNvFlexFeatureModeSimpleFluids; 463 | 464 | g_params.fluidRestDistance = g_params.radius*0.65f; 465 | 466 | if (g_params.solidRestDistance == 0.0f) 467 | g_params.solidRestDistance = g_params.radius; 468 | 469 | // if fluid present then we assume solid particles have the same radius 470 | if (g_params.fluidRestDistance > 0.0f) 471 | g_params.solidRestDistance = g_params.fluidRestDistance; 472 | 473 | // set collision distance automatically based on rest distance if not alraedy set 474 | if (g_params.collisionDistance == 0.0f) 475 | g_params.collisionDistance = Max(g_params.solidRestDistance, g_params.fluidRestDistance)*0.5f; 476 | 477 | // default particle friction to 10% of shape friction 478 | if (g_params.particleFriction == 0.0f) 479 | g_params.particleFriction = g_params.dynamicFriction*0.1f; 480 | 481 | // add a margin for detecting contacts between particles and shapes 482 | if (g_params.shapeCollisionMargin == 0.0f) 483 | g_params.shapeCollisionMargin = g_params.collisionDistance*0.5f; 484 | 485 | 486 | g_maxDiffuseParticles = 0; 487 | 488 | //if (g_params.fluid) { 489 | 490 | for (int i = 0; i < nVolumeBoxes; i++) { 491 | 492 | CreateCenteredParticleGrid(Point3(g_volumeBoxes[i].mPos.x, g_volumeBoxes[i].mPos.y, g_volumeBoxes[i].mPos.z), g_volumeBoxes[i].mRot, Point3(g_volumeBoxes[i].mSize.x, g_volumeBoxes[i].mSize.y, g_volumeBoxes[i].mSize.z), g_params.fluidRestDistance, Vec3(0.0f), 1, false, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), g_params.fluidRestDistance*0.01f); 493 | } 494 | /*} 495 | else { 496 | 497 | for (int i = 0; i < nVolumeBoxes; i++) { 498 | 499 | CreateCenteredParticleGrid(Point3(g_volumeBoxes[i].mPos.x, g_volumeBoxes[i].mPos.y, g_volumeBoxes[i].mPos.z), g_volumeBoxes[i].mRot, Point3(g_volumeBoxes[i].mSize.x, g_volumeBoxes[i].mSize.y, g_volumeBoxes[i].mSize.z), g_params.radius, Vec3(0.0f), 1, false, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), g_params.radius*0.01f); 500 | } 501 | 502 | }*/ 503 | 504 | //g_params.anisotropyScale = 3.0f/g_params.radius; 505 | g_params.anisotropyScale = 1.0f; 506 | 507 | //*******CREATE TRIANGLE MESH 508 | 509 | if (g_triangleCollisionMesh) { 510 | triangleCollisionMeshId = CreateTriangleMesh(g_triangleCollisionMesh); 511 | } 512 | 513 | uint32_t numParticles = g_buffers->positions.size(); //non zero if init volume boxes 514 | 515 | //**** IF PARTICLE GRID 516 | 517 | if (g_buffers->positions.size()) { 518 | g_buffers->activeIndices.resize(numParticles); 519 | for (size_t i = 0; i < g_buffers->activeIndices.size(); ++i) 520 | g_buffers->activeIndices[i] = i; 521 | } 522 | g_inactiveIndices.resize(maxParticles - numParticles); 523 | 524 | for (size_t i = 0; i < g_inactiveIndices.size(); ++i) 525 | g_inactiveIndices[i] = i + numParticles; 526 | 527 | 528 | g_buffers->diffusePositions.resize(g_maxDiffuseParticles); 529 | g_buffers->diffuseVelocities.resize(g_maxDiffuseParticles); 530 | g_buffers->diffuseIndices.resize(g_maxDiffuseParticles); 531 | 532 | // for fluid rendering these are the Laplacian smoothed positions 533 | g_buffers->smoothPositions.resize(maxParticles); 534 | 535 | g_buffers->normals.resize(0); 536 | g_buffers->normals.resize(maxParticles); 537 | 538 | 539 | // resize particle buffers to fit 540 | g_buffers->positions.resize(maxParticles); 541 | g_buffers->velocities.resize(maxParticles); 542 | g_buffers->phases.resize(maxParticles); 543 | 544 | g_buffers->densities.resize(maxParticles); 545 | g_buffers->anisotropy1.resize(maxParticles); 546 | g_buffers->anisotropy2.resize(maxParticles); 547 | g_buffers->anisotropy3.resize(maxParticles); 548 | 549 | // save rest positions 550 | g_buffers->restPositions.resize(g_buffers->positions.size()); 551 | for (int i = 0; i < g_buffers->positions.size(); ++i) 552 | g_buffers->restPositions[i] = g_buffers->positions[i]; 553 | 554 | g_solverDesc.maxParticles = maxParticles; 555 | g_solverDesc.maxDiffuseParticles = g_maxDiffuseParticles; 556 | g_solverDesc.maxNeighborsPerParticle = g_maxNeighborsPerParticle; 557 | g_solverDesc.maxContactsPerParticle = g_maxContactsPerParticle; 558 | 559 | // main create method for the Flex solver 560 | g_solver = NvFlexCreateSolver(g_flexLib, &g_solverDesc); 561 | 562 | //g_flex = NvFlexCreateSolver(g_flexLib, maxParticles, g_maxDiffuseParticles, g_maxNeighborsPerParticle); 563 | 564 | } 565 | 566 | 567 | void FlexSystem::AddBox(Vec3 halfEdge, Vec3 center, Quat quat, bool dynamic) 568 | { 569 | // transform 570 | g_buffers->shapePositions.push_back(Vec4(center.x, center.y, center.z, 0.0f)); 571 | g_buffers->shapeRotations.push_back(quat); 572 | 573 | g_buffers->shapePrevPositions.push_back(g_buffers->shapePositions.back()); 574 | g_buffers->shapePrevRotations.push_back(g_buffers->shapeRotations.back()); 575 | 576 | NvFlexCollisionGeometry geo; 577 | geo.box.halfExtents[0] = halfEdge.x; 578 | geo.box.halfExtents[1] = halfEdge.y; 579 | geo.box.halfExtents[2] = halfEdge.z; 580 | 581 | g_buffers->shapeGeometry.push_back(geo); 582 | g_buffers->shapeFlags.push_back(NvFlexMakeShapeFlags(eNvFlexShapeBox, dynamic)); 583 | } 584 | 585 | void FlexSystem::AddSphere(float radius, Vec3 position, Quat rotation) 586 | { 587 | NvFlexCollisionGeometry geo; 588 | geo.sphere.radius = radius; 589 | g_buffers->shapeGeometry.push_back(geo); 590 | 591 | g_buffers->shapePositions.push_back(Vec4(position, 0.0f)); 592 | g_buffers->shapeRotations.push_back(rotation); 593 | 594 | g_buffers->shapePrevPositions.push_back(g_buffers->shapePositions.back()); 595 | g_buffers->shapePrevRotations.push_back(g_buffers->shapeRotations.back()); 596 | 597 | int flags = NvFlexMakeShapeFlags(eNvFlexShapeSphere, false); 598 | g_buffers->shapeFlags.push_back(flags); 599 | } 600 | 601 | void FlexSystem::GetParticleBounds(Vec3& lower, Vec3& upper) 602 | { 603 | lower = Vec3(FLT_MAX); 604 | upper = Vec3(-FLT_MAX); 605 | 606 | for (size_t i=0; i < g_buffers->positions.size(); ++i) 607 | { 608 | lower = Min(Vec3(g_buffers->positions[i]), lower); 609 | upper = Max(Vec3(g_buffers->positions[i]), upper); 610 | } 611 | } 612 | 613 | void FlexSystem::CreateParticleGrid(Vec3 lower, int dimx, int dimy, int dimz, float radius, Vec3 velocity, float invMass, bool rigid, float rigidStiffness, int phase, float jitter=0.005f) 614 | { 615 | 616 | 617 | for (int x=0; x < dimx; ++x) 618 | { 619 | for (int y=0; y < dimy; ++y) 620 | { 621 | for (int z=0; z < dimz; ++z) 622 | { 623 | 624 | Vec3 position = lower + Vec3(float(x), float(y), float(z))*radius + RandomUnitVector()*jitter; 625 | 626 | g_buffers->positions.push_back(Vec4(position.x, position.y, position.z, invMass)); 627 | g_buffers->velocities.push_back(velocity); 628 | g_buffers->phases.push_back(phase); 629 | } 630 | } 631 | } 632 | 633 | 634 | } 635 | 636 | void FlexSystem::CreateCenteredParticleGrid(Point3 center, Vec3 rotation, Point3 size, float restDistance, Vec3 velocity, float invMass, bool rigid, int phase, float jitter) 637 | { 638 | 639 | int dx = int(ceilf(size.x / restDistance)); 640 | int dy = int(ceilf(size.y / restDistance)); 641 | int dz = int(ceilf(size.z / restDistance)); 642 | 643 | for (int x=0; x < dx; ++x) 644 | { 645 | for (int y=0; y < dy; ++y) 646 | { 647 | for (int z=0; z < dz; ++z) 648 | { 649 | Point3 position = restDistance*Point3(float(x) - 0.5*(dx-1), float(y) - 0.5*(dy-1), float(z) - 0.5*(dz-1)) + RandomUnitVector()*jitter; 650 | position = TranslationMatrix(center) * TransformMatrix(Rotation(rotation.y, rotation.z, rotation.x), Point3(0.f))*position; 651 | 652 | if(cursorpositions.push_back(Vec4(position.x, position.y, position.z, invMass)); 655 | g_buffers->velocities.push_back(velocity); 656 | g_buffers->phases.push_back(phase); 657 | 658 | cursor++; 659 | } 660 | 661 | 662 | 663 | } 664 | } 665 | } 666 | } 667 | 668 | void FlexSystem::ClearShapes() 669 | { 670 | g_buffers->shapeGeometry.resize(0); 671 | g_buffers->shapePositions.resize(0); 672 | g_buffers->shapeRotations.resize(0); 673 | g_buffers->shapePrevPositions.resize(0); 674 | g_buffers->shapePrevRotations.resize(0); 675 | g_buffers->shapeFlags.resize(0); 676 | } 677 | 678 | void FlexSystem::setShapes(){ 679 | 680 | if(g_buffers->shapeFlags.size()){ 681 | NvFlexSetShapes( 682 | g_solver, 683 | g_buffers->shapeGeometry.buffer, 684 | g_buffers->shapePositions.buffer, 685 | g_buffers->shapeRotations.buffer, 686 | g_buffers->shapePrevPositions.buffer, 687 | g_buffers->shapePrevRotations.buffer, 688 | g_buffers->shapeFlags.buffer, 689 | g_buffers->shapeFlags.size()); 690 | } 691 | 692 | 693 | } 694 | 695 | 696 | void FlexSystem::emission(){ 697 | 698 | size_t e=0; 699 | 700 | 701 | for (; e < nEmitter; ++e) 702 | { 703 | if (!g_rectEmitters[e].mEnabled) 704 | continue; 705 | 706 | Vec3 emitterRot = g_rectEmitters[e].mRot; 707 | Point3 emitterSize = g_rectEmitters[e].mSize; 708 | Point3 emitterPos = g_rectEmitters[e].mPos; 709 | 710 | /*float r; 711 | int phase; 712 | 713 | if (g_params.fluid) 714 | { 715 | r = g_params.fluidRestDistance; 716 | phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid); 717 | } 718 | else 719 | { 720 | r = g_params.solidRestDistance; 721 | phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide); 722 | }*/ 723 | 724 | float r = g_params.fluidRestDistance; 725 | int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid); 726 | 727 | float numSlices = (g_rectEmitters[e].mSpeed / r)*g_dt; 728 | 729 | // whole number to emit 730 | int n = int(numSlices + g_rectEmitters[e].mLeftOver); 731 | 732 | if (n) 733 | g_rectEmitters[e].mLeftOver = (numSlices + g_rectEmitters[e].mLeftOver)-n; 734 | else 735 | g_rectEmitters[e].mLeftOver += numSlices; 736 | 737 | //int circle = 1; 738 | 739 | int disc = g_rectEmitters[e].mDisc; 740 | 741 | int dy = 0; 742 | 743 | 744 | int dx = int(ceilf(emitterSize.x / g_params.fluidRestDistance)); 745 | if(disc) 746 | dy = int(ceilf(emitterSize.x / g_params.fluidRestDistance)); 747 | else 748 | dy = int(ceilf(emitterSize.y / g_params.fluidRestDistance)); 749 | Mat44 tMat = TransformMatrix(Rotation(emitterRot.y, emitterRot.z, emitterRot.x), emitterPos); 750 | 751 | 752 | for (int z=0; z < n; ++z) 753 | { 754 | for (int x=0; x < dx; ++x) 755 | { 756 | for (int y=0; y < dy; ++y) 757 | { 758 | 759 | Point3 position = g_params.fluidRestDistance*Point3(float(x) - 0.5*(dx-1), float(y) - 0.5*(dy-1), float(z)) + RandomUnitVector()*g_params.fluidRestDistance*0.01f; 760 | 761 | int keep = 1; 762 | 763 | if(disc){ 764 | if(position.x*position.x + position.y*position.y>0.25*emitterSize.x*emitterSize.x) 765 | keep=0; 766 | } 767 | 768 | if(g_rectEmitters[e].mNoise){ 769 | Point3 scaledP = position*g_rectEmitters[e].mNoiseFreq + Point3(0,0,g_rectEmitters[e].mNoiseOffset); 770 | const float kNoise = Perlin3D(scaledP.x, scaledP.y, scaledP.z, 1, 0.25f); 771 | 772 | if(kNoisepositions[cursor] = Vec4(Vec3(position), 1.0f); 785 | g_buffers->velocities[cursor] = vel; 786 | g_buffers->phases[cursor] = phase; 787 | 788 | if(g_buffers->activeIndices.size()activeIndices.push_back(cursor); 790 | 791 | if(cursorUnmapBuffers(); 824 | 825 | NvFlexCopyDesc copyDesc; 826 | copyDesc.dstOffset = 0; 827 | copyDesc.srcOffset = 0; 828 | 829 | if (g_buffers->activeIndices.size()) { 830 | 831 | copyDesc.elementCount = g_buffers->activeIndices.size(); 832 | 833 | NvFlexSetActive(g_solver, g_buffers->activeIndices.buffer, ©Desc); 834 | NvFlexSetActiveCount(g_solver, g_buffers->activeIndices.size()); 835 | } 836 | 837 | if (g_buffers->positions.size()) { 838 | 839 | copyDesc.elementCount = g_buffers->positions.size(); 840 | 841 | NvFlexSetParticles(g_solver, g_buffers->positions.buffer, ©Desc); 842 | NvFlexSetVelocities(g_solver, g_buffers->velocities.buffer, ©Desc); 843 | NvFlexSetPhases(g_solver, g_buffers->phases.buffer, ©Desc); 844 | 845 | } 846 | 847 | setShapes(); 848 | 849 | NvFlexSetParams(g_solver, &g_params); 850 | 851 | NvFlexUpdateSolver(g_solver, g_dt, g_numSubsteps, g_profile); 852 | 853 | 854 | if(g_buffers->positions.size()){ 855 | 856 | copyDesc.elementCount = g_buffers->positions.size(); 857 | 858 | NvFlexGetParticles(g_solver, g_buffers->positions.buffer, ©Desc); 859 | NvFlexGetVelocities(g_solver, g_buffers->velocities.buffer, ©Desc); 860 | 861 | } 862 | 863 | activeParticles = NvFlexGetActiveCount(g_solver); 864 | 865 | } 866 | 867 | NvFlexTriangleMeshId FlexSystem::CreateTriangleMesh(VMesh* m) 868 | { 869 | /*if (!m) 870 | return 0;*/ 871 | 872 | Vec3 lower, upper; 873 | //m->GetBounds(lower, upper); 874 | 875 | lower = m->minExtents; 876 | upper = m->maxExtents; 877 | 878 | NvFlexVector positions(g_flexLib); 879 | NvFlexVector indices(g_flexLib); 880 | 881 | positions.assign((Vec4*)&m->m_positions[0], m->m_positions.size()); 882 | indices.assign((int*)&m->m_indices[0], m->m_indices.size()); 883 | 884 | positions.unmap(); 885 | indices.unmap(); 886 | 887 | NvFlexTriangleMeshId flexMesh = NvFlexCreateTriangleMesh(g_flexLib); 888 | NvFlexUpdateTriangleMesh(g_flexLib, flexMesh, positions.buffer, indices.buffer, m->GetNumVertices(), m->GetNumFaces(), (float*)&lower, (float*)&upper); 889 | 890 | // entry in the collision->render map 891 | //g_meshes[flexMesh] = CreateGpuMesh(m); 892 | 893 | return flexMesh; 894 | } 895 | 896 | 897 | void FlexSystem::UpdateTriangleMesh(VMesh* m, NvFlexTriangleMeshId flexMeshId) 898 | { 899 | //if (m) { 900 | 901 | 902 | Vec3 lower, upper; 903 | //m->GetBounds(lower, upper); 904 | 905 | lower = m->minExtents; 906 | upper = m->maxExtents; 907 | 908 | NvFlexVector positions(g_flexLib); 909 | NvFlexVector indices(g_flexLib); 910 | 911 | positions.assign((Vec4*)&m->m_positions[0], m->m_positions.size()); 912 | indices.assign((int*)&m->m_indices[0], m->m_indices.size()); 913 | 914 | positions.unmap(); 915 | indices.unmap(); 916 | 917 | NvFlexUpdateTriangleMesh(g_flexLib, flexMeshId, positions.buffer, indices.buffer, m->GetNumVertices(), m->GetNumFaces(), (float*)&lower, (float*)&upper); 918 | 919 | // entry in the collision->render map 920 | //g_meshes[flexMesh] = CreateGpuMesh(m); 921 | //} 922 | } 923 | 924 | void FlexSystem::AddTriangleMesh(NvFlexTriangleMeshId mesh, Vec3 translation, Quat rotation, Vec3 prevTrans, Quat prevRot, Vec3 scale) 925 | { 926 | //Vec3 lower, upper; 927 | //NvFlexGetTriangleMeshBounds(g_flexLib, mesh, lower, upper); 928 | 929 | NvFlexCollisionGeometry geo; 930 | geo.triMesh.mesh = mesh; 931 | geo.triMesh.scale[0] = scale.x; 932 | geo.triMesh.scale[1] = scale.y; 933 | geo.triMesh.scale[2] = scale.z; 934 | 935 | g_buffers->shapePositions.push_back(Vec4(translation, 0.0f)); 936 | g_buffers->shapeRotations.push_back(Quat(rotation)); 937 | g_buffers->shapePrevPositions.push_back(Vec4(prevTrans, 0.0f)); 938 | g_buffers->shapePrevRotations.push_back(Quat(prevRot)); 939 | g_buffers->shapeGeometry.push_back((NvFlexCollisionGeometry&)geo); 940 | g_buffers->shapeFlags.push_back(NvFlexMakeShapeFlags(eNvFlexShapeTriangleMesh, false)); 941 | } 942 | 943 | -------------------------------------------------------------------------------- /CHOP/Source/FlexCHOP.cpp: -------------------------------------------------------------------------------- 1 | #include "FlexCHOP.h" 2 | 3 | #include 4 | #include 5 | 6 | 7 | typedef unsigned int uint32_t; 8 | 9 | // These functions are basic C function, which the DLL loader can find 10 | // much easier than finding a C++ Class. 11 | // The DLLEXPORT prefix is needed so the compile exports these functions from the .dll 12 | // you are creating 13 | extern "C" { 14 | 15 | DLLEXPORT void FillCHOPPluginInfo(CHOP_PluginInfo *info) 16 | { 17 | // Always set this to CHOPCPlusPlusAPIVersion. 18 | info->apiVersion = CHOPCPlusPlusAPIVersion; 19 | 20 | // The opType is the unique name for this CHOP. It must start with a 21 | // capital A-Z character, and all the following characters must lower case 22 | // or numbers (a-z, 0-9) 23 | info->customOPInfo.opType->setString("Flex"); 24 | 25 | // The opLabel is the text that will show up in the OP Create Dialog 26 | info->customOPInfo.opLabel->setString("Flex"); 27 | 28 | // Information about the author of this OP 29 | info->customOPInfo.authorName->setString("Vincent Houze"); 30 | //info->customOPInfo.authorEmail->setString("email@email.com"); 31 | 32 | // This CHOP can work with 0 inputs 33 | info->customOPInfo.minInputs = 0; 34 | 35 | // It can accept up to 1 input though, which changes it's behavior 36 | info->customOPInfo.maxInputs = 0; 37 | } 38 | 39 | DLLEXPORT CHOP_CPlusPlusBase* CreateCHOPInstance(const OP_NodeInfo *info) 40 | { 41 | // Return a new instance of your class every time this is called. 42 | // It will be called once per CHOP that is using the .dll 43 | return new FlexCHOP(info); 44 | } 45 | 46 | DLLEXPORT void DestroyCHOPInstance(CHOP_CPlusPlusBase *instance) 47 | { 48 | // Delete the instance here, this will be called when 49 | // Touch is shutting down, when the CHOP using that instance is deleted, or 50 | // if the CHOP loads a different DLL 51 | delete (FlexCHOP*)instance; 52 | } 53 | 54 | }; 55 | 56 | 57 | 58 | FlexCHOP::FlexCHOP(const OP_NodeInfo *info) : myNodeInfo(info) 59 | { 60 | myExecuteCount = 0; 61 | 62 | printf("\n\n*************************INITCHOP**************************\n"); 63 | 64 | 65 | FlexSys = new FlexSystem(); 66 | 67 | FlexSys->initSystem(); 68 | 69 | maxVel = 1; 70 | 71 | maxParticles = 0; 72 | activeIndicesSize = 0; 73 | inactiveIndicesSize = 0; 74 | 75 | } 76 | 77 | FlexCHOP::~FlexCHOP() 78 | { 79 | 80 | delete FlexSys; 81 | 82 | } 83 | 84 | void FlexCHOP::getGeneralInfo(CHOP_GeneralInfo* ginfo, const OP_Inputs *inputs, void* reserved1) 85 | { 86 | // This will cause the node to cook every frame 87 | ginfo->cookEveryFrameIfAsked = true; 88 | ginfo->timeslice = false; 89 | ginfo->inputMatchIndex = 0; 90 | } 91 | 92 | bool FlexCHOP::getOutputInfo(CHOP_OutputInfo* info, const OP_Inputs *inputs, void *reserved1) 93 | { 94 | 95 | if (FlexSys->g_solver){ 96 | info->numSamples = FlexSys->g_buffers->positions.size(); 97 | }else{ 98 | info->numSamples = 1; 99 | } 100 | 101 | 102 | info->numChannels = 6; 103 | 104 | info->sampleRate = 60; 105 | 106 | return true; 107 | 108 | } 109 | 110 | void FlexCHOP::updateParams(const OP_Inputs* inputs) { 111 | 112 | FlexSys->g_profile = inputs->getParInt("Profile"); 113 | 114 | FlexSys->g_params.maxSpeed = inputs->getParDouble("Maxspeed"); 115 | FlexSys->g_numSubsteps = inputs->getParInt("Numsubsteps"); 116 | FlexSys->g_params.numIterations = inputs->getParInt("Numiterations"); 117 | 118 | float fps = inputs->getParDouble("Fps"); 119 | fps = max(1.0f, fps); 120 | 121 | FlexSys->g_dt = 1.0 / fps; 122 | maxVel = 0.5f*FlexSys->g_params.radius*FlexSys->g_numSubsteps / FlexSys->g_dt; 123 | 124 | 125 | double gravity[3]; 126 | inputs->getParDouble3("Gravity", gravity[0], gravity[1], gravity[2]); 127 | 128 | FlexSys->g_params.gravity[0] = gravity[0]; 129 | FlexSys->g_params.gravity[1] = gravity[1]; 130 | FlexSys->g_params.gravity[2] = gravity[2]; 131 | 132 | FlexSys->g_params.dynamicFriction = inputs->getParDouble("Dynamicfriction"); 133 | FlexSys->g_params.restitution = inputs->getParDouble("Restitution"); 134 | FlexSys->g_params.adhesion = inputs->getParDouble("Adhesion"); 135 | FlexSys->g_params.dissipation = inputs->getParDouble("Dissipation"); 136 | 137 | FlexSys->g_params.cohesion = inputs->getParDouble("Cohesion"); 138 | FlexSys->g_params.surfaceTension = inputs->getParDouble("Surfacetension"); 139 | FlexSys->g_params.viscosity = inputs->getParDouble("Viscosity"); 140 | FlexSys->g_params.vorticityConfinement = inputs->getParDouble("Vorticityconfinement"); 141 | 142 | FlexSys->g_params.damping = inputs->getParDouble("Damping"); 143 | 144 | FlexSys->g_params.collisionDistance = inputs->getParDouble("Collisiondistance"); 145 | FlexSys->g_params.shapeCollisionMargin = inputs->getParDouble("Shapecollisionmargin"); 146 | 147 | // set collision distance automatically based on rest distance if not alraedy set 148 | if (FlexSys->g_params.collisionDistance == 0.0f) 149 | FlexSys->g_params.collisionDistance = FlexSys->g_params.fluidRestDistance*0.5f; 150 | 151 | // add a margin for detecting contacts between particles and shapes 152 | if (FlexSys->g_params.shapeCollisionMargin == 0.0f) 153 | FlexSys->g_params.shapeCollisionMargin = FlexSys->g_params.collisionDistance*0.5f; 154 | 155 | } 156 | 157 | void FlexCHOP::getChannelName(int32_t index, OP_String *name, const OP_Inputs *inputs, void* reserved1) 158 | { 159 | switch(index) { 160 | case 0: 161 | name->setString("tx"); 162 | break; 163 | case 1: 164 | name->setString("ty"); 165 | break; 166 | case 2: 167 | name->setString("tz"); 168 | break; 169 | case 3: 170 | name->setString("vx"); 171 | break; 172 | case 4: 173 | name->setString("vy"); 174 | break; 175 | case 5: 176 | name->setString("vz"); 177 | break; 178 | } 179 | } 180 | 181 | void FlexCHOP::updateTriangleMesh(const OP_Inputs* inputs) { 182 | const OP_SOPInput* sopInput = inputs->getParSOP("Trianglespolysop"); 183 | if (FlexSys->deformingMesh && sopInput && sopInput->getNumPrimitives()>0 && FlexSys->g_triangleCollisionMesh->GetNumVertices() == sopInput->getNumPoints()) { 184 | 185 | Point3 minExt(FLT_MAX); 186 | Point3 maxExt(-FLT_MAX); 187 | 188 | for (int i = 0; i < sopInput->getNumPoints(); i++) { 189 | Position curPos = sopInput->getPointPositions()[i]; 190 | FlexSys->g_triangleCollisionMesh->m_positions[i] = Vec4(curPos.x, curPos.y, curPos.z, 0.0); 191 | 192 | const Point3& a = Point3(curPos.x, curPos.y, curPos.z); 193 | 194 | minExt = Min(a, minExt); 195 | maxExt = Max(a, maxExt); 196 | } 197 | 198 | FlexSys->g_triangleCollisionMesh->minExtents = Vector3(minExt); 199 | FlexSys->g_triangleCollisionMesh->maxExtents = Vector3(maxExt); 200 | 201 | FlexSys->UpdateTriangleMesh(FlexSys->g_triangleCollisionMesh, FlexSys->triangleCollisionMeshId); 202 | } 203 | } 204 | 205 | void FlexCHOP::initTriangleMesh(const OP_Inputs* inputs) { 206 | const OP_SOPInput* sopInput = inputs->getParSOP("Trianglespolysop"); 207 | if (sopInput && sopInput->getNumPrimitives()>0) { 208 | 209 | if (FlexSys->g_triangleCollisionMesh) 210 | delete FlexSys->g_triangleCollisionMesh; 211 | 212 | FlexSys->g_triangleCollisionMesh = new VMesh(); 213 | 214 | FlexSys->deformingMesh = inputs->getParInt("Deformingmesh"); 215 | 216 | Point3 minExt(FLT_MAX); 217 | Point3 maxExt(-FLT_MAX); 218 | 219 | for (int i = 0; i < sopInput->getNumPoints(); i++) { 220 | Position curPos = sopInput->getPointPositions()[i]; 221 | FlexSys->g_triangleCollisionMesh->m_positions.push_back(Vec4(curPos.x, curPos.y, curPos.z, 0.0)); 222 | 223 | const Point3& a = Point3(curPos.x, curPos.y, curPos.z); 224 | 225 | minExt = Min(a, minExt); 226 | maxExt = Max(a, maxExt); 227 | 228 | } 229 | 230 | FlexSys->g_triangleCollisionMesh->minExtents = Vector3(minExt); 231 | FlexSys->g_triangleCollisionMesh->maxExtents = Vector3(maxExt); 232 | 233 | 234 | for (int i = 0; i < sopInput->getNumPrimitives(); i++) { 235 | SOP_PrimitiveInfo curPrim = sopInput->getPrimitive(i); 236 | FlexSys->g_triangleCollisionMesh->m_indices.push_back(curPrim.pointIndices[2]); 237 | FlexSys->g_triangleCollisionMesh->m_indices.push_back(curPrim.pointIndices[1]); 238 | FlexSys->g_triangleCollisionMesh->m_indices.push_back(curPrim.pointIndices[0]); 239 | 240 | } 241 | 242 | double meshTrans[3]; 243 | inputs->getParDouble3("Meshtranslation", meshTrans[0], meshTrans[1], meshTrans[2]); 244 | 245 | double meshRot[3]; 246 | inputs->getParDouble3("Meshrotation", meshRot[0], meshRot[1], meshRot[2]); 247 | 248 | FlexSys->curMeshTrans = Vec3(meshTrans[0], meshTrans[1], meshTrans[2]); 249 | Vec3 rot = Vec3(meshRot[0], meshRot[1], meshRot[2]); 250 | 251 | Quat qx = QuatFromAxisAngle(Vec3(1, 0, 0), DegToRad(rot.x)); 252 | Quat qy = QuatFromAxisAngle(Vec3(0, 1, 0), DegToRad(rot.y)); 253 | Quat qz = QuatFromAxisAngle(Vec3(0, 0, 1), DegToRad(rot.z)); 254 | 255 | FlexSys->curMeshRot = qz*qy*qx; 256 | 257 | FlexSys->previousMeshTrans = FlexSys->curMeshTrans; 258 | FlexSys->previousMeshRot = FlexSys->curMeshRot; 259 | 260 | } 261 | 262 | } 263 | 264 | void FlexCHOP::initVolumeBoxes(const OP_Inputs* inputs) { 265 | const OP_CHOPInput* volumeBoxesInput = inputs->getParCHOP("Boxesemitterschop"); 266 | if (volumeBoxesInput && volumeBoxesInput->numChannels == 9) { 267 | FlexSys->nVolumeBoxes = inputs->getParCHOP("Boxesemitterschop")->numSamples; 268 | VolumeBox vb1; 269 | 270 | for (int i = 0; i < FlexSys->nVolumeBoxes; i++) { 271 | vb1.mPos = Point3(volumeBoxesInput->getChannelData(0)[i], 272 | volumeBoxesInput->getChannelData(1)[i], 273 | volumeBoxesInput->getChannelData(2)[i]); 274 | 275 | vb1.mRot = Vec3(volumeBoxesInput->getChannelData(3)[i], 276 | volumeBoxesInput->getChannelData(4)[i], 277 | volumeBoxesInput->getChannelData(5)[i]); 278 | 279 | vb1.mSize = Point3(volumeBoxesInput->getChannelData(6)[i], 280 | volumeBoxesInput->getChannelData(7)[i], 281 | volumeBoxesInput->getChannelData(8)[i]); 282 | 283 | 284 | FlexSys->g_volumeBoxes.push_back(vb1); 285 | } 286 | } 287 | else 288 | FlexSys->nVolumeBoxes = 0; 289 | } 290 | 291 | void FlexCHOP::initEmitters(const OP_Inputs* inputs) { 292 | const OP_CHOPInput* emittersInput = inputs->getParCHOP("Emitterschop"); 293 | if (emittersInput && emittersInput->numChannels == 15) { 294 | 295 | FlexSys->nEmitter = emittersInput->numSamples; 296 | RectEmitter re1; 297 | for (int i = 0; i < FlexSys->nEmitter; i++) { 298 | FlexSys->g_rectEmitters.push_back(re1); 299 | } 300 | } 301 | else 302 | FlexSys->nEmitter = 0; 303 | } 304 | 305 | void FlexCHOP::updatePlanes(const OP_Inputs* inputs) { 306 | 307 | const OP_CHOPInput* colPlanesInput = inputs->getParCHOP("Colplaneschop"); 308 | if (colPlanesInput) { 309 | if (colPlanesInput->numChannels == 4) { 310 | 311 | int nPlanes = colPlanesInput->numSamples; 312 | FlexSys->g_params.numPlanes = nPlanes; 313 | 314 | for (int i = 0; i < nPlanes; i++) { 315 | (Vec4&)FlexSys->g_params.planes[i] = Vec4(colPlanesInput->getChannelData(0)[i], 316 | colPlanesInput->getChannelData(1)[i], 317 | colPlanesInput->getChannelData(2)[i], 318 | colPlanesInput->getChannelData(3)[i]); 319 | } 320 | } 321 | } 322 | } 323 | 324 | void FlexCHOP::updateEmitters(const OP_Inputs* inputs) { 325 | const OP_CHOPInput* emitterInput = inputs->getParCHOP("Emitterschop"); 326 | if (emitterInput && FlexSys->nEmitter>0 && emitterInput->numChannels == 15) { 327 | 328 | int length = min(FlexSys->nEmitter, emitterInput->numSamples); 329 | 330 | for (int i = 0; i < length; i++) { 331 | 332 | FlexSys->g_rectEmitters[i].mPos = Point3(emitterInput->getChannelData(0)[i], 333 | emitterInput->getChannelData(1)[i], 334 | emitterInput->getChannelData(2)[i]); 335 | 336 | FlexSys->g_rectEmitters[i].mRot = Vec3(emitterInput->getChannelData(3)[i], 337 | emitterInput->getChannelData(4)[i], 338 | emitterInput->getChannelData(5)[i]); 339 | 340 | FlexSys->g_rectEmitters[i].mSize = Point3(emitterInput->getChannelData(6)[i], 341 | emitterInput->getChannelData(7)[i], 342 | 0); 343 | 344 | FlexSys->g_rectEmitters[i].mSpeed = emitterInput->getChannelData(8)[i]; 345 | FlexSys->g_rectEmitters[i].mDisc = emitterInput->getChannelData(9)[i]; 346 | 347 | FlexSys->g_rectEmitters[i].mEnabled = emitterInput->getChannelData(10)[i]; 348 | 349 | FlexSys->g_rectEmitters[i].mNoise = emitterInput->getChannelData(11)[i]; 350 | FlexSys->g_rectEmitters[i].mNoiseThreshold = emitterInput->getChannelData(12)[i]; 351 | FlexSys->g_rectEmitters[i].mNoiseFreq = emitterInput->getChannelData(13)[i]; 352 | FlexSys->g_rectEmitters[i].mNoiseOffset = emitterInput->getChannelData(14)[i]; 353 | } 354 | } 355 | } 356 | 357 | void FlexCHOP::updateSpheresCols(const OP_Inputs* inputs) { 358 | const OP_CHOPInput* spheresInput = inputs->getParCHOP("Colsphereschop"); 359 | if (spheresInput && spheresInput->numChannels == 4) { 360 | for (int i = 0; i < spheresInput->numSamples; i++) { 361 | 362 | Vec3 spherePos = Vec3(spheresInput->getChannelData(0)[i], 363 | spheresInput->getChannelData(1)[i], 364 | spheresInput->getChannelData(2)[i]); 365 | 366 | float sphereRadius = spheresInput->getChannelData(3)[i]; 367 | 368 | FlexSys->AddSphere(sphereRadius, spherePos, Quat()); 369 | } 370 | } 371 | } 372 | 373 | void FlexCHOP::updateBoxesCols(const OP_Inputs* inputs) { 374 | 375 | const OP_CHOPInput* boxesInput = inputs->getParCHOP("Colboxeschop"); 376 | if (boxesInput && boxesInput->numChannels == 9) { 377 | for (int i = 0; i < boxesInput->numSamples; i++) { 378 | 379 | Vec3 boxPos = Vec3(boxesInput->getChannelData(0)[i], 380 | boxesInput->getChannelData(1)[i], 381 | boxesInput->getChannelData(2)[i]); 382 | 383 | Vec3 boxSize = Vec3(boxesInput->getChannelData(3)[i], 384 | boxesInput->getChannelData(4)[i], 385 | boxesInput->getChannelData(5)[i]); 386 | 387 | Vec3 boxRot = Vec3(boxesInput->getChannelData(6)[i], 388 | boxesInput->getChannelData(7)[i], 389 | boxesInput->getChannelData(8)[i]); 390 | 391 | Quat qx = QuatFromAxisAngle(Vec3(1, 0, 0), DegToRad(boxRot.x)); 392 | Quat qy = QuatFromAxisAngle(Vec3(0, 1, 0), DegToRad(boxRot.y)); 393 | Quat qz = QuatFromAxisAngle(Vec3(0, 0, 1), DegToRad(boxRot.z)); 394 | 395 | FlexSys->AddBox(boxSize, boxPos, qz*qy*qx); 396 | } 397 | } 398 | } 399 | 400 | void FlexCHOP::execute(CHOP_Output* output, const OP_Inputs* inputs, void* reserved1) 401 | { 402 | myExecuteCount++; 403 | //double t1 = GetSeconds(); 404 | 405 | int posRender = 0; 406 | 407 | int reset = inputs->getParInt("Reset"); 408 | 409 | if (reset == 1) { 410 | 411 | printf("\n********INITFLEX***********\n"); 412 | 413 | FlexSys->initScene(); 414 | FlexSys->g_params.radius = inputs->getParDouble("Radius"); 415 | 416 | initVolumeBoxes(inputs); 417 | initEmitters(inputs); 418 | 419 | FlexSys->maxParticles = inputs->getParInt("Maxparticles"); 420 | 421 | initTriangleMesh(inputs); 422 | 423 | } //reset End 424 | 425 | updateParams(inputs); 426 | 427 | 428 | if (FlexSys->g_solver) { 429 | 430 | FlexSys->g_buffers->MapBuffers(); 431 | FlexSys->getSimTimers(); 432 | 433 | //planes collision 434 | updatePlanes(inputs); 435 | 436 | //emission 437 | updateEmitters(inputs); 438 | 439 | FlexSys->ClearShapes(); 440 | 441 | updateSpheresCols(inputs); 442 | updateBoxesCols(inputs); 443 | 444 | if (FlexSys->g_triangleCollisionMesh) { 445 | updateTriangleMesh(inputs); 446 | 447 | FlexSys->previousMeshTrans = FlexSys->curMeshTrans; 448 | FlexSys->previousMeshRot = FlexSys->curMeshRot; 449 | 450 | double meshTrans[3]; 451 | inputs->getParDouble3("Meshtranslation", meshTrans[0], meshTrans[1], meshTrans[2]); 452 | 453 | double meshRot[3]; 454 | inputs->getParDouble3("Meshrotation", meshRot[0], meshRot[1], meshRot[2]); 455 | 456 | FlexSys->curMeshTrans = Vec3(meshTrans[0], meshTrans[1], meshTrans[2]); 457 | Vec3 rot = Vec3(meshRot[0], meshRot[1], meshRot[2]); 458 | 459 | Quat qx = QuatFromAxisAngle(Vec3(1, 0, 0), DegToRad(rot.x)); 460 | Quat qy = QuatFromAxisAngle(Vec3(0, 1, 0), DegToRad(rot.y)); 461 | Quat qz = QuatFromAxisAngle(Vec3(0, 0, 1), DegToRad(rot.z)); 462 | 463 | FlexSys->curMeshRot = qz*qy*qx; 464 | 465 | FlexSys->AddTriangleMesh(FlexSys->triangleCollisionMeshId, FlexSys->curMeshTrans, FlexSys->curMeshRot, FlexSys->previousMeshTrans, FlexSys->previousMeshRot, 1.0f); 466 | 467 | } 468 | 469 | } 470 | 471 | int simulate = inputs->getParInt("Simulate"); 472 | 473 | if (reset == 1) { 474 | 475 | FlexSys->postInitScene(); 476 | FlexSys->update(); 477 | 478 | } 479 | else if (FlexSys->g_solver) { 480 | 481 | double t1 = GetSeconds(); 482 | for (int i = 0; i < output->numSamples; i++) { 483 | 484 | //memcpy(output->channels[i], &FlexSys->g_buffers->positions[0], FlexSys->g_buffers->positions.size() * sizeof(Vec4)); 485 | //memcpy(output->channels[i], &FlexSys->g_buffers->velocities[0], FlexSys->g_buffers->positions.size() * sizeof(Vec3)); 486 | 487 | 488 | output->channels[TX][i] = FlexSys->g_buffers->positions[i].x; 489 | output->channels[TY][i] = FlexSys->g_buffers->positions[i].y; 490 | output->channels[TZ][i] = FlexSys->g_buffers->positions[i].z; 491 | 492 | output->channels[VX][i] = FlexSys->g_buffers->velocities[i].x; 493 | output->channels[VY][i] = FlexSys->g_buffers->velocities[i].y; 494 | output->channels[VZ][i] = FlexSys->g_buffers->velocities[i].z; 495 | 496 | } 497 | 498 | double t2 = GetSeconds(); 499 | 500 | timer = 1000*(t2 - t1); 501 | 502 | if (simulate == 1) 503 | FlexSys->update(); 504 | 505 | 506 | //activeCount = FlexSys->activeParticles; 507 | maxParticles = FlexSys->maxParticles; 508 | 509 | //positionsSize = FlexSys->g_buffers->positions.size(); 510 | activeIndicesSize = FlexSys->g_buffers->activeIndices.size(); 511 | inactiveIndicesSize = FlexSys->g_inactiveIndices.size(); 512 | 513 | } 514 | 515 | } 516 | 517 | int32_t FlexCHOP::getNumInfoCHOPChans(void *reserved1) 518 | { 519 | // We return the number of channel we want to output to any Info CHOP 520 | // connected to the CHOP. In this example we are just going to send one channel. 521 | return 3; 522 | } 523 | 524 | void FlexCHOP::getInfoCHOPChan(int32_t index, OP_InfoCHOPChan* chan, void* reserved1) 525 | { 526 | // This function will be called once for each channel we said we'd want to return 527 | // In this example it'll only be called once. 528 | 529 | switch (index) { 530 | 531 | case 0: 532 | chan->name->setString("executeCount"); 533 | chan->value = myExecuteCount; 534 | break; 535 | 536 | case 1: 537 | chan->name->setString("flexTime"); 538 | chan->value = FlexSys->simLatency; 539 | break; 540 | 541 | case 2: 542 | chan->name->setString("solveVelocities"); 543 | chan->value = FlexSys->g_timers.solveVelocities; 544 | break; 545 | 546 | 547 | } 548 | } 549 | 550 | bool FlexCHOP::getInfoDATSize(OP_InfoDATSize* infoSize, void *reserved1) 551 | { 552 | infoSize->rows = 7; 553 | infoSize->cols = 2; 554 | // Setting this to false means we'll be assigning values to the table 555 | // one row at a time. True means we'll do it one column at a time. 556 | infoSize->byColumn = false; 557 | return true; 558 | } 559 | 560 | void FlexCHOP::getInfoDATEntries(int32_t index, int32_t nEntries, OP_InfoDATEntries* entries, void *reserved1) 561 | { 562 | 563 | static char tempBuffer1[4096]; 564 | static char tempBuffer2[4096]; 565 | 566 | switch (index) { 567 | 568 | case 0: 569 | strcpy(tempBuffer1, "maxParticles"); 570 | sprintf(tempBuffer2, "%d", maxParticles); 571 | break; 572 | 573 | case 1: 574 | strcpy(tempBuffer1, "activeIndicesSize"); 575 | sprintf(tempBuffer2, "%d", activeIndicesSize); 576 | break; 577 | 578 | case 2: 579 | strcpy(tempBuffer1, "inactiveIndicesSize"); 580 | sprintf(tempBuffer2, "%d", inactiveIndicesSize); 581 | break; 582 | 583 | case 3: 584 | strcpy(tempBuffer1, "maxVel"); 585 | sprintf(tempBuffer2, "%f", maxVel); 586 | break; 587 | 588 | case 4: 589 | strcpy(tempBuffer1, "cursor"); 590 | sprintf(tempBuffer2, "%d", FlexSys->cursor); 591 | break; 592 | 593 | case 5: 594 | strcpy(tempBuffer1, "collisionDistance"); 595 | sprintf(tempBuffer2, "%f", FlexSys->g_params.collisionDistance); 596 | break; 597 | 598 | case 6: 599 | strcpy(tempBuffer1, "shapeCollisionMargin"); 600 | sprintf(tempBuffer2, "%f", FlexSys->g_params.shapeCollisionMargin); 601 | break; 602 | 603 | } 604 | 605 | entries->values[0]->setString(tempBuffer1); 606 | entries->values[1]->setString(tempBuffer2); 607 | } 608 | 609 | void FlexCHOP::setupParamsCustom(OP_ParameterManager* manager) { 610 | // reset 611 | { 612 | OP_NumericParameter np; 613 | 614 | np.name = "Reset"; 615 | np.label = "Reset"; 616 | np.defaultValues[0] = 0.0; 617 | 618 | OP_ParAppendResult res = manager->appendInt(np); 619 | assert(res == OP_ParAppendResult::Success); 620 | } 621 | 622 | //Simulate 623 | { 624 | OP_NumericParameter np; 625 | 626 | np.name = "Simulate"; 627 | np.label = "Simulate"; 628 | np.defaultValues[0] = 1.0; 629 | 630 | OP_ParAppendResult res = manager->appendToggle(np); 631 | assert(res == OP_ParAppendResult::Success); 632 | } 633 | 634 | //Radius 635 | { 636 | OP_NumericParameter np; 637 | 638 | np.name = "Radius"; 639 | np.label = "Radius"; 640 | np.defaultValues[0] = 0.05; 641 | 642 | OP_ParAppendResult res = manager->appendFloat(np); 643 | assert(res == OP_ParAppendResult::Success); 644 | } 645 | 646 | 647 | // Profile 648 | { 649 | OP_NumericParameter np; 650 | 651 | np.name = "Profile"; 652 | np.label = "Profile"; 653 | np.defaultValues[0] = 0; 654 | 655 | OP_ParAppendResult res = manager->appendToggle(np); 656 | assert(res == OP_ParAppendResult::Success); 657 | } 658 | 659 | 660 | // Maxspeed 661 | { 662 | OP_NumericParameter np; 663 | 664 | np.name = "Maxspeed"; 665 | np.label = "Max Speed"; 666 | np.defaultValues[0] = 100; 667 | 668 | OP_ParAppendResult res = manager->appendFloat(np); 669 | assert(res == OP_ParAppendResult::Success); 670 | } 671 | 672 | 673 | //Gravity 674 | { 675 | OP_NumericParameter np; 676 | 677 | np.name = "Gravity"; 678 | np.label = "Gravity"; 679 | 680 | np.defaultValues[0] = 0.0; 681 | np.defaultValues[1] = -3.0; 682 | np.defaultValues[2] = 0.0; 683 | 684 | 685 | OP_ParAppendResult res = manager->appendXYZ(np); 686 | assert(res == OP_ParAppendResult::Success); 687 | } 688 | } 689 | 690 | void FlexCHOP::setupParamsSolver(OP_ParameterManager* manager) { 691 | // Fps 692 | { 693 | OP_NumericParameter np; 694 | 695 | np.name = "Fps"; 696 | np.label = "FPS"; 697 | np.page = "Solver"; 698 | np.defaultValues[0] = 30; 699 | np.clampMins[0] = true; 700 | np.minValues[0] = 1.0; 701 | 702 | OP_ParAppendResult res = manager->appendFloat(np); 703 | assert(res == OP_ParAppendResult::Success); 704 | } 705 | 706 | // Numsubsteps 707 | { 708 | OP_NumericParameter np; 709 | 710 | np.name = "Numsubsteps"; 711 | np.label = "Num Substeps"; 712 | np.page = "Solver"; 713 | np.defaultValues[0] = 3; 714 | 715 | OP_ParAppendResult res = manager->appendInt(np); 716 | assert(res == OP_ParAppendResult::Success); 717 | } 718 | 719 | //Numiterations 720 | { 721 | OP_NumericParameter np; 722 | 723 | np.name = "Numiterations"; 724 | np.label = "Num Iterations"; 725 | np.page = "Solver"; 726 | np.defaultValues[0] = 3; 727 | 728 | OP_ParAppendResult res = manager->appendInt(np); 729 | assert(res == OP_ParAppendResult::Success); 730 | } 731 | 732 | // Maxparticles 733 | { 734 | OP_NumericParameter np; 735 | 736 | np.name = "Maxparticles"; 737 | np.label = "Max Number of Particles"; 738 | np.page = "Solver"; 739 | np.defaultValues[0] = 160000; 740 | 741 | OP_ParAppendResult res = manager->appendInt(np); 742 | assert(res == OP_ParAppendResult::Success); 743 | } 744 | } 745 | 746 | void FlexCHOP::setupParamsParts(OP_ParameterManager* manager) { 747 | //Damping 748 | { 749 | OP_NumericParameter np; 750 | 751 | np.name = "Damping"; 752 | np.label = "Damping"; 753 | np.defaultValues[0] = 0; 754 | np.page = "PartsParams"; 755 | 756 | OP_ParAppendResult res = manager->appendFloat(np); 757 | assert(res == OP_ParAppendResult::Success); 758 | } 759 | 760 | //Dynamicfriction 761 | { 762 | OP_NumericParameter np; 763 | 764 | np.name = "Dynamicfriction"; 765 | np.label = "Dynamic Friction"; 766 | np.defaultValues[0] = 0; 767 | np.page = "PartsParams"; 768 | 769 | OP_ParAppendResult res = manager->appendFloat(np); 770 | assert(res == OP_ParAppendResult::Success); 771 | } 772 | 773 | //Restitution 774 | { 775 | OP_NumericParameter np; 776 | 777 | np.name = "Restitution"; 778 | np.label = "Restitution"; 779 | np.defaultValues[0] = 0.001f; 780 | np.page = "PartsParams"; 781 | 782 | OP_ParAppendResult res = manager->appendFloat(np); 783 | assert(res == OP_ParAppendResult::Success); 784 | } 785 | 786 | //Adhesion 787 | { 788 | OP_NumericParameter np; 789 | 790 | np.name = "Adhesion"; 791 | np.label = "Adhesion"; 792 | np.defaultValues[0] = 0.0f; 793 | np.page = "PartsParams"; 794 | 795 | OP_ParAppendResult res = manager->appendFloat(np); 796 | assert(res == OP_ParAppendResult::Success); 797 | } 798 | 799 | //Dissipation 800 | { 801 | OP_NumericParameter np; 802 | 803 | np.name = "Dissipation"; 804 | np.label = "Dissipation"; 805 | np.defaultValues[0] = 0.0f; 806 | np.page = "PartsParams"; 807 | 808 | OP_ParAppendResult res = manager->appendFloat(np); 809 | assert(res == OP_ParAppendResult::Success); 810 | } 811 | 812 | //Cohesion 813 | { 814 | OP_NumericParameter np; 815 | 816 | np.name = "Cohesion"; 817 | np.label = "Cohesion"; 818 | np.defaultValues[0] = 0.1f; 819 | np.page = "PartsParams"; 820 | 821 | OP_ParAppendResult res = manager->appendFloat(np); 822 | assert(res == OP_ParAppendResult::Success); 823 | } 824 | 825 | //Surfacetension 826 | { 827 | OP_NumericParameter np; 828 | 829 | np.name = "Surfacetension"; 830 | np.label = "Surface Tension"; 831 | np.defaultValues[0] = 0.0f; 832 | np.page = "PartsParams"; 833 | 834 | OP_ParAppendResult res = manager->appendFloat(np); 835 | assert(res == OP_ParAppendResult::Success); 836 | } 837 | 838 | //Viscosity 839 | { 840 | OP_NumericParameter np; 841 | 842 | np.name = "Viscosity"; 843 | np.label = "Viscosity"; 844 | np.defaultValues[0] = 0.0f; 845 | np.page = "PartsParams"; 846 | 847 | OP_ParAppendResult res = manager->appendFloat(np); 848 | assert(res == OP_ParAppendResult::Success); 849 | } 850 | 851 | //Vorticityconfinement 852 | { 853 | OP_NumericParameter np; 854 | 855 | np.name = "Vorticityconfinement"; 856 | np.label = "Vorticity Confinement"; 857 | np.defaultValues[0] = 80.0f; 858 | np.page = "PartsParams"; 859 | 860 | OP_ParAppendResult res = manager->appendFloat(np); 861 | assert(res == OP_ParAppendResult::Success); 862 | } 863 | } 864 | 865 | void FlexCHOP::setupParamsEmission(OP_ParameterManager* manager) { 866 | //Boxesemitterschop 867 | { 868 | OP_StringParameter sp; 869 | 870 | sp.name = "Boxesemitterschop"; 871 | sp.label = "Boxes Emitters CHOP"; 872 | sp.page = "Emission"; 873 | 874 | OP_ParAppendResult res = manager->appendCHOP(sp); 875 | assert(res == OP_ParAppendResult::Success); 876 | } 877 | 878 | 879 | //Emitterschop 880 | { 881 | OP_StringParameter sp; 882 | 883 | sp.name = "Emitterschop"; 884 | sp.label = "Emitters CHOP"; 885 | sp.page = "Emission"; 886 | 887 | OP_ParAppendResult res = manager->appendCHOP(sp); 888 | assert(res == OP_ParAppendResult::Success); 889 | } 890 | } 891 | 892 | void FlexCHOP::setupParamsCollisions(OP_ParameterManager* manager) { 893 | //Colplaneschop 894 | { 895 | OP_StringParameter sp; 896 | 897 | sp.name = "Colplaneschop"; 898 | sp.label = "Collision Planes CHOP"; 899 | sp.page = "Collisions"; 900 | 901 | OP_ParAppendResult res = manager->appendCHOP(sp); 902 | assert(res == OP_ParAppendResult::Success); 903 | } 904 | 905 | //Colspheres 906 | { 907 | OP_StringParameter sp; 908 | 909 | sp.name = "Colsphereschop"; 910 | sp.label = "Collision Spheres CHOP"; 911 | sp.page = "Collisions"; 912 | 913 | OP_ParAppendResult res = manager->appendCHOP(sp); 914 | assert(res == OP_ParAppendResult::Success); 915 | } 916 | 917 | //Colboxes 918 | { 919 | OP_StringParameter sp; 920 | 921 | sp.name = "Colboxeschop"; 922 | sp.label = "Collision Boxes CHOP"; 923 | sp.page = "Collisions"; 924 | 925 | OP_ParAppendResult res = manager->appendCHOP(sp); 926 | assert(res == OP_ParAppendResult::Success); 927 | } 928 | 929 | //Trianglespolysop 930 | { 931 | OP_StringParameter sp; 932 | 933 | sp.name = "Trianglespolysop"; 934 | sp.label = "Triangles Polygons SOP"; 935 | sp.page = "Collisions"; 936 | 937 | OP_ParAppendResult res = manager->appendSOP(sp); 938 | assert(res == OP_ParAppendResult::Success); 939 | } 940 | 941 | // Deformingmesh 942 | { 943 | OP_NumericParameter np; 944 | 945 | np.name = "Deformingmesh"; 946 | np.label = "Deforming Mesh"; 947 | np.page = "Collisions"; 948 | np.defaultValues[0] = 0; 949 | 950 | OP_ParAppendResult res = manager->appendToggle(np); 951 | assert(res == OP_ParAppendResult::Success); 952 | } 953 | 954 | // Meshtranslation 955 | { 956 | OP_NumericParameter np; 957 | 958 | np.name = "Meshtranslation"; 959 | np.label = "Mesh Translation"; 960 | np.page = "Collisions"; 961 | np.defaultValues[0] = 0; 962 | np.defaultValues[1] = 0; 963 | np.defaultValues[2] = 0; 964 | 965 | OP_ParAppendResult res = manager->appendXYZ(np); 966 | assert(res == OP_ParAppendResult::Success); 967 | } 968 | 969 | // Meshrotation 970 | { 971 | OP_NumericParameter np; 972 | 973 | np.name = "Meshrotation"; 974 | np.label = "Mesh Rotation"; 975 | np.page = "Collisions"; 976 | np.defaultValues[0] = 0; 977 | np.defaultValues[1] = 0; 978 | np.defaultValues[2] = 0; 979 | 980 | OP_ParAppendResult res = manager->appendXYZ(np); 981 | assert(res == OP_ParAppendResult::Success); 982 | } 983 | 984 | //Collisiondistance 985 | { 986 | OP_NumericParameter np; 987 | 988 | np.name = "Collisiondistance"; 989 | np.label = "Collision Distance"; 990 | np.defaultValues[0] = 0.0f; // 0.05f; 991 | np.page = "Collisions"; 992 | 993 | OP_ParAppendResult res = manager->appendFloat(np); 994 | assert(res == OP_ParAppendResult::Success); 995 | } 996 | 997 | //Shapecollisionmargin 998 | { 999 | OP_NumericParameter np; 1000 | 1001 | np.name = "Shapecollisionmargin"; 1002 | np.label = "Shape Collision Margin"; 1003 | np.defaultValues[0] = 0.0f; //0.00001f 1004 | np.page = "Collisions"; 1005 | 1006 | OP_ParAppendResult res = manager->appendFloat(np); 1007 | assert(res == OP_ParAppendResult::Success); 1008 | } 1009 | } 1010 | 1011 | void FlexCHOP::setupParameters(OP_ParameterManager* manager, void* reserved1) 1012 | { 1013 | setupParamsCustom(manager); 1014 | setupParamsSolver(manager); 1015 | setupParamsParts(manager); 1016 | setupParamsEmission(manager); 1017 | setupParamsCollisions(manager); 1018 | 1019 | } -------------------------------------------------------------------------------- /CHOP/Source/CPlusPlus_Common.h: -------------------------------------------------------------------------------- 1 | /* Shared Use License: This file is owned by Derivative Inc. (Derivative) and 2 | * can only be used, and/or modified for use, in conjunction with 3 | * Derivative's TouchDesigner software, and only if you are a licensee who has 4 | * accepted Derivative's TouchDesigner license or assignment agreement (which 5 | * also govern the use of this file). You may share a modified version of this 6 | * file with another authorized licensee of Derivative's TouchDesigner software. 7 | * Otherwise, no redistribution or sharing of this file, with or without 8 | * modification, is permitted. 9 | */ 10 | 11 | /* 12 | * Produced by: 13 | * 14 | * Derivative Inc 15 | * 401 Richmond Street West, Unit 386 16 | * Toronto, Ontario 17 | * Canada M5V 3A8 18 | * 416-591-3555 19 | * 20 | * NAME: CPlusPlus_Common.h 21 | * 22 | */ 23 | 24 | /******* 25 | Derivative Developers:: Make sure the virtual function order 26 | stays the same, otherwise changes won't be backwards compatible 27 | ********/ 28 | 29 | 30 | #ifndef __CPlusPlus_Common 31 | #define __CPlusPlus_Common 32 | 33 | 34 | #ifdef _WIN32 35 | #define NOMINMAX 36 | #include 37 | #include 38 | #include "GL_Extensions.h" 39 | #define DLLEXPORT __declspec (dllexport) 40 | #else 41 | #include 42 | #define DLLEXPORT 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | #ifndef PyObject_HEAD 50 | struct _object; 51 | typedef _object PyObject; 52 | #endif 53 | 54 | struct cudaArray; 55 | 56 | enum class OP_CPUMemPixelType : int32_t 57 | { 58 | // 8-bit per color, BGRA pixels. This is preferred for 4 channel 8-bit data 59 | BGRA8Fixed = 0, 60 | // 8-bit per color, RGBA pixels. Only use this one if absolutely nesseary. 61 | RGBA8Fixed, 62 | // 32-bit float per color, RGBA pixels 63 | RGBA32Float, 64 | 65 | // Single and double channel options 66 | // Fixed 67 | R8Fixed, 68 | RG8Fixed, 69 | // Float 70 | R32Float, 71 | RG32Float, 72 | }; 73 | 74 | class OP_String; 75 | 76 | // Used to describe this Plugin so it can be used as a custom OP. 77 | // Can be filled in as part of the Fill*PluginInfo() callback 78 | class OP_CustomOPInfo 79 | { 80 | public: 81 | // For this plugin to be treated as a Custom OP, all of the below fields 82 | // must be filled in correctly. Otherwise the .dll can only be used 83 | // when manually loaded into the C++ TOP 84 | 85 | // The type name of the node, this needs to be unique from all the other 86 | // TOP plugins loaded on the system. The name must start with an upper case 87 | // character (A-Z), and the rest should be lower case 88 | // Only the characters a-z and 0-9 are allowed in the opType. 89 | // Spaces are not allowed 90 | OP_String* opType; 91 | 92 | // The english readable label for the node. This is what is show in the 93 | // OP Create Menu dialog. 94 | // Spaces and other special characters are allowed. 95 | // This can be a UTF-8 encoded string for non-english langauge label 96 | OP_String* opLabel; 97 | 98 | // This should be three letters (upper or lower case), or numbers, which 99 | // are used to create an icon for this Custom OP. 100 | OP_String* opIcon; 101 | 102 | // The minimum number of wired inputs required for this OP to function. 103 | int32_t minInputs = 0; 104 | 105 | // The maximum number of connected inputs allowed for this OP. If this plugin 106 | // always requires 1 input, then set both min and max to 1. 107 | int32_t maxInputs = 0; 108 | 109 | // The name of the author 110 | OP_String* authorName; 111 | 112 | // The email of the author 113 | OP_String* authorEmail; 114 | 115 | // Major version should be used to differentiate between drastically different 116 | // versions of this Custom OP. In particular changes that arn't backwards 117 | // compatible. 118 | // A project file will compare the major version of OPs saved in it with the 119 | // major version of the plugin installed on the system, and expect them to be 120 | // the same. 121 | int32_t majorVersion = 0; 122 | 123 | // Minor version is used to denote upgrades to a plugin. It should be increased 124 | // when new features are added to a plugin that would cause loading up a project 125 | // with an older version of the plguin to behavior incorrectly. For example 126 | // if new parameters are added to the plugin. 127 | // A project file will expect the plugin installed on the system to be greater than 128 | // or equal to the plugin version the project was created with. Assuming 129 | // the majorVersion is the same. 130 | int32_t minorVersion = 1; 131 | 132 | // If this Custom OP is using CPython objects (PyObject* etc.) obtained via 133 | // getParPython() calls, this needs to be set to the Python 134 | // version this plugin is compiled against. 135 | // 136 | // This ensures when TD's Python version is upgraded the plugins will 137 | // error cleanly. This should be set to PY_VERSION as defined in 138 | // patchlevel.h from the Python include folder. (E.g, "3.5.1") 139 | // It should be left unchanged if CPython isn't being used in this plugin. 140 | OP_String* pythonVersion; 141 | 142 | int32_t reserved[98]; 143 | }; 144 | 145 | 146 | class OP_NodeInfo 147 | { 148 | public: 149 | // The full path to the operator 150 | 151 | const char* opPath; 152 | 153 | // A unique ID representing the operator, no two operators will ever 154 | // have the same ID in a single TouchDesigner instance. 155 | 156 | uint32_t opId; 157 | 158 | // This is the handle to the main TouchDesigner window. 159 | // It's possible this will be 0 the first few times the operator cooks, 160 | // incase it cooks while TouchDesigner is still loading up 161 | 162 | #ifdef _WIN32 163 | HWND mainWindowHandle; 164 | #endif 165 | 166 | int32_t reserved[19]; 167 | }; 168 | 169 | 170 | class OP_DATInput 171 | { 172 | public: 173 | const char* opPath; 174 | uint32_t opId; 175 | 176 | int32_t numRows; 177 | int32_t numCols; 178 | bool isTable; 179 | 180 | // data, referenced by (row,col), which will be a const char* for the 181 | // contents of the cell 182 | // E.g getCell(1,2) will be the contents of the cell located at (1,2) 183 | // The string will be in UTF-8 encoding. 184 | const char* 185 | getCell(int32_t row, int32_t col) const 186 | { 187 | return cellData[row * numCols + col]; 188 | } 189 | 190 | const char** cellData; 191 | 192 | // The number of times this node has cooked 193 | int64_t totalCooks; 194 | 195 | int32_t reserved[18]; 196 | }; 197 | 198 | 199 | class OP_TOPInput 200 | { 201 | public: 202 | const char* opPath; 203 | uint32_t opId; 204 | 205 | int32_t width; 206 | int32_t height; 207 | 208 | // You can use OP_Inputs::getTOPDataInCPUMemory() to download the 209 | // data from a TOP input into CPU memory easily. 210 | 211 | // The OpenGL Texture index for this TOP. 212 | // This is only valid when accessed from C++ TOPs. 213 | // Other C++ OPs will have this value set to 0 (invalid). 214 | GLuint textureIndex; 215 | 216 | // The OpenGL Texture target for this TOP. 217 | // E.g GL_TEXTURE_2D, GL_TEXTURE_CUBE, 218 | // GL_TEXTURE_2D_ARRAY 219 | GLenum textureType; 220 | 221 | // Depth for 3D and 2D_ARRAY textures, undefined 222 | // for other texture types 223 | uint32_t depth; 224 | 225 | // contains the internalFormat for the texture 226 | // such as GL_RGBA8, GL_RGBA32F, GL_R16 227 | GLint pixelFormat; 228 | 229 | int32_t reserved1; 230 | 231 | // When the TOP_ExecuteMode is CUDA, this will be filled in 232 | cudaArray* cudaInput; 233 | 234 | // The number of times this node has cooked 235 | int64_t totalCooks; 236 | 237 | int32_t reserved[14]; 238 | }; 239 | 240 | class OP_String 241 | { 242 | protected: 243 | OP_String() 244 | { 245 | } 246 | 247 | virtual ~OP_String() 248 | { 249 | } 250 | 251 | public: 252 | 253 | // val is expected to be UTF-8 encoded 254 | virtual void setString(const char* val) = 0; 255 | 256 | 257 | int32_t reserved[20]; 258 | 259 | }; 260 | 261 | 262 | class OP_CHOPInput 263 | { 264 | public: 265 | 266 | const char* opPath; 267 | uint32_t opId; 268 | 269 | int32_t numChannels; 270 | int32_t numSamples; 271 | double sampleRate; 272 | double startIndex; 273 | 274 | 275 | 276 | // Retrieve a float array for a specific channel. 277 | // 'i' ranges from 0 to numChannels-1 278 | // The returned arrray contains 'numSamples' samples. 279 | // e.g: getChannelData(1)[10] will refer to the 11th sample in the 2nd channel 280 | 281 | const float* 282 | getChannelData(int32_t i) const 283 | { 284 | return channelData[i]; 285 | } 286 | 287 | 288 | // Retrieve the name of a specific channel. 289 | // 'i' ranges from 0 to numChannels-1 290 | // For example getChannelName(1) is the name of the 2nd channel 291 | 292 | const char* 293 | getChannelName(int32_t i) const 294 | { 295 | return nameData[i]; 296 | } 297 | 298 | const float** channelData; 299 | const char** nameData; 300 | 301 | // The number of times this node has cooked 302 | int64_t totalCooks; 303 | 304 | int32_t reserved[18]; 305 | }; 306 | 307 | 308 | class OP_ObjectInput 309 | { 310 | public: 311 | 312 | const char* opPath; 313 | uint32_t opId; 314 | 315 | // Use these methods to calculate object transforms 316 | double worldTransform[4][4]; 317 | double localTransform[4][4]; 318 | 319 | // The number of times this node has cooked 320 | int64_t totalCooks; 321 | 322 | int32_t reserved[18]; 323 | }; 324 | 325 | 326 | // The type of data the attribute holds 327 | enum class AttribType : int32_t 328 | { 329 | // One or more floats 330 | Float = 0, 331 | 332 | // One or more integers 333 | Int, 334 | }; 335 | 336 | // Right now we only support point attributes. 337 | enum class AttribSet : int32_t 338 | { 339 | Invalid, 340 | Point = 0, 341 | }; 342 | 343 | // The type of the primitives, currently only Polygon type 344 | // is supported 345 | enum class PrimitiveType : int32_t 346 | { 347 | Invalid, 348 | Polygon = 0, 349 | }; 350 | 351 | 352 | class Vector 353 | { 354 | public: 355 | Vector() 356 | { 357 | x = 0.0f; 358 | y = 0.0f; 359 | z = 0.0f; 360 | } 361 | 362 | Vector(float xx, float yy, float zz) 363 | { 364 | x = xx; 365 | y = yy; 366 | z = zz; 367 | } 368 | 369 | // inplace operators 370 | inline Vector& 371 | operator*=(const float scalar) 372 | { 373 | x *= scalar; 374 | y *= scalar; 375 | z *= scalar; 376 | return *this; 377 | } 378 | 379 | inline Vector& 380 | operator/=(const float scalar) 381 | { 382 | x /= scalar; 383 | y /= scalar; 384 | z /= scalar; 385 | return *this; 386 | } 387 | 388 | inline Vector& 389 | operator-=(const Vector& trans) 390 | { 391 | x -= trans.x; 392 | y -= trans.y; 393 | z -= trans.z; 394 | return *this; 395 | } 396 | 397 | inline Vector& 398 | operator+=(const Vector& trans) 399 | { 400 | x += trans.x; 401 | y += trans.y; 402 | z += trans.z; 403 | return *this; 404 | } 405 | 406 | // non-inplace operations: 407 | inline Vector 408 | operator*(const float scalar) 409 | { 410 | Vector temp(*this); 411 | temp.x *= scalar; 412 | temp.y *= scalar; 413 | temp.z *= scalar; 414 | return temp; 415 | } 416 | 417 | inline Vector 418 | operator/(const float scalar) 419 | { 420 | Vector temp(*this); 421 | temp.x /= scalar; 422 | temp.y /= scalar; 423 | temp.z /= scalar; 424 | return temp; 425 | } 426 | 427 | inline Vector 428 | operator-(const Vector& trans) 429 | { 430 | Vector temp(*this); 431 | temp.x -= trans.x; 432 | temp.y -= trans.y; 433 | temp.z -= trans.z; 434 | return temp; 435 | } 436 | 437 | inline Vector 438 | operator+(const Vector& trans) 439 | { 440 | Vector temp(*this); 441 | temp.x += trans.x; 442 | temp.y += trans.y; 443 | temp.z += trans.z; 444 | return temp; 445 | } 446 | 447 | //------ 448 | float 449 | dot(const Vector &v) const 450 | { 451 | return x * v.x + y * v.y + z * v.z; 452 | } 453 | 454 | inline float 455 | length() 456 | { 457 | return sqrtf(dot(*this)); 458 | } 459 | 460 | inline float 461 | normalize() 462 | { 463 | float dn = x * x + y * y + z * z; 464 | if (dn > FLT_MIN && dn != 1.0F) 465 | { 466 | dn = sqrtf(dn); 467 | (*this) /= dn; 468 | } 469 | return dn; 470 | } 471 | 472 | float x; 473 | float y; 474 | float z; 475 | }; 476 | 477 | class Position 478 | { 479 | public: 480 | Position() 481 | { 482 | x = 0.0f; 483 | y = 0.0f; 484 | z = 0.0f; 485 | } 486 | 487 | Position(float xx, float yy, float zz) 488 | { 489 | x = xx; 490 | y = yy; 491 | z = zz; 492 | } 493 | 494 | // in-place operators 495 | inline Position& operator*=(const float scalar) 496 | { 497 | x *= scalar; 498 | y *= scalar; 499 | z *= scalar; 500 | return *this; 501 | } 502 | 503 | inline Position& operator/=(const float scalar) 504 | { 505 | x /= scalar; 506 | y /= scalar; 507 | z /= scalar; 508 | return *this; 509 | } 510 | 511 | inline Position& operator-=(const Vector& trans) 512 | { 513 | x -= trans.x; 514 | y -= trans.y; 515 | z -= trans.z; 516 | return *this; 517 | } 518 | 519 | inline Position& operator+=(const Vector& trans) 520 | { 521 | x += trans.x; 522 | y += trans.y; 523 | z += trans.z; 524 | return *this; 525 | } 526 | 527 | // non-inplace operators 528 | inline Position operator*(const float scalar) 529 | { 530 | Position temp(*this); 531 | temp.x *= scalar; 532 | temp.y *= scalar; 533 | temp.z *= scalar; 534 | return temp; 535 | } 536 | 537 | inline Position operator/(const float scalar) 538 | { 539 | Position temp(*this); 540 | temp.x /= scalar; 541 | temp.y /= scalar; 542 | temp.z /= scalar; 543 | return temp; 544 | } 545 | 546 | inline Position operator+(const Vector& trans) 547 | { 548 | Position temp(*this); 549 | temp.x += trans.x; 550 | temp.y += trans.y; 551 | temp.z += trans.z; 552 | return temp; 553 | } 554 | 555 | inline Position operator-(const Vector& trans) 556 | { 557 | Position temp(*this); 558 | temp.x -= trans.x; 559 | temp.y -= trans.y; 560 | temp.z -= trans.z; 561 | return temp; 562 | } 563 | 564 | float x; 565 | float y; 566 | float z; 567 | }; 568 | 569 | 570 | class Color 571 | { 572 | public: 573 | Color () 574 | { 575 | r = 1.0f; 576 | g = 1.0f; 577 | b = 1.0f; 578 | a = 1.0f; 579 | } 580 | 581 | Color (float rr, float gg, float bb, float aa) 582 | { 583 | r = rr; 584 | g = gg; 585 | b = bb; 586 | a = aa; 587 | } 588 | 589 | float r; 590 | float g; 591 | float b; 592 | float a; 593 | }; 594 | 595 | 596 | class TexCoord 597 | { 598 | public: 599 | TexCoord() 600 | { 601 | u = 0.0f; 602 | v = 0.0f; 603 | w = 0.0f; 604 | } 605 | 606 | TexCoord(float uu, float vv, float ww) 607 | { 608 | u = uu; 609 | v = vv; 610 | w = ww; 611 | } 612 | 613 | float u; 614 | float v; 615 | float w; 616 | }; 617 | 618 | class BoundingBox 619 | { 620 | public: 621 | BoundingBox(float minx, float miny, float minz, 622 | float maxx, float maxy, float maxz) : 623 | minX(minx), minY(miny), minZ(minz), maxX(maxx), maxY(maxy), maxZ(maxz) 624 | { 625 | } 626 | 627 | BoundingBox(const Position& min, const Position& max) 628 | { 629 | minX = min.x; 630 | maxX = max.x; 631 | minY = min.y; 632 | maxY = max.y; 633 | minZ = min.z; 634 | maxZ = max.z; 635 | } 636 | 637 | BoundingBox(const Position& center, float x, float y, float z) 638 | { 639 | minX = center.x - x; 640 | maxX = center.x + x; 641 | minY = center.y - y; 642 | maxY = center.y + y; 643 | minZ = center.z - z; 644 | maxZ = center.z + z; 645 | } 646 | 647 | // enlarge the bounding box by the input point Position 648 | void 649 | enlargeBounds(const Position& pos) 650 | { 651 | if (pos.x < minX) 652 | minX = pos.x; 653 | if (pos.x > maxX) 654 | maxX = pos.x; 655 | if (pos.y < minY) 656 | minY = pos.y; 657 | if (pos.y > maxY) 658 | maxY = pos.y; 659 | if (pos.z < minZ) 660 | minZ = pos.z; 661 | if (pos.z > maxZ) 662 | maxZ = pos.z; 663 | } 664 | 665 | // enlarge the bounding box by the input bounding box: 666 | void 667 | enlargeBounds(const BoundingBox &box) 668 | { 669 | if (box.minX < minX) 670 | minX = box.minX; 671 | if (box.maxX > maxX) 672 | maxX = box.maxX; 673 | if (box.minY < minY) 674 | minY = box.minY; 675 | if (box.maxY > maxY) 676 | maxY = box.maxY; 677 | if (box.minZ < minZ) 678 | minZ = box.minZ; 679 | if (box.maxZ > maxZ) 680 | maxZ = box.maxZ; 681 | } 682 | 683 | // returns the bounding box length in x axis: 684 | float 685 | sizeX() 686 | { 687 | return maxX - minX; 688 | } 689 | 690 | // returns the bounding box length in y axis: 691 | float 692 | sizeY() 693 | { 694 | return maxY - minY; 695 | } 696 | 697 | // returns the bounding box length in z axis: 698 | float 699 | sizeZ() 700 | { 701 | return maxZ - minZ; 702 | } 703 | 704 | bool 705 | getCenter(Position* pos) 706 | { 707 | if (!pos) 708 | return false; 709 | pos->x = (minX + maxX) / 2.0f; 710 | pos->y = (minY + maxY) / 2.0f; 711 | pos->z = (minZ + maxZ) / 2.0f; 712 | return true; 713 | } 714 | 715 | // verifies if the input position (pos) is inside the current bounding box or not: 716 | bool 717 | isInside(const Position& pos) 718 | { 719 | if (pos.x >= minX && pos.x <= maxX && 720 | pos.y >= minY && pos.y <= maxY && 721 | pos.z >= minZ && pos.z <= maxZ) 722 | return true; 723 | else 724 | return false; 725 | } 726 | 727 | 728 | float minX; 729 | float minY; 730 | float minZ; 731 | 732 | float maxX; 733 | float maxY; 734 | float maxZ; 735 | 736 | }; 737 | 738 | 739 | class SOP_NormalInfo 740 | { 741 | public: 742 | 743 | SOP_NormalInfo() 744 | { 745 | numNormals = 0; 746 | attribSet = AttribSet::Point; 747 | normals = nullptr; 748 | } 749 | 750 | int32_t numNormals; 751 | AttribSet attribSet; 752 | const Vector* normals; 753 | }; 754 | 755 | class SOP_ColorInfo 756 | { 757 | public: 758 | 759 | SOP_ColorInfo() 760 | { 761 | numColors = 0; 762 | attribSet = AttribSet::Point; 763 | colors = nullptr; 764 | } 765 | 766 | int32_t numColors; 767 | AttribSet attribSet; 768 | const Color* colors; 769 | }; 770 | 771 | class SOP_TextureInfo 772 | { 773 | public: 774 | 775 | SOP_TextureInfo() 776 | { 777 | numTextures = 0; 778 | attribSet = AttribSet::Point; 779 | textures = nullptr; 780 | numTextureLayers = 0; 781 | } 782 | 783 | int32_t numTextures; 784 | AttribSet attribSet; 785 | const TexCoord* textures; 786 | int32_t numTextureLayers; 787 | }; 788 | 789 | 790 | 791 | // CustomAttribInfo, all the required data for each custom attribute 792 | // this info can be queried by calling getCustomAttribute() which accepts 793 | // two types of argument: 794 | // 1) a valid index of a custom attribute 795 | // 2) a valid name of a custom attribute 796 | class SOP_CustomAttribInfo 797 | { 798 | public: 799 | 800 | SOP_CustomAttribInfo() 801 | { 802 | name = nullptr; 803 | numComponents = 0; 804 | attribType = AttribType::Float; 805 | } 806 | 807 | SOP_CustomAttribInfo(const char* n, int32_t numComp, const AttribType& type) 808 | { 809 | name = n; 810 | numComponents = numComp; 811 | attribType = type; 812 | } 813 | 814 | const char* name; 815 | int32_t numComponents; 816 | AttribType attribType; 817 | }; 818 | 819 | // SOP_CustomAttribData, all the required data for each custom attribute 820 | // this info can be queried by calling getCustomAttribute() which accepts 821 | // a valid name of a custom attribute 822 | class SOP_CustomAttribData : public SOP_CustomAttribInfo 823 | { 824 | public: 825 | 826 | SOP_CustomAttribData() 827 | { 828 | name = nullptr; 829 | numComponents = 0; 830 | attribType = AttribType::Float; 831 | floatData = nullptr; 832 | intData = nullptr; 833 | } 834 | 835 | SOP_CustomAttribData(const char* n, int32_t numComp, const AttribType& type) 836 | { 837 | name = n; 838 | numComponents = numComp; 839 | attribType = type; 840 | floatData = nullptr; 841 | intData = nullptr; 842 | } 843 | 844 | const float* floatData; 845 | const int32_t* intData; 846 | 847 | }; 848 | 849 | // SOP_PrimitiveInfo, all the required data for each primitive 850 | // this info can be queried by calling getPrimitive() which accepts 851 | // a valid index of a primitive as an input argument 852 | class SOP_PrimitiveInfo 853 | { 854 | public: 855 | 856 | SOP_PrimitiveInfo() 857 | { 858 | pointIndices = nullptr; 859 | numVertices = 0; 860 | type = PrimitiveType::Invalid; 861 | pointIndicesOffset = 0; 862 | } 863 | 864 | // number of vertices of this prim 865 | int32_t numVertices; 866 | 867 | // all the indices of the vertices of the primitive. This array has 868 | // numVertices entries in it 869 | const int32_t* pointIndices; 870 | 871 | // The type of this primitive 872 | PrimitiveType type; 873 | 874 | // the offset of the this primitive's point indices in the index array 875 | // returned from getAllPrimPointIndices() 876 | int32_t pointIndicesOffset; 877 | 878 | }; 879 | 880 | 881 | 882 | 883 | class OP_SOPInput 884 | { 885 | public: 886 | 887 | virtual ~OP_SOPInput() 888 | { 889 | } 890 | 891 | 892 | 893 | const char* opPath; 894 | uint32_t opId; 895 | 896 | 897 | // Returns the total number of points 898 | virtual int32_t getNumPoints() const = 0; 899 | 900 | // The total number of vertices, across all primitives. 901 | virtual int32_t getNumVertices() const = 0; 902 | 903 | // The total number of primitives 904 | virtual int32_t getNumPrimitives() const = 0; 905 | 906 | // The total number of custom attributes 907 | virtual int32_t getNumCustomAttributes() const = 0; 908 | 909 | // Returns an array of point positions. This array is getNumPoints() long. 910 | virtual const Position* getPointPositions() const = 0; 911 | 912 | // Returns an array of normals. 913 | // 914 | // Returns nullptr if no normals are present 915 | virtual const SOP_NormalInfo* getNormals() const = 0; 916 | 917 | // Returns an array of colors. 918 | // Returns nullptr if no colors are present 919 | virtual const SOP_ColorInfo* getColors() const = 0; 920 | 921 | // Returns an array of texture coordinates. 922 | // If multiple texture coordinate layers are present, they will be placed 923 | // interleaved back-to-back. 924 | // E.g layer0 followed by layer1 followed by layer0 etc. 925 | // 926 | // Returns nullptr if no texture layers are present 927 | virtual const SOP_TextureInfo* getTextures() const = 0; 928 | 929 | // Returns the custom attribute data with an input index 930 | virtual const SOP_CustomAttribData* getCustomAttribute(int32_t customAttribIndex) const = 0; 931 | 932 | // Returns the custom attribute data with its name 933 | virtual const SOP_CustomAttribData* getCustomAttribute(const char* customAttribName) const = 0; 934 | 935 | // Returns true if the SOP has a normal attribute of the given source 936 | // attribute 'N' 937 | virtual bool hasNormals() const = 0; 938 | 939 | // Returns true if the SOP has a color the given source 940 | // attribute 'Cd' 941 | virtual bool hasColors() const = 0; 942 | 943 | // Returns the SOP_PrimitiveInfo with primIndex 944 | const SOP_PrimitiveInfo 945 | getPrimitive(int32_t primIndex) const 946 | { 947 | return myPrimsInfo[primIndex]; 948 | } 949 | 950 | // Returns the full list of all the point indices for all primitives. 951 | // The primitives are stored back to back in this array. 952 | // This is a faster but harder way to work with primitives than 953 | // getPrimPointIndices() 954 | const int32_t* 955 | getAllPrimPointIndices() 956 | { 957 | return myPrimPointIndices; 958 | } 959 | 960 | SOP_PrimitiveInfo* myPrimsInfo; 961 | const int32_t* myPrimPointIndices; 962 | 963 | // The number of times this node has cooked 964 | int64_t totalCooks; 965 | 966 | int32_t reserved[98]; 967 | }; 968 | 969 | 970 | 971 | enum class OP_TOPInputDownloadType : int32_t 972 | { 973 | // The texture data will be downloaded and and available on the next frame. 974 | // Except for the first time this is used, getTOPDataInCPUMemory() 975 | // will return the texture data on the CPU from the previous frame. 976 | // The first getTOPDataInCPUMemory() is called it will be nullptr. 977 | // ** This mode should be used is most cases for performance reasons ** 978 | Delayed = 0, 979 | 980 | // The texture data will be downloaded immediately and be available 981 | // this frame. This can cause a large stall though and should be avoided 982 | // in most cases 983 | Instant, 984 | }; 985 | 986 | class OP_TOPInputDownloadOptions 987 | { 988 | public: 989 | OP_TOPInputDownloadOptions() 990 | { 991 | downloadType = OP_TOPInputDownloadType::Delayed; 992 | verticalFlip = false; 993 | cpuMemPixelType = OP_CPUMemPixelType::BGRA8Fixed; 994 | } 995 | 996 | OP_TOPInputDownloadType downloadType; 997 | 998 | // Set this to true if you want the image vertically flipped in the 999 | // downloaded data 1000 | bool verticalFlip; 1001 | 1002 | // Set this to how you want the pixel data to be give to you in CPU 1003 | // memory. BGRA8Fixed should be used for 4 channel 8-bit data if possible 1004 | OP_CPUMemPixelType cpuMemPixelType; 1005 | 1006 | }; 1007 | 1008 | class OP_TimeInfo 1009 | { 1010 | public: 1011 | 1012 | // same as global Python value absTime.frame. Counts up forever 1013 | // since the application started. In rootFPS units. 1014 | int64_t absFrame; 1015 | 1016 | // The timeline frame number for this cook 1017 | double frame; 1018 | 1019 | // The timeline FPS/rate this node is cooking at. 1020 | // If the component this node is located in has Component Time, it's FPS 1021 | // may be different than the Root FPS 1022 | double rate; 1023 | 1024 | // The frame number for the root timeline. Different than frame 1025 | // if the node is in a component that has component time. 1026 | double rootFrame; 1027 | 1028 | // The Root FPS/Rate the file is running at. 1029 | double rootRate; 1030 | 1031 | // The number of frames that have elapsed since the last cook occured. 1032 | // This can be more than one if frames were dropped. 1033 | // If this is the first time this node is cooking, this will be 0.0 1034 | // This is in 'rate' units, not 'rootRate' units. 1035 | double deltaFrames; 1036 | 1037 | // The number of milliseconds that have elapsed since the last cook. 1038 | // Note that this isn't done via CPU timers, but is instead 1039 | // simply deltaFrames * milliSecondsPerFrame 1040 | double deltaMS; 1041 | 1042 | 1043 | 1044 | int32_t reserved[40]; 1045 | }; 1046 | 1047 | 1048 | class OP_Inputs 1049 | { 1050 | public: 1051 | // NOTE: When writting a TOP, none of these functions should 1052 | // be called inside a beginGLCommands()/endGLCommands() section 1053 | // as they may require GL themselves to complete execution. 1054 | 1055 | // Inputs that are wired into the node. Note that since some inputs 1056 | // may not be connected this number doesn't mean that that the first N 1057 | // inputs are connected. For example on a 3 input node if the 3rd input 1058 | // is only one connected, this will return 1, and getInput*(0) and (1) 1059 | // will return nullptr. 1060 | virtual int32_t getNumInputs() const = 0; 1061 | 1062 | // Will return nullptr when the input has nothing connected to it. 1063 | // only valid for C++ TOP operators 1064 | virtual const OP_TOPInput* getInputTOP(int32_t index) const = 0; 1065 | // Only valid for C++ CHOP operators 1066 | virtual const OP_CHOPInput* getInputCHOP(int32_t index) const = 0; 1067 | // getInputSOP() declared later on in the class 1068 | // getInputDAT() declared later on in the class 1069 | 1070 | // these are defined by parameters. 1071 | // may return nullptr when invalid input 1072 | // this value is valid until the parameters are rebuilt or it is called with the same parameter name. 1073 | virtual const OP_DATInput* getParDAT(const char *name) const = 0; 1074 | virtual const OP_TOPInput* getParTOP(const char *name) const = 0; 1075 | virtual const OP_CHOPInput* getParCHOP(const char *name) const = 0; 1076 | virtual const OP_ObjectInput* getParObject(const char *name) const = 0; 1077 | // getParSOP() declared later on in the class 1078 | 1079 | // these work on any type of parameter and can be interchanged 1080 | // for menu types, int returns the menu selection index, string returns the item 1081 | 1082 | // returns the requested value, index may be 0 to 4. 1083 | virtual double getParDouble(const char* name, int32_t index = 0) const = 0; 1084 | 1085 | // for multiple values: returns True on success/false otherwise 1086 | virtual bool getParDouble2(const char* name, double &v0, double &v1) const = 0; 1087 | virtual bool getParDouble3(const char* name, double &v0, double &v1, double &v2) const = 0; 1088 | virtual bool getParDouble4(const char* name, double &v0, double &v1, double &v2, double &v3) const = 0; 1089 | 1090 | 1091 | // returns the requested value 1092 | virtual int32_t getParInt(const char* name, int32_t index = 0) const = 0; 1093 | 1094 | // for multiple values: returns True on success/false otherwise 1095 | virtual bool getParInt2(const char* name, int32_t &v0, int32_t &v1) const = 0; 1096 | virtual bool getParInt3(const char* name, int32_t &v0, int32_t &v1, int32_t &v2) const = 0; 1097 | virtual bool getParInt4(const char* name, int32_t &v0, int32_t &v1, int32_t &v2, int32_t &v3) const = 0; 1098 | 1099 | // returns the requested value 1100 | // this value is valid until the parameters are rebuilt or it is called with the same parameter name. 1101 | // return value usable for life of parameter 1102 | // The returned string will be in UTF-8 encoding. 1103 | virtual const char* getParString(const char* name) const = 0; 1104 | 1105 | 1106 | // this is similar to getParString, but will return an absolute path if it exists, with 1107 | // slash direction consistent with O/S requirements. 1108 | // to get the original parameter value, use getParString 1109 | // return value usable for life of parameter 1110 | // The returned string will be in UTF-8 encoding. 1111 | virtual const char* getParFilePath(const char* name) const = 0; 1112 | 1113 | // returns true on success 1114 | // from_name and to_name must be Object parameters 1115 | virtual bool getRelativeTransform(const char* from_name, const char* to_name, double matrix[4][4]) const = 0; 1116 | 1117 | 1118 | // disable or enable updating of the parameter 1119 | virtual void enablePar(const char* name, bool onoff) const = 0; 1120 | 1121 | 1122 | // these are defined by paths. 1123 | // may return nullptr when invalid input 1124 | // this value is valid until the parameters are rebuilt or it is called with the same parameter name. 1125 | virtual const OP_DATInput* getDAT(const char *path) const = 0; 1126 | virtual const OP_TOPInput* getTOP(const char *path) const = 0; 1127 | virtual const OP_CHOPInput* getCHOP(const char *path) const = 0; 1128 | virtual const OP_ObjectInput* getObject(const char *path) const = 0; 1129 | 1130 | 1131 | // This function can be used to retrieve the TOPs texture data in CPU 1132 | // memory. You must pass the OP_TOPInput object you get from 1133 | // getParTOP/getInputTOP into this, not a copy you've made 1134 | // 1135 | // Fill in a OP_TOPIputDownloadOptions class with the desired options set 1136 | // 1137 | // Returns the data, which will be valid until the end of execute() 1138 | // Returned value may be nullptr in some cases, such as the first call 1139 | // to this with options->downloadType == OP_TOP_DOWNLOAD_DELAYED. 1140 | virtual void* getTOPDataInCPUMemory(const OP_TOPInput *top, 1141 | const OP_TOPInputDownloadOptions *options) const = 0; 1142 | 1143 | 1144 | virtual const OP_SOPInput* getParSOP(const char *name) const = 0; 1145 | // only valid for C++ SOP operators 1146 | virtual const OP_SOPInput* getInputSOP(int32_t index) const = 0; 1147 | virtual const OP_SOPInput* getSOP(const char *path) const = 0; 1148 | 1149 | // only valid for C++ DAT operators 1150 | virtual const OP_DATInput* getInputDAT(int32_t index) const = 0; 1151 | 1152 | // To use Python in your Plugin you need to fill the 1153 | // customOPInfo.pythonVersion member in Fill*PluginInfo. 1154 | // 1155 | // The returned object does NOT have it's reference count incremented. 1156 | // So increment it if you want to hold onto the object, and only 1157 | // decement it if you've incremented it. 1158 | virtual PyObject* getParPython(const char* name) const = 0; 1159 | 1160 | 1161 | // Returns a class whose members gives you information about timing 1162 | // such as FPS and delta-time since the last cook. 1163 | // See OP_TimeInfo for more information 1164 | virtual const OP_TimeInfo* getTimeInfo() const = 0; 1165 | 1166 | }; 1167 | 1168 | class OP_InfoCHOPChan 1169 | { 1170 | public: 1171 | OP_String* name; 1172 | float value; 1173 | 1174 | int32_t reserved[10]; 1175 | }; 1176 | 1177 | 1178 | class OP_InfoDATSize 1179 | { 1180 | public: 1181 | 1182 | // Set this to the size you want the table to be 1183 | 1184 | int32_t rows; 1185 | int32_t cols; 1186 | 1187 | // Set this to true if you want to return DAT entries on a column 1188 | // by column basis. 1189 | // Otherwise set to false, and you'll be expected to set them on 1190 | // a row by row basis. 1191 | // DEFAULT : false 1192 | 1193 | bool byColumn; 1194 | 1195 | int32_t reserved[10]; 1196 | }; 1197 | 1198 | 1199 | class OP_InfoDATEntries 1200 | { 1201 | public: 1202 | 1203 | // This is an array of OP_String* pointers which you are expected to assign 1204 | // values to. 1205 | // e.g values[1]->setString("myColumnName"); 1206 | // The string should be in UTF-8 encoding. 1207 | OP_String** values; 1208 | 1209 | int32_t reserved[10]; 1210 | }; 1211 | 1212 | 1213 | class OP_NumericParameter 1214 | { 1215 | public: 1216 | 1217 | OP_NumericParameter(const char* iname = nullptr) 1218 | { 1219 | name = iname; 1220 | label = page = nullptr; 1221 | 1222 | for (int i = 0; i<4; i++) 1223 | { 1224 | defaultValues[i] = 0.0; 1225 | 1226 | minSliders[i] = 0.0; 1227 | maxSliders[i] = 1.0; 1228 | 1229 | minValues[i] = 0.0; 1230 | maxValues[i] = 1.0; 1231 | 1232 | clampMins[i] = false; 1233 | clampMaxes[i] = false; 1234 | } 1235 | } 1236 | 1237 | // Any char* values passed are copied immediately by the append parameter functions, 1238 | // and do not need to be retained by the calling function. 1239 | // Must begin with capital letter, and contain no spaces 1240 | const char* name; 1241 | const char* label; 1242 | const char* page; 1243 | 1244 | double defaultValues[4]; 1245 | double minValues[4]; 1246 | double maxValues[4]; 1247 | 1248 | bool clampMins[4]; 1249 | bool clampMaxes[4]; 1250 | 1251 | double minSliders[4]; 1252 | double maxSliders[4]; 1253 | 1254 | int32_t reserved[20]; 1255 | 1256 | }; 1257 | 1258 | 1259 | class OP_StringParameter 1260 | { 1261 | public: 1262 | 1263 | OP_StringParameter(const char* iname = nullptr) 1264 | { 1265 | name = iname; 1266 | label = page = nullptr; 1267 | defaultValue = nullptr; 1268 | } 1269 | 1270 | // Any char* values passed are copied immediately by the append parameter functions, 1271 | // and do not need to be retained by the calling function. 1272 | 1273 | // Must begin with capital letter, and contain no spaces 1274 | const char* name; 1275 | const char* label; 1276 | const char* page; 1277 | 1278 | // This should be in UTF-8 encoding. 1279 | const char* defaultValue; 1280 | 1281 | int32_t reserved[20]; 1282 | }; 1283 | 1284 | 1285 | enum class OP_ParAppendResult : int32_t 1286 | { 1287 | Success = 0, 1288 | InvalidName, // invalid or duplicate name 1289 | InvalidSize, // size out of range 1290 | }; 1291 | 1292 | 1293 | class OP_ParameterManager 1294 | { 1295 | 1296 | public: 1297 | 1298 | // Returns PARAMETER_APPEND_SUCCESS on succesful 1299 | 1300 | virtual OP_ParAppendResult appendFloat(const OP_NumericParameter &np, int32_t size = 1) = 0; 1301 | virtual OP_ParAppendResult appendInt(const OP_NumericParameter &np, int32_t size = 1) = 0; 1302 | 1303 | virtual OP_ParAppendResult appendXY(const OP_NumericParameter &np) = 0; 1304 | virtual OP_ParAppendResult appendXYZ(const OP_NumericParameter &np) = 0; 1305 | 1306 | virtual OP_ParAppendResult appendUV(const OP_NumericParameter &np) = 0; 1307 | virtual OP_ParAppendResult appendUVW(const OP_NumericParameter &np) = 0; 1308 | 1309 | virtual OP_ParAppendResult appendRGB(const OP_NumericParameter &np) = 0; 1310 | virtual OP_ParAppendResult appendRGBA(const OP_NumericParameter &np) = 0; 1311 | 1312 | virtual OP_ParAppendResult appendToggle(const OP_NumericParameter &np) = 0; 1313 | virtual OP_ParAppendResult appendPulse(const OP_NumericParameter &np) = 0; 1314 | 1315 | virtual OP_ParAppendResult appendString(const OP_StringParameter &sp) = 0; 1316 | virtual OP_ParAppendResult appendFile(const OP_StringParameter &sp) = 0; 1317 | virtual OP_ParAppendResult appendFolder(const OP_StringParameter &sp) = 0; 1318 | 1319 | virtual OP_ParAppendResult appendDAT(const OP_StringParameter &sp) = 0; 1320 | virtual OP_ParAppendResult appendCHOP(const OP_StringParameter &sp) = 0; 1321 | virtual OP_ParAppendResult appendTOP(const OP_StringParameter &sp) = 0; 1322 | virtual OP_ParAppendResult appendObject(const OP_StringParameter &sp) = 0; 1323 | // appendSOP() located further down in the class 1324 | 1325 | 1326 | // Any char* values passed are copied immediately by the append parameter functions, 1327 | // and do not need to be retained by the calling function. 1328 | virtual OP_ParAppendResult appendMenu(const OP_StringParameter &sp, 1329 | int32_t nitems, const char **names, 1330 | const char **labels) = 0; 1331 | 1332 | // Any char* values passed are copied immediately by the append parameter functions, 1333 | // and do not need to be retained by the calling function. 1334 | virtual OP_ParAppendResult appendStringMenu(const OP_StringParameter &sp, 1335 | int32_t nitems, const char **names, 1336 | const char **labels) = 0; 1337 | 1338 | virtual OP_ParAppendResult appendSOP(const OP_StringParameter &sp) = 0; 1339 | 1340 | // To use Python in your Plugin you need to fill the 1341 | // customOPInfo.pythonVersion member in Fill*PluginInfo. 1342 | virtual OP_ParAppendResult appendPython(const OP_StringParameter &sp) = 0; 1343 | 1344 | 1345 | }; 1346 | 1347 | static_assert(offsetof(OP_CustomOPInfo, opType) == 0, "Incorrect Alignment"); 1348 | static_assert(offsetof(OP_CustomOPInfo, opLabel) == 8, "Incorrect Alignment"); 1349 | static_assert(offsetof(OP_CustomOPInfo, opIcon) == 16, "Incorrect Alignment"); 1350 | static_assert(offsetof(OP_CustomOPInfo, minInputs) == 24, "Incorrect Alignment"); 1351 | static_assert(offsetof(OP_CustomOPInfo, maxInputs) == 28, "Incorrect Alignment"); 1352 | static_assert(offsetof(OP_CustomOPInfo, authorName) == 32, "Incorrect Alignment"); 1353 | static_assert(offsetof(OP_CustomOPInfo, authorEmail) == 40, "Incorrect Alignment"); 1354 | static_assert(offsetof(OP_CustomOPInfo, majorVersion) == 48, "Incorrect Alignment"); 1355 | static_assert(offsetof(OP_CustomOPInfo, minorVersion) == 52, "Incorrect Alignment"); 1356 | static_assert(sizeof(OP_CustomOPInfo) == 456, "Incorrect Size"); 1357 | 1358 | static_assert(offsetof(OP_NodeInfo, opPath) == 0, "Incorrect Alignment"); 1359 | static_assert(offsetof(OP_NodeInfo, opId) == 8, "Incorrect Alignment"); 1360 | #ifdef _WIN32 1361 | static_assert(offsetof(OP_NodeInfo, mainWindowHandle) == 16, "Incorrect Alignment"); 1362 | static_assert(sizeof(OP_NodeInfo) == 104, "Incorrect Size"); 1363 | #else 1364 | static_assert(sizeof(OP_NodeInfo) == 88, "Incorrect Size"); 1365 | #endif 1366 | 1367 | static_assert(offsetof(OP_DATInput, opPath) == 0, "Incorrect Alignment"); 1368 | static_assert(offsetof(OP_DATInput, opId) == 8, "Incorrect Alignment"); 1369 | static_assert(offsetof(OP_DATInput, numRows) == 12, "Incorrect Alignment"); 1370 | static_assert(offsetof(OP_DATInput, numCols) == 16, "Incorrect Alignment"); 1371 | static_assert(offsetof(OP_DATInput, isTable) == 20, "Incorrect Alignment"); 1372 | static_assert(offsetof(OP_DATInput, cellData) == 24, "Incorrect Alignment"); 1373 | static_assert(offsetof(OP_DATInput, totalCooks) == 32, "Incorrect Alignment"); 1374 | static_assert(sizeof(OP_DATInput) == 112, "Incorrect Size"); 1375 | 1376 | static_assert(offsetof(OP_TOPInput, opPath) == 0, "Incorrect Alignment"); 1377 | static_assert(offsetof(OP_TOPInput, opId) == 8, "Incorrect Alignment"); 1378 | static_assert(offsetof(OP_TOPInput, width) == 12, "Incorrect Alignment"); 1379 | static_assert(offsetof(OP_TOPInput, height) == 16, "Incorrect Alignment"); 1380 | static_assert(offsetof(OP_TOPInput, textureIndex) == 20, "Incorrect Alignment"); 1381 | static_assert(offsetof(OP_TOPInput, textureType) == 24, "Incorrect Alignment"); 1382 | static_assert(offsetof(OP_TOPInput, depth) == 28, "Incorrect Alignment"); 1383 | static_assert(offsetof(OP_TOPInput, pixelFormat) == 32, "Incorrect Alignment"); 1384 | static_assert(offsetof(OP_TOPInput, cudaInput) == 40, "Incorrect Alignment"); 1385 | static_assert(offsetof(OP_TOPInput, totalCooks) == 48, "Incorrect Alignment"); 1386 | static_assert(sizeof(OP_TOPInput) == 112, "Incorrect Size"); 1387 | 1388 | static_assert(offsetof(OP_CHOPInput, opPath) == 0, "Incorrect Alignment"); 1389 | static_assert(offsetof(OP_CHOPInput, opId) == 8, "Incorrect Alignment"); 1390 | static_assert(offsetof(OP_CHOPInput, numChannels) == 12, "Incorrect Alignment"); 1391 | static_assert(offsetof(OP_CHOPInput, numSamples) == 16, "Incorrect Alignment"); 1392 | static_assert(offsetof(OP_CHOPInput, sampleRate) == 24, "Incorrect Alignment"); 1393 | static_assert(offsetof(OP_CHOPInput, startIndex) == 32, "Incorrect Alignment"); 1394 | static_assert(offsetof(OP_CHOPInput, channelData) == 40, "Incorrect Alignment"); 1395 | static_assert(offsetof(OP_CHOPInput, nameData) == 48, "Incorrect Alignment"); 1396 | static_assert(offsetof(OP_CHOPInput, totalCooks) == 56, "Incorrect Alignment"); 1397 | static_assert(sizeof(OP_CHOPInput) == 136, "Incorrect Size"); 1398 | 1399 | static_assert(offsetof(OP_ObjectInput, opPath) == 0, "Incorrect Alignment"); 1400 | static_assert(offsetof(OP_ObjectInput, opId) == 8, "Incorrect Alignment"); 1401 | static_assert(offsetof(OP_ObjectInput, worldTransform) == 16, "Incorrect Alignment"); 1402 | static_assert(offsetof(OP_ObjectInput, localTransform) == 144, "Incorrect Alignment"); 1403 | static_assert(offsetof(OP_ObjectInput, totalCooks) == 272, "Incorrect Alignment"); 1404 | static_assert(sizeof(OP_ObjectInput) == 352, "Incorrect Size"); 1405 | 1406 | static_assert(offsetof(Position, x) == 0, "Incorrect Alignment"); 1407 | static_assert(offsetof(Position, y) == 4, "Incorrect Alignment"); 1408 | static_assert(offsetof(Position, z) == 8, "Incorrect Alignment"); 1409 | static_assert(sizeof(Position) == 12, "Incorrect Size"); 1410 | 1411 | static_assert(offsetof(Vector, x) == 0, "Incorrect Alignment"); 1412 | static_assert(offsetof(Vector, y) == 4, "Incorrect Alignment"); 1413 | static_assert(offsetof(Vector, z) == 8, "Incorrect Alignment"); 1414 | static_assert(sizeof(Vector) == 12, "Incorrect Size"); 1415 | 1416 | static_assert(offsetof(Color, r) == 0, "Incorrect Alignment"); 1417 | static_assert(offsetof(Color, g) == 4, "Incorrect Alignment"); 1418 | static_assert(offsetof(Color, b) == 8, "Incorrect Alignment"); 1419 | static_assert(offsetof(Color, a) == 12, "Incorrect Alignment"); 1420 | static_assert(sizeof(Color) == 16, "Incorrect Size"); 1421 | 1422 | static_assert(offsetof(TexCoord, u) == 0, "Incorrect Alignment"); 1423 | static_assert(offsetof(TexCoord, v) == 4, "Incorrect Alignment"); 1424 | static_assert(offsetof(TexCoord, w) == 8, "Incorrect Alignment"); 1425 | static_assert(sizeof(TexCoord) == 12, "Incorrect Size"); 1426 | 1427 | static_assert(offsetof(SOP_NormalInfo, numNormals) == 0, "Incorrect Alignment"); 1428 | static_assert(offsetof(SOP_NormalInfo, attribSet) == 4, "Incorrect Alignment"); 1429 | static_assert(offsetof(SOP_NormalInfo, normals) == 8, "Incorrect Alignment"); 1430 | static_assert(sizeof(SOP_NormalInfo) == 16, "Incorrect Size"); 1431 | 1432 | static_assert(offsetof(SOP_ColorInfo, numColors) == 0, "Incorrect Alignment"); 1433 | static_assert(offsetof(SOP_ColorInfo, attribSet) == 4, "Incorrect Alignment"); 1434 | static_assert(offsetof(SOP_ColorInfo, colors) == 8, "Incorrect Alignment"); 1435 | static_assert(sizeof(SOP_ColorInfo) == 16, "Incorrect Size"); 1436 | 1437 | static_assert(offsetof(SOP_TextureInfo, numTextures) == 0, "Incorrect Alignment"); 1438 | static_assert(offsetof(SOP_TextureInfo, attribSet) == 4, "Incorrect Alignment"); 1439 | static_assert(offsetof(SOP_TextureInfo, textures) == 8, "Incorrect Alignment"); 1440 | static_assert(offsetof(SOP_TextureInfo, numTextureLayers) == 16, "Incorrect Alignment"); 1441 | static_assert(sizeof(SOP_TextureInfo) == 24, "Incorrect Size"); 1442 | 1443 | static_assert(offsetof(SOP_CustomAttribData, name) == 0, "Incorrect Alignment"); 1444 | static_assert(offsetof(SOP_CustomAttribData, numComponents) == 8, "Incorrect Alignment"); 1445 | static_assert(offsetof(SOP_CustomAttribData, attribType) == 12, "Incorrect Alignment"); 1446 | static_assert(offsetof(SOP_CustomAttribData, floatData) == 16, "Incorrect Alignment"); 1447 | static_assert(offsetof(SOP_CustomAttribData, intData) == 24, "Incorrect Alignment"); 1448 | static_assert(sizeof(SOP_CustomAttribData) == 32, "Incorrect Size"); 1449 | 1450 | static_assert(offsetof(SOP_PrimitiveInfo, numVertices) == 0, "Incorrect Alignment"); 1451 | static_assert(offsetof(SOP_PrimitiveInfo, pointIndices) == 8, "Incorrect Alignment"); 1452 | static_assert(offsetof(SOP_PrimitiveInfo, type) == 16, "Incorrect Alignment"); 1453 | static_assert(offsetof(SOP_PrimitiveInfo, pointIndicesOffset) == 20, "Incorrect Alignment"); 1454 | static_assert(sizeof(SOP_PrimitiveInfo) == 24, "Incorrect Size"); 1455 | 1456 | static_assert(sizeof(OP_SOPInput) == 440, "Incorrect Size"); 1457 | 1458 | static_assert(offsetof(OP_TOPInputDownloadOptions, downloadType) == 0, "Incorrect Alignment"); 1459 | static_assert(offsetof(OP_TOPInputDownloadOptions, verticalFlip) == 4, "Incorrect Alignment"); 1460 | static_assert(offsetof(OP_TOPInputDownloadOptions, cpuMemPixelType) == 8, "Incorrect Alignment"); 1461 | static_assert(sizeof(OP_TOPInputDownloadOptions) == 12, "Incorrect Size"); 1462 | 1463 | static_assert(offsetof(OP_InfoCHOPChan, name) == 0, "Incorrect Alignment"); 1464 | static_assert(offsetof(OP_InfoCHOPChan, value) == 8, "Incorrect Alignment"); 1465 | static_assert(sizeof(OP_InfoCHOPChan) == 56, "Incorrect Size"); 1466 | 1467 | static_assert(offsetof(OP_InfoDATSize, rows) == 0, "Incorrect Alignment"); 1468 | static_assert(offsetof(OP_InfoDATSize, cols) == 4, "Incorrect Alignment"); 1469 | static_assert(offsetof(OP_InfoDATSize, byColumn) == 8, "Incorrect Alignment"); 1470 | static_assert(sizeof(OP_InfoDATSize) == 52, "Incorrect Size"); 1471 | 1472 | static_assert(offsetof(OP_InfoDATEntries, values) == 0, "Incorrect Alignment"); 1473 | static_assert(sizeof(OP_InfoDATEntries) == 48, "Incorrect Size"); 1474 | 1475 | static_assert(offsetof(OP_NumericParameter, name) == 0, "Incorrect Alignment"); 1476 | static_assert(offsetof(OP_NumericParameter, label) == 8, "Incorrect Alignment"); 1477 | static_assert(offsetof(OP_NumericParameter, page) == 16, "Incorrect Alignment"); 1478 | static_assert(offsetof(OP_NumericParameter, defaultValues) == 24, "Incorrect Alignment"); 1479 | static_assert(offsetof(OP_NumericParameter, minValues) == 56, "Incorrect Alignment"); 1480 | static_assert(offsetof(OP_NumericParameter, maxValues) == 88, "Incorrect Alignment"); 1481 | static_assert(offsetof(OP_NumericParameter, clampMins) == 120, "Incorrect Alignment"); 1482 | static_assert(offsetof(OP_NumericParameter, clampMaxes) == 124, "Incorrect Alignment"); 1483 | static_assert(offsetof(OP_NumericParameter, minSliders) == 128, "Incorrect Alignment"); 1484 | static_assert(offsetof(OP_NumericParameter, maxSliders) == 160, "Incorrect Alignment"); 1485 | static_assert(sizeof(OP_NumericParameter) == 272, "Incorrect Size"); 1486 | 1487 | static_assert(offsetof(OP_StringParameter, name) == 0, "Incorrect Alignment"); 1488 | static_assert(offsetof(OP_StringParameter, label) == 8, "Incorrect Alignment"); 1489 | static_assert(offsetof(OP_StringParameter, page) == 16, "Incorrect Alignment"); 1490 | static_assert(offsetof(OP_StringParameter, defaultValue) == 24, "Incorrect Alignment"); 1491 | static_assert(sizeof(OP_StringParameter) == 112, "Incorrect Size"); 1492 | static_assert(sizeof(OP_TimeInfo) == 216, "Incorrect Size"); 1493 | #endif 1494 | --------------------------------------------------------------------------------