├── Plugins └── .gitignore ├── FaceCHOP.toe ├── docs └── images │ ├── header.png │ ├── properties_1.png │ ├── properties_2.png │ └── properties_3.png ├── GL_Extensions.h ├── CPlusPlusCHOPExample.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── Info.plist ├── TD-FaceCHOP.sln ├── LICENSE.md ├── FaceCHOP.h ├── README.md ├── CPlusPlusCHOPExample.vcxproj ├── .gitignore ├── CHOP_CPlusPlusBase.h ├── FaceCHOP.cpp └── CPlusPlus_Common.h /Plugins/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /FaceCHOP.toe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DBraun/TD-FaceCHOP/HEAD/FaceCHOP.toe -------------------------------------------------------------------------------- /docs/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DBraun/TD-FaceCHOP/HEAD/docs/images/header.png -------------------------------------------------------------------------------- /GL_Extensions.h: -------------------------------------------------------------------------------- 1 | // Stub file for simpler CHOP usage than an OpenGLTOP 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /docs/images/properties_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DBraun/TD-FaceCHOP/HEAD/docs/images/properties_1.png -------------------------------------------------------------------------------- /docs/images/properties_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DBraun/TD-FaceCHOP/HEAD/docs/images/properties_2.png -------------------------------------------------------------------------------- /docs/images/properties_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DBraun/TD-FaceCHOP/HEAD/docs/images/properties_3.png -------------------------------------------------------------------------------- /CPlusPlusCHOPExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CPlusPlusCHOPExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | NSHumanReadableCopyright 22 | Copyright © 2016 Derivative. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TD-FaceCHOP.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29102.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TD-FaceCHOP", "CPlusPlusCHOPExample.vcxproj", "{3F5BEECD-FA36-459F-91B8-BB481A67EF44}" 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 | {3F5BEECD-FA36-459F-91B8-BB481A67EF44}.Debug|x64.ActiveCfg = Debug|x64 15 | {3F5BEECD-FA36-459F-91B8-BB481A67EF44}.Debug|x64.Build.0 = Debug|x64 16 | {3F5BEECD-FA36-459F-91B8-BB481A67EF44}.Release|x64.ActiveCfg = Release|x64 17 | {3F5BEECD-FA36-459F-91B8-BB481A67EF44}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {029B0203-11A4-4450-B41B-A243C3D46483} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2007 by pedromartins * 3 | * pedromartins@isr.uc.pt * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 | ***************************************************************************/ 20 | -------------------------------------------------------------------------------- /FaceCHOP.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 | #include "CHOP_CPlusPlusBase.h" 12 | 13 | /* 14 | 15 | This example file implements a class that does 2 different things depending on 16 | if a CHOP is connected to the CPlusPlus CHOPs input or not. 17 | The example is timesliced, which is the more complex way of working. 18 | 19 | If an input is connected the node will output the same number of channels as the 20 | input and divide the first 'N' samples in the input channel by 2. 'N' being the current 21 | timeslice size. This is noteworthy because if the input isn't changing then the output 22 | will look wierd since depending on the timeslice size some number of the first samples 23 | of the input will get used. 24 | 25 | If no input is connected then the node will output a smooth sine wave at 120hz. 26 | */ 27 | 28 | #include "opencv2/objdetect.hpp" 29 | #include "opencv2/imgproc.hpp" 30 | #include "opencv2/highgui.hpp" 31 | 32 | #include 33 | //#include "dlib/opencv.h" 34 | //#include 35 | #include 36 | //#include 37 | #include 38 | //#include 39 | #include 40 | 41 | 42 | // To get more help about these functions, look at CHOP_CPlusPlusBase.h 43 | class FaceCHOP : public CHOP_CPlusPlusBase 44 | { 45 | public: 46 | FaceCHOP(const OP_NodeInfo* info); 47 | virtual ~FaceCHOP(); 48 | 49 | virtual void getGeneralInfo(CHOP_GeneralInfo*, const OP_Inputs*, void* ) override; 50 | virtual bool getOutputInfo(CHOP_OutputInfo*, const OP_Inputs*, void*) override; 51 | virtual void getChannelName(int32_t index, OP_String *name, const OP_Inputs*, void* reserved) override; 52 | 53 | virtual void execute(CHOP_Output*, 54 | const OP_Inputs*, 55 | void* reserved) override; 56 | 57 | 58 | virtual int32_t getNumInfoCHOPChans(void* reserved1) override; 59 | virtual void getInfoCHOPChan(int index, 60 | OP_InfoCHOPChan* chan, 61 | void* reserved1) override; 62 | 63 | virtual bool getInfoDATSize(OP_InfoDATSize* infoSize, void* resereved1) override; 64 | virtual void getInfoDATEntries(int32_t index, 65 | int32_t nEntries, 66 | OP_InfoDATEntries* entries, 67 | void* reserved1) override; 68 | 69 | virtual void setupParameters(OP_ParameterManager* manager, void *reserved1) override; 70 | virtual void pulsePressed(const char* name, void* reserved1) override; 71 | 72 | private: 73 | 74 | // We don't need to store this pointer, but we do for the example. 75 | // The OP_NodeInfo class store information about the node that's using 76 | // this instance of the class (like its name). 77 | const OP_NodeInfo* myNodeInfo; 78 | 79 | // In this example this value will be incremented each time the execute() 80 | // function is called, then passes back to the CHOP 81 | int32_t myExecuteCount; 82 | 83 | 84 | // FaceCHOP functions 85 | cv::Point2d FaceCHOP::pToUV(dlib::full_object_detection shape, int index, double width, double height, double aspect); 86 | bool FaceCHOP::loadFaceLandmarks(const char* FaceCascadePar); 87 | void setup(); 88 | virtual void getErrorString(OP_String* error, void* reserved1) override; 89 | virtual void getWarningString(OP_String* warning, void* reserved1) override; 90 | virtual void getInfoPopupString(OP_String* warning, void* reserved1) override; 91 | 92 | bool checkLandmarksFile(const char* Facelandmarksfile); 93 | 94 | int myErrors = 0; 95 | 96 | OP_TOPInputDownloadOptions imageDownloadOptions; 97 | 98 | std::string emptyString = ""; 99 | 100 | bool hasSetup = false; 101 | bool hasLoadedLandmarks = false; 102 | dlib::frontal_face_detector detector; // this finds rectangles of faces 103 | dlib::shape_predictor predictor; // this finds the 68 landmarks on the faces given the rectangles 104 | 105 | // fill in 3D ref points (world coordinates), model referenced from http://aifi.isr.uc.pt/Downloads/OpenGL/glAnthropometric3DModel.cpp 106 | std::vector object_pts; 107 | 108 | // 2D ref points (image coordinates), referenced from detected facial feature 109 | std::vector image_pts; 110 | 111 | // result 112 | cv::Mat rotation_vec = cv::Mat(3, 1, CV_32F, 0.); 113 | cv::Mat rotation_mat; 114 | cv::Mat translation_vec = cv::Mat(3, 1, CV_32F, 0.); 115 | cv::Mat pose_mat = cv::Mat(3, 4, CV_64FC1); 116 | cv::Mat euler_angle = cv::Mat(3, 1, CV_64FC1); 117 | 118 | // temporary buffer for decomposeProjectionMatrix() 119 | cv::Mat out_intrinsics = cv::Mat(3, 3, CV_64FC1); 120 | cv::Mat out_rotation = cv::Mat(3, 3, CV_64FC1); 121 | cv::Mat out_translation = cv::Mat(3, 1, CV_64FC1); 122 | 123 | bool solvePnP_success = false; 124 | 125 | int frameCounter = 0; 126 | std::vector faces; 127 | 128 | int MAXFACES = 8; 129 | int numFacesFound = 0; 130 | }; 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [//]: # (For development of this README.md, use http://markdownlivepreview.com/) 2 | 3 | # TD-FaceCHOP 4 | #### Face landmark detection with OpenCV and dlib (in TouchDesigner). 5 | 6 | ![](docs/images/header.png) 7 | 8 | FaceCHOP takes an image and an expected camera horizontal field-of-view. The output is 4 channels and many samples. Each detected face corresponds to 71 samples. The first face involves samples 0-70; the second is 71-141; then 142-212 and so on. Of the 71 samples, the first 68 samples are the 2D (tx/ty) locations of the face landmarks. These correspond to the indices of the [iBUG 300-W dataset](https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/). 9 | 10 | After these 68 samples, the next sample is a rectangular box indicating where the face is. Basically, it's (center x, center y, size x, size y). Check the example TouchDesigner project for more context. 11 | The next sample is the translation of the face (tx, ty, tz). The fourth channel here is unused. 12 | The next sample is the rotation of the face (rx, ry, rz). The fourth channel here is unused. 13 | 14 | FaceCHOP has a toggle for using face landmark detection, so if you only care about getting the rectangular box of a face, leave it unchecked. Note that if you use face landmark detection, which is a step in finding the pose (rotation & translation) of a face, then you need to follow the licensing of iBUG. iBUG does not allow commercial use, so contact them for more information. 15 | 16 | There's a useful parameter "Facerectframeskip" whose label is "Face Rectangle Frame Skip." Processing the rectangles of faces is far more time-consuming that placing the landmarks on the faces. If you want to get some speed savings and can sacrifice a little accuracy of the landmarks, it can be a good idea to skip 1, maybe 2 frames. If the value is 0, then you'll calculate new face rectangles for every frame. 17 | 18 | # Installation 19 | In order to run the demo project, there are three basic requirements: 20 | * Download `TD-FaceCHOP.dll` from the [Releases](https://github.com/DBraun/TD-FaceCHOP/releases) page and place it in the `Plugins` folder in this repo. 21 | * Install [OpenCV 4.1.1](https://sourceforge.net/projects/opencvlibrary/files/4.1.1/) (`opencv-4.1.1-vc14_vc15.exe`) to `C:/tools/opencv`. Copy the file `C:/tools/opencv/build/x64/vc15/bin/opencv_world411.dll` into `TD-FaceCHOP/Plugins`. Wherever you use `TD-FaceCHOP.dll`, `opencv_world411.dll` must be its direct neighbor. 22 | * Download the dlib pretrained [shape_predictor_68_face_landmarks.dat](http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2). **By using this file you must obey the licensing of the iBUG dataset.** Use an application such as 7-zip to turn the `.bz2` file into a `.dat` file and place it in the root of this repo, sibling to `FaceCHOP.toe`. You can use a custom parameter on Face-CHOP to keep this model in a different location. The model is also available [here](https://github.com/davisking/dlib-models). 23 | 24 | That's it! The remaining instructions in this guide are for compiling `TD-FaceCHOP.dll` in case you want to use a different version of OpenCV or a different landmarks model. 25 | 26 | ## OpenCV 27 | Download [OpenCV](https://opencv.org/releases/). I use 4.1.1, but you can probably use other versions. 28 | On Windows, I've placed it at `C:/tools/opencv` so that I have `C:/tools/opencv/build/x64/vc15/lib/opencv_world411.lib`. 29 | 30 | ## dlib 31 | Clone [dlib](https://github.com/davisking/dlib) to `C:/tools/dlib`. These are my three steps for building on Windows. Open a cmd window into `C:/tools/dlib`. Then 32 | 33 | mkdir build; cd build; 34 | cmake -g "Visual Studio 16 2019 Win64" -T host=x64 -DCMAKE_INSTALL_PREFIX=install .. -DUSE_AVX_INSTRUCTIONS=1; 35 | cmake --build . --config Release --target INSTALL 36 | 37 | You should end up with `C:/tools/dlib/build/install/lib/dlib19.17.99_release_64bit_msvc1922.lib` 38 | 39 | ## Visual Studio Properties 40 | If you install OpenCV or dlib with other methods or in other locations, you'll need to modify the properties of the Visual Studio solution. For my build setup, 41 | note how both the OpenCV and dlib directories have been added to "Additional Include Directories" 42 | ![](docs/images/properties_1.png) 43 | Note how both the OpenCV and dlib directories have been added to "Additional Library Directories" 44 | ![](docs/images/properties_2.png) 45 | **Note how both the OpenCV and dlib `.lib` files have been added to "Additional Dependencies"** 46 | ![](docs/images/properties_3.png) 47 | 48 | # To do 49 | * Mac OS support 50 | * Avoid using OpenCV Mat container 51 | * Try other methods 52 | * [https://github.com/1adrianb/face-alignment](https://github.com/1adrianb/face-alignment) 53 | * [https://github.com/ageitgey/face_recognition/](https://github.com/ageitgey/face_recognition/) 54 | * [https://github.com/cleardusk/3DDFA](https://github.com/cleardusk/3DDFA) for a better 3D mesh 55 | * [https://github.com/yinguobing/head-pose-estimation](https://github.com/yinguobing/head-pose-estimation) 56 | 57 | # Thanks 58 | * [http://dlib.net/webcam\_face\_pose\_ex.cpp.html](http://dlib.net/webcam\_face\_pose\_ex.cpp.html) 59 | * [https://www.learnopencv.com/head-pose-estimation-using-opencv-and-dlib/](https://www.learnopencv.com/head-pose-estimation-using-opencv-and-dlib/) 60 | * [http://aifi.isr.uc.pt/HeadPoseEstimation.html](http://aifi.isr.uc.pt/HeadPoseEstimation.html) 61 | * [https://github.com/lincolnhard/head-pose-estimation](https://github.com/lincolnhard/head-pose-estimation) 62 | * [https://github.com/mourendxu/TD-OpenCV3TOP](https://github.com/mourendxu/TD-OpenCV3TOP) 63 | 64 | # Dependent Licenses 65 | [iBUG 300-W dataset](https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/) does not offer commercial usage. This dataset was used to make `shape_predictor_68_face_landmarks.dat`, which is what makes it possible to identify the 68 landmarks on a face. 66 | 67 | FaceCHOP also uses numbers from [http://aifi.isr.uc.pt/HeadPoseEstimation.html](http://aifi.isr.uc.pt/HeadPoseEstimation.html) that represent an expected 3D head shape in centimeters. That project is copyright of [Pedro Martins](pedromartins@isr.uc.pt) but licensed under GNU General Public License. 68 | -------------------------------------------------------------------------------- /CPlusPlusCHOPExample.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {3F5BEECD-FA36-459F-91B8-BB481A67EF44} 15 | CPlusPlusCHOPExample 16 | Win32Proj 17 | 10.0 18 | TD-FaceCHOP 19 | 20 | 21 | 22 | DynamicLibrary 23 | NotSet 24 | true 25 | v142 26 | 27 | 28 | DynamicLibrary 29 | NotSet 30 | v142 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | <_ProjectFileVersion>10.0.40219.1 44 | Plugins\ 45 | $(Configuration)\ 46 | false 47 | Plugins\ 48 | $(Configuration)\ 49 | false 50 | 51 | 52 | 53 | Disabled 54 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CPLUSPLUSCHOPEXAMPLE_EXPORTS;%(PreprocessorDefinitions) 55 | EnableFastChecks 56 | MultiThreadedDebugDLL 57 | 58 | 59 | Level3 60 | ProgramDatabase 61 | C:\tools\opencv\build\include;C:\tools\dlib\build\install\include;%(AdditionalIncludeDirectories) 62 | 63 | 64 | true 65 | Windows 66 | C:\tools\opencv\build\x64\vc15\lib;C:\tools\opencv\build\bin;C:\tools\dlib\build\install\lib;%(AdditionalLibraryDirectories) 67 | OpenGL32.lib;opencv_world411.lib;dlib19.17.99_release_64bit_msvc1922.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) 68 | 69 | 70 | 71 | 72 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CPLUSPLUSCHOPEXAMPLE_EXPORTS;%(PreprocessorDefinitions) 73 | MultiThreadedDLL 74 | 75 | 76 | Level3 77 | ProgramDatabase 78 | C:\tools\opencv\build\include;C:\tools\dlib\build\install\include;%(AdditionalIncludeDirectories) 79 | 80 | 81 | true 82 | Windows 83 | true 84 | true 85 | C:\tools\opencv\build\x64\vc15\lib;C:\tools\opencv\build\bin;C:\tools\dlib\build\install\lib;%(AdditionalLibraryDirectories) 86 | OpenGL32.lib;opencv_world411.lib;dlib19.17.99_release_64bit_msvc1922.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) 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | 332 | Backup/ 333 | shape_predictor_68_face_landmarks.dat -------------------------------------------------------------------------------- /CPlusPlusCHOPExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E23329E31DF092C90002B4FE /* CPlusPlusCHOPExample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E23329E11DF092C90002B4FE /* CPlusPlusCHOPExample.cpp */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXFileReference section */ 14 | E23329D61DF092AD0002B4FE /* CPlusPlusCHOPExample.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPlusPlusCHOPExample.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; 15 | E23329D91DF092AD0002B4FE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 16 | E23329DF1DF092C90002B4FE /* CHOP_CPlusPlusBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHOP_CPlusPlusBase.h; sourceTree = SOURCE_ROOT; }; 17 | E23329E01DF092C90002B4FE /* CPlusPlus_Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPlusPlus_Common.h; sourceTree = SOURCE_ROOT; }; 18 | E23329E11DF092C90002B4FE /* CPlusPlusCHOPExample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPlusPlusCHOPExample.cpp; sourceTree = SOURCE_ROOT; }; 19 | E23329E21DF092C90002B4FE /* CPlusPlusCHOPExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPlusPlusCHOPExample.h; sourceTree = SOURCE_ROOT; }; 20 | /* End PBXFileReference section */ 21 | 22 | /* Begin PBXFrameworksBuildPhase section */ 23 | E23329D31DF092AD0002B4FE /* Frameworks */ = { 24 | isa = PBXFrameworksBuildPhase; 25 | buildActionMask = 2147483647; 26 | files = ( 27 | ); 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXFrameworksBuildPhase section */ 31 | 32 | /* Begin PBXGroup section */ 33 | E23329CD1DF092AD0002B4FE = { 34 | isa = PBXGroup; 35 | children = ( 36 | E23329F11DF1D64A0002B4FE /* CHOP */, 37 | E23329D71DF092AD0002B4FE /* Products */, 38 | ); 39 | sourceTree = ""; 40 | }; 41 | E23329D71DF092AD0002B4FE /* Products */ = { 42 | isa = PBXGroup; 43 | children = ( 44 | E23329D61DF092AD0002B4FE /* CPlusPlusCHOPExample.plugin */, 45 | ); 46 | name = Products; 47 | sourceTree = ""; 48 | }; 49 | E23329F11DF1D64A0002B4FE /* CHOP */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | E23329DF1DF092C90002B4FE /* CHOP_CPlusPlusBase.h */, 53 | E23329E01DF092C90002B4FE /* CPlusPlus_Common.h */, 54 | E23329E11DF092C90002B4FE /* CPlusPlusCHOPExample.cpp */, 55 | E23329E21DF092C90002B4FE /* CPlusPlusCHOPExample.h */, 56 | E23329D91DF092AD0002B4FE /* Info.plist */, 57 | ); 58 | name = CHOP; 59 | sourceTree = ""; 60 | }; 61 | /* End PBXGroup section */ 62 | 63 | /* Begin PBXNativeTarget section */ 64 | E23329D51DF092AD0002B4FE /* CPlusPlusCHOPExample */ = { 65 | isa = PBXNativeTarget; 66 | buildConfigurationList = E23329DC1DF092AD0002B4FE /* Build configuration list for PBXNativeTarget "CPlusPlusCHOPExample" */; 67 | buildPhases = ( 68 | E23329D21DF092AD0002B4FE /* Sources */, 69 | E23329D31DF092AD0002B4FE /* Frameworks */, 70 | E23329D41DF092AD0002B4FE /* Resources */, 71 | ); 72 | buildRules = ( 73 | ); 74 | dependencies = ( 75 | ); 76 | name = CPlusPlusCHOPExample; 77 | productName = CPlusPlusCHOPExample; 78 | productReference = E23329D61DF092AD0002B4FE /* CPlusPlusCHOPExample.plugin */; 79 | productType = "com.apple.product-type.bundle"; 80 | }; 81 | /* End PBXNativeTarget section */ 82 | 83 | /* Begin PBXProject section */ 84 | E23329CE1DF092AD0002B4FE /* Project object */ = { 85 | isa = PBXProject; 86 | attributes = { 87 | LastUpgradeCheck = 1010; 88 | ORGANIZATIONNAME = Derivative; 89 | TargetAttributes = { 90 | E23329D51DF092AD0002B4FE = { 91 | CreatedOnToolsVersion = 8.0; 92 | ProvisioningStyle = Automatic; 93 | }; 94 | }; 95 | }; 96 | buildConfigurationList = E23329D11DF092AD0002B4FE /* Build configuration list for PBXProject "CPlusPlusCHOPExample" */; 97 | compatibilityVersion = "Xcode 3.2"; 98 | developmentRegion = English; 99 | hasScannedForEncodings = 0; 100 | knownRegions = ( 101 | en, 102 | ); 103 | mainGroup = E23329CD1DF092AD0002B4FE; 104 | productRefGroup = E23329D71DF092AD0002B4FE /* Products */; 105 | projectDirPath = ""; 106 | projectRoot = ""; 107 | targets = ( 108 | E23329D51DF092AD0002B4FE /* CPlusPlusCHOPExample */, 109 | ); 110 | }; 111 | /* End PBXProject section */ 112 | 113 | /* Begin PBXResourcesBuildPhase section */ 114 | E23329D41DF092AD0002B4FE /* Resources */ = { 115 | isa = PBXResourcesBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXResourcesBuildPhase section */ 122 | 123 | /* Begin PBXSourcesBuildPhase section */ 124 | E23329D21DF092AD0002B4FE /* Sources */ = { 125 | isa = PBXSourcesBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | E23329E31DF092C90002B4FE /* CPlusPlusCHOPExample.cpp in Sources */, 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | /* End PBXSourcesBuildPhase section */ 133 | 134 | /* Begin XCBuildConfiguration section */ 135 | E23329DA1DF092AD0002B4FE /* Debug */ = { 136 | isa = XCBuildConfiguration; 137 | buildSettings = { 138 | ALWAYS_SEARCH_USER_PATHS = NO; 139 | CLANG_ANALYZER_NONNULL = YES; 140 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 141 | CLANG_CXX_LIBRARY = "libc++"; 142 | CLANG_ENABLE_MODULES = YES; 143 | CLANG_ENABLE_OBJC_ARC = YES; 144 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 145 | CLANG_WARN_BOOL_CONVERSION = YES; 146 | CLANG_WARN_COMMA = YES; 147 | CLANG_WARN_CONSTANT_CONVERSION = YES; 148 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 149 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 150 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 151 | CLANG_WARN_EMPTY_BODY = YES; 152 | CLANG_WARN_ENUM_CONVERSION = YES; 153 | CLANG_WARN_INFINITE_RECURSION = YES; 154 | CLANG_WARN_INT_CONVERSION = YES; 155 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 156 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 157 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 158 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 159 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 160 | CLANG_WARN_STRICT_PROTOTYPES = YES; 161 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 162 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 163 | CLANG_WARN_UNREACHABLE_CODE = YES; 164 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 165 | CODE_SIGN_IDENTITY = "-"; 166 | COPY_PHASE_STRIP = NO; 167 | DEBUG_INFORMATION_FORMAT = dwarf; 168 | ENABLE_STRICT_OBJC_MSGSEND = YES; 169 | ENABLE_TESTABILITY = YES; 170 | GCC_C_LANGUAGE_STANDARD = gnu99; 171 | GCC_DYNAMIC_NO_PIC = NO; 172 | GCC_NO_COMMON_BLOCKS = YES; 173 | GCC_OPTIMIZATION_LEVEL = 0; 174 | GCC_PREPROCESSOR_DEFINITIONS = ( 175 | "DEBUG=1", 176 | "$(inherited)", 177 | ); 178 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 179 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 180 | GCC_WARN_UNDECLARED_SELECTOR = YES; 181 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 182 | GCC_WARN_UNUSED_FUNCTION = YES; 183 | GCC_WARN_UNUSED_VARIABLE = YES; 184 | MACOSX_DEPLOYMENT_TARGET = 10.11; 185 | MTL_ENABLE_DEBUG_INFO = YES; 186 | ONLY_ACTIVE_ARCH = YES; 187 | SDKROOT = macosx; 188 | }; 189 | name = Debug; 190 | }; 191 | E23329DB1DF092AD0002B4FE /* Release */ = { 192 | isa = XCBuildConfiguration; 193 | buildSettings = { 194 | ALWAYS_SEARCH_USER_PATHS = NO; 195 | CLANG_ANALYZER_NONNULL = YES; 196 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 197 | CLANG_CXX_LIBRARY = "libc++"; 198 | CLANG_ENABLE_MODULES = YES; 199 | CLANG_ENABLE_OBJC_ARC = YES; 200 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 201 | CLANG_WARN_BOOL_CONVERSION = YES; 202 | CLANG_WARN_COMMA = YES; 203 | CLANG_WARN_CONSTANT_CONVERSION = YES; 204 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 206 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 207 | CLANG_WARN_EMPTY_BODY = YES; 208 | CLANG_WARN_ENUM_CONVERSION = YES; 209 | CLANG_WARN_INFINITE_RECURSION = YES; 210 | CLANG_WARN_INT_CONVERSION = YES; 211 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 212 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 213 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 214 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 215 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 216 | CLANG_WARN_STRICT_PROTOTYPES = YES; 217 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 218 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 219 | CLANG_WARN_UNREACHABLE_CODE = YES; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | CODE_SIGN_IDENTITY = "-"; 222 | COPY_PHASE_STRIP = NO; 223 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 224 | ENABLE_NS_ASSERTIONS = NO; 225 | ENABLE_STRICT_OBJC_MSGSEND = YES; 226 | GCC_C_LANGUAGE_STANDARD = gnu99; 227 | GCC_NO_COMMON_BLOCKS = YES; 228 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 229 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 230 | GCC_WARN_UNDECLARED_SELECTOR = YES; 231 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 232 | GCC_WARN_UNUSED_FUNCTION = YES; 233 | GCC_WARN_UNUSED_VARIABLE = YES; 234 | MACOSX_DEPLOYMENT_TARGET = 10.11; 235 | MTL_ENABLE_DEBUG_INFO = NO; 236 | SDKROOT = macosx; 237 | }; 238 | name = Release; 239 | }; 240 | E23329DD1DF092AD0002B4FE /* Debug */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | COMBINE_HIDPI_IMAGES = YES; 244 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 245 | INSTALL_PATH = /; 246 | PRODUCT_BUNDLE_IDENTIFIER = ca.derivative.cpp.CPlusPlusCHOPExample; 247 | PRODUCT_NAME = "$(TARGET_NAME)"; 248 | WRAPPER_EXTENSION = plugin; 249 | }; 250 | name = Debug; 251 | }; 252 | E23329DE1DF092AD0002B4FE /* Release */ = { 253 | isa = XCBuildConfiguration; 254 | buildSettings = { 255 | COMBINE_HIDPI_IMAGES = YES; 256 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 257 | INSTALL_PATH = /; 258 | PRODUCT_BUNDLE_IDENTIFIER = ca.derivative.cpp.CPlusPlusCHOPExample; 259 | PRODUCT_NAME = "$(TARGET_NAME)"; 260 | WRAPPER_EXTENSION = plugin; 261 | }; 262 | name = Release; 263 | }; 264 | /* End XCBuildConfiguration section */ 265 | 266 | /* Begin XCConfigurationList section */ 267 | E23329D11DF092AD0002B4FE /* Build configuration list for PBXProject "CPlusPlusCHOPExample" */ = { 268 | isa = XCConfigurationList; 269 | buildConfigurations = ( 270 | E23329DA1DF092AD0002B4FE /* Debug */, 271 | E23329DB1DF092AD0002B4FE /* Release */, 272 | ); 273 | defaultConfigurationIsVisible = 0; 274 | defaultConfigurationName = Release; 275 | }; 276 | E23329DC1DF092AD0002B4FE /* Build configuration list for PBXNativeTarget "CPlusPlusCHOPExample" */ = { 277 | isa = XCConfigurationList; 278 | buildConfigurations = ( 279 | E23329DD1DF092AD0002B4FE /* Debug */, 280 | E23329DE1DF092AD0002B4FE /* Release */, 281 | ); 282 | defaultConfigurationIsVisible = 0; 283 | defaultConfigurationName = Release; 284 | }; 285 | /* End XCConfigurationList section */ 286 | }; 287 | rootObject = E23329CE1DF092AD0002B4FE /* Project object */; 288 | } 289 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /FaceCHOP.cpp: -------------------------------------------------------------------------------- 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 | * Copyright (C) 2007 by pedromartins * 13 | * pedromartins@isr.uc.pt * 14 | * * 15 | * This program is free software; you can redistribute it and/or modify * 16 | * it under the terms of the GNU General Public License as published by * 17 | * the Free Software Foundation; either version 2 of the License, or * 18 | * (at your option) any later version. * 19 | * * 20 | * This program is distributed in the hope that it will be useful, * 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 23 | * GNU General Public License for more details. * 24 | * * 25 | * You should have received a copy of the GNU General Public License * 26 | * along with this program; if not, write to the * 27 | * Free Software Foundation, Inc., * 28 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 29 | ***************************************************************************/ 30 | 31 | #include "FaceCHOP.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | // OpenCV 39 | using namespace cv; 40 | 41 | // These functions are basic C function, which the DLL loader can find 42 | // much easier than finding a C++ Class. 43 | // The DLLEXPORT prefix is needed so the compile exports these functions from the .dll 44 | // you are creating 45 | extern "C" 46 | { 47 | 48 | DLLEXPORT 49 | void 50 | FillCHOPPluginInfo(CHOP_PluginInfo *info) 51 | { 52 | // Always set this to CHOPCPlusPlusAPIVersion. 53 | info->apiVersion = CHOPCPlusPlusAPIVersion; 54 | 55 | // The opType is the unique name for this CHOP. It must start with a 56 | // capital A-Z character, and all the following characters must lower case 57 | // or numbers (a-z, 0-9) 58 | info->customOPInfo.opType->setString("Facechop"); 59 | 60 | // The opLabel is the text that will show up in the OP Create Dialog 61 | info->customOPInfo.opLabel->setString("Face CHOP"); 62 | info->customOPInfo.opIcon->setString("FC"); 63 | 64 | // Information about the author of this OP 65 | info->customOPInfo.authorName->setString("David Braun"); 66 | info->customOPInfo.authorEmail->setString("github.com/DBraun"); 67 | 68 | // This CHOP can work with 0 inputs 69 | info->customOPInfo.minInputs = 0; 70 | 71 | // It can accept up to 1 input though, which changes its behavior 72 | info->customOPInfo.maxInputs = 0; 73 | } 74 | 75 | DLLEXPORT 76 | CHOP_CPlusPlusBase* 77 | CreateCHOPInstance(const OP_NodeInfo* info) 78 | { 79 | // Return a new instance of your class every time this is called. 80 | // It will be called once per CHOP that is using the .dll 81 | return new FaceCHOP(info); 82 | } 83 | 84 | DLLEXPORT 85 | void 86 | DestroyCHOPInstance(CHOP_CPlusPlusBase* instance) 87 | { 88 | // Delete the instance here, this will be called when 89 | // Touch is shutting down, when the CHOP using that instance is deleted, or 90 | // if the CHOP loads a different DLL 91 | delete (FaceCHOP*)instance; 92 | } 93 | 94 | }; 95 | 96 | 97 | FaceCHOP::FaceCHOP(const OP_NodeInfo* info) : myNodeInfo(info) 98 | { 99 | myExecuteCount = 0; 100 | } 101 | 102 | FaceCHOP::~FaceCHOP() 103 | { 104 | 105 | } 106 | 107 | void 108 | FaceCHOP::getGeneralInfo(CHOP_GeneralInfo* ginfo, const OP_Inputs* inputs, void* reserved1) 109 | { 110 | // This will cause the node to cook every frame 111 | ginfo->cookEveryFrameIfAsked = false; 112 | 113 | // Note: To disable timeslicing you'll need to turn this off, as well as ensure that 114 | // getOutputInfo() returns true, and likely also set the info->numSamples to how many 115 | // samples you want to generate for this CHOP. Otherwise it'll take on length of the 116 | // input CHOP, which may be timesliced. 117 | ginfo->timeslice = false; 118 | 119 | ginfo->inputMatchIndex = 0; 120 | } 121 | 122 | bool 123 | FaceCHOP::getOutputInfo(CHOP_OutputInfo* info, const OP_Inputs* inputs, void* reserved1) 124 | { 125 | info->numChannels = 4; 126 | info->numSamples = MAXFACES*71; 127 | // info->sampleRate = 60; 128 | return true; 129 | } 130 | 131 | void 132 | FaceCHOP::getChannelName(int32_t index, OP_String *name, const OP_Inputs* inputs, void* reserved1) 133 | { 134 | switch (index) { 135 | case 0: 136 | name->setString("x"); 137 | break; 138 | case 1: 139 | name->setString("y"); 140 | break; 141 | case 2: 142 | name->setString("z"); 143 | break; 144 | case 3: 145 | name->setString("w"); 146 | break; 147 | } 148 | } 149 | 150 | // return true if successfully loaded landmarks file. otherwise false 151 | bool FaceCHOP::loadFaceLandmarks(const char* FaceCascadePar) { 152 | try 153 | { 154 | dlib::deserialize(FaceCascadePar) >> predictor; 155 | } 156 | catch (const std::exception&) { 157 | return false; 158 | } 159 | hasLoadedLandmarks = true; 160 | return true; 161 | } 162 | 163 | void FaceCHOP::setup() { 164 | 165 | // the default face detector. it's a HOG method, not CNN 166 | // so it's not great when faces are rotated. 167 | detector = dlib::get_frontal_face_detector(); 168 | 169 | // convert centimeters to meters. 170 | double cmToMeters = 1. / 100.; 171 | 172 | // The numbers immediately below are copyright of Pedro Martins 173 | // Copyright(C) 2007 by pedromartins 174 | // http://aifi.isr.uc.pt/Downloads/OpenGL/glAnthropometric3DModel.cpp 175 | // http://aifi.isr.uc.pt/HeadPoseEstimation.html 176 | object_pts.push_back(cmToMeters * cv::Point3d(6.825897, 6.760612, 4.402142)); // #33 left brow left corner 177 | object_pts.push_back(cmToMeters * cv::Point3d(1.330353, 7.122144, 6.903745)); // #29 left brow right corner 178 | object_pts.push_back(cmToMeters * cv::Point3d(-1.330353, 7.122144, 6.903745)); // #34 right brow left corner 179 | object_pts.push_back(cmToMeters * cv::Point3d(-6.825897, 6.760612, 4.402142)); // #38 right brow right corner 180 | object_pts.push_back(cmToMeters * cv::Point3d(5.311432, 5.485328, 3.987654)); // #13 left eye left corner 181 | object_pts.push_back(cmToMeters * cv::Point3d(1.789930, 5.393625, 4.413414)); // #17 left eye right corner 182 | object_pts.push_back(cmToMeters * cv::Point3d(-1.789930, 5.393625, 4.413414)); // #25 right eye left corner 183 | object_pts.push_back(cmToMeters * cv::Point3d(-5.311432, 5.485328, 3.987654)); // #21 right eye right corner 184 | object_pts.push_back(cmToMeters * cv::Point3d(2.005628, 1.409845, 6.165652)); // #55 nose left corner 185 | object_pts.push_back(cmToMeters * cv::Point3d(-2.005628, 1.409845, 6.165652)); // #49 nose right corner 186 | object_pts.push_back(cmToMeters * cv::Point3d(2.774015, -2.080775, 5.048531)); // #43 mouth left corner 187 | object_pts.push_back(cmToMeters * cv::Point3d(-2.774015, -2.080775, 5.048531)); // #39 mouth right corner 188 | object_pts.push_back(cmToMeters * cv::Point3d(0.000000, -3.116408, 6.097667)); // #45 mouth central bottom corner 189 | object_pts.push_back(cmToMeters * cv::Point3d(0.000000, -7.415691, 4.070434)); // #6 chin corner 190 | 191 | // other one-time stuff 192 | imageDownloadOptions.cpuMemPixelType = OP_CPUMemPixelType::BGRA8Fixed; 193 | imageDownloadOptions.verticalFlip = true; // openCV is upside-down compared to TouchDesigner so flip the image. 194 | 195 | hasSetup = true; 196 | } 197 | 198 | cv::Point2d FaceCHOP::pToUV(dlib::full_object_detection shape, int index, double width, double height, double aspect) { 199 | dlib::point p = shape.part(index); 200 | 201 | return cv::Point2d(p.x() / width - .5, (p.y() / height - .5) / aspect); 202 | } 203 | 204 | bool FaceCHOP::checkLandmarksFile(const char* Facelandmarksfile) { 205 | 206 | if (hasLoadedLandmarks) { 207 | return true; 208 | } 209 | 210 | if (emptyString.compare(Facelandmarksfile) == 0) { 211 | 212 | myErrors = 1; 213 | return false; 214 | } 215 | else if (!hasLoadedLandmarks) { 216 | if (!loadFaceLandmarks(Facelandmarksfile)) { 217 | myErrors = 2; 218 | return false; 219 | } 220 | } 221 | 222 | return true; 223 | } 224 | 225 | void 226 | FaceCHOP::execute(CHOP_Output* output, 227 | const OP_Inputs* inputs, 228 | void* reserved) 229 | { 230 | myExecuteCount++; 231 | myErrors = 0; // Reset errors 232 | numFacesFound = 0; // reset num found faces 233 | 234 | if (!hasSetup) { 235 | setup(); 236 | } 237 | 238 | const char* Facelandmarksfile = inputs->getParFilePath("Facelandmarksfile"); 239 | 240 | if (!checkLandmarksFile(Facelandmarksfile)) { 241 | return; 242 | } 243 | 244 | // Get the image that might have faces in it. 245 | const OP_TOPInput* input = inputs->getParTOP("Image"); 246 | 247 | const char* Imagedownload = inputs->getParString("Imagedownload"); 248 | if (!strcmp(Imagedownload, "Delayed")) { 249 | imageDownloadOptions.downloadType = OP_TOPInputDownloadType::Delayed; 250 | } 251 | else { 252 | imageDownloadOptions.downloadType = OP_TOPInputDownloadType::Instant; 253 | } 254 | 255 | const uchar* videoSrc = (const uchar*)inputs->getTOPDataInCPUMemory(input, &imageDownloadOptions); 256 | 257 | if (!videoSrc) { 258 | myErrors = 3; 259 | return; 260 | } 261 | 262 | double height = input->height; 263 | double width = input->width; 264 | 265 | // todo: avoid using openCV mat entirely. 266 | // Loading input buffer into cv::Mat 267 | Mat frame(height, width, CV_8UC4, (void*)videoSrc); 268 | Mat frameBGR; 269 | cvtColor(frame, frameBGR, COLOR_BGRA2BGR); 270 | 271 | solvePnP_success = false; 272 | 273 | dlib::cv_image cimg(frameBGR); 274 | 275 | if (frameCounter == 0) { 276 | // Get rectangle bounding boxes of faces. 277 | // We could do this with openCV instead, but the landmark detection 278 | // was trained on a model with dlib's face rectangles, not openCV's. 279 | // More info: https://github.com/davisking/dlib-models 280 | double adjust_threshold = .0; 281 | faces = detector(cimg, adjust_threshold); 282 | } 283 | int modulus = inputs->getParInt("Facerectframeskip") + 1; 284 | frameCounter = (frameCounter + 1) % modulus; 285 | 286 | double aspect = width / height; 287 | const double math_pi = std::acos(-1); 288 | 289 | // Camera internals 290 | double cam_width = 1.; 291 | double fov = inputs->getParDouble("Fov"); 292 | double fov_radians = fov * (math_pi / 180.); 293 | double focal_length = (cam_width / 2.) / std::tan(fov_radians / 2.); 294 | 295 | Point2d center = cv::Point2d(0., 0.); 296 | cv::Mat myCameraMatrix = (cv::Mat_(3, 3) << focal_length, 0, center.x, 0, focal_length, center.y, 0, 0, 1); 297 | 298 | cv::Mat_ distCoeffs(cv::Size(1, 4)); 299 | distCoeffs = 0.f; 300 | 301 | numFacesFound = int(faces.size()); 302 | 303 | bool Facelandmarkstoggle = bool(inputs->getParDouble("Facelandmarkstoggle")); 304 | 305 | for (int face_i = 0; face_i < MAXFACES; face_i++) { 306 | 307 | if (face_i < numFacesFound) { 308 | 309 | dlib::rectangle faceRect = faces[face_i]; 310 | 311 | // first save the face rectangle 312 | int offset = 71 * face_i + 68; 313 | 314 | output->channels[0][offset] = .5 * (faceRect.right() + faceRect.left()) / width; // avg x normalized to pct 315 | output->channels[1][offset] = 1.-.5 * (faceRect.top() + faceRect.bottom()) / height; // avg y normalized to pct 316 | output->channels[2][offset] = (faceRect.right() - faceRect.left()) / width; // face size x normalize to pct 317 | output->channels[3][offset] = (faceRect.bottom() - faceRect.top()) / height; // face size y normalized to pct 318 | 319 | if (!Facelandmarkstoggle) { 320 | for (int i = 0; i < 68; i++) { 321 | offset = 71 * face_i + i; 322 | output->channels[0][offset] = 0.0f; 323 | output->channels[1][offset] = 0.0f; 324 | output->channels[2][offset] = 0.0f; 325 | output->channels[3][offset] = 0.0f; 326 | } 327 | continue; 328 | } 329 | 330 | dlib::full_object_detection shape = predictor(cimg, faceRect); 331 | 332 | for (int i = 0; i < 68; i++) { 333 | dlib::point p = shape.part(i); 334 | offset = 71 * face_i + i; 335 | output->channels[0][offset] = ((p.x() / width) - .5); 336 | output->channels[1][offset] = -((p.y() / height) - .5) / aspect; 337 | output->channels[2][offset] = 0.0f; 338 | output->channels[3][offset] = 1.f; 339 | 340 | } 341 | 342 | // fill in 2D ref points, annotations follow https://ibug.doc.ic.ac.uk/resources/300-W/ 343 | image_pts.push_back(pToUV(shape, 17, width, height, aspect)); //#17 left brow left corner 344 | image_pts.push_back(pToUV(shape, 21, width, height, aspect)); //#21 left brow right corner 345 | image_pts.push_back(pToUV(shape, 22, width, height, aspect)); //#22 right brow left corner 346 | image_pts.push_back(pToUV(shape, 26, width, height, aspect)); //#26 right brow right corner 347 | image_pts.push_back(pToUV(shape, 36, width, height, aspect)); //#36 left eye left corner 348 | image_pts.push_back(pToUV(shape, 39, width, height, aspect)); //#39 left eye right corner 349 | image_pts.push_back(pToUV(shape, 42, width, height, aspect)); //#42 right eye left corner 350 | image_pts.push_back(pToUV(shape, 45, width, height, aspect)); //#45 right eye right corner 351 | image_pts.push_back(pToUV(shape, 31, width, height, aspect)); //#31 nose left corner 352 | image_pts.push_back(pToUV(shape, 35, width, height, aspect)); //#35 nose right corner 353 | image_pts.push_back(pToUV(shape, 48, width, height, aspect)); //#48 mouth left corner 354 | image_pts.push_back(pToUV(shape, 54, width, height, aspect)); //#54 mouth right corner 355 | image_pts.push_back(pToUV(shape, 57, width, height, aspect)); //#57 mouth central bottom corner 356 | image_pts.push_back(pToUV(shape, 8, width, height, aspect)); //#8 chin corner 357 | 358 | // start with no rotation. 359 | rotation_vec = cv::Mat(3, 1, CV_32F, 0.); 360 | // important to start with a negative tz value. 361 | translation_vec = (cv::Mat_(3, 1) << 0.f, 0.f, -1.f); 362 | 363 | solvePnP_success = cv::solvePnP(object_pts, image_pts, myCameraMatrix, distCoeffs, rotation_vec, translation_vec, 1); 364 | //solvePnP_success = solvePnPRansac(object_pts, image_pts, myCameraMatrix, distCoeffs, rotation_vec, translation_vec); 365 | image_pts.clear(); 366 | 367 | if (solvePnP_success) { 368 | 369 | // calculate the euler angle 370 | cv::Rodrigues(rotation_vec, rotation_mat); 371 | cv::hconcat(rotation_mat, translation_vec, pose_mat); 372 | cv::decomposeProjectionMatrix(pose_mat, out_intrinsics, out_rotation, out_translation, cv::noArray(), cv::noArray(), cv::noArray(), euler_angle); 373 | 374 | offset = 71 * face_i + 68; 375 | offset += 1; 376 | output->channels[0][offset] = -translation_vec.at(0, 0); 377 | output->channels[1][offset] = translation_vec.at(1, 0); 378 | output->channels[2][offset] = translation_vec.at(2, 0); 379 | output->channels[3][offset] = 1.f; 380 | offset += 1; 381 | output->channels[0][offset] = euler_angle.at(0); 382 | output->channels[1][offset] = -euler_angle.at(1); 383 | output->channels[2][offset] = -euler_angle.at(2); 384 | output->channels[3][offset] = 1.f; 385 | } 386 | } 387 | else { 388 | for (int i = 0; i < 71; i++) { 389 | int offset = face_i * 71 + i; 390 | output->channels[0][offset] = 0.f; 391 | output->channels[1][offset] = 0.f; 392 | output->channels[2][offset] = 0.f; 393 | output->channels[3][offset] = 0.f; 394 | } 395 | } 396 | } 397 | } 398 | 399 | // You can use this function to put the node into a error state 400 | // by calling setSting() on 'error' with a non empty string. 401 | // Leave 'error' unchanged to not go into error state. 402 | void FaceCHOP::getErrorString(OP_String* error, void* reserved1) { 403 | 404 | switch (myErrors) { 405 | case 0: 406 | // no errors. must set string to blank. 407 | error->setString(""); 408 | break; 409 | case 1: 410 | error->setString("shape_predictor_68_face_landmarks.dat File is missing"); 411 | break; 412 | case 2: 413 | error->setString("Error while loading face landmarks file."); 414 | break; 415 | case 3: 416 | error->setString("No video input."); 417 | break; 418 | } 419 | 420 | } 421 | 422 | // You can use this function to put the node into a warning state 423 | // by calling setSting() on 'warning' with a non empty string. 424 | // Leave 'warning' unchanged to not go into warning state. 425 | void FaceCHOP::getWarningString(OP_String* warning, void* reserved1) 426 | { 427 | } 428 | 429 | // Use this function to return some text that will show up in the 430 | // info popup (when you middle click on a node) 431 | // call setString() on info and give it some info if desired. 432 | void FaceCHOP::getInfoPopupString(OP_String* info, void* reserved1) 433 | { 434 | } 435 | 436 | int32_t 437 | FaceCHOP::getNumInfoCHOPChans(void * reserved1) 438 | { 439 | // We return the number of channel we want to output to any Info CHOP 440 | // connected to the CHOP. In this example we are just going to send one channel. 441 | return 2; 442 | } 443 | 444 | void 445 | FaceCHOP::getInfoCHOPChan(int32_t index, 446 | OP_InfoCHOPChan* chan, 447 | void* reserved1) 448 | { 449 | // This function will be called once for each channel we said we'd want to return 450 | // In this example it'll only be called once. 451 | 452 | if (index == 0) 453 | { 454 | chan->name->setString("executeCount"); 455 | chan->value = (float)myExecuteCount; 456 | } 457 | 458 | if (index == 1) { 459 | chan->name->setString("numFacesFound"); 460 | chan->value = numFacesFound; 461 | } 462 | } 463 | 464 | bool 465 | FaceCHOP::getInfoDATSize(OP_InfoDATSize* infoSize, void* reserved1) 466 | { 467 | infoSize->rows = 1; 468 | infoSize->cols = 2; 469 | // Setting this to false means we'll be assigning values to the table 470 | // one row at a time. True means we'll do it one column at a time. 471 | infoSize->byColumn = false; 472 | return true; 473 | } 474 | 475 | void 476 | FaceCHOP::getInfoDATEntries(int32_t index, 477 | int32_t nEntries, 478 | OP_InfoDATEntries* entries, 479 | void* reserved1) 480 | { 481 | char tempBuffer[4096]; 482 | 483 | if (index == 0) 484 | { 485 | // Set the value for the first column 486 | entries->values[0]->setString("executeCount"); 487 | 488 | // Set the value for the second column 489 | #ifdef _WIN32 490 | sprintf_s(tempBuffer, "%d", myExecuteCount); 491 | #else // macOS 492 | snprintf(tempBuffer, sizeof(tempBuffer), "%d", myExecuteCount); 493 | #endif 494 | entries->values[1]->setString(tempBuffer); 495 | } 496 | 497 | } 498 | 499 | void 500 | FaceCHOP::setupParameters(OP_ParameterManager* manager, void *reserved1) 501 | { 502 | 503 | // Face Landmarks File 504 | { 505 | OP_StringParameter sp; 506 | 507 | sp.name = "Facelandmarksfile"; 508 | sp.label = "Face Landmarks File"; 509 | sp.defaultValue = "shape_predictor_68_face_landmarks.dat"; 510 | OP_ParAppendResult res = manager->appendFile(sp); 511 | assert(res == OP_ParAppendResult::Success); 512 | } 513 | 514 | // Field of View of the camera 515 | { 516 | OP_NumericParameter np; 517 | np.name = "Fov"; 518 | np.label = "Field of View (Horizontal)"; 519 | np.defaultValues[0] = 60.; 520 | 521 | np.minSliders[0] = 30; 522 | np.maxSliders[0] = 90; 523 | 524 | np.minValues[0] = 0.001; 525 | np.maxValues[0] = 180; 526 | 527 | np.clampMins[0] = true; 528 | np.clampMaxes[0] = true; 529 | 530 | OP_ParAppendResult res = manager->appendFloat(np); 531 | assert(res == OP_ParAppendResult::Success); 532 | } 533 | 534 | // input TOP 535 | { 536 | OP_StringParameter sp; 537 | 538 | sp.name = "Image"; 539 | sp.label = "Image"; 540 | 541 | sp.defaultValue = ""; 542 | 543 | OP_ParAppendResult res = manager->appendTOP(sp); 544 | assert(res == OP_ParAppendResult::Success); 545 | } 546 | 547 | // toggle face landmarks 548 | { 549 | OP_NumericParameter np; 550 | 551 | np.name = "Facelandmarkstoggle"; 552 | np.label = "Face Landmarks"; 553 | np.defaultValues[0] = 1.0; 554 | 555 | OP_ParAppendResult res = manager->appendToggle(np); 556 | assert(res == OP_ParAppendResult::Success); 557 | } 558 | 559 | // Frame skip for face rectangle 560 | // value of 0 means update face rectangles EVERY frame 561 | // value of 1 means update face rectangles every other frame 562 | // value of 2 means update face rectangles once out of three frames 563 | { 564 | OP_NumericParameter np; 565 | 566 | np.name = "Facerectframeskip"; 567 | np.label = "Face Rectangle Frame Skip"; 568 | np.defaultValues[0] = 1.0; 569 | np.clampMins[0] = true; 570 | np.minValues[0] = 0; 571 | np.minSliders[0] = 0; 572 | np.maxSliders[0] = 3; 573 | 574 | OP_ParAppendResult res = manager->appendInt(np); 575 | assert(res == OP_ParAppendResult::Success); 576 | } 577 | 578 | // Image download type 579 | { 580 | OP_StringParameter sp; 581 | 582 | sp.name = "Imagedownload"; 583 | sp.label = "Image Download"; 584 | 585 | sp.defaultValue = "Instant"; 586 | 587 | const char *names[] = { "Instant", "Delayed"}; 588 | const char *labels[] = { "Instant", "Delayed"}; 589 | 590 | OP_ParAppendResult res = manager->appendMenu(sp, 2, names, labels); 591 | assert(res == OP_ParAppendResult::Success); 592 | } 593 | 594 | } 595 | 596 | void 597 | FaceCHOP::pulsePressed(const char* name, void* reserved1) 598 | { 599 | //if (!strcmp(name, "Reset")) 600 | //{ 601 | //} 602 | } 603 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------