diff options
| author | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
|---|---|---|
| committer | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
| commit | bd0027e737c6512397f841c22786274ed74b927f (patch) | |
| tree | f7ffbdb8f3741bb7f24635616cc189cba5cb865c /mayaPlug | |
| download | shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.tar.xz shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.zip | |
Adding Shave-and-a-Haircut 9.6
Diffstat (limited to 'mayaPlug')
163 files changed, 67257 insertions, 0 deletions
diff --git a/mayaPlug/.gitignore b/mayaPlug/.gitignore new file mode 100644 index 0000000..140f8cf --- /dev/null +++ b/mayaPlug/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/mayaPlug/Makefile-vrayexporter.linux b/mayaPlug/Makefile-vrayexporter.linux new file mode 100644 index 0000000..b3076b7 --- /dev/null +++ b/mayaPlug/Makefile-vrayexporter.linux @@ -0,0 +1,169 @@ +# Shave and a Haircut +# (c) 2019 Epic Games +# US Patent 6720962 + +#################################################################### +# +# Maya/V-Ray Version & Location +# +#################################################################### + +ifndef MAYA_LOCATION +$(error "MAYA_LOCATION is not defined") +endif + +ifeq ($(wildcard $(MAYA_LOCATION)),) +$(error "MAYA_LOCATION contains '$(MAYA_LOCATION)', which does not exist.") +endif + +ifndef SHAVE_VRAY_SDKS +$(error "SHAVE_VRAY_SDKS is not defined") +endif + +ifndef VRAY_VERSION +$(error "VRAY_VERSION is not defined") +endif + +# We use two-digit tags to identify build products which serve a range +# of V-Ray versions. +# +vrayTag := $(shell ../vrayPlug/getVersionTag.sh $(VRAY_VERSION)) + +ifeq ($(vrayTag),) + $(error "Unrecognized V-Ray version '$(VRAY_VERSION)'.") +endif + +versionInfo := $(shell ../utils/getMayaAPIVersion.sh) +mayaAPI := $(word 1,$(versionInfo)) +mayaVersion := $(word 2,$(versionInfo)) +mayaMajorVersion := $(word 3, $(versionInfo)) +mayaMinorVersion := $(word 4, $(versionInfo)) +mayaDirVersion := $(word 6, $(versionInfo)) + +bits := 64 + + +#################################################################### +# +# Linux Version +# +#################################################################### + +osVersion := $(shell ../utils/getos.sh) + +ifeq ($(osVersion),) + $(error "Operating system type not recognized. Is /etc/issue present?") +endif + +arch := $(shell ../utils/getarch.sh) + + +#################################################################### +# +# Compiler & Linker +# +#################################################################### + +# +# Some versions of Maya use non-standard versions of gcc, which are +# expected to be installed in /opt (e.g. /opt/gcc322) +# +CFLAGS = -pipe -D_BOOL -DUNIX -DLINUX -DFUNCPROTO -D_GNU_SOURCE \ + -DREQUIRE_IOSTREAM -Wall -Wall \ + -Wno-deprecated -fno-gnu-keywords +C++ = $(shell ../utils/getg++vray.sh $(mayaVersion) $(vrayTag)) + +ifeq ($(C++),) + $(error "Maya version $(mayaVersion) not supported.") +endif + +CFLAGS += -fPIC -DBits64_ -DLINUX_64 -march=athlon64 -m64 -fpermissive \ + -Dnullptr=0 + +ifdef debug +_DEBUG=1 +endif + +ifdef DEBUG +_DEBUG=1 +endif + +ifdef _DEBUG +# It's tempting to add -gstabs+ here to get more C++ specific info, but +# that confuses gdb with the result that it gives 'Cannot access memory' +# errors when you try to print local vars or member vars. +CFLAGS += -D_DEBUG -O0 -g -ggdb -fno-inline +else +CFLAGS += -O3 +endif + +ifdef _INSECURE +CFLAGS += -D_INSECURE +endif + +ifdef _BETA +CFLAGS += -D_BETA +endif + +ifdef SHAVE_PROFILE +profile = 1 +endif + +ifdef profile +CFLAGS += -g +endif + +C++FLAGS = $(CFLAGS) $(WARNFLAGS) + +ifeq ($(vrayTag),40) + CFLAGS += -std=c++11 +else +ifeq ($(shell echo '$(mayaAPI) >= 20180000' | bc),1) + C++FLAGS += -std=c++0x +endif +endif + +INCLUDES = -I. -isystem $(MAYA_LOCATION)/include -isystem /usr/X11R6/include +LD = $(C++) -shared $(C++FLAGS) +LIBS = -L$(MAYA_LOCATION)/lib -L/usr/X11R6/lib -lOpenMaya -lOpenMayaFX -lOpenMayaRender -lOpenMayaUI -lOpenMayaAnim -lFoundation -lGLU -lGL -lpthread + + +####################### Vray stuff ################################# +# vlad |23Apr2010 +# +INCLUDES += -I$(SHAVE_VRAY_SDKS)/$(VRAY_VERSION)/include +VRAY_LIBS := $(firstword $(wildcard ../vrayPlug/libs/vray$(vrayTag)/linux_x64/*)) +LIBS += -L$(SHAVE_VRAY_SDKS)/$(VRAY_VERSION)/lib/linux -l plugman_s -lvutils_s +#################################################################### + +#################################################################### +# +# Specific Build Rules +# +#################################################################### + +OBJFILE := shaveVray$(vrayTag)Exporter.o +TARGET := vray/libShaveVray$(vrayTag)Exporter.so + +all: $(TARGET) + +$(TARGET): $(OBJFILE) + -rm -f $@ + $(LD) -o $@ $(OBJFILE) \ + -Wl,-Bsymbolic -Wl,--allow-shlib-undefined \ + $(LIBS) + +$(OBJFILE): shaveVrayExporterImpl.cpp + $(C++) -c $(INCLUDES) $(C++FLAGS) -o $(OBJFILE) $< + +#################################################################### +# +# Cleanup +# +#################################################################### + +clean: + -rm -f *.o + +Clean: + -rm -f *.o *.so *.bak diff --git a/mayaPlug/Makefile-vrayexporter.osx b/mayaPlug/Makefile-vrayexporter.osx new file mode 100644 index 0000000..0e47940 --- /dev/null +++ b/mayaPlug/Makefile-vrayexporter.osx @@ -0,0 +1,161 @@ +# Shave and a Haircut +# (c) 2019 Epic Games +# US Patent 6720962 + +include ../config-osx.mak + +ifndef SHAVE_VRAY_SDKS +$(error "SHAVE_VRAY_SDKS is not defined") +endif + +ifndef VRAY_VERSION +$(error "VRAY_VERSION is not defined") +endif + +#################################################################### +# +# OSX Version +# +#################################################################### +osVersion := $(shell ../utils/getos.sh) + +ifeq ($(findstring osx,$(osVersion)),) + $(error This Makefile is only for use on OSX) +endif + +osVersion := $(subst osx,,$(osVersion)) +osVersionMajor := $(word 1,$(subst ., ,$(osVersion))) +osVersionMinor := $(word 2,$(subst ., ,$(osVersion))) +osVersion := $(osVersionMajor).$(osVersionMinor) + + +#################################################################### +# +# Maya Version & Location +# +#################################################################### + +versionInfo := $(shell ../utils/getMayaAPIVersion.sh) +mayaAPI := $(word 1,$(versionInfo)) +mayaVersion := $(word 2,$(versionInfo)) +mayaMajorVersion := $(word 3, $(versionInfo)) +mayaMinorVersion := $(word 4, $(versionInfo)) + +# Maya requires OSX 10.4 or better. +ifeq ($(osVersionMajor),10) + ifeq ($(filter-out 0 1 2 3,$(osVersionMinor)),) + $(error Maya $(mayaVersion) requires OSX 10.4 or better) + endif +endif + + +#################################################################### +# +# Compiler & Linker +# +#################################################################### + +mayaShared = /Users/Shared/Autodesk/maya/$(mayaVersion) +mayaIncludeDir = $(MAYA_LOCATION)/include + + +CFLAGS += -DCC_GNU_ -DOSMac_ -DOSMacOSX_ \ + -DOSMac_MachO_ -fno-gnu-keywords -fpascal-strings \ + -DVRAY_DUAL -DVRAY_EXPORTS + +C++FLAGS += $(CFLAGS) $(WARNFLAGS) $(ERROR_FLAGS) \ + -D_LANGUAGE_C_PLUS_PLUS -DREQUIRE_IOSTREAM + +ifeq ($(shell echo '$(mayaMajorVersion) < 2017' | bc),1) + C++FLAGS += -Dnullptr=0 +endif + + +INCLUDES = -I. -I../libexe/sample/include -I"$(mayaIncludeDir)" + + +####################### Vray stuff ################################# +# vlad |23Apr2010 +# + +# We use two-digit tags to identify build products which serve a range +# of V-Ray versions. +# +vrayTag := $(shell ../vrayPlug/getVersionTag.sh $(VRAY_VERSION)) + +ifeq ($(vrayTag),) + $(error "Unrecognized V-Ray version '$(VRAY_VERSION)'.") +endif + +ifeq ($(vrayTag),36) + C++FLAGS += -DVRAY30 -Wno-c++11-extensions +else + C++FLAGS += -DVRAY40 -arch x86_64 -std=c++11 +endif + +INCLUDES += -I$(SHAVE_VRAY_SDKS)/$(VRAY_VERSION)/include +LIBS += -stdlib=libstdc++ -L$(SHAVE_VRAY_SDKS)/$(VRAY_VERSION)/lib/osx -lplugman_s -lvutils_s -lvray + +#################################################################### + + +DYNLIB_LOCATION = $(MAYA_LOCATION)/Maya.app/Contents/MacOS + +LDFLAGS = -dynamiclib -single_module -fno-gnu-keywords -fpascal-strings \ + -multiply_defined suppress -dead_strip + +LIBS += -L$(DYNLIB_LOCATION) -lFoundation -lOpenMaya + + +#################################################################### +# +# Generic Build Rules +# +#################################################################### + +.SUFFIXES: .c .cpp .o .io .po .xo + +.c.o: + $(error Attempt to build $< into a .o file: should be .io or .po or .xo) + +.cpp.o: + $(error Attempt to build $< into a .o file: should be .io or .po or .xo) + +.cpp.xo: + $(C++) -c -o $@ $(X86_64_INCLUDES) $(X86_64_C++FLAGS) $< + + +#################################################################### +# +# Specific Build Rules +# +#################################################################### + +OBJFILE := shaveVray$(vrayTag)Exporter.o +TARGET := libShaveVray$(vrayTag)Exporter.so + +all: $(TARGET) + +$(TARGET): $(OBJFILE) + -rm -f $@ + $(LD) -o $@ $(LDFLAGS) $(OBJFILE) $(LIBS) + +$(OBJFILE): shaveVrayExporterImpl.cpp + $(C++) -c $(INCLUDES) $(C++FLAGS) -o $(OBJFILE) $< + + +#################################################################### +# +# Cleanup +# +#################################################################### + +.PHONY: clean + +clean: + -rm -f $(OBJFILE) + +.PHONY: Clean + +Clean: clean + -rm -f $(TARGET) diff --git a/mayaPlug/Makefile.linux b/mayaPlug/Makefile.linux new file mode 100644 index 0000000..5d24f00 --- /dev/null +++ b/mayaPlug/Makefile.linux @@ -0,0 +1,254 @@ +# Shave and a Haircut +# (c) 2019 Epic Games +# US Patent 6720962 + +#################################################################### +# +# Maya Version & Location +# +#################################################################### + +ifndef MAYA_LOCATION +$(error "MAYA_LOCATION is not defined") +endif + +ifeq ($(wildcard $(MAYA_LOCATION)),) +$(error "MAYA_LOCATION contains '$(MAYA_LOCATION)', which does not exist.") +endif + +versionInfo := $(shell ../utils/getMayaAPIVersion.sh) +mayaAPI := $(word 1,$(versionInfo)) +mayaVersion := $(word 2,$(versionInfo)) +mayaMajorVersion := $(word 3, $(versionInfo)) +mayaMinorVersion := $(word 4, $(versionInfo)) +mayaDirVersion := $(word 6, $(versionInfo)) + +bits := 64 + + +#################################################################### +# +# Linux Version +# +#################################################################### + +osVersion := $(shell ../utils/getos.sh) + +ifeq ($(osVersion),) + $(error "Operating system type not recognized. Is /etc/issue present?") +endif + +arch := $(shell ../utils/getarch.sh) + + +#################################################################### +# +# Compiler & Linker +# +#################################################################### + +# +# Some versions of Maya use non-standard versions of gcc, which are +# expected to be installed in /opt (e.g. /opt/gcc322) +# +CFLAGS = -pipe -D_BOOL -DUNIX -DLINUX -DFUNCPROTO -D_GNU_SOURCE \ + -DREQUIRE_IOSTREAM -Wall -Wall \ + -Wno-deprecated -fno-gnu-keywords +C++ = $(shell ../utils/getg++.sh $(mayaDirVersion)) + +ifeq ($(C++),) + $(error "Maya version $(mayaVersion) not supported.") +endif + +CFLAGS += -fPIC -DBits64_ -DLINUX_64 -march=athlon64 -m64 -fpermissive \ + -Dnullptr=0 + +ifndef SHAVE_RMAN_SDKS + $(error "SHAVE_RMAN_SDKS not defined.") +endif + +PRMAN = $(SHAVE_RMAN_SDKS)/21.7/maya$(mayaVersion)/linux +SHAVELIB = ../libexe/shavelibAW-x64.a + +ifdef debug +_DEBUG=1 +endif + +ifdef DEBUG +_DEBUG=1 +endif + +ifdef _DEBUG +# It's tempting to add -gstabs+ here to get more C++ specific info, but +# that confuses gdb with the result that it gives 'Cannot access memory' +# errors when you try to print local vars or member vars. +CFLAGS += -D_DEBUG -O0 -g -ggdb -fno-inline +else +CFLAGS += -O3 +endif + +ifdef _INSECURE +CFLAGS += -D_INSECURE +endif + +ifdef _BETA +CFLAGS += -D_BETA +endif + +ifdef SHAVE_PROFILE +profile = 1 +endif + +ifdef profile +CFLAGS += -g +endif + +C++FLAGS = $(CFLAGS) $(WARNFLAGS) + +ifeq ($(shell echo '$(mayaAPI) >= 20180000' | bc),1) + C++FLAGS += -std=c++0x +endif + +INCLUDES = -I. -I.. -I../libexe/sample/include -isystem $(PRMAN)/include -isystem $(MAYA_LOCATION)/include -isystem /usr/X11R6/include +LD = $(C++) -shared $(C++FLAGS) +LIBS = -L$(MAYA_LOCATION)/lib -L/usr/X11R6/lib -lOpenMaya -lOpenMayaFX -lOpenMayaRender -lOpenMayaUI -lOpenMayaAnim -lFoundation -lGLU -lGL -lpthread + +######################### Qt stuff ################################# +# vlad|17Mar2010 +# + +qtIncludeDir := $(MAYA_LOCATION)/include/qt + +ifeq ($(wildcard $(qtIncludeDir)/QtGui),) + $(error "Could not find Qt header files for Maya $(mayaVersion).") +endif + +INCLUDES += -I$(qtIncludeDir) + +ifeq ($(shell echo '$(mayaVersion) < 2017' | bc),1) +LIBS += -lQtCore -lQtGui +else +LIBS += -lQt5Core -lQt5Gui +endif + +######################## procedrals ############################### +#vlad |20Jun2011 + +INCLUDES += -I../procedurals/includes + +#################################################################### + +#################################################################### +# +# Generic Build Rules +# +#################################################################### + +.SUFFIXES: .cpp .cc .o .so .c + +.cc.o: + $(C++) -c $(INCLUDES) $(C++FLAGS) $< + +.cpp.o: + $(C++) -c $(INCLUDES) $(C++FLAGS) $< + +.cc.i: + $(C++) -E $(INCLUDES) $(C++FLAGS) $*.cc > $*.i + +.cc.so: + -rm -f $@ + $(LD) -o $@ $(INCLUDES) $< $(LIBS) + +.cpp.so: + -rm -f $@ + $(LD) -o $@ $(INCLUDES) $< $(LIBS) + +.o.so: + -rm -f $@ + $(LD) -o $@ $< $(LIBS) + + +#################################################################### +# +# Specific Build Rules +# +#################################################################### + +SHAVEFILES = shaveNode.o shaveShadowNode.o shaveObjExporter.o \ + shaveRenderCallback.o shaveRender.o shaveTextureStore.o \ + shaveGlobals.o shaveMaya.o shaveWriteRib.o shaveBlindData.o \ + shaveUtil.o shaveCheckObjectVisibility.o shaveInfo.o \ + isSharedCmd.o shaveVertexShader.o shaveNodeCmd.o \ + shaveRenderCmd.o shaveAPIimpl.o \ + pluginImpl.o shaveXPM.o shaveIcon.o shaveIconCmd.o \ + shaveBackgroundShader.o shaveVolumeShader.o shaveItHairImpl.o \ + shaveRenderer.o shaveMayaRenderer.o \ + shaveSDKCALLBACKS.o shavePadCmd.o \ + shaveHairShape.o shaveHairUI.o shaveCallbacks.o shaveUtilCmd.o \ + shaveHairGeomIt.o shaveHairShapeAttrs.o shaveBrushCtx.o \ + shaveBrushCtxCmd.o shaveBrushManip.o shaveStyleCmd.o \ + shaveCursorCtx.o shaveCursorCtxCmd.o shaveCutCtx.o shaveCutCtxCmd.o \ + shaveWriteHairCmd.o ShavePerVertTexInfo.o \ + shaveVrayRenderer.o shaveVraySharedFunctions.o shaveVrayNode.o \ + shaveVrayCmd.o shavePack2TexCmd.o shaveExportGame.o \ + shaveGeometryOverride.o + + +ifdef _DEBUG +SHAVEFILES += shaveDebug.o +endif + +ifdef _BETA +all: shaveNode.so +else +all: shaveNode.so libShaveAPI.so +endif + +libShave.so: $(SHAVEFILES) + -rm -f $@ + $(LD) -o $@ $(SHAVEFILES) \ + -Wl,-Bsymbolic -Wl,--allow-shlib-undefined \ + $(SHAVELIB) $(LIBS) + +shaveNode.so: plugin.o libShave.so + -rm -f $@ + $(LD) -o $@ plugin.o libShave.so \ + -Wl,-Bsymbolic -Wl,--allow-shlib-undefined \ + $(LIBS) + +libShaveAPI.so: shaveAPI.o shaveItHair.o libShave.so + -rm -f $@ + $(LD) -o $@ $^ $(LIBS) + +shaveAPITestCmd.so: shaveAPITestCmd.o libShaveAPI.so + -rm -f $@ + $(LD) -o $@ shaveAPITestCmd.o -L. -lShaveAPI + +testo.so: testo.o + -rm -f $@ + $(LD) -o $@ testo.o $(LIBS) -Wl,-Bsymbolic \ + -Wl,--allow-shlib-undefined + +force: ; + +CCDEP = gcc + +depend dep deps depends: .depends + +.depends: $(SHAVEFILES:.o=.cpp) plugin.cpp Makefile.linux + @$(CCDEP) -MM $(C++FLAGS) $(INCLUDES) $(SHAVEFILES:.o=.cpp) plugin.cpp >.depends + + +#################################################################### +# +# Cleanup +# +#################################################################### + +clean: + -rm -f *.o + +Clean: + -rm -f *.o *.so *.bak + +include .depends diff --git a/mayaPlug/Makefile.osx b/mayaPlug/Makefile.osx new file mode 100644 index 0000000..514d8e0 --- /dev/null +++ b/mayaPlug/Makefile.osx @@ -0,0 +1,297 @@ +# Shave and a Haircut +# (c) 2019 Epic Games +# US Patent 6720962 + +include ../config-osx.mak + +#################################################################### +# +# OSX Version +# +#################################################################### +osVersion := $(shell ../utils/getos.sh) + +ifeq ($(findstring osx,$(osVersion)),) + $(error This Makefile is only for use on OSX) +endif + +osVersion := $(subst osx,,$(osVersion)) +osVersionMajor := $(word 1,$(subst ., ,$(osVersion))) +osVersionMinor := $(word 2,$(subst ., ,$(osVersion))) +osVersion := $(osVersionMajor).$(osVersionMinor) + + +#################################################################### +# +# Maya Version & Location +# +#################################################################### + +versionInfo := $(shell ../utils/getMayaAPIVersion.sh) +mayaAPI := $(word 1,$(versionInfo)) +mayaVersion := $(word 2,$(versionInfo)) +mayaMajorVersion := $(word 3, $(versionInfo)) +mayaMinorVersion := $(word 4, $(versionInfo)) + +# Maya requires OSX 10.4 or better. +ifeq ($(osVersionMajor),10) + ifeq ($(filter-out 0 1 2 3,$(osVersionMinor)),) + $(error Maya $(mayaVersion) requires OSX 10.4 or better) + endif +endif + + +#################################################################### +# +# Compiler & Linker +# +#################################################################### + +mayaShared = /Users/Shared/Autodesk/maya/$(mayaVersion) + + + +# +# From Maya 2012 onward we use Qt. +# +useQt := y + +EXT = bundle +LPLUGINFLAGS = -bundle + +mayaIncludeDir = $(MAYA_LOCATION)/include +apiPreInclude = + + +ifdef DEBUG +_DEBUG=1 +endif + +ifdef _DEBUG +CFLAGS += -D_DEBUG -g +LDFLAGS += -g +else +CFLAGS += -O3 +endif + +ifdef _INSECURE +CFLAGS += -D_INSECURE +endif + +ifdef _BETA +CFLAGS += -D_BETA +endif + +LDYLIBFLAGS = -dynamiclib -single_module + +CFLAGS += -DCC_GNU_ -DOSMac_ -DOSMacOSX_ \ + -DOSMac_MachO_ -fno-gnu-keywords -fpascal-strings $(apiPreInclude) + +C++FLAGS += $(CFLAGS) $(WARNFLAGS) $(ERROR_FLAGS) \ + -D_LANGUAGE_C_PLUS_PLUS -DREQUIRE_IOSTREAM + +ifeq ($(shell echo '$(mayaMajorVersion) < 2017' | bc),1) + C++FLAGS += -Dnullptr=0 +endif + +ifndef SHAVE_RMAN_SDKS + $(error "SHAVE_RMAN_SDKS not defined.") +endif + +PRMAN = $(SHAVE_RMAN_SDKS)/21.7/maya$(mayaVersion)/osx + +INCLUDES = -I. -I../libexe/sample/include -I"$(mayaIncludeDir)" + + +####################### Vray stuff ################################# +# vlad |23Apr2010 +# +INCLUDES += -I../vrayPlug/include36 -I../vrayPlug/plugin + +#################################################################### + + +DYNLIB_LOCATION = $(MAYA_LOCATION)/Maya.app/Contents/MacOS + +FWORKS = -framework CoreFoundation \ + -framework CoreServices \ + -framework System \ + -framework Carbon \ + -framework ApplicationServices \ + -framework AGL \ + -framework OpenGL \ + -framework IOKit +# -framework AppKit + + +ifeq ($(useQt),y) +FWORKS += -framework Cocoa +endif + +LREMAP = -Wl,-executable_path,"$(DYNLIB_LOCATION)" +LDFLAGS += -fno-gnu-keywords -fpascal-strings $(FWORKS) -multiply_defined suppress + +LIBS = -L$(DYNLIB_LOCATION) $(LREMAP) -lFoundation -lOpenMaya \ + -lOpenMayaFX -lOpenMayaRender -lOpenMayaUI \ + -lOpenMayaAnim + +ifeq ($(useQt),y) +# Maya uses a customized version of Qt so we have to build with the Qt headers +# provided by Maya. +# +INCLUDES += -I$(MAYA_LOCATION)/include/qt + +#use distribyted with maya libs, they have weird names a bit - no 'lib' prefix and .a or .dynlib suffix +#so should be included explicitly +# +LIBS += -dead_strip -lz + +ifeq ($(shell echo "${mayaVersion} < 2017" | bc),1) +LIBS += $(DYNLIB_LOCATION)/QtCore \ + $(DYNLIB_LOCATION)/QtGui \ + $(DYNLIB_LOCATION)/QtOpenGL +else +LIBS += $(DYNLIB_LOCATION)/libQt5Core.dylib \ + $(DYNLIB_LOCATION)/libQt5Gui.dylib \ + $(DYNLIB_LOCATION)/libQt5OpenGL.dylib \ + $(DYNLIB_LOCATION)/libQt5Widgets.dylib +endif + + +# static Qt libs make MEL crashy +# +#LIBS += -dead_strip -L/qt-mac-opensource-src-4.7.1/lib -lz -lQtCore -lQtGui + +endif + +X86_64_CFLAGS = $(CFLAGS) -DBits64_ -arch x86_64 +X86_64_C++FLAGS = $(C++FLAGS) -DBits64_ -arch x86_64 + +X86_64_INCLUDES = -I"$(PRMAN)/include" $(INCLUDES) +X86_64_LFLAGS = $(LDFLAGS) -DBits64_ -arch x86_64 + + +X86_64_LIBS = $(LIBS) + +X86_64_SHAVELIB = ../libexe/shaveLibAW-x86_64.a + + +#################################################################### +# +# Generic Build Rules +# +#################################################################### + +.SUFFIXES: .c .cpp .o .io .po .xo + +.c.o: + $(error Attempt to build $< into a .o file: should be .io or .po or .xo) + +.cpp.o: + $(error Attempt to build $< into a .o file: should be .io or .po or .xo) + +.cpp.xo: + $(C++) -c -o $@ $(X86_64_INCLUDES) $(X86_64_C++FLAGS) $< + + +#################################################################### +# +# Specific Build Rules +# +#################################################################### + +SHAVEFILES = shaveNode.o shaveShadowNode.o shaveObjExporter.o \ + shaveRenderCallback.o shaveRender.o shaveTextureStore.o \ + shaveGlobals.o shaveMaya.o shaveWriteRib.o shaveBlindData.o \ + shaveUtil.o shaveCheckObjectVisibility.o shaveInfo.o \ + isSharedCmd.o shaveVertexShader.o shaveNodeCmd.o \ + shaveRenderCmd.o shaveAPIimpl.o \ + pluginImpl.o shaveXPM.o shaveIcon.o shaveIconCmd.o \ + shaveBackgroundShader.o shaveVolumeShader.o shaveItHairImpl.o \ + shaveMacCarbon.o \ + shaveRenderer.o shaveMayaRenderer.o \ + shaveSDKCALLBACKS.o shavePadCmd.o \ + shaveHairShape.o shaveHairUI.o shaveCallbacks.o shaveUtilCmd.o \ + shaveHairGeomIt.o shaveHairShapeAttrs.o shaveBrushCtx.o \ + shaveBrushCtxCmd.o shaveBrushManip.o shaveStyleCmd.o \ + shaveCursorCtx.o shaveCursorCtxCmd.o shaveCutCtx.o shaveCutCtxCmd.o \ + shaveWriteHairCmd.o ShavePerVertTexInfo.o \ + shaveVrayRenderer.o shaveVraySharedFunctions.o shaveVrayNode.o \ + shaveVrayCmd.o shavePack2TexCmd.o shaveExportGame.o \ + shaveGeometryOverride.o + +ifdef _DEBUG +SHAVEFILES += shaveDebug.o +endif + +X86_64_SHAVEFILES = $(SHAVEFILES:.o=.xo) + +.PHONY: all +.PHONY: shaveNode-lib + +all: shaveNode-lib libShave.dylib libShaveAPI.dylib +shaveNode-lib: shaveNode.$(EXT) + +libShave.dylib: $(X86_64_SHAVEFILES) + -rm -f $@ + $(LD) -o $@ \ + -install_name @executable_path/libShave.dylib \ + $(X86_64_SHAVEFILES) $(X86_64_SHAVELIB) $(X86_64_LFLAGS) \ + $(LDYLIBFLAGS) $(X86_64_LIBS) + +shaveNode.$(EXT): plugin.xo libShave.dylib + -rm -f $@ + $(LD) -o $@ plugin.xo -L. -lShave $(X86_64_LFLAGS) \ + $(LPLUGINFLAGS) $(X86_64_LIBS) + + +libShaveAPI.dylib: shaveAPI.xo shaveItHair.xo libShave.dylib + -rm -f $@ + $(LD) -o $@ \ + -install_name @executable_path/libShaveAPI.dylib \ + shaveAPI.xo shaveItHair.xo -L. -lShave $(X86_64_LFLAGS) \ + $(LDYLIBFLAGS) $(X86_64_LIBS) + +# +# This is annoying. The linker won't accept a static lib whose +# modification date is later than when its internal table was last updated +# with 'ranlib'. So whenever we get a fresh copy of the lib from cvs, it +# gets a new date, making it invalid for linking. We can run ranlib on the +# lib, but then cvs will think it changed and get a bounce next time we +# update. So we have to copy the libs into a temp dir, run ranlib on them, +# then use from there. +# +PRMAN_LIBS=$(wildcard $(PRMAN)/lib/*.a) +PRMAN_RANLIBS=$(addprefix $(PRMAN)/lib/ranlib/,$(notdir $(PRMAN_LIBS))) + +.PHONY: prman-ranlib +prman-ranlib: $(PRMAN)/lib/ranlib $(PRMAN_RANLIBS) + +$(PRMAN)/lib/ranlib: + mkdir $(PRMAN)/lib/ranlib + @echo '$(PRMAN_LIBS)' + @echo '$(PRMAN_RANLIBS)' + +$(PRMAN_RANLIBS): $(PRMAN)/lib/ranlib/%: $(PRMAN)/lib/% + cp $< $@ + ranlib $@ + +depend: + makedepend $(INCLUDES) -I/usr/include/CC *.cpp + + +#################################################################### +# +# Cleanup +# +#################################################################### + +.PHONY: clean + +clean: + -rm -f *.io *.po *.xo + +.PHONY: Clean + +Clean: clean + -rm -f *.so *.bak *.$(EXT) diff --git a/mayaPlug/README-samples.linux b/mayaPlug/README-samples.linux new file mode 100644 index 0000000..5b743a3 --- /dev/null +++ b/mayaPlug/README-samples.linux @@ -0,0 +1,65 @@ +Shave And A Haircut APIs +------------------------ +There are two APIs for use with Shave: a standalone API for building +applications which process Shave archive files (such as those produced by +the 'shaveRender -createDRAFile' command in Maya), and a plugin API for +building Maya plugins which can access Shave's data from within Maya. + + +Standalone API +-------------- +The standalone API consists of the following two header files, found in this +directory: + + shaveEngine.h + shaveSDKTYPES.h + +as well as the following dynamic library, also found in this directory: + + libShaveEngine.so + + +Maya Plugin API +--------------- +The Maya plugin API consists of the following two header files, found in the +same directory as Maya's own API header files +(e.g. /usr/aw/maya7.0/include/maya): + + shaveAPI.h + shaveItHair.h + +as well as the following dynamic library, found in Maya's 'lib' directory +(e.g. /usr/aw/maya/7.0/lib): + + libShaveAPI.so + +The header files contain complete documentation on the plugin API. + + +Samples +------- +'shaveAPITestCmd.cpp' is a sample Maya plugin using Shave's plugin API. + +'shaveAPITestApp.cpp' is a sample application using Shave's standalone API. + +To build them, set your MAYA_LOCATION environment variable to point to the +installation directory for the version of Maya you plan on using the plugin +with, then do the following command: + + make + + +Note that different versions of Maya require different versions of the +compiler. The Makefile assumes that the compilers for the various versions +of Maya are located as follows: + + Maya Version Compiler Path Red Hat Version + ------------ ------------- --------------- + 5.0/5.0.1 /opt/gcc322/bin/c++322 7.3 + 6.0 /opt/gcc332/bin/c++332 7.3 + 6.5 default 'c++' command 9.0 + 7.0 /opt/gcc334/bin/c++334 9.0 + + +If your compiler versions are located in different directories, then you +will have to modify the Makefile accordingly. diff --git a/mayaPlug/README-samples.osx b/mayaPlug/README-samples.osx new file mode 100644 index 0000000..678e60e --- /dev/null +++ b/mayaPlug/README-samples.osx @@ -0,0 +1,62 @@ +Shave And A Haircut APIs +------------------------ +There are two APIs for use with Shave: a standalone API for building +applications which process Shave archive files (such as those produced by +the 'shaveRender -createDRAFile' command in Maya), and a plugin API for +building Maya plugins which can access Shave's data from within Maya. + + +Standalone API +-------------- +The standalone API consists of the following two header files, found in this +directory: + + shaveEngine.h + shaveSDKTYPES.h + +as well as the following dynamic library, also found in this directory: + + libShaveEngine.dylib + + +Maya Plugin API +--------------- +The Maya plugin API consists of the following two header files, found in the +same directory as Maya's own API header files +(e.g. /Applications/Alias/maya7.0/devkit/include/maya): + + shaveAPI.h + shaveItHair.h + +as well as the following dynamic libraries, found in Maya's shared 'plug-ins' +directory (e.g. /Users/Shared/Alias/maya/7.0/plug-ins): + + libShave.dylib + libShaveAPI.dylib + +The header files contain complete documentation on the plugin API. + + +Sample Code +----------- +'shaveAPITestCmd.cpp' is a sample Maya plugin using Shave's plugin API. + +'shaveAPITestApp.cpp' is a sample application using Shave's standalone API. + +To build them, set your MAYA_LOCATION environment variable to point to the +Maya.app/Contents directory for the version of Maya you plan on using the +plugin with. E.g: + + setenv MAYA_LOCATION /Applications/Alias/maya7.0/Maya.app/Contents + +Then execute the following command: + + make + +To execute a standalone application, either run the application from the +same directory which contains the libShaveEngine.dylib file, or else +set the DYLD_LIBRARY_PATH environment variable to point to that directory. +E.g: + + setenv DYLD_LIBRARY_PATH /Users/Shared/Epic Games/shaveHaircut/maya2017/samples + diff --git a/mayaPlug/README-samples.win b/mayaPlug/README-samples.win new file mode 100644 index 0000000..c5478c9 --- /dev/null +++ b/mayaPlug/README-samples.win @@ -0,0 +1,60 @@ +Shave And A Haircut APIs +------------------------ +There are two APIs for use with Shave: a standalone API for building +applications which process Shave archive files (such as those produced by +the 'shaveRender -createDRAFile' command in Maya), and a plugin API for +building Maya plugins which can access Shave's data from within Maya. + + +Standalone API +-------------- +The standalone API consists of the following two header files, found in this +directory: + + shaveEngine.h + shaveSDKTYPES.h + +as well as the following dynamic library and its corresponding link +library, both also found in this directory: + + libShaveEngine.dll + libShaveEngine.lib + +To use the DLL library you must either place it in a folder which is in +your PATH, or else add the folder it is currently in to your PATH. + + +Sample Application +------------------ +'shaveAPITestApp.cpp' is a sample application using Shave's standalone API. + + +Maya Plugin API +--------------- +The Maya plugin API consists of the following two header files: + + shaveAPI.h + shaveItHair.h + +as well as the following dynamic library: + + libShaveAPI.dll + +and its associated link library: + + libShaveAPI.lib + +The header files contain complete documentation on the plugin API. + +These files are located in the appropriate sub-directories of the Shave +install directory. E.g: + + c:\Program Files\Epic Games\shaveHaircut\maya2018\include\maya + c:\Program Files\Epic Games\shaveHaircut\maya2018\bin + c:\Program Files\Epic Games\shaveHaircut\maya2018\lib + + +Sample Plugin +------------- +'shaveAPITestCmd.cpp' is a sample Maya plugin using Shave's plugin API. + diff --git a/mayaPlug/ShavePerVertTexInfo.cpp b/mayaPlug/ShavePerVertTexInfo.cpp new file mode 100644 index 0000000..0d56838 --- /dev/null +++ b/mayaPlug/ShavePerVertTexInfo.cpp @@ -0,0 +1,80 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "ShavePerVertTexInfo.h" +#include "shaveTextureStore.h" + + +const int ShavePerVertTexInfo::mVertParams[] = { 8, 21, 40 }; +const unsigned int ShavePerVertTexInfo::mNumVertParams = sizeof(mVertParams) / sizeof(int); + + +ShavePerVertTexInfo::ShavePerVertTexInfo() +: mNumVertices(0) +, mTextureValues(0) +{ +} + + +ShavePerVertTexInfo::~ShavePerVertTexInfo() +{ + clear(true); +} + + +void ShavePerVertTexInfo::clear(bool all) +{ + // If we have any existing texture data, delete it. + if (mTextureValues) + { + for (unsigned int param = 0; param < mNumVertParams; ++param) + { + if (mTextureValues[param]) + { + delete [] mTextureValues[param]; + mTextureValues[param] = 0; + } + } + + if (all) + { + delete [] mTextureValues; + mTextureValues = 0; + } + } + + mNumVertices = 0; +} + + +void ShavePerVertTexInfo::init(unsigned int numVertices) +{ + clear(false); + mNumVertices = numVertices; +} + + +bool ShavePerVertTexInfo::setValue( + unsigned int paramIdx, + unsigned int vertIdx, + float value +) +{ + if ((paramIdx >= mNumVertParams) || (vertIdx > mNumVertices)) return false; + + if (mTextureValues == 0) + { + mTextureValues = new float*[mNumVertParams]; + + for (unsigned int i = 0; i < mNumVertParams; ++i) + mTextureValues[i] = 0; + } + + if (mTextureValues[paramIdx] == 0) + mTextureValues[paramIdx] = new float[mNumVertices]; + + mTextureValues[paramIdx][vertIdx] = value; + + return true; +} diff --git a/mayaPlug/ShavePerVertTexInfo.h b/mayaPlug/ShavePerVertTexInfo.h new file mode 100644 index 0000000..0e3911e --- /dev/null +++ b/mayaPlug/ShavePerVertTexInfo.h @@ -0,0 +1,106 @@ +#ifndef ShavePerVertTexInfo_h +#define ShavePerVertTexInfo_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +// This class is used to store a node's per-vertex texture data. +class ShavePerVertTexInfo +{ +public: + ShavePerVertTexInfo(); + virtual ~ShavePerVertTexInfo(); + + // Only a small number of hair parameters are applied per-vertex, as + // opposed to per-hair. These next three methods provide a compact + // enumeration for these parameters from 0 to numParams()-1 + + // Returns the total number of per-vertex parameters available. + static unsigned int numParams() { return mNumVertParams; } + + // Stores in 'paramIdx' the index from 0 to numParams()-1 of the given + // parameter number and returns true. + // If 'paramNumber' is not a per-vertex parameter + // then false is returned and the value of 'paramIdx' is undefined. + static bool getParamIdx(int paramNumber, unsigned int& paramIdx); + + // Returns the parameter number of the i'th per-vertex parameter. + // Returns -1 if 'paramIdx' is invalid. + static int getParamNumber(unsigned int paramIdx); + + float getValue( + unsigned int paramIdx, + unsigned int vertIdx + ) const; + + // This must be called before storing any texture values. If there is + // existing texture data it will be deleted. + void init(unsigned int numVertices); + + static bool isPerVertParam(int paramNumber); + + bool setValue( + unsigned int paramIdx, + unsigned int vertIdx, + float value + ); + +protected: + void clear(bool all); + + bool mIsInitialized; + unsigned int mNumVertices; + float** mTextureValues; + + static const int mVertParams[]; + static const unsigned int mNumVertParams; +}; + + +inline bool ShavePerVertTexInfo::getParamIdx( + int paramNumber, unsigned int& paramIdx +) +{ + for (paramIdx = 0; paramIdx < mNumVertParams; ++paramIdx) + if (mVertParams[paramIdx] == paramNumber) return true; + + paramIdx = 0; + + return false; +} + + +inline int ShavePerVertTexInfo::getParamNumber(unsigned int paramIdx) +{ + if (paramIdx < mNumVertParams) return mVertParams[paramIdx]; + + return -1; +} + + +inline float ShavePerVertTexInfo::getValue( + unsigned int paramIdx, unsigned int vertIdx +) const +{ + if ((mTextureValues != 0) + && (paramIdx < mNumVertParams) + && (mTextureValues[paramIdx] != 0) + && (vertIdx < mNumVertices)) + { + return mTextureValues[paramIdx][vertIdx]; + } + + return 1.0f; +} + + +inline bool ShavePerVertTexInfo::isPerVertParam(int paramNum) +{ + for (unsigned int i = 0; i < mNumVertParams; ++i) + if (mVertParams[i] == paramNum) return true; + + return false; +} + +#endif diff --git a/mayaPlug/icons/render_shaveBackground.png b/mayaPlug/icons/render_shaveBackground.png Binary files differnew file mode 100644 index 0000000..fab454d --- /dev/null +++ b/mayaPlug/icons/render_shaveBackground.png diff --git a/mayaPlug/icons/render_shaveBackground.xpm b/mayaPlug/icons/render_shaveBackground.xpm new file mode 100644 index 0000000..3e6c4d7 --- /dev/null +++ b/mayaPlug/icons/render_shaveBackground.xpm @@ -0,0 +1,262 @@ +/* XPM */ +static char * render_shaveBackground2_xpm[] = { +"32 32 227 2", +" c None", +". c #C1C1C1", +"+ c #C0C0C0", +"@ c #969596", +"# c #3B393B", +"$ c #838283", +"% c #BABABA", +"& c #B4B4B4", +"* c #9E9D9E", +"= c #B1B1B1", +"- c #717171", +"; c #8A898A", +"> c #939293", +", c #ABABAB", +"' c #BDBDBD", +") c #B7B7B7", +"! c #BFBFBF", +"~ c #4D4C4D", +"{ c #050305", +"] c #5C5B5C", +"^ c #B2B1B2", +"/ c #818081", +"( c #312F31", +"_ c #757475", +": c #201F20", +"< c #262526", +"[ c #3F3E3F", +"} c #6A696A", +"| c #A4A4A4", +"1 c #939393", +"2 c #6D6C6D", +"3 c #ADADAD", +"4 c #8D8C8D", +"5 c #201E20", +"6 c #1D1C1D", +"7 c #878687", +"8 c #AFAEAF", +"9 c #545354", +"0 c #161416", +"a c #090709", +"b c #171617", +"c c #898889", +"d c #ACABAC", +"e c #5A595A", +"f c #413F41", +"g c #1C1A1C", +"h c #0D0B0D", +"i c #0E0C0E", +"j c #403F40", +"k c #A8A8A8", +"l c #A6A6A6", +"m c #474647", +"n c #222022", +"o c #8B8B8B", +"p c #B8B8B8", +"q c #504F50", +"r c #080608", +"s c #2D2B2D", +"t c #A1A1A1", +"u c #BEBEBE", +"v c #AAAAAA", +"w c #585758", +"x c #100E10", +"y c #343334", +"z c #797979", +"A c #A3A2A3", +"B c #5D5C5D", +"C c #121012", +"D c #787778", +"E c #BBBBBB", +"F c #9A999A", +"G c #5C5A5B", +"H c #1B191B", +"I c #0C0A0C", +"J c #605F60", +"K c #A1A0A1", +"L c #141314", +"M c #131113", +"N c #575657", +"O c #454345", +"P c #4F4E4F", +"Q c #373637", +"R c #302E30", +"S c #3C3B3C", +"T c #181618", +"U c #1B1A1B", +"V c #B2B2B2", +"W c #494849", +"X c #0A080A", +"Y c #171517", +"Z c #616061", +"` c #B5B5B5", +" . c #8B8A8B", +".. c #242324", +"+. c #121112", +"@. c #282728", +"#. c #6D6E6E", +"$. c #878787", +"%. c #232123", +"&. c #141214", +"*. c #B9B9B9", +"=. c #959695", +"-. c #414141", +";. c #292829", +">. c #343234", +",. c #424142", +"'. c #616161", +"). c #6F6E6F", +"!. c #A2A2A2", +"~. c #9D9C9D", +"{. c #909090", +"]. c #858585", +"^. c #464446", +"/. c #6E6D6E", +"(. c #949594", +"_. c #3A3A3A", +":. c #313031", +"<. c #6B6A6B", +"[. c #383838", +"}. c #8F908F", +"|. c #9B9B9B", +"1. c #2E2C2E", +"2. c #252425", +"3. c #AFAFAF", +"4. c #4C4B4C", +"5. c #3E3E3E", +"6. c #343434", +"7. c #7E7E7E", +"8. c #110F11", +"9. c #454445", +"0. c #151415", +"a. c #1D1B1D", +"b. c #1A181A", +"c. c #626162", +"d. c #8C8B8C", +"e. c #B0B0B0", +"f. c #706E6F", +"g. c #0F0D0F", +"h. c #262426", +"i. c #797879", +"j. c #BCBCBC", +"k. c #7B7B7B", +"l. c #696969", +"m. c #535253", +"n. c #252426", +"o. c #434243", +"p. c #ACACAC", +"q. c #A4A3A4", +"r. c #363436", +"s. c #514F50", +"t. c #595859", +"u. c #363536", +"v. c #999999", +"w. c #151315", +"x. c #868586", +"y. c #979797", +"z. c #858485", +"A. c #696869", +"B. c #2A292A", +"C. c #242224", +"D. c #5B5A5B", +"E. c #7F7E7F", +"F. c #989798", +"G. c #1A191A", +"H. c #3C3A3C", +"I. c #2F2E2F", +"J. c #6B6B6B", +"K. c #9A9A9A", +"L. c #9B9A9B", +"M. c #959495", +"N. c #252325", +"O. c #969696", +"P. c #211F21", +"Q. c #2D2C2D", +"R. c #737373", +"S. c #B3B3B3", +"T. c #9C9C9C", +"U. c #474748", +"V. c #232223", +"W. c #070507", +"X. c #0D0C0D", +"Y. c #676667", +"Z. c #7F7F7F", +"`. c #4B4A4B", +" + c #353335", +".+ c #797A7A", +"++ c #403E40", +"@+ c #848384", +"#+ c #A0A1A1", +"$+ c #A7A7A7", +"%+ c #464546", +"&+ c #0E0D0E", +"*+ c #181718", +"=+ c #7A797A", +"-+ c #1F1D1F", +";+ c #828182", +">+ c #8D8D8D", +",+ c #929292", +"'+ c #565556", +")+ c #2B2A2B", +"!+ c #323132", +"~+ c #272627", +"{+ c #A8A7A8", +"]+ c #555355", +"^+ c #888888", +"/+ c #272527", +"(+ c #6E6E6F", +"_+ c #777677", +":+ c #807F80", +"<+ c #0B090B", +"[+ c #979697", +"}+ c #7D7D7D", +"|+ c #3B3A3B", +"1+ c #242223", +"2+ c #747575", +"3+ c #3E3C3E", +"4+ c #323032", +"5+ c #908F90", +"6+ c #868686", +"7+ c #7C7B7C", +"8+ c #8F8E8F", +"9+ c #666566", +"0+ c #767576", +"a+ c #525152", +"b+ c #4A484A", +"c+ c #5F5E5F", +"d+ c #383638", +". . . . . . . . . . . . . . . . . . . + @ # $ % . . . + & * % . ", +". . . . = - ; > , ' . . . . . ' ) ! . & ~ { ] % . + ^ / ~ ( @ . ", +". . . . _ { : < [ } | ' . . % 1 2 3 . 4 5 6 7 + . 8 9 0 a b c . ", +". . . . d e f g h i j k . . l m n o p q r s t u v w x x y z ) . ", +". . . . + ) A 7 B n C D E ) F G q @ c H I J E K f L M N l u . . ", +". . . . . % u u & ~ { O - P Q R R Q S T U $ V W X Y Z ` . . . . ", +". . . . . u ! ! u .....C { { { { { { +.@.#.$.%.&.Z ` . . . . . ", +". + p = 3 *.V u =.-.{ { I ;.( >.>.( ;.I { { ,.'.).!.. E ~.{.v u ", +". ) ].] ^./.8 (._.X X :.<., % . . % , <.:.X X [.}.' |.w 1.2.B 3.", +". k 4.6 I L w 5.X { 4.k u . . . . . . u k 4.{ X 6.7.1.a 8.n B 3.", +". ' t / 9.0.a.{ X 4.. . . . . . . . . . . . 4.X { b.a b.c.d.v u ", +". . . + e.f.a.{ :.k . . . . . . . . . . . . k :.{ g.h.i.% . . . ", +". . . . j.k.0 I <.u . . . . . . . . . . . . u <.I x l.E . . . . ", +". . . . *.m.{ ;., . . . . . . . . . . . . . . | n.{ m.*.. . . . ", +". . . . = o.{ ( % . . . . . . . . . . . . . . . >.{ S p.E q.E . ", +". . . . v r.{ >.. . . . . . . . . . . . . . . . >.{ Y s.t.u.v.. ", +". . . . v r.{ >.. . . . . . . . . . . . . . . . >.{ { { a w.x.. ", +". % y.z.A.B.{ R % . . . . . . . . . . . . . . . >.{ C.D.} E.` . ", +". F.s G.g.X { .., . . . . . . . . . . . . . . | n.{ m.*.. . . . ", +". F.( R H.I.8.I J.u . . . . . . . . . . . . j.t.{ x J.% . . . . ", +". % K.L.k M.N.{ :.k . . . . . . . . . . . . O.P.{ h Q.m.N R.S.. ", +". . . + ) T.U.{ X 4.. . . . . . . . . . . V V.{ { &.x W.W.X.Y.E ", +". + V Z.`. +x 0 a { 4.k u . . . . . . S..+b.{ X o.$ _ e ++L Q q.", +"V @+~ b { b.5 J [.X X :.J.#+% . u $+Z.%+&+{ r *+k.E % . ` x.v.E ", +"=+-+I -+o.;+>+,+'+)+{ { I C.( y !+~+M { { { T { u.{+u . . . . . ", +"].S ]+^+S.3.k.;.I &.C.n Y { { { { { { M /+(+_+a.H 7 u ! . . . . ", +"` t ` + ' :+6 <+/+J.[+!.}+m.|+1+1+|+m.2+$+u , 3+I Y.% u . . . . ", +". . . . !.4+X :.5+j.. . j.*.6+h.n ].*.j.. . ' q { w . . . . . . ", +". . . . 7+M H E.u . . . . . 8+-+w.;+. . . . ! J a 9+. . . . . . ", +". . . . 0+i a+V . . . . . . 3.b+C D . . . . ` ]+I - . . . . . . ", +". . . . k c+A + . . . . . . ! 5+d+F.. . . . ! l =+^ . . . . . . ", +". . . . ! ` ! . . . . . . . . E q.E . . . . . . . . . . . . . . "}; diff --git a/mayaPlug/icons/shaveDefaultSwatch.png b/mayaPlug/icons/shaveDefaultSwatch.png Binary files differnew file mode 100644 index 0000000..b9c494c --- /dev/null +++ b/mayaPlug/icons/shaveDefaultSwatch.png diff --git a/mayaPlug/icons/shaveDefaultSwatch.xpm b/mayaPlug/icons/shaveDefaultSwatch.xpm new file mode 100644 index 0000000..5072686 --- /dev/null +++ b/mayaPlug/icons/shaveDefaultSwatch.xpm @@ -0,0 +1,135 @@ +/* XPM */ +static char *shaveDefaultSwatch[] = { +/* columns rows colors chars-per-pixel */ +"100 100 29 1", +" c #1c1c1c", +". c #232323", +"X c #2a2a2a", +"o c #333333", +"O c #3c3c3c", +"+ c #434343", +"@ c #4b4b4b", +"# c #535353", +"$ c #5c5c5c", +"% c #636363", +"& c #6a6a6a", +"* c #747474", +"= c #7c7c7c", +"- c #848484", +"; c #8c8c8c", +": c #949494", +"> c #9c9c9c", +", c #a2a2a2", +"< c #ababab", +"1 c #b2b2b2", +"2 c #bababa", +"3 c #c3c3c3", +"4 c #cbcbcb", +"5 c #d3d3d3", +"6 c #d9d9d9", +"7 c #e3e3e3", +"8 c #ededed", +"9 c #f2f2f2", +"0 c #fefefe", +/* pixels */ +"0090799988877877777777777777776777776776776777776776777777776776776776776777776776767776767875878700", +"0988<<>,<<<<<<<<<<<<<<<1<<<<<<1<<<1<<1<1<<1<<<1<<1<1<<<<<<1<<1<11<<1<11<1<<<1<1<<<<<<<1<1<<1<2788800", +"9997<>><><,<<<<11<<2111112112111111111112111111111111111111111112<1112<112111111211111112<1<<<788800", +"8875,>>,,,,<,<<<1<<<<1<1<<1<<1<1<1<1<1<1<1<1<1<1<1<11<11<1<1<1<1<1<11<1<<<1<<1<1<<<1<1<<<1<1<<678800", +"9752,>,><<<,<<,<<<<<<<<<<<1<<<<<1<<<<<<1<<<<<1<<<<<<<1<<<<<<<<<<1<<<1<<<<<<1<<<<<11<<<<11<<1<1787800", +"742,>>>,>>,<,<,<,<<<<<<<1<<<<1<<<<<<1<<<<<<1<<<<<1<<<<<<1<<<<1<1<<<1<<<<<<<<1<<<<<<1<1<<1<<<<<678800", +"64<:>>>>>>,,,,<,<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<111677800", +"62:=>>>>,,>,,<,,,<,,<<<<<<<,<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,<<<<<<<<<<<<<<<<<<<<<1<<<<1<,678800", +"42;*>>>>>>>,,,,,,<,,<<,<<<,<,<<,,,<,<,<<,<<,<<,<<,,<,<<,,<,<<,<<,<<<,<,<<<<,,<<,<,<<<<<<<<<<<<578800", +"52;*>:>>>>>>>,,,,<,<,,<,,,<<<,<<<<<<<,,<<,,<,<<,,<<<<,<<,<<<,<<,<<,<<,<<,,<<<<,<<<<,<,,<<<<<<,578800", +"52;*>::>>>>>>,,>,,,<<,<<,<<,,<<,<,,,,<,<,,<,<,<,,<<,,<<,,,<,,<,,,,,<,<,,,<<,,,,<,<,<<<<<<<,<<<577800", +"51:*:>>>>>>>>,>,,,,,,,,,,,,,,,,<,,<,<,,,<<,,,,,<,,,,,,,<,<,,,,,<<,,,,,,<,,,,<,,<,,<,,<,<,<,<<<678800", +"52:*>::::>>>>>,>>,,,,,,,,,,,,,,,>,,,,,,,>,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,<,,,<<<<,<678800", +"42:*:::>>>:>>>>>,,>,,,>,,,,>,,,>,,,,,,,,,,,,,>,,,,,>,,,>,,,,>,,,,,,,,,,,>,,,,,>,,,,<,,<,,<,,,<578800", +"42:*:::>::>:>>>>>>>,>>,,,>>,>,>,>,>,>>,,>,>,,>,>,>,>>,>,>,,>>,>,>,>,>,,>>,>,,,,>,,,,,<,,,,,<<<588800", +"52:*:::::>>>>>>>,>>>>,>,>,>,,>,,,>,>>,,>>,,>>,>,>,>,,>,>,>,>,>,>,>,>,>>,,>,>>,>,,,>,,,,,<<<,,<679800", +"52:=:::::>::>:>>>>>,>,>>,>>>>>>>>>,>>,>>,>,>>>>>>>>>>>>>>>>>>>>,,>>,>>>>>>>>>>>>>>>,,>,,,,,,,<678800", +"52:=:::::>>>>>>::>>>>>>,>>>,,>>,,>>>>>>,>>>>>,>>>,,>,>>,,>>,,>>>>>>>,>>,,>>,>,,>,,>,,,,,,,<<<,679800", +"52:=:>::::>:::>>,>>>>>,>>>>>>,>>>>,>>,>>>>>>,>>,>>>>>>>>>,>>,>>>>>>,>>,>>>>>>>>>>,>,>,,,><,,,,588800", +"52:=::::>::>>>>>>>>>>>,>,>>,>>>>>>>>>>>>,>,>>>>>>>,>,>>,>>>>>>>,,>>>>>>>,>>,>,>>>>,>,,,,><,<,<678800", +"52:=;:::>::::>>>>,>><:>>>>,>>,>,,>,>>,>,>>,>,>>,,>,>>>>>,>,>>,>>>>,>,>>,>>>>>,>,,,>,,,,,,,<,,<689800", +"52>=::::::>>>>:>>:*&<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,>,>>>>>>>>>>>>>>>>,>,>>>>>>>>,>,,,><<,,<678800", +"52:=:::::>>::>:>>*XX>,>,>,,>>>>>>,>,,>>,>,,>>>>>,>>>>,>>>>>>>>,>>,>>>>>>>>,>>>>,,>,,,,,,><,,<,679800", +"52:=:::::>:>:>>>>=..>>><>>>>>,>>>>>>>>>>>>>>,>>>>>,>>>>>,>,>>,>>>>,>>,>>>>>>>,>>>>,>,>,,,,,<,,688800", +"52:=::::::>:>>>>>-X.>>>,>>>>>>,>>>>>>>>,,,>>><>>>>>,>>>>,>>,>>,>>>,>>,:,>>>>>>>><>>,>>,,><,,<,679800", +"52:=:::::>:>:>:>>=XX,>>>,:>>>>>>,,><>>,,>>>,>>>>>>>>>>>>>>::>>>>>>>>>,>>,:>>><,>>>>,,,,<,,,<,<678800", +"52:=::::::>::>>>>-XX>>>,$%:-%@@$;2;&@+#*>,>>>:*%++@%-><>><>*#O#;;&$><:>>:&@@@%-<>>>,,,,,,<,,,<679800", +"52:=::::>>:>>:>>>-oo,>>>.o%@X...+:$X X.X%,>>-#X..X O*,>>-@. XX.#XX>>>>-OX.XX.O=,<>>,>,,><,<<,588800", +"52:=:::::::>:>>:>-Oo>>>,X.O@%**Xo+o&-&O.o:<>#.+%=*%o @<>,#.O&-*o. .>>>-OX#**&+.o-,>>,>>,><,,,,579800", +"52>=:::::>>::>>>>-OO>>>>oo#-<<>@XO*>>,-.o=>;+o=>>>>$.X>>=o+=>>>-O.X,>>%X%:,>>>% $><><>>,,,,<<<688800", +"52:=::::::>>>>>>>-+O>>>>++*>>:>*+@>,:>>oX=><=-<>>>>&oo>>*O#>>><>&oX,,:++;,>>><=XX;<><>,<,<,,,<679800", +"52:=::::>::>:>>:>-OO,>>>++=,><>*+$>>,>>OO->,,>>--*%+oO>:$@*,>>>>*+o>>:O@%$$%$#$o.-,>>>,,><,<,,588800", +"52:=::::>::>>>>>>-O+>,>>@@;>>>,*@$,>>,>@+=>,:%+#@#@++O>>#@*>>,,>-+o>>-@@@#@@@+Ooo=<><,,,,<,,,<679800", +"52:=::::::>:::>:>=OO>>>>@@->,>>=@$,>>>>O+-,>%+@$&*=*#O,,%+*>>>>>-@+>>;@@---:;---=>>>,>><>,,<,<678800", +"52:=:::::>:>>>>>,-++>>>>@+->>>>=@$>>>,>@+=>=+#->>>>=#+>>&+%>>:><*@+>>;##;>>>>>>>>>>>,>,,,<,<,<589800", +"52:=::::::>:>:>>>=O+>,>>OO->,>>*O#>>,>>++=>*+$>,>1>&@#><=++=,,>>#@@>,>%@&:<>>,;$*:>,><<>,,,,<,678800", +"52:=::::>>:>:>>:>-++>>:>@+->>>>*+$,>>,,@@=>=@#->>:*$@$:>>$o@->-&@@@>,>=$#*>,>;$X#:<>><<>,<,,,<679800", +"52>=:::::::>>>>>,-OO>>>>Oo=>,>>*o@>>>>>@@;,:#O#&&##&#@->>=+oO+@@$#+>>>>*@@@%$+O@-><>,,<>><,<,,688800", +"52:=:::::>:>>:>:>=@@>>,>OO->,>>=+#>>,>:@#=>,=%@##%->%@=,>,;&@+%**#@>>,,:*#O+++%-,,>,,>,,,,,<<,679800", +"52:=:::::::>:>>>>>>:<:>>>:<>>>,>>:<:>>1:>,>,,>:-;>>,>:>>>,>>>>>>:@#>>>>>>>;-->>>>>><,,<>><,,,<678800", +"52>=:::::>:>>:>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,>>>>,>*#&>>>>>*@$>>>>>>>>>,>>,>>,>,,,,,<,,<,679800", +"52:=:::::>>:>>>:>>>>>>>>,>,>>,>>>>>,>,,>>,>,,>>,>>>,,>>>=@@=>>>-@@*>>>>>>,>>>>>>>,,,>,,,,,,<,,688800", +"52:=::::::>::>>>>>>>>>>>>>>>>>>,>>>>>>>>>>>>>>>>>,>>>>,>>&@@###@@&:,>>>>>,>,>>>>>,>,,,,,>,,<,<579800", +"52:=::::>::>>:>>>>>>>>>>>,>>>,>>,>>,>,>>>>,>,>>,>>>>,>>>,:=&@@$&-:<>>>,>>>>>,>,>>>>,>,,,,<,,,<688800", +"52>=::::>::::>>>>>>>>>>>>,>,>>>>>>>>>,>,,>>>>>>>>>>>>>>>>,<>>>>,>,>>>>>>>>>>>>,>,>,>,,,,,<,<,<679800", +"52:=;::::>>>>>:>>>>>>,>,>>>>>,>>,>,>>>>>>>,>>,>>>,>,>,,>>>>>>,>>>>>>,,>,,>>,>,>>,>>,,,,,>,,<,,678800", +"52:=::::::>::>>>>>>>>>>>,>>,>,>>>>,>>>>,,>>>>,>,>>>>>>>>>>>>>>>,>>>>,>>>>>>>>>>>>>,,>,,,,,,,<,689800", +"52:*:::>>::>:>>>>>>>>>,>>>>>>>>>>>>>>,>>>>,>>>>>>>,>>>>>>>>>>>>>>>,>>>,>,>>,>>>>,>>,,,,,,<<,,<678800", +"52:=::::>:>>>>>>:>>>,:>>>>>>>,>>>>>,>>><>>>,>>>>>>,>>>>>>,,>><>>>,>>,>>,>>>>>>>>,>><>>,>><,,,,579800", +"52:=::::::::::>>>>>>>><>>>><>,>,>,>>,>>>>>>>,>>>,>>>>>>>>>>>>>>,>,>>>,>>>>>>>>,,>>,,>,<,,,<,<<688800", +"52:=:::::>==**>>>>>:>,>***;>>>==->>>>>>>>>>>>,>,,>>>>,>>>>>:=:>>>,>>>>>>,>,,>:>,>>,,,,<>><,<,<679800", +"52>=::::>:+..o;>>>>>>>*o.X%>,>@.&>,,>>>,>>,>>>>>>,>,,>>>><:+.&>>>>>>,>>,>>>>>>>>>,>>><,>,,,<,,678800", +"52:=::::;>+X..*>>:>>>>% X.&><:@.&:>>>>>>>>>>>>>>,>>>>>>,>,:@X&>>>>>,,,>>>>>>>>>,>>>>,<,,,<,,,<589800", +"52:=:::::>@Xo.$>>>>>,:@XOX%>><>>><>:>>,><>>>>,,>>>>>>>>>>>>>>>>>>>,>>>,>,>>>>,>,><,>>>><,,,<<,678800", +"52:=:::::>#O#oo:>>>>>-O@@o&>:>>>>>>>>>>>>>,>>>>>,>>>,,,>,>>,>>>>,>><,>>,>>,>>>>>>>>><<>,>,,<,<679800", +"52:=::::::$@&+.=>>,>>=+&$O&>>>*&->>>=#@+$*>,>>>>*%@+$*:,>,>*&->>-%&:-@O#*:>>,>>,&#+$-:*%,<,,,<588800", +"52:=::::;>$#**X$>>>>>$$:$+*>>>@X&,<$.X.X. @:>>>%.X.XX.@>>>>#X&,>-.o$o X .#>>:<;+..X..@o ,<,,<,679800", +"52:=:::::>%#*-oO>>>>:$$>%+&>>>#X&>=oo%*=&X &,,=oo%==$..&,>>#X&>>*X.+$=*+ o=>>:$X+&=*+. .>,,<,<678800", +"52:=::::::&$*:+X->>>=$*>$+&>>>@o*>&X#>,,:$O%>>*o#:<>:%O%>>,#o&>>-+o&;<,:o *>>-OX=>,,=O.X,<,,,<579800", +"52:=::::::&#*>%o$>>>%$-,$+*>>,#O&>*O@->>>>>>>>*++->>,>:<>>>#o&>,-++->>>,@X&>,&O#>,><>&oX><,<<,688800", +"52:=:::::>&#*,-O+>>;#$>>$+*>>,$+*>:%O+#&=><<,>-$@@@$=>>>,>>#O*>>-+@>>>>>#o&>>$+*,>>,>=Oo,<,,,<679800", +"52>=::::>>%#=>:+O;>=#&>>$+*>>>$+*>>-%@++O+$=>,>-$@@+O+&;,>>$+*>>;@@>>><>#+&>>#@*>><>>;oo>,,<<,678800", +"52:=::::>:&#*>>$O*>%#->>$+*>>,$@*>>,>;&#+Oo#;>>,>-&#Ooo+;>,$@*>>;++>>:>>$+*>>%@*<>>>,;+o,<,,,<589800", +"52:=::::::%#*>,*+#:@#>>>%+*,>>%#*>>>>,>>-*+O*,>>><>>:&+O*>,$@*,>-@+><>>>%+*>>*O%>>><>*+o><,<,,588800", +"52>=::::>:%@*>>>+@&@&>>>%O*>>>%@=>=@*>,>>,#O*>=@&>,>,>$O*>>%+*,>;+o>>>>>%+=>,-OO;<><>$+o,<,,<,688800", +"52:=:::::;$@*>>:%@$@*>>>$+*>>>%+*>-@#->>,-++-,-+$->>>=@O->>%+*>>-+O>>>,>%+=>,:$o@=>:*+oO>,,<,<679800", +"52:=::::::%@*>>>-@@@-<>>$+*>>>%+*>>%@@#%$+O%:<:%+#$%$++$>>>%@*>>;Oo>,>>>$+*>>>=+OoO+##Oo,,,,,,588800", +"52:=::::>:%@&::>;$#%:<>>#@*>>:%+=>>-*#@#@$&:,>,:&$@@##*:,>:%#*>,=++:>>>>%#=>>,,:%@@#=-@o,<,<<<579800", +"52>=:::::>;:>>>>,:>:>,>>,:>,><>>>>>>,>-;::>,>:,,>:--;:><>>,>:>>><>>,>>,>>:>>>>:<,>>>,-O+>,,<,<588800", +"52:=;:::>::>>>:>>>,>>,>>>>><>>>>>>>>>,>>>>>>>>>>,,,,><,>>>>>>>>,>,>>>>,>,,>>:*#%>><>>*+%,<,,<,679800", +"52:=::::>::>>>>>>>>>,,>>>>>>>>>>>,,,>>,>>>>>,,>>>>>>>>>>>>>>>,>>>>>>,>>>>>>>,-@+=:>>-@O*,<<,,,588800", +"52:*:>::::::>:>:>>>>>>>>>>>>>>>>>>>>>>,,>,>>>>>>>>>,>>>>,>,>>,>>>>>>>>>>>>,>>>&@@###$@%,>,,,<<579800", +"52:*;::::>:>:>:>>>>>>,>>>>>>,>>,>,>>,>,>,>,>>>>>>>>>,>>,>,>>>,>,>>>>>,>,>>>>,>>-$##$&->,,<,<,,688800", +"52:=::::::::>:>>>>>>>>>,,>>,>>>>>>>>>>>>>>>>>,>>>>,>>>>>>>>>>>>>>>>>>>>>>>>>>>>:<,>>><,,><,,,<678900", +"52:=;:;:>:>:>>>:>>>>>>>>>,>>,>>>>,,>,>>>>>>>>,>,>>>>>>>,>,,>,>,>>>>>,>>,>>>>><,>>>,,,,>>,<>,,,688900", +"52:=:::::::>::>>>>>>>>>,>>>>>>>,>>>>>>,>,>,>>>>>>,>>,>>>>>>>>>>>>,>>>>>>,>>>>>>>>>>,>,<<,<,<,<578800", +"52:=:::::::>:>>>>>>>>,>>,>>,>,>>>>>>>>,>>>,>,>>,>,,>>>>>>,>,>>>>>,>,>>>>>>,>>>>>,,<>>,,,><,,<,688900", +"52:=::::>::>:>:>>>>>>>>>>>>>>>>>,>,>,>>>>>>>>>>>>>>>,>>,>>>>>>,>>>>>,>,>>>>>>>>>>>,,,>>,,<,,,<678900", +"42:=;::::>::>:>>>>>>>>>>>>>>>>,>>>>>>>,>>,,>>>>>>>>>>>,>>>>>,>,>>>>>>>,>>>>>,>>,,>>>>,><,<,<,<588800", +"52:*:::::::>:>>:>>>>>>,>,>,>>,>>>,>>,>>>>>>>>>>>,>>,>>>>>,>,>>>>>,>,>>>>,>>>>>>>>>>,,,>>,,,,,,678900", +"52:=:::;:::::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>:>>:>>>>>>>>>>>>>>>>>>>>>>>>>>>>>:>>>>>>>,>,,,>,,<,778900", +"52:=::;:>::>>::::>>:>>:>>>>>>:>>>>>:>>>>>>>>>>>>>>>>>>>>>:>>>>>>>>:>>>>>>>>>>>>>>>>>>,>,,,,,,,689900", +"52:=;:;::::::::>>::>>>>::>>>:>>:>>>>>:>>>>>:>>>>:>>>>:>:>:>>:>:>>:>>:>:>:>>:>>>>>>>>>>>>>>,,,,789900", +"52>=;::;::::::>:::>:::>>>::>>>:>:>:>:>>:::>>:>:>>::>:>>::>:>>:>>:>>:>:>>>:>:>::>>>>>>>>>,>,>>>689900", +"52>=;:;:;::::::::::::>>:>:>::>::>:>::>:>:>>::>>::>:::>:>>:>:>:>:>:>:>::::::>:>:>>:>>>:>>>>,>,,789900", +"52>=:;;::;:;:;:::::::::::::::::>:::::::::::::::::::>>:::::::::::::::::>::>::::::>::>:>:>>>>>>>789000", +"62>=:;;;:;;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::>:>>>>,>>>699900", +"62>=;;::;;:;:;::::;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::>::>:>:>,>>>699900", +"62>=;;:;:;;;;:;::;;:;:;::::;::::::;;:::;:::::;:;::;::::;::;:;::;::::;:;:;:::::;:::::>::>:>>>>>799000", +"62>-:::;;:;:;;;:;:;;;:;;;::::;:;::::;::::;:::::::;::;:::::::;:::::::::;::::;:;:::::::::::>>>>>799000", +"62>=;-;-;;;;;;;;:;::::::;;:;;;;;;;;;;:;;;;;;;;;:;;:;;;;;;;;;;:;;;;;;:;;::;;:;;:;:::::::::>::>>890900", +"62>=::::;;;::;;;;;;;;-;;:;;:::;::;:::;;::;:;;::::;;:::;::;:::;:;;::;;;:;;:;::;;:;-;;:;:;::>:>>799000", +"52>-;;;;-;--;-;-;:;;:;;;-;-;--;;;;;;-;-;--;;;;--;-;---;;-;;;-;-;;-;-;;;-;-;-;-;-;;:::::::::>>>790000", +"52>-;;:-;;:;;;;;-;;-;--;:;:;:;:;;;;;:;:;:;:;;;:;:;::;:;;:;;;:;:;;:;:;;;:;::;;:;::;:;::::::::::800000", +"52,-;;;;-;;-;--;;-;;;;;--;;;;--;;;-;-;;;;--;;;;--;;-;-;;-;;;-;;;;;-;;;;-;-;-;;-;;:-:;;;;;:::>>790000", +"72>=*&&%&&&&&&&&&&%&%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%$%%%&=>22090000", +"63<:=**********=**=*=***=*=**************************=*****=*=***********************&*&*=;,26900000", +"641>:-------;-;-------------;--;-;;-;-;--;--;--;--;-----;------;--;-;-;--;--;--;--=-=====;>136900000", +"8522<>>>>>>>,>>,>>>>,>>>>>,,>>>>>>>,>>>>,>>>>>,>>,,>,>>>>>,,>>>>>>,>>>>>,>>>>>,>>>>>>>>>><1257990000", +"8653222121212112112121212112112121121121212121121<212121211211212112112121212112121222<1223579900000" +}; diff --git a/mayaPlug/icons/shaveDoubleArrowCursor.cur b/mayaPlug/icons/shaveDoubleArrowCursor.cur Binary files differnew file mode 100644 index 0000000..c832b4f --- /dev/null +++ b/mayaPlug/icons/shaveDoubleArrowCursor.cur diff --git a/mayaPlug/icons/shaveDoubleArrowCursor.xbm b/mayaPlug/icons/shaveDoubleArrowCursor.xbm new file mode 100644 index 0000000..46f37e5 --- /dev/null +++ b/mayaPlug/icons/shaveDoubleArrowCursor.xbm @@ -0,0 +1,4 @@ +#define shaveDoubleArrowCursor_width 16 +#define shaveDoubleArrowCursor_height 7 +static char shaveDoubleArrowCursor_bits[] = { + 0x20,0x04,0x38,0x1c,0x3e,0x7c,0xff,0xff,0x3e,0x7c,0x38,0x1c,0x20,0x04}; diff --git a/mayaPlug/icons/shaveDoubleArrowCursorOSX.xbm b/mayaPlug/icons/shaveDoubleArrowCursorOSX.xbm new file mode 100644 index 0000000..4a0390d --- /dev/null +++ b/mayaPlug/icons/shaveDoubleArrowCursorOSX.xbm @@ -0,0 +1,20 @@ +#define shaveDoubleArrowCursor_width 16 +#define shaveDoubleArrowCursor_height 16 +static char shaveDoubleArrowCursor_bits[] = { + 0x04,0x20, + 0x1c,0x38, + 0x7c,0x3e, + 0xff,0xff, + 0x7c,0x3e, + 0x1c,0x38, + 0x04,0x20, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00, + 0x00,0x00 +}; diff --git a/mayaPlug/isSharedCmd.cpp b/mayaPlug/isSharedCmd.cpp new file mode 100644 index 0000000..18881eb --- /dev/null +++ b/mayaPlug/isSharedCmd.cpp @@ -0,0 +1,80 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDagPath.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +#include "isSharedCmd.h" + +const MString isSharedCmd::commandName("isShared"); +const MString shaveRepair::commandName("shaveRepair"); +const MString shavePurge::commandName("shavePurge"); + + +isSharedCmd::isSharedCmd() {} +isSharedCmd::~isSharedCmd() {} + + +void* isSharedCmd::createCmd() +{ + return new isSharedCmd(); +} + + +MSyntax isSharedCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(false); + + syntax.setObjectType(MSyntax::kSelectionList, 1, 1); + syntax.useSelectionAsDefault(false); + + return syntax; +} + + +MStatus isSharedCmd::doIt(const MArgList& argList) +{ + MStatus status; + MArgDatabase args(syntax(), argList, &status); + + if (!status) return status; + + // + // Get the shaveNode to be operated on. + // + // Note that if no node was specified on the command line, then + // getObjects() will return whatever is currently selected. + // + MSelectionList selection; + args.getObjects(selection); + + MObject node(MObject::kNullObj); + selection.getDependNode(0, node); + + MFnDependencyNode nodeFn(node, &status); + + if (node.isNull() || !status) + { + status = MS::kInvalidParameter; + } + else + { + bool result = nodeFn.isShared(); + setResult(result); + } + + return status; +} + diff --git a/mayaPlug/isSharedCmd.h b/mayaPlug/isSharedCmd.h new file mode 100644 index 0000000..23ee5d6 --- /dev/null +++ b/mayaPlug/isSharedCmd.h @@ -0,0 +1,78 @@ +#ifndef isSharedCmd_h +#define isSharedCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +class isSharedCmd : public MPxCommand +{ +public: + isSharedCmd(); + virtual ~isSharedCmd(); + + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + + static void* createCmd(); + static MSyntax createSyntax(); + + static const MString commandName; + +private: +}; + + +inline bool isSharedCmd::isUndoable() const +{ return false; } + + +class shaveRepair : public MPxCommand +{ +public: + shaveRepair(){} + virtual ~shaveRepair(){} + + MStatus doIt( const MArgList& args ) + { + MGlobal::executeCommand("shave_Repair"); + return MStatus::kSuccess; + } + bool isUndoable() const {return false;} + + static void* createCmd() {return new shaveRepair();} + static MSyntax createSyntax() {return MSyntax();} + + static const MString commandName; + +private: +}; + +class shavePurge : public MPxCommand +{ +public: + shavePurge(){} + virtual ~shavePurge(){} + + MStatus doIt( const MArgList& args ) + { + MGlobal::executeCommand("shave_Purge"); + return MStatus::kSuccess; + } + bool isUndoable() const {return false;} + + static void* createCmd() {return new shavePurge();} + static MSyntax createSyntax() {return MSyntax();} + + static const MString commandName; + +private: +}; + +#endif + diff --git a/mayaPlug/libShave-vs11.vcxproj b/mayaPlug/libShave-vs11.vcxproj new file mode 100644 index 0000000..acde19b --- /dev/null +++ b/mayaPlug/libShave-vs11.vcxproj @@ -0,0 +1,383 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug20170|x64"> + <Configuration>Debug20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelDbg20170|x64"> + <Configuration>RelDbg20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20170|x64"> + <Configuration>Release20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{2CCDF057-5380-4CE8-ABD7-3D6B21032B49}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libShavevs10</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>libShave</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp1\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>libShave</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp1\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + <CustomBuildAfterTargets>Build</CustomBuildAfterTargets> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\debug\20170\</OutDir> + <TargetName>libShave</TargetName> + <CustomBuildAfterTargets>Build</CustomBuildAfterTargets> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSHAVEVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_ALLOW_MSC_VER_MISMATCH;_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2017\include;..\..\lib64;..\libexe\sample\include;$(SHAVE_RMAN_SDKS)\21.7\maya2017\win\include;..\vrayPlug\include36;$(AUTODESK_LOCATION)\Maya2017\include\qt;..\vrayPlug\plugin;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\..\raw64\release\;..\lib64;$(AUTODESK_LOCATION)\Maya2017\lib;$(SHAVE_RMAN_SDKS)\21.7\maya2017\win\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDK.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;libprman.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>VASSERT_ENABLED=1;_ALLOW_MSC_VER_MISMATCH;_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2017\include;..\..\lib64;..\libexe\sample\include;$(SHAVE_RMAN_SDKS)\21.7\maya2017\win\include;..\vrayPlug\include36;$(AUTODESK_LOCATION)\Maya2017\include\qt;..\vrayPlug\plugin;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\..\raw64\release\;..\lib64;$(AUTODESK_LOCATION)\Maya2017\lib;$(SHAVE_RMAN_SDKS)\21.7\maya2017\win\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDK.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;libprman.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + <CustomBuildStep> + <Command>copy /Y /B "$(TargetPath)" "$(AUTODESK_LOCATION)\Maya2017\bin"</Command> + <Outputs>$(AUTODESK_LOCATION)\Maya2017\bin\$(TargetFileName)</Outputs> + <Inputs>$(TargetPath)</Inputs> + </CustomBuildStep> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;_DEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2017\include;..\libexe\sample\include;$(SHAVE_RMAN_SDKS)\21.7\maya2017\win\include;..\vrayPlug\include2;$(AUTODESK_LOCATION)\Maya2017\include\qt;..\vrayPlug\plugin;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>Default</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\lib64;$(AUTODESK_LOCATION)\Maya2017\lib;$(SHAVE_RMAN_SDKS)\21.7\maya2017\win\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDKDEBUG.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;libprman.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + <IgnoreAllDefaultLibraries> + </IgnoreAllDefaultLibraries> + <IgnoreSpecificDefaultLibraries>LIBCMT.lib</IgnoreSpecificDefaultLibraries> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + <CustomBuildStep> + <Command>copy /Y /B "$(TargetPath)" "$(AUTODESK_LOCATION)\Maya2017\bin"</Command> + </CustomBuildStep> + <CustomBuildStep> + <Outputs>$(AUTODESK_LOCATION)\Maya2017\bin\$(TargetFileName)</Outputs> + <Inputs>$(TargetPath)</Inputs> + </CustomBuildStep> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="isSharedCmd.h" /> + <ClInclude Include="libShave.h" /> + <ClInclude Include="licensing.h" /> + <ClInclude Include="pluginImpl.h" /> + <ClInclude Include="shaveAPIimpl.h" /> + <ClInclude Include="shaveBackgroundShader.h" /> + <ClInclude Include="shaveBlindData.h" /> + <ClInclude Include="shaveBrushCtx.h" /> + <ClInclude Include="shaveBrushCtxCmd.h" /> + <ClInclude Include="shaveBrushManip.h" /> + <ClInclude Include="shaveCallbacks.h" /> + <ClInclude Include="shaveCheckObjectVisibility.h" /> + <ClInclude Include="shaveConstant.h" /> + <ClInclude Include="shaveCursorCtx.h" /> + <ClInclude Include="shaveCursorCtxCmd.h" /> + <ClInclude Include="shaveCutCtx.h" /> + <ClInclude Include="shaveCutCtxCmd.h" /> + <ClInclude Include="shaveDebug.h" /> + <ClInclude Include="shaveError.h" /> + <ClInclude Include="shaveExportGame.h" /> + <ClInclude Include="shaveGeometryOverride.h" /> + <ClInclude Include="shaveGlobals.h" /> + <ClInclude Include="shaveHairGeomIt.h" /> + <ClInclude Include="shaveHairShape.h" /> + <ClInclude Include="shaveHairUI.h" /> + <ClInclude Include="shaveIcon.h" /> + <ClInclude Include="shaveIconCmd.h" /> + <ClInclude Include="shaveInfo.h" /> + <ClInclude Include="shaveIO.h" /> + <ClInclude Include="shaveItHairImpl.h" /> + <ClInclude Include="shaveMaya.h" /> + <ClInclude Include="shaveMayaRenderer.h" /> + <ClInclude Include="shaveNode.h" /> + <ClInclude Include="shaveNodeCmd.h" /> + <ClInclude Include="shaveObjExporter.h" /> + <ClInclude Include="shavePack2TexCmd.h" /> + <ClInclude Include="shavePadCmd.h" /> + <ClInclude Include="ShavePerVertTexInfo.h" /> + <ClInclude Include="shaveProceduralsCmd.h" /> + <ClInclude Include="shaveRender.h" /> + <ClInclude Include="shaveRenderCallback.h" /> + <ClInclude Include="shaveRenderCmd.h" /> + <ClInclude Include="shaveRenderer.h" /> + <ClInclude Include="shaveSDK.h" /> + <ClInclude Include="shaveShadowNode.h" /> + <ClInclude Include="shaveStyleCmd.h" /> + <ClInclude Include="shaveTextureStore.h" /> + <ClInclude Include="shaveUtil.h" /> + <ClInclude Include="shaveUtilCmd.h" /> + <ClInclude Include="shaveVertexShader.h" /> + <ClInclude Include="shaveVolumeShader.h" /> + <ClInclude Include="shaveVrayCmd.h" /> + <ClInclude Include="shaveVrayNode.h" /> + <ClInclude Include="shaveVrayRenderer.h" /> + <ClInclude Include="shaveVraySharedFunctions.h" /> + <ClInclude Include="shaveWriteHairCmd.h" /> + <ClInclude Include="shaveWriteRib.h"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'">true</ExcludedFromBuild> + </ClInclude> + <ClInclude Include="shaveXPM.h" /> + </ItemGroup> + <ItemGroup> + <None Include="..\scripts\AEshaveGlobalsTemplate.mel" /> + <None Include="..\scripts\AEshaveHairTemplate.mel" /> + <None Include="..\scripts\AEshaveNodeTemplate.mel" /> + <None Include="..\scripts\shaveAEOverrides.mel" /> + <None Include="..\scripts\shaveBrush.mel" /> + <None Include="..\scripts\shaveBrushProperties.mel" /> + <None Include="..\scripts\shaveBrushSetModeButton.mel" /> + <None Include="..\scripts\shaveBrushValues.mel" /> + <None Include="..\scripts\shaveCursorCtxCommonProperties.mel" /> + <None Include="..\scripts\shaveCursorCtxCommonValues.mel" /> + <None Include="..\scripts\shaveCutProperties.mel" /> + <None Include="..\scripts\shaveCutValues.mel" /> + <None Include="..\scripts\shaveDiag.mel" /> + <None Include="..\scripts\shavePresetWin.mel" /> + <None Include="..\scripts\shaveRelationshipEditor.mel" /> + <None Include="..\scripts\shaveRenderman.mel" /> + <None Include="..\scripts\shaveRunTimeCommands.mel" /> + <None Include="..\scripts\shaveShelf.mel" /> + <None Include="..\scripts\shaveUI.mel" /> + <None Include="..\scripts\shaveVersion.mel" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="isSharedCmd.cpp" /> + <ClCompile Include="pluginImpl.cpp" /> + <ClCompile Include="shaveAPIimpl.cpp" /> + <ClCompile Include="shaveAPITestApp.cpp" /> + <ClCompile Include="shaveBackgroundShader.cpp" /> + <ClCompile Include="shaveBlindData.cpp" /> + <ClCompile Include="shaveBrushCtx.cpp" /> + <ClCompile Include="shaveBrushCtxCmd.cpp" /> + <ClCompile Include="shaveBrushManip.cpp" /> + <ClCompile Include="shaveCallbacks.cpp" /> + <ClCompile Include="shaveCheckObjectVisibility.cpp" /> + <ClCompile Include="shaveCursorCtx.cpp" /> + <ClCompile Include="shaveCursorCtxCmd.cpp" /> + <ClCompile Include="shaveCutCtx.cpp" /> + <ClCompile Include="shaveCutCtxCmd.cpp" /> + <ClCompile Include="shaveDebug.cpp" /> + <ClCompile Include="shaveExportGame.cpp" /> + <ClCompile Include="shaveGeometryOverride.cpp" /> + <ClCompile Include="shaveGlobals.cpp" /> + <ClCompile Include="shaveHairGeomIt.cpp" /> + <ClCompile Include="shaveHairShape.cpp" /> + <ClCompile Include="shaveHairShapeAttrs.cpp" /> + <ClCompile Include="shaveHairUI.cpp" /> + <ClCompile Include="shaveIcon.cpp" /> + <ClCompile Include="shaveIconCmd.cpp" /> + <ClCompile Include="shaveInfo.cpp" /> + <ClCompile Include="shaveItHairImpl.cpp" /> + <ClCompile Include="shaveMaya.cpp" /> + <ClCompile Include="shaveMayaRenderer.cpp" /> + <ClCompile Include="shaveNode.cpp" /> + <ClCompile Include="shaveNodeCmd.cpp" /> + <ClCompile Include="shaveObjExporter.cpp" /> + <ClCompile Include="shavePack2TexCmd.cpp" /> + <ClCompile Include="shavePadCmd.cpp" /> + <ClCompile Include="ShavePerVertTexInfo.cpp" /> + <ClCompile Include="shaveProceduralsCmd.cpp" /> + <ClCompile Include="shaveRender.cpp" /> + <ClCompile Include="shaveRenderCallback.cpp" /> + <ClCompile Include="shaveRenderCmd.cpp" /> + <ClCompile Include="shaveRenderer.cpp" /> + <ClCompile Include="shaveSDKCALLBACKS.cpp" /> + <ClCompile Include="shaveShadowNode.cpp" /> + <ClCompile Include="shaveStyleCmd.cpp" /> + <ClCompile Include="shaveTextureStore.cpp" /> + <ClCompile Include="shaveUtil.cpp" /> + <ClCompile Include="shaveUtilCmd.cpp" /> + <ClCompile Include="shaveVertexShader.cpp" /> + <ClCompile Include="shaveVolumeShader.cpp" /> + <ClCompile Include="shaveVrayCmd.cpp" /> + <ClCompile Include="shaveVrayNode.cpp" /> + <ClCompile Include="shaveVrayRenderer.cpp" /> + <ClCompile Include="shaveVraySharedFunctions.cpp" /> + <ClCompile Include="shaveWriteHairCmd.cpp" /> + <ClCompile Include="shaveWriteRib.cpp"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'">true</ExcludedFromBuild> + </ClCompile> + <ClCompile Include="shaveXPM.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/libShave-vs14.vcxproj b/mayaPlug/libShave-vs14.vcxproj new file mode 100644 index 0000000..b701612 --- /dev/null +++ b/mayaPlug/libShave-vs14.vcxproj @@ -0,0 +1,485 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug20170|x64"> + <Configuration>Debug20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug20180|x64"> + <Configuration>Debug20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelDbg20180|x64"> + <Configuration>RelDbg20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20170|x64"> + <Configuration>Release20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20180|x64"> + <Configuration>Release20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{2CCDF057-5380-4CE8-ABD7-3D6B21032B49}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libShavevs10</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>libShave</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp1\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20180\</OutDir> + <TargetName>libShave</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp1\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20180\</OutDir> + <TargetName>libShave</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp1\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\debug\20170\</OutDir> + <TargetName>libShave</TargetName> + <CustomBuildAfterTargets>Build</CustomBuildAfterTargets> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\debug\20180\</OutDir> + <TargetName>libShave</TargetName> + <CustomBuildAfterTargets>Build</CustomBuildAfterTargets> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSHAVEVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_ALLOW_MSC_VER_MISMATCH;_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2017\include;..\..\lib64;..\libexe\sample\include;..\vrayPlug\include2;$(AUTODESK_LOCATION)\Maya2017\include\qt;..\vrayPlug\plugin;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\..\raw64\release\;..\lib64;$(AUTODESK_LOCATION)\Maya2017\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDK.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;ri2rib-x64-vs14.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_ALLOW_MSC_VER_MISMATCH;_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2018\include;..\..\lib64;..\libexe\sample\include;..\vrayPlug\include36;$(AUTODESK_LOCATION)\Maya2018\include\qt;..\vrayPlug\plugin;$(SHAVE_RMAN_SDKS)\21.7\maya2018\win\include;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\..\raw64\release\;..\lib64;$(AUTODESK_LOCATION)\Maya2018\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDK.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_ALLOW_MSC_VER_MISMATCH;_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;VASSERT_ENABLED=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2018\include;..\..\lib64;..\libexe\sample\include;..\vrayPlug\include36;$(AUTODESK_LOCATION)\Maya2018\include\qt;..\vrayPlug\plugin;$(SHAVE_RMAN_SDKS)\21.7\maya2018\win\include;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\..\raw64\release\;..\lib64;$(AUTODESK_LOCATION)\Maya2018\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDK.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;_DEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2017\include;..\libexe\sample\include;..\vrayPlug\include2;$(AUTODESK_LOCATION)\Maya2017\include\qt;..\vrayPlug\plugin;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>Default</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\lib64;$(AUTODESK_LOCATION)\Maya2017\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDKDEBUG.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;ri2rib-x64-vs14.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + <IgnoreAllDefaultLibraries> + </IgnoreAllDefaultLibraries> + <IgnoreSpecificDefaultLibraries>LIBCMT.lib</IgnoreSpecificDefaultLibraries> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + <CustomBuildStep> + <Command>copy /Y /B "$(TargetPath)" "$(AUTODESK_LOCATION)\Maya2017\bin"</Command> + </CustomBuildStep> + <CustomBuildStep> + <Outputs>$(AUTODESK_LOCATION)\Maya2017\bin\$(TargetFileName)</Outputs> + <Inputs>$(TargetPath)</Inputs> + </CustomBuildStep> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;Bits64_;REQUIRE_IOSTREAM;WIN32;_DEBUG;_WINDOWS;NT_PLUGIN;_USRDLL;LIBSHAVE_EXPORTS;STANDALONESDK_STATIC;_WIN32_WINNT=0x0500;_ITERATOR_DEBUG_LEVEL=0;VASSERT_ENABLED=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;$(AUTODESK_LOCATION)\Maya2018\include;..\libexe\sample\include;$(SHAVE_RMAN_SDKS)\21.7\maya2018\win\include;..\vrayPlug\include36;$(AUTODESK_LOCATION)\Maya2018\include\qt;..\vrayPlug\plugin;..\procedurals\includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>Default</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>..\lib64;$(AUTODESK_LOCATION)\Maya2018\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>shaveSDKDEBUG.lib;ws2_32.lib;rpcrt4.lib;netapi32.lib;glu32.lib;OpenMaya.lib;OpenMayaAnim.lib;OpenMayaRender.lib;OpenMayaUI.lib;OpenMayaFX.lib;Image.lib;Foundation.lib;odbc32.lib;odbccp32.lib;opengl32.lib;ri2rib-x64-vs11.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies> + <IgnoreAllDefaultLibraries> + </IgnoreAllDefaultLibraries> + <IgnoreSpecificDefaultLibraries>LIBCMT.lib</IgnoreSpecificDefaultLibraries> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + <CustomBuildStep> + <Command>copy /Y /B "$(TargetPath)" "$(AUTODESK_LOCATION)\Maya2018\bin"</Command> + </CustomBuildStep> + <CustomBuildStep> + <Outputs>$(AUTODESK_LOCATION)\Maya2018\bin\$(TargetFileName)</Outputs> + <Inputs>$(TargetPath)</Inputs> + </CustomBuildStep> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="isSharedCmd.h" /> + <ClInclude Include="libShave.h" /> + <ClInclude Include="licensing.h" /> + <ClInclude Include="pluginImpl.h" /> + <ClInclude Include="shaveAPIimpl.h" /> + <ClInclude Include="shaveBackgroundShader.h" /> + <ClInclude Include="shaveBlindData.h" /> + <ClInclude Include="shaveBrushCtx.h" /> + <ClInclude Include="shaveBrushCtxCmd.h" /> + <ClInclude Include="shaveBrushManip.h" /> + <ClInclude Include="shaveCallbacks.h" /> + <ClInclude Include="shaveCheckObjectVisibility.h" /> + <ClInclude Include="shaveConstant.h" /> + <ClInclude Include="shaveCursorCtx.h" /> + <ClInclude Include="shaveCursorCtxCmd.h" /> + <ClInclude Include="shaveCutCtx.h" /> + <ClInclude Include="shaveCutCtxCmd.h" /> + <ClInclude Include="shaveDebug.h" /> + <ClInclude Include="shaveError.h" /> + <ClInclude Include="shaveExportGame.h" /> + <ClInclude Include="shaveGeometryOverride.h" /> + <ClInclude Include="shaveGlobals.h" /> + <ClInclude Include="shaveHairGeomIt.h" /> + <ClInclude Include="shaveHairShape.h" /> + <ClInclude Include="shaveHairUI.h" /> + <ClInclude Include="shaveIcon.h" /> + <ClInclude Include="shaveIconCmd.h" /> + <ClInclude Include="shaveInfo.h" /> + <ClInclude Include="shaveIO.h" /> + <ClInclude Include="shaveItHairImpl.h" /> + <ClInclude Include="shaveMaya.h" /> + <ClInclude Include="shaveMayaRenderer.h" /> + <ClInclude Include="shaveNode.h" /> + <ClInclude Include="shaveNodeCmd.h" /> + <ClInclude Include="shaveObjExporter.h" /> + <ClInclude Include="shavePack2TexCmd.h" /> + <ClInclude Include="shavePadCmd.h" /> + <ClInclude Include="ShavePerVertTexInfo.h" /> + <ClInclude Include="shaveProceduralsCmd.h" /> + <ClInclude Include="shaveRender.h" /> + <ClInclude Include="shaveRenderCallback.h" /> + <ClInclude Include="shaveRenderCmd.h" /> + <ClInclude Include="shaveRenderer.h" /> + <ClInclude Include="shaveSDK.h" /> + <ClInclude Include="shaveShadowNode.h" /> + <ClInclude Include="shaveStyleCmd.h" /> + <ClInclude Include="shaveTextureStore.h" /> + <ClInclude Include="shaveUtil.h" /> + <ClInclude Include="shaveUtilCmd.h" /> + <ClInclude Include="shaveVertexShader.h" /> + <ClInclude Include="shaveVolumeShader.h" /> + <ClInclude Include="shaveVrayCmd.h" /> + <ClInclude Include="shaveVrayNode.h" /> + <ClInclude Include="shaveVrayRenderer.h" /> + <ClInclude Include="shaveVraySharedFunctions.h" /> + <ClInclude Include="shaveWriteHairCmd.h" /> + <ClInclude Include="shaveWriteRib.h"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'">true</ExcludedFromBuild> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'">true</ExcludedFromBuild> + </ClInclude> + <ClInclude Include="shaveXPM.h" /> + </ItemGroup> + <ItemGroup> + <None Include="..\scripts\AEshaveGlobalsTemplate.mel" /> + <None Include="..\scripts\AEshaveHairTemplate.mel" /> + <None Include="..\scripts\AEshaveNodeTemplate.mel" /> + <None Include="..\scripts\shaveAEOverrides.mel" /> + <None Include="..\scripts\shaveBrush.mel" /> + <None Include="..\scripts\shaveBrushProperties.mel" /> + <None Include="..\scripts\shaveBrushSetModeButton.mel" /> + <None Include="..\scripts\shaveBrushValues.mel" /> + <None Include="..\scripts\shaveCursorCtxCommonProperties.mel" /> + <None Include="..\scripts\shaveCursorCtxCommonValues.mel" /> + <None Include="..\scripts\shaveCutProperties.mel" /> + <None Include="..\scripts\shaveCutValues.mel" /> + <None Include="..\scripts\shaveDiag.mel" /> + <None Include="..\scripts\shavePresetWin.mel" /> + <None Include="..\scripts\shaveRelationshipEditor.mel" /> + <None Include="..\scripts\shaveRenderman.mel" /> + <None Include="..\scripts\shaveRunTimeCommands.mel" /> + <None Include="..\scripts\shaveShelf.mel" /> + <None Include="..\scripts\shaveUI.mel" /> + <None Include="..\scripts\shaveVersion.mel" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="isSharedCmd.cpp" /> + <ClCompile Include="pluginImpl.cpp" /> + <ClCompile Include="shaveAPIimpl.cpp" /> + <ClCompile Include="shaveAPITestApp.cpp" /> + <ClCompile Include="shaveBackgroundShader.cpp" /> + <ClCompile Include="shaveBlindData.cpp" /> + <ClCompile Include="shaveBrushCtx.cpp" /> + <ClCompile Include="shaveBrushCtxCmd.cpp" /> + <ClCompile Include="shaveBrushManip.cpp" /> + <ClCompile Include="shaveCallbacks.cpp" /> + <ClCompile Include="shaveCheckObjectVisibility.cpp" /> + <ClCompile Include="shaveCursorCtx.cpp" /> + <ClCompile Include="shaveCursorCtxCmd.cpp" /> + <ClCompile Include="shaveCutCtx.cpp" /> + <ClCompile Include="shaveCutCtxCmd.cpp" /> + <ClCompile Include="shaveDebug.cpp" /> + <ClCompile Include="shaveExportGame.cpp" /> + <ClCompile Include="shaveGeometryOverride.cpp" /> + <ClCompile Include="shaveGlobals.cpp" /> + <ClCompile Include="shaveHairGeomIt.cpp" /> + <ClCompile Include="shaveHairShape.cpp" /> + <ClCompile Include="shaveHairShapeAttrs.cpp" /> + <ClCompile Include="shaveHairUI.cpp" /> + <ClCompile Include="shaveIcon.cpp" /> + <ClCompile Include="shaveIconCmd.cpp" /> + <ClCompile Include="shaveInfo.cpp" /> + <ClCompile Include="shaveItHairImpl.cpp" /> + <ClCompile Include="shaveMaya.cpp" /> + <ClCompile Include="shaveMayaRenderer.cpp" /> + <ClCompile Include="shaveNode.cpp" /> + <ClCompile Include="shaveNodeCmd.cpp" /> + <ClCompile Include="shaveObjExporter.cpp" /> + <ClCompile Include="shavePack2TexCmd.cpp" /> + <ClCompile Include="shavePadCmd.cpp" /> + <ClCompile Include="ShavePerVertTexInfo.cpp" /> + <ClCompile Include="shaveProceduralsCmd.cpp" /> + <ClCompile Include="shaveRender.cpp" /> + <ClCompile Include="shaveRenderCallback.cpp" /> + <ClCompile Include="shaveRenderCmd.cpp" /> + <ClCompile Include="shaveRenderer.cpp" /> + <ClCompile Include="shaveSDKCALLBACKS.cpp" /> + <ClCompile Include="shaveShadowNode.cpp" /> + <ClCompile Include="shaveStyleCmd.cpp" /> + <ClCompile Include="shaveTextureStore.cpp" /> + <ClCompile Include="shaveUtil.cpp" /> + <ClCompile Include="shaveUtilCmd.cpp" /> + <ClCompile Include="shaveVertexShader.cpp" /> + <ClCompile Include="shaveVolumeShader.cpp" /> + <ClCompile Include="shaveVrayCmd.cpp" /> + <ClCompile Include="shaveVrayNode.cpp" /> + <ClCompile Include="shaveVrayRenderer.cpp" /> + <ClCompile Include="shaveVraySharedFunctions.cpp" /> + <ClCompile Include="shaveWriteHairCmd.cpp" /> + <ClCompile Include="shaveWriteRib.cpp"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'">true</ExcludedFromBuild> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'">true</ExcludedFromBuild> + </ClCompile> + <ClCompile Include="shaveXPM.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/libShave.h b/mayaPlug/libShave.h new file mode 100644 index 0000000..1a96b45 --- /dev/null +++ b/mayaPlug/libShave.h @@ -0,0 +1,22 @@ +#ifndef libShave_h +#define libShave_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#ifdef WIN32 +# ifdef LIBSHAVE_EXPORTS +# define LIBSHAVE_API __declspec(dllexport) +# else +# define LIBSHAVE_API __declspec(dllimport) +# endif +#else +# define LIBSHAVE_API +#endif + +#include <maya/MObject.h> + +LIBSHAVE_API MStatus shaveInitializePlugin(MObject obj); +LIBSHAVE_API MStatus shaveUninitializePlugin(MObject obj); + +#endif diff --git a/mayaPlug/libShaveAPI-vs11.vcxproj b/mayaPlug/libShaveAPI-vs11.vcxproj new file mode 100644 index 0000000..1de6432 --- /dev/null +++ b/mayaPlug/libShaveAPI-vs11.vcxproj @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug20170|x64"> + <Configuration>Debug20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelDbg20170|x64"> + <Configuration>RelDbg20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20170|x64"> + <Configuration>Release20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libShaveAPIvs10</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>libShaveAPI</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp2\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>libShaveAPI</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp2\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(AUTODESK_LOCATION)\Maya2017\plug-ins\</OutDir> + <TargetName>libShaveAPI</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPIVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPIVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\release\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>VASSERT_ENABLED=1;AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\release\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;./tmp64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command>xcopy C:\git\shave7tmp\shave\mayaPlug\tmp64\*.* C:\git\shave7tmp\shave\raw64\release\20160\*.* /Y </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="shaveAPI.h" /> + <ClInclude Include="shaveItHair.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="shaveAPI.cpp" /> + <ClCompile Include="shaveItHair.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/libShaveAPI-vs11.vcxproj.filters b/mayaPlug/libShaveAPI-vs11.vcxproj.filters new file mode 100644 index 0000000..e7c5c08 --- /dev/null +++ b/mayaPlug/libShaveAPI-vs11.vcxproj.filters @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Headers"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Source"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="shaveAPI.h"> + <Filter>Headers</Filter> + </ClInclude> + <ClInclude Include="shaveItHair.h"> + <Filter>Headers</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="shaveAPI.cpp"> + <Filter>Source</Filter> + </ClCompile> + <ClCompile Include="shaveItHair.cpp"> + <Filter>Source</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/mayaPlug/libShaveAPI-vs14.vcxproj b/mayaPlug/libShaveAPI-vs14.vcxproj new file mode 100644 index 0000000..88692fb --- /dev/null +++ b/mayaPlug/libShaveAPI-vs14.vcxproj @@ -0,0 +1,323 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug20170|x64"> + <Configuration>Debug20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug20180|x64"> + <Configuration>Debug20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelDbg20180|x64"> + <Configuration>RelDbg20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20170|x64"> + <Configuration>Release20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20180|x64"> + <Configuration>Release20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libShaveAPIvs10</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>libShaveAPI</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp2\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20180\</OutDir> + <TargetName>libShaveAPI</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp2\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20180\</OutDir> + <TargetName>libShaveAPI</TargetName> + <IntDir>$(Platform)\$(Configuration)\tmp2\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(AUTODESK_LOCATION)\Maya2017\plug-ins\</OutDir> + <TargetName>libShaveAPI</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(AUTODESK_LOCATION)\Maya2018\plug-ins\</OutDir> + <TargetName>libShaveAPI</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPIVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPIVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\release\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2018\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2018\lib;..\raw64\release\20180\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2018\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2018\lib;..\raw64\release\20180\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;./tmp64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command>xcopy C:\git\shave7tmp\shave\mayaPlug\tmp64\*.* C:\git\shave7tmp\shave\raw64\release\20160\*.* /Y </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>AFTER2011;REQUIRE_IOSTREAM;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSHAVEAPI_EXPORTS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2018\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2018\lib;./tmp64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="shaveAPI.h" /> + <ClInclude Include="shaveItHair.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="shaveAPI.cpp" /> + <ClCompile Include="shaveItHair.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/mayaTypeIds.txt b/mayaPlug/mayaTypeIds.txt new file mode 100644 index 0000000..cd9caf1 --- /dev/null +++ b/mayaPlug/mayaTypeIds.txt @@ -0,0 +1,67 @@ +The following Maya type ids have been reserved for use by Epic Games +-------------------------------------------------------------------- + +0x00102998 shaveVertexShader +0x00102999 shaveSubNode +0x0010299A-0x001029A1 ??? +0x001029A2 shaveVolumeShader +0x001029A3 shaveBackgroundShader +0x001029A4 shaveMRHairGeom +0x001029A5 shaveMRHairIllum +0x001029A6 shaveMRRenderer / shaveMRVertexIllum +0x001029A7 shaveMRHairComp +0x001029A8 ??? +0x001029A9 shaveMRHairShadows / shaveMRHairShadowsVE +0x001029AA shaveMRHairGeomDRA +0x001029AB shaveMRRenderComp +0x001029AC-0x001029AD ??? +0x001029AE shaveMRSatData +0x001029AF-0x001029B2 ??? +0x001029B3 shaveMRShadowMatte +0x001029B4 shaveMRGeomPasser +0x001029B5 shaveMRInstanceGeom +0x001029B6 ??? +0x001029B7 shaveHairShape +0x001029B8 shaveBrushManip +0x001029B9 shaveVrayNode +0x001029BA-0x001029BB ??? +0x001029BC shaveShadowFilter +0x001029BD LBrushNode +0x001029BE LBrushBlindData +0x001029BF LBrushToolLocator +0x001029C0 LBrushStencilLocator +0x001029C1 LBrushSurfaceShape +0x001029C2 LBrushSurfaceCallback +0x001029C3 LBrushStencilCallback +0x001029C4 LBrushRefImageCallback +0x001029C5 LBrushRefImageLocator +0x001029C6 LBrushNeutral +0x001029C7 LBrushNeutralBlindData +0x001029C8 LBrushNode2 +0x001029C9 LBrushHelper +0x001029CA shaveMRHairOpacity +0x001029CB shaveBlower +0x001029CD shaveBlowerMgr +0x001029CE shaveBlowerData + +0x00106500 shaveNode +0x00106501 shaveGlobals +0x00106502 shaveShadowNode + + +The following type ids are invalid +---------------------------------- + +Duplicates: + +0x001029C0 LBrushSurfaceData + + +From the 0-7FFFF range reserved for internal plugins: + +0x00070031 LBrushHwShadowed + + +From the 0x80000 - 0xFFFFF range reserved for the Maya devkit: + +0x00083001 shaveBlindData diff --git a/mayaPlug/mkvrayexporter.sh b/mayaPlug/mkvrayexporter.sh new file mode 100644 index 0000000..6c9e1d6 --- /dev/null +++ b/mayaPlug/mkvrayexporter.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Shave and a Haircut +# (c) 2019 Epic Games +# US Patent 6720962 + +if [ "$2" = "" ]; then + echo "Usage: $0 vrayVersion numJobs [debugOpt]" + exit 1 +fi + +vrayVersion=$1 +numJobs=$2 +debugOpt=$3 +os=`../utils/getos.sh` +vrayTag=`../vrayPlug/getVersionTag.sh ${vrayVersion}` + +if [ "${os/.*/}" = "ce6" ]; then + isCentOS6=y +else + isCentOS6=n +fi + +if [ ${isCentOS6} = y -a ${vrayTag} -ge 40 ]; then + source /opt/rh/devtoolset-2/enable +fi + +make -f Makefile-vrayexporter.linux -j ${numJobs} VRAY_VERSION=${vrayVersion} ${debugOpt} diff --git a/mayaPlug/osxPkgDesc.template b/mayaPlug/osxPkgDesc.template new file mode 100644 index 0000000..3bd8f82 --- /dev/null +++ b/mayaPlug/osxPkgDesc.template @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IFPkgDescriptionDeleteWarning</key> + <string></string> + <key>IFPkgDescriptionDescription</key> + <string></string> + <key>IFPkgDescriptionTitle</key> + <string>Shave And A Haircut</string> + <key>IFPkgDescriptionVersion</key> + <string>%%shaveVersion%%</string> +</dict> +</plist> diff --git a/mayaPlug/osxPkgInfo.template b/mayaPlug/osxPkgInfo.template new file mode 100644 index 0000000..b423859 --- /dev/null +++ b/mayaPlug/osxPkgInfo.template @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleGetInfoString</key> + <string>ShaveHaircut %%shaveVersion%%</string> + <key>CFBundleName</key> + <string>Shave And A Haircut</string> + <key>CFBundleIdentifier</key> + <string>com.epicgames.shave.maya%%mayaMajor%%_%%mayaMinor%%.maya_plugin</string> + <key>CFBundleShortVersionString</key> + <string>%%shaveVersion%%</string> + <key>IFMajorVersion</key> + <integer>%%shaveMajor%%</integer> + <key>IFMinorVersion</key> + <integer>%%shaveMinor%%</integer> + <key>IFPkgFlagAllowBackRev</key> + <false/> + <key>IFPkgFlagAuthorizationAction</key> + <string>RootAuthorization</string> + <key>IFPkgFlagDefaultLocation</key> + <string>/</string> + <key>IFPkgFlagUseUserMask</key> + <true/> + <key>IFPkgFlagInstallFat</key> + <false/> + <key>IFPkgFlagIsRequired</key> + <false/> + <key>IFPkgFlagRelocatable</key> + <false/> + <key>IFPkgFlagRestartAction</key> + <string>NoRestart</string> + <key>IFPkgFlagRootVolumeOnly</key> + <false/> + <key>IFPkgFlagUpdateInstalledLanguages</key> + <false/> + <key>IFPkgFormatVersion</key> + <real>0.10000000149011612</real> +</dict> +</plist> diff --git a/mayaPlug/plugin.cpp b/mayaPlug/plugin.cpp new file mode 100644 index 0000000..6b4b602 --- /dev/null +++ b/mayaPlug/plugin.cpp @@ -0,0 +1,35 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +// +// Maya Bug Workaround +// ------------------- +// +// Even though libShave includes MFnPlugin.h, if we don't also include it +// here then MPxSurfaceShapes never fire their setDependentsDirty(). I +// don't know what that's so, but it is, as of Maya 7.0. +// +// Although the API docs say that MFnPlugin.h should only be include "once +// per plugin", to avoid symbol clashes, what they really mean is "once per +// shared object". Since this is in a separate shared object from libShave +// we can safely include it here as well. +// +#include <maya/MFnPlugin.h> + +#include <maya/MObject.h> +#include <maya/MStatus.h> + +#include "libShave.h" + +MStatus initializePlugin(MObject obj) +{ + return shaveInitializePlugin(obj); +} + + +MStatus uninitializePlugin(MObject obj) +{ + return shaveUninitializePlugin(obj); +} + diff --git a/mayaPlug/pluginImpl.cpp b/mayaPlug/pluginImpl.cpp new file mode 100644 index 0000000..b86107e --- /dev/null +++ b/mayaPlug/pluginImpl.cpp @@ -0,0 +1,509 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MCommonSystemUtils.h> +#include <maya/MFileIO.h> +#include <maya/MFnPlugin.h> +#include <maya/MGlobal.h> +#include <maya/MShaderManager.h> +#include <maya/MString.h> +#include <maya/MViewport2Renderer.h> + +#include "isSharedCmd.h" +#include "libShave.h" +#include "pluginImpl.h" +#include "shaveBackgroundShader.h" +#include "shaveBlindData.h" +#include "shaveBrushCtxCmd.h" +#include "shaveBrushManip.h" +#include "shaveCutCtxCmd.h" +#include "shaveDebug.h" +#include "shaveCallbacks.h" +#include "shaveGeometryOverride.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveHairUI.h" +#include "shaveIconCmd.h" +#include "shaveInfo.h" +#include "shaveNode.h" +#include "shaveNodeCmd.h" +#include "shavePadCmd.h" +#include "shaveRenderCmd.h" +#include "shaveSDK.h" +#include "shaveShadowNode.h" +#include "shaveStyleCmd.h" +#include "shaveTextureStore.h" +#include "shaveUtilCmd.h" +#include "shaveWriteHairCmd.h" +#include "shaveVertexShader.h" +#include "shaveVolumeShader.h" +/// vlad|22Apr2010 -- include vray stuff +#include "shaveVrayNode.h" +#include "shaveVrayCmd.h" +/// vlad|15Jun2011 -- procedurals +#include "shaveProceduralsCmd.h" +/// vlad|32apr2013 -- exp to image +#include "shavePack2TexCmd.h" +#include "shaveExportGame.h" + +#if !defined(OSMac_CFM_) && (!defined(_DEBUG) || defined(LINUX)) +#include "shaveWriteRib.h" +#endif + +#include <maya/MDrawRegistry.h> + +// We use these flags to keep track of what has been registered. If we +// get a failure partway through plugin initialization these will allow +// the plugin to be unloaded without incurring even more errors. +// +static bool blindShaveData_isRegistered = false; + +static bool isSharedCmd_isRegistered = false; +static bool shaveBrushCtxCmd_isRegistered = false; +static bool shaveCutCtxCmd_isRegistered = false; +static bool shaveIconCmd_isRegistered = false; +static bool shaveInfo_isRegistered = false; +static bool shaveNodeCmd_isRegistered = false; +static bool shavePadCmd_isRegistered = false; +static bool ShavePreprocTex_isRegistered = false; +static bool shaveRenderCmd_isRegistered = false; +static bool shaveStyleCmd_isRegistered = false; +static bool shaveUtilCmd_isRegistered = false; +static bool shaveWriteHairCmd_isRegistered = false; +#if !defined(OSMac_CFM_) && (!defined(_DEBUG) || defined(LINUX)) +static bool shaveWriteRib_isRegistered = false; +#endif +static bool shaveRepair_isRegistered = false; +static bool shavePurge_isRegistered = false; + +static bool shaveBackgroundShader_isRegistered = false; +static bool shaveBrushManip_isRegistered = false; +static bool shaveGlobals_isRegistered = false; +static bool shaveHairShape_isRegistered = false; +static bool shaveNode_isRegistered = false; +static bool shaveShadowNode_isRegistered = false; +static bool shaveVertexShader_isRegistered = false; +static bool shaveVolumeShader_isRegistered = false; + + +static void popupError(MString title, MString msg) +{ + MGlobal::displayError(msg); + +#if 0 + // If you once had Shave hair in a scene but no longer do it's still + // possible to end up with a 'requires' command in the scene file + // which loads Shave. If you later try to edit that scene on a machine + // which doesn't have Shave properly installed, you'll get these error + // msgs. + // + // Users can live with the error msgs as they are used to them from + // other plugins that fail their 'requires'. However they dislike + // having to dismiss dialog boxes to continue working, so we've now + // ifdefed out the popup dialogs + + if (MGlobal::mayaState() == MGlobal::kInteractive) + { + MString cmd; + + cmd = MString("confirmDialog -title \"") + title + + "\" -button \"OK\" -message \"" + msg + "\""; + + MGlobal::executeCommand(cmd); + } +#endif +} + + +// +// Plugin Registration +// +MStatus shaveInitializePlugin(MObject obj) +{ + MStatus st; + MString msg; + MFnPlugin plugin(obj, "2019 Epic Games Maya Shave.", "1.1", "Any"); + + SHAVEinit(); + + // Before we go any further, let's make sure that the version of the + // scripts matches the version of the plugin. + // + MString pluginVersion(SHAVEquery_version()); + int haveVersionCmd; + + MGlobal::executeCommand("exists shaveVersion", haveVersionCmd); + + // Let's say that the user starts Maya and tries to load shave but + // finds a mismatch. They leave Maya running while they copy the + // correct scripts into their script directory. They then try loading + // the plugin again. However, Maya will still have the old version of + // the 'shaveVersion' command stored in memory. So we have to source + // it to be sure that we get the new one. + // + if (haveVersionCmd && !MGlobal::executeCommand("source shaveVersion")) + haveVersionCmd = false; + + if (!haveVersionCmd) + { + msg = "Cannot find the 'shaveVersion.mel' script. Shave appears\\n" + "to not be properly installed on this machine. Please try\\n" + "reinstalling Shave. If the problem persists then send mail\\n" + "to [email protected]"; + + popupError("Shave Installation Error", msg); + + return MS::kFailure; + } + + MString scriptVersion; + + MGlobal::executeCommand("shaveVersion", scriptVersion); + + if (scriptVersion != pluginVersion) + { + msg = MString("There is a mismatch between the Shave plugin, which is") + + " for version " + pluginVersion + " of Shave, and the Shave" + + " scripts, which are for version " + scriptVersion + " of Shave."; + + popupError("Shave Version Mismatch", msg); + + return MS::kFailure; + } + + // + // Register Data Types. + // + // Must be done before registering any nodes which use them. + // + REGISTER_DATA(blindShaveData) + + // + // Register Nodes. + // + st = plugin.registerShape( shaveHairShape::nodeTypeName, + shaveHairShape::id, + &shaveHairShape::creator, + &shaveHairShape::initialize, + &shaveHairUI::creator, + &shaveHairShape::drawDbClassification ); + + if (!st) + st.perror("register shaveHairShape."); + else + shaveHairShape_isRegistered = true; + + st = plugin.registerNode( shaveBrushManip::nodeTypeName, + shaveBrushManip::id, + &shaveBrushManip::creator, + &shaveBrushManip::initialize, + MPxNode::kManipContainer, + &shaveBrushManip::drawDbClassification ); + + if (!st) + st.perror("register shave Brush."); + else + shaveBrushManip_isRegistered = true; + + REGISTER_NODE(shaveGlobals, kDependNode, 0) + REGISTER_NODE(shaveShadowNode, kDependNode, 0) + + // + // We need this to be able to load and convert pre-4.0 scenes. + // + REGISTER_NODE(shaveNode, kDependNode,0) + + // + // The following are all render nodes, but only shaveBackgroundShader + // is accessible to users. By not giving the others classification + // strings they won't show up in the Hypershade window and users + // won't be tempted to use them. + // + MString classification("shader/surface"); + + REGISTER_NODE(shaveBackgroundShader, kDependNode, &classification) + REGISTER_NODE(shaveVertexShader, kDependNode, 0) + REGISTER_NODE(shaveVolumeShader, kDependNode, 0) + + // + // Register Commands. + // + REGISTER_CMD(shaveRepair) + REGISTER_CMD(shavePurge) + REGISTER_CMD(isSharedCmd) + REGISTER_CMD(shaveIconCmd) + REGISTER_CMD(shaveInfo); + REGISTER_CMD(shaveNodeCmd) + REGISTER_CMD(shavePadCmd) + REGISTER_CMD(ShavePreprocTex) + REGISTER_CMD(shaveRenderCmd) + REGISTER_CMD(shaveStyleCmd) + REGISTER_CMD(shaveUtilCmd) + REGISTER_CMD(shaveWriteHairCmd) +#if !defined(OSMac_CFM_) && (!defined(_DEBUG) || defined(LINUX)) + REGISTER_CMD(shaveWriteRib) +#endif + + + REGISTER_CTX_CMD(shaveBrushCtxCmd); + REGISTER_CTX_CMD(shaveCutCtxCmd); + +#ifdef USE_VRAY + // vlad|22Apr2010 - register stuff needed by V-Ray + //register the node + const MString vclassification(shaveVrayNode::classification); + st = plugin.registerNode( shaveVrayNode::typeName, + shaveVrayNode::typeId, + shaveVrayNode::creator, + shaveVrayNode::initialize, + MPxNode::kDependNode, + &vclassification); + if (!st) + st.perror("register shaveVrayNode."); + + //register command + st = plugin.registerCommand(shaveVrayCmd::cmd, + shaveVrayCmd::creator, + shaveVrayCmd::newSyntax); + if (!st) + st.perror("register shaveVrayCmd."); + +#endif + +#ifdef USE_PROCEDURALS + st = plugin.registerCommand(shaveProceduralsCmd::cmd, + shaveProceduralsCmd::creator, + shaveProceduralsCmd::newSyntax); + if (!st) + st.perror("register shaveProceduralsCmd."); +#endif + + st = MHWRender::MDrawRegistry::registerGeometryOverrideCreator( + shaveHairShape::drawDbClassification, + shaveHairShape::drawRegistrantId, + shaveGeometryOverride::Creator); + if (!st) + st.perror("register shaveGeometryOverride."); + +#if MAYA_API_VERSION >= 201600 + st = shaveGeometryOverride::registerComponentConverters(); + if (!st) + { + st.perror("register shaveGeometryOverride component converters"); + } +#endif + + st = MHWRender::MDrawRegistry::registerGeometryOverrideCreator( + shaveBrushManip::drawDbClassification, + shaveBrushManip::drawRegistrantId, + shaveBrushOverride::Creator); + if (!st) + st.perror("register shaveBrushOverride."); + + //register command + st = plugin.registerCommand(shavePack2TexCmd::cmd, + shavePack2TexCmd::creator, + shavePack2TexCmd::newSyntax); + if (!st) + st.perror("register shavePack2TexCmd."); + + st = plugin.registerCommand(shaveExportGame::cmd, + shaveExportGame::creator, + shaveExportGame::newSyntax); + if (!st) + st.perror("register shaveExportGame."); + + if (MGlobal::mayaState() == MGlobal::kInteractive) + { + // Register selection type for the entire hair object. + // + int meshPriority = MSelectionMask::getSelectionTypePriority("polymesh"); + + if (!MSelectionMask::registerSelectionType(shaveHairShape::kShapeSelectionMaskName, meshPriority)) + { + std::cerr << "Error registering '" + << shaveHairShape::kShapeSelectionMaskName + << "' selection type." << std::endl; + } + + // Add it to Maya's 'Surface' component menu. + // + st = MGlobal::executeCommand( + MString("addSelectTypeItem(\"Surface\", \"") + + shaveHairShape::kShapeSelectionMaskName + + "\", \"Shave Hair\")" + ); + if (!st) + { + st.perror("addSelectTypeItem"); + } + } + + // Normally, we initialize SHAVE just before starting a new scene. + // However, the plugin will typically be loaded into a scene after it + // has already been started, so we have to force the initialization + // here. + // + shaveCallbacks::prepareForNewScene(true); + + // Allow our CFGX shaders (and anyone else's, really) to be relocated + // to the directory set in the SHAVE_SHADER_PATH envariable. + // + MString shaderPath = MCommonSystemUtils::getEnv("SHAVE_SHADER_PATH"); + + if (shaderPath.length() > 0) + { + MHWRender::MRenderer::theRenderer()->getShaderManager()->addShaderPath(shaderPath); + } + + // We used to call the 'shaveUI' command ('shaveInit', actually) + // directly, but that caused problems when the plugin was loaded + // via a referenced file because the so-called "permanent" nodes, + // such as defaultRenderGlobals, might not yet exist which then led to + // many problems. By using registerUI() we're guaranteed that Maya + // will have finished its own initialization before we do ours. + // + // However, even while disabled, the nodes can still call certain + // basic MEL utility procedures, such as shaveExpandTempFilePath(), + // so we must still force the sourcing of 'shaveUI.mel' right away, + // rather than waiting until Maya calls shaveUI() for us. + MGlobal::executeCommand("source shaveUI.mel"); //goes up 06may2014 + plugin.registerUI("shaveUI", "", "shaveUI", ""); + + return st; +} + + +// +// Plugin Registration +// +MStatus shaveUninitializePlugin(MObject obj) +{ + MStatus st; + MFnPlugin plugin(obj); + + shaveCallbacks::mayaExiting(); + + // + // Remove all callbacks. + // + shaveCallbacks::removeCallbacks(); + + // We don't want to delete our hotkeySet in case the user has made + // changes to it. But we should make sure that it's not the current + // set. + // + MString keySet; + MGlobal::executeCommand("hotkeySet -q -current", keySet); + + if (keySet == "Shave_Hotkeys") + { + MGlobal::executeCommand("hotkeySet -e -current Maya_Default"); + } + +#if MAYA_API_VERSION >= 201600 + st = shaveGeometryOverride::deregisterComponentConverters(); + if (!st) + { + st.perror("deregistering shaveGeometryOverride component converters"); + } +#endif + + st = MHWRender::MDrawRegistry::deregisterGeometryOverrideCreator( + shaveHairShape::drawDbClassification, + shaveHairShape::drawRegistrantId); + if (!st) + st.perror("deregister shaveGeometryOverride."); + + st = MHWRender::MDrawRegistry::deregisterGeometryOverrideCreator( + shaveBrushManip::drawDbClassification, + shaveBrushManip::drawRegistrantId); + if (!st) + st.perror("deregister shaveBrushOverride."); + + // Deregister the various elements in the reverse order to which they + // were registered. (Reversing the order may not be essential but is + // generally the best approach to avoid problems.) + // + + if (MGlobal::mayaState() == MGlobal::kInteractive) + { + // Custom selection types. + // + st = MGlobal::executeCommand( + MString("deleteSelectTypeItem(\"Surface\", \"") + + shaveHairShape::kShapeSelectionMaskName + "\")" + ); + if (!st) + { + st.perror("deleteSelectTypeItem"); + } + + if (!MSelectionMask::deregisterSelectionType(shaveHairShape::kShapeSelectionMaskName)) + { + std::cerr << "Error deregistering '" + << shaveHairShape::kShapeSelectionMaskName + << "' selection type." << std::endl; + } + } + + DEREGISTER_CTX_CMD(shaveCutCtxCmd); + DEREGISTER_CTX_CMD(shaveBrushCtxCmd); + + DEREGISTER_CMD(shaveRepair) + DEREGISTER_CMD(shavePurge) +#if !defined(OSMac_CFM_) && (!defined(_DEBUG) || defined(LINUX)) + DEREGISTER_CMD(shaveWriteRib) +#endif + DEREGISTER_CMD(shaveWriteHairCmd) + DEREGISTER_CMD(shaveUtilCmd) + DEREGISTER_CMD(shaveStyleCmd) + DEREGISTER_CMD(shaveRenderCmd) + DEREGISTER_CMD(ShavePreprocTex) + DEREGISTER_CMD(shavePadCmd) + DEREGISTER_CMD(shaveNodeCmd) + DEREGISTER_CMD(shaveInfo) + DEREGISTER_CMD(shaveIconCmd) + DEREGISTER_CMD(isSharedCmd) + + DEREGISTER_MANIP(shaveBrushManip) + + DEREGISTER_NODE(shaveVolumeShader) + DEREGISTER_NODE(shaveVertexShader) + DEREGISTER_NODE(shaveBackgroundShader) + DEREGISTER_NODE(shaveNode) + DEREGISTER_NODE(shaveShadowNode) + DEREGISTER_NODE(shaveGlobals) + DEREGISTER_NODE(shaveHairShape) + + DEREGISTER_DATA(blindShaveData); + +#ifdef USE_VRAY + // vlad|22Apr2010 - deregister stuff needed by V-Ray + st = plugin.deregisterNode( shaveVrayNode::typeId ); + if (!st) + st.perror("deregister shaveVrayNode."); + + st = plugin.deregisterCommand( shaveVrayCmd::cmd ); + if (!st) + st.perror("deregister shaveVrayCmd."); +#endif + +#ifdef USE_PROCEDURALS + st = plugin.deregisterCommand( shaveProceduralsCmd::cmd ); + if (!st) + st.perror("deregister shaveProceduralsCmd."); +#endif + + st = plugin.deregisterCommand( shavePack2TexCmd::cmd ); + if (!st) + st.perror("deregister shavePack2TexCmd."); + + st = plugin.deregisterCommand( shaveExportGame::cmd ); + if (!st) + st.perror("deregister shaveExportGame."); + + return st; +} + diff --git a/mayaPlug/pluginImpl.h b/mayaPlug/pluginImpl.h new file mode 100644 index 0000000..cb5fda1 --- /dev/null +++ b/mayaPlug/pluginImpl.h @@ -0,0 +1,181 @@ +#ifndef pluginImpl_h +#define pluginImpl_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +// +// Some macros to make the source code a bit more readable. +// +#define REGISTER_CMD(cmd) \ + st = plugin.registerCommand( \ + cmd::commandName, cmd::createCmd, cmd::createSyntax \ + ); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("registering the '") + cmd::commandName \ + + "' command: " + st.errorString() \ + ); \ + shaveUninitializePlugin(obj); \ + RETURN(st); \ + } \ + cmd##_isRegistered = true; + + +#define DEREGISTER_CMD(cmd) \ + if (cmd##_isRegistered) \ + { \ + cmd##_isRegistered = false; \ + st = plugin.deregisterCommand(cmd::commandName); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("deregistering the '") + cmd::commandName \ + + "' command: " + st.errorString() \ + ); \ + RETURN(st); \ + } \ + } + + +#define REGISTER_CTX_CMD(cmd) \ + st = plugin.registerContextCommand( \ + cmd::commandName, cmd::createCmd \ + ); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("registering the '") + cmd::commandName \ + + "' context command: " + st.errorString() \ + ); \ + shaveUninitializePlugin(obj); \ + RETURN(st); \ + } \ + cmd##_isRegistered = true; + + +#define DEREGISTER_CTX_CMD(cmd) \ + if (cmd##_isRegistered) \ + { \ + cmd##_isRegistered = false; \ + st = plugin.deregisterContextCommand(cmd::commandName); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("deregistering the '") + cmd::commandName \ + + "' context command: " + st.errorString() \ + ); \ + RETURN(st); \ + } \ + } + + +#define REGISTER_DATA(data) \ + st = plugin.registerData(data::dataTypeName, data::dataTypeId, data::creator); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("registering the '") + data::dataTypeName \ + + "' data type: " + st.errorString() \ + ); \ + RETURN(st); \ + } \ + data##_isRegistered = true; + + +#define DEREGISTER_DATA(data) \ + st = plugin.deregisterData(data::dataTypeId); \ + if (!st) \ + { \ + data##_isRegistered = false; \ + MGlobal::displayError( \ + MString("deregistering the '") + data::dataTypeName \ + + "' data type: " + st.errorString() \ + ); \ + RETURN(st); \ + } \ + +#define REGISTER_NODE(node,type,classification) \ + st = plugin.registerNode( \ + node::nodeTypeName, \ + node::id, \ + node::creator, \ + node::initialize, \ + MPxNode::type, \ + classification \ + ); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("registering the '") + node::nodeTypeName \ + + "' node: " + st.errorString() \ + ); \ + shaveUninitializePlugin(obj); \ + RETURN(st); \ + } \ + node##_isRegistered = true; \ + + +#define DEREGISTER_NODE(node) \ + if (node##_isRegistered) \ + { \ + node##_isRegistered = false; \ + st = plugin.deregisterNode(node::id); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("deregistering the '") + node::nodeTypeName \ + + "' node: " + st.errorString() \ + ); \ + RETURN(st); \ + } \ + } + + +#define REGISTER_MANIP(node) \ + st = plugin.registerNode( \ + node::nodeTypeName, \ + node::id, \ + node::creator, \ + node::initialize, \ + MPxNode::kManipContainer \ + ); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("registering the '") + node::nodeTypeName \ + + "' manip: " + st.errorString() \ + ); \ + shaveUninitializePlugin(obj); \ + RETURN(st); \ + } \ + node##_isRegistered = true; \ + +#define DEREGISTER_MANIP(node) DEREGISTER_NODE(node) + + +#define REGISTER_SHAPE(node,ui) \ + st = plugin.registerShape( \ + node::nodeTypeName, \ + node::id, \ + node::creator, \ + node::initialize, \ + ui::creator \ + ); \ + if (!st) \ + { \ + MGlobal::displayError( \ + MString("registering the '") + node::nodeTypeName \ + + "' shape: " + st.errorString() \ + ); \ + shaveUninitializePlugin(obj); \ + RETURN(st); \ + } \ + node##_isRegistered = true; \ + + +#define DEREGISTER_SHAPE(node) DEREGISTER_NODE(node) + +#endif diff --git a/mayaPlug/shaveAPI.cpp b/mayaPlug/shaveAPI.cpp new file mode 100644 index 0000000..242b951 --- /dev/null +++ b/mayaPlug/shaveAPI.cpp @@ -0,0 +1,127 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MObjectArray.h> +#include <maya/MStatus.h> +#include <maya/MString.h> + +#include "shaveAPI.h" +#include "shaveAPIimpl.h" + +#include <assert.h> + +#ifdef _WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + + +MStatus shaveAPI::exportAllHair( + shaveAPI::HairInfo* hairInfo, bool renderableOnly +) +{ + return shaveAPIimpl::exportAllHair(hairInfo, renderableOnly); +} + + +#if 0 +MStatus shaveAPI::exportDRAFile(MString filename) +{ + return shaveAPIimpl::exportDRAFile(filename); +} +#endif + + +MStatus shaveAPI::exportHair( + MObjectArray& shaveNodes, shaveAPI::HairInfo* hairInfo +) +{ + + MStatus stat = shaveAPIimpl::exportHair(shaveNodes, hairInfo); +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + return stat; +} + + +MStatus shaveAPI::exportOcclusions( + shaveAPI::SceneGeom* hairOcclusions, + shaveAPI::SceneGeom* shadowOcclusions +) +{ + return shaveAPIimpl::exportOcclusions(hairOcclusions, shadowOcclusions); +} + + +void shaveAPI::freeHairInfo(shaveAPI::HairInfo* hairInfo) +{ + hairInfo->clear(); +} + + +void shaveAPI::initHairInfo(shaveAPI::HairInfo* hairInfo) +{ +} + + +//****************************************** +// +// HairInfo Interfaces +// +//****************************************** + +shaveAPI::HairInfo::HairInfo() +{ + shaveAPIimpl::initHairInfo(this); +} + + +shaveAPI::HairInfo::~HairInfo() +{ + clear(); +} + + +void shaveAPI::HairInfo::clear() +{ + shaveAPIimpl::freeHairInfo(this); +} + + +//****************************************** +// +// SceneGeom Interfaces +// +//****************************************** + +shaveAPI::SceneGeom::SceneGeom() +{ + shaveAPIimpl::initSceneGeom(this); +} + + +shaveAPI::SceneGeom::~SceneGeom() +{ + clear(); +} + + +void shaveAPI::SceneGeom::clear() +{ + shaveAPIimpl::freeSceneGeom(this); +} diff --git a/mayaPlug/shaveAPI.h b/mayaPlug/shaveAPI.h new file mode 100644 index 0000000..fee8e28 --- /dev/null +++ b/mayaPlug/shaveAPI.h @@ -0,0 +1,331 @@ +#ifndef shaveAPI_h +#define shaveAPI_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#define SHAVE_API_VERSION 4 + +#ifdef WIN32 +# ifdef LIBSHAVEAPI_EXPORTS +# define LIBSHAVEAPI_API __declspec(dllexport) +# else +# define LIBSHAVEAPI_API __declspec(dllimport) +# endif +#else +# define LIBSHAVEAPI_API +#endif + +#include <maya/MStatus.h> +#include <maya/MString.h> + +#if MAYA_API_VERSION < 20180000 +class MObjectArray; +#endif + + +class LIBSHAVEAPI_API shaveAPI +{ +public: + typedef struct + { + float x; + float y; + float z; + } Vertex; + + typedef struct + { + float r; + float g; + float b; + } Color; + + class LIBSHAVEAPI_API HairInfo + { + public: + HairInfo(); + ~HairInfo(); + + // + // Free up all storage allocated by a call to shaveExportHair(). + // + // Note that a HairInfo object is automatically cleared when it is + // destroyed. + // + void clear(); + + // + // Number of unique vertices for all hairs in the structure. + // + int numVertices; + + // + // Total number of hairs in the structure. + // + int numHairs; + + // + // Total number of hair vertices in the structure. + // + // If several hairs share a vertex, it will only be counted once in + // 'numVertices', but will be counted once for each hair in + // 'numHairVertices'. + // + int numHairVertices; + + // + // 'hairVertices' is an array of indices into the 'vertices' array + // and contains one entry for each vertex of each hair. A hair's + // vertices run up the center of the hair. + // + // The indices for hair N start at 'hairStartIndices[N]' and end at + // 'hairEndIndices[N] - 1'. <-- NOTE THE -1 !!! + // + // 'hairVertices' will have 'numHairVertices' elements + // 'hairStartIndices' will have 'numHairs' elements. + // 'hairEndIndices' will have 'numHairs' elements. + // + int *hairVertices; + int *hairStartIndices; + int *hairEndIndices; + + // + // Material characteristics. + // + // Each array has one entry per hair. + // + float* opacities; + float* speculars; + float* glosses; + float* ambDiffs; + Color* rootColors; + Color* tipColors; + float* rootRadii; + float* tipRadii; + + // + // 'vertices' is an array of all of the vertices in all of the + // exported hair nodes. + // + // It contains 'numVertices' entries. + // + Vertex* vertices; + + // + // 'velocities' contains the corresponding velocities for each + // vertex in the 'vertices' array. + // + Vertex* velocities; + + // + // Texture space coordinates for each vertex. U and V are taken + // from the U and V of the growth surface at the point where the + // hair is rooted. W ranges from 0.0 at the root of the hair to + // 1.0 at the tip. + // + // 'secondaryUVWs' is not currently used. + // + Vertex* uvws; + Vertex* secondaryUVWs; + + // + // Surface normals at the root of each hair. There is one entry + // per hair. + // + // (Added April 16, 2004, version 2.7v4) + // + Vertex* surfaceNormals; + + // + // So let's say that hair number N has 3 vertices. Then its + // structure might look something like this: + // + // hairVertices vertices + // : : + // : : + // +--+ +--+ + // | | | | + // +--+ +--+ + // | | --> | | + // +--+ / +--+ + // hairStartIndices[N] --> | | ---- / | | + // +--+ \ / +--+ + // | | -------- | | + // +--+ \ +--+ + // | | --- \ | | + // +--+ \ \ +--+ + // hairEndIndices[N] ----> | | \ ----> | | + // +--+ \ +--+ + // | | -------> | | + // +--+ +--+ + // : : + // + // The X component of the first vertex of hair N would be: + // + // vertices[hairVertices[hairStartIndices[N]]].x + // + // The Z component of the velocity vector of the last vertex of + // hair N would be: + // + // velocities[hairVertices[hairEndIndices[N]-1]].z + // + // Note that per-hair values, such as the various material + // characteristics, are simply indexed by hair number. So the green + // component of hair N's tip colour would be: + // + // tipColors[N] + // + int* index; + float *alpha; //was missing + }; + + + class LIBSHAVEAPI_API SceneGeom + { + public: + SceneGeom(); + ~SceneGeom(); + + void clear(); + + // + // Number of unique vertices for all polygons in the structure. + // + int numVertices; + + // + // Total number of polygonal faces in the structure. + // + int numFaces; + + // + // Total number of face vertices in the structure. + // + // If several faces share a vertex, it will only be counted once in + // 'numVertices', but will be counted once for each face in + // 'numFaceVertices'. + // + int numFaceVertices; + + // + // 'faceVertices' is an array of indices into the 'vertices' array + // and contains one entry for each vertex of each face. + // + // The indices for face N start at 'faceStartIndices[N]' and end at + // 'faceEndIndices[N] - 1'. <-- NOTE THE -1 !!! + // + // 'faceVertices' will have 'numFaceVertices' elements + // 'faceStartIndices' will have 'numFaces' elements. + // 'faceEndIndices' will have 'numFaces' elements. + // + int *faceVertices; + int *faceStartIndices; + int *faceEndIndices; + + // + // 'vertices' is an array of all of the vertices in all of the + // geometery contained in the structure. + // + // It contains 'numVertices' entries. + // + Vertex* vertices; + + // + // 'velocities' contains the corresponding velocities for each + // vertex in the 'vertices' array. + // + Vertex* velocities; + }; + + + // + // Export all of the hair for all of the shaveNodes in the scene and + // place the result in 'hairInfo'. + // + // If 'renderableOnly' is true then only renderable shaveNodes (e.g. + // those which are active and whose display nodes are visible, not + // templated, etc) will be exported. + // + // Return Value: + // MS::kSuccess Nodes found and exported. + // + // MS::kNotFound No shaveNodes found in the scene. + // + // MS::kUnknownParameter Motion blur is on, but no render camera + // was found, so the hair has been exported + // without blur (all velocities are zero). + // + static MStatus exportAllHair( + HairInfo* hairInfo, bool renderableOnly = false + ); + + // + // Export all of the hair for all of the shaveNodes specified in the + // 'shaveNodes' array and place the result in 'hairInfo'. + // + // Return Value: + // MS::kSuccess Nodes found and exported. + // + // MS::kNotFound 'shaveNodes' is empty. + // + // MS::kInvalidParameter 'shaveNodes' contains objects which are + // not shaveNodes + // + // MS::kUnknownParameter Motion blur is on, but no render camera + // was found, so the hair has been exported + // without blur (all velocities are zero). + // + static MStatus exportHair( + MObjectArray& shaveNodes, HairInfo* hairInfo + ); + + + // + // Export any geometry capable of occluding hair and/or hair shadows, + // based on the 'Occlusion Objects' list in Shave Globals + // (shaveGlobals.hairOcclusionObjects attribute). + // + // If the 'Occlusion Objects' list is set to 'all' then + // 'hairOcclusions' will return all visible, non-transparent objects in + // the scene while 'shadowOcclusions' will also contain those objects + // which have primary visibility off or have transparency. + // + // If the 'Occlusion Objects' list in Shave Globals is set to an + // explicit list of objects then those objects will be exported, + // regardless of their visibility and transparency. + // + // If 'Native Illumination' is turned on in Shave Globals + // (shaveGlobals.nativeIllumination attribute) then shadows are being + // handled by the renderer itself and 'hairOccclusions' will be empty. + // + // Either parameter may be NULL. + // + // Return Value: + // + // MS::kSuccess Success. + // + // MS::kUnknownParameter Motion blur is on, but no render camera + // was found, so the geometry has been + // exported without blur (all velocities + // are zero). + // + static MStatus exportOcclusions( + SceneGeom* hairOcclusions, SceneGeom* shadowOcclusions + ); + + + //******************************************************************** + // + // OBSOLETE METHODS + // + //******************************************************************** + + // Use shaveAPI::HairInfo::clear() instead + static void freeHairInfo(HairInfo* hairInfo); + + // HairInfo objects automatically initialize themselves now. + static void initHairInfo(HairInfo* hairInfo); +}; + +#endif diff --git a/mayaPlug/shaveAPITestApp.cpp b/mayaPlug/shaveAPITestApp.cpp new file mode 100644 index 0000000..0185f6a --- /dev/null +++ b/mayaPlug/shaveAPITestApp.cpp @@ -0,0 +1,155 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <iostream> + +#include "shaveEngine.h" + +using namespace std; + + +int main(int argc, char* argv[]) +{ + if (argc < 2) + { + cerr << "Usage: " << argv[0] << " archiveFile [nodeName]" << endl; + return 1; + } + + // + // Get the command line parameters. + // + char* archiveFile = argv[1]; + char* nodeName = (argc > 2 ? argv[2] : NULL); + + // + // Grab a Shave license. + // + if (!PRIMlogin()) + { + cerr << "Could not get Shave license." << endl; + return 2; + } + + // + // Load in the archive file. + // + int numVoxels = PRIMinit_hairstack(archiveFile); + + cout << archiveFile << " contains " << numVoxels << " voxels." << endl; + + // + // Display some information about the first 10 voxels (if there are + // that many). + // + int i; + int j; + int numVoxelsToShow = (numVoxels > 10 ? 10 : numVoxels); + + for (i = 0; i < numVoxelsToShow; i++) + { + // + // Fetch the hairs in the voxel. + // + // If a node name was given on the command line, then only fetch + // hairs from that node. + // + HAIRTYPE hair; + UVSETS uvs; + + if (nodeName) + { + PRIMfetch_voxel_by_name2(i, nodeName, &hair, &uvs, false); + + cout << "Voxel " << i << " contains " << hair.totalfaces + << " hairs with " << (int)uvs.totalUVSets + << " uvs per hair, for node '" << nodeName << "'." << endl; + } + else + { + PRIMfetch_voxel(i, &hair, false); + + cout << "Voxel " << i << " contains " << hair.totalfaces + << " hairs." << endl; + } + + // + // If there are some hairs in the voxel then dump out more + // information about the voxel. + // + if (hair.totalfaces > 0) + { + // + // Display the voxel's bounding box. + // + VERT minBound; + VERT maxBound; + + PRIMfetch_bbox(i, &minBound, &maxBound); + + cout << " bounding box is (" + << minBound.x << ", " << minBound.y << ", " << minBound.z + << ") to ( " + << maxBound.x << ", " << maxBound.y << ", " << maxBound.z + << ")" << endl; + + // + // Display the vertices of the first hair in the voxel. + // + int startOfHairVertIndices = hair.face_start[0]; + int endOfHairVertIndices = hair.face_end[0]; + + cout << " first hair has " + << (endOfHairVertIndices - startOfHairVertIndices) + << " vertices:" << endl; + + for (j = startOfHairVertIndices; j < endOfHairVertIndices; j++) + { + int vertIndex = hair.facelist[j]; + VERT& vert = hair.v[vertIndex]; + + cout << " (" << vert.x << ", " << vert.y << ", " << vert.z + << ")" << endl; + } + + // + // Display the uvs for the root of the first hair in the voxel. + // + if (nodeName && (uvs.totalUVSets > 0) && (uvs.totalRoots > 0)) + { + cout << " first hair's uvs are:"; + + for (j = 0; j < (int)uvs.totalUVSets; j++) + { + cout << " (" << uvs.uvRoot[j].x << ", " << uvs.uvRoot[j].y + << "," << uvs.uvRoot[j].z << ")" << endl; + } + } + else + cout << " (no UV info available)" << endl; + } + + // + // We're done with 'hair' and 'uvs' so free up their memory. + // + PRIMfree_hairtype(&hair); + + if (nodeName) PRIMfree_uvset(&uvs); + } + + // + // Let Shave clean up its internals. + // + // We really don't need to do this in this case since we're about to + // exit, but it doesn't hurt. + // + PRIMclear_hairstack(); + + // + // Release the Shave license. + // + PRIMlogout(); + + return 0; +} diff --git a/mayaPlug/shaveAPITestCmd.cpp b/mayaPlug/shaveAPITestCmd.cpp new file mode 100644 index 0000000..8a1421f --- /dev/null +++ b/mayaPlug/shaveAPITestCmd.cpp @@ -0,0 +1,380 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MFnPlugin.h> +#include <maya/MFStream.h> +#include <maya/MGlobal.h> +#include <maya/MIOStream.h> +#include <maya/MPxCommand.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> +#include <maya/MTypes.h> + +#include <maya/shaveAPI.h> +#include <maya/shaveItHair.h> + + +class shaveAPITestCmd : public MPxCommand +{ +public: + static MSyntax createSyntax(); + + shaveAPITestCmd(); + virtual ~shaveAPITestCmd(); + + void displayGeomInfo(MString name, shaveAPI::SceneGeom& geom) const; + + void displayHairInfo( + shaveAPI::HairInfo* hairInfo, bool instances + ) const; + + MStatus doIt(const MArgList&); + bool isUndoable() const; + + static void* creator(); + +private: +}; + + +void* shaveAPITestCmd::creator() +{ + return new shaveAPITestCmd(); +} + + +shaveAPITestCmd::shaveAPITestCmd() +{ +} + + +shaveAPITestCmd::~shaveAPITestCmd() +{ +} + + +bool shaveAPITestCmd::isUndoable() const +{ + return false; +} + + +MStatus shaveAPITestCmd::doIt(const MArgList& argList) +{ + MStatus status; + char msg[200]; + + MGlobal::displayInfo("Test 1: export all shaveHairShapes..."); + + shaveAPI::HairInfo hairInfo; + + status = shaveAPI::exportAllHair(&hairInfo); + + if (status == MS::kNotFound) + { + MGlobal::displayInfo( + " exportAllHair says there are no shaveHairShapes in the scene." + ); + } + else if (!status) + { + MGlobal::displayError( + MString(" we got back the following unexpected error: ") + + status.errorString() + ); + } + else + { + MGlobal::displayInfo(" Scene contains..."); + displayHairInfo(&hairInfo, false); + } + + + MGlobal::displayInfo("Test 2: export renderable shaveHairShapes..."); + + status = shaveAPI::exportAllHair(&hairInfo, true); + + if (status == MS::kNotFound) + { + MGlobal::displayInfo( + " exportAllHair says there are no renderable shaveHairShapes" + " in the scene." + ); + + status = MS::kSuccess; + } + else + { + MGlobal::displayInfo(" Renderable shaveHairShapes contain..."); + displayHairInfo(&hairInfo, false); + } + + + MGlobal::displayInfo("Test 3: iterate through non-instanced hairs..."); + + status = shaveItHair::init(false, true); + + if (status == MS::kNotFound) + { + MGlobal::displayInfo( + " shaveItHair::init says there are no renderable, non-instanced" + " shaveHairShapes in the scene." + ); + } + else + { + int hairNum = 0; + + while (shaveItHair::nextHair(&hairInfo)) + { + sprintf(msg, " Hair %d contains...", hairNum); + MGlobal::displayInfo(msg); + displayHairInfo(&hairInfo, false); + + if (++hairNum >= 3) break; + } + } + + + MGlobal::displayInfo("Test 4: iterate through instanced hairs..."); + + status = shaveItHair::init(true, true); + + if (status == MS::kNotFound) + { + MGlobal::displayInfo( + " shaveItHair::init says there are no renderable, instanced" + " shaveHairShapes in the scene." + ); + } + else + { + int hairNum = 0; + + while (shaveItHair::nextHair(&hairInfo)) + { + sprintf(msg, " Hair %d contains...", hairNum); + MGlobal::displayInfo(msg); + displayHairInfo(&hairInfo, true); + + if (++hairNum >= 3) break; + } + } + + + MGlobal::displayInfo("Test 5: get occlusion geometry..."); + + shaveAPI::SceneGeom hairOcclusions; + shaveAPI::SceneGeom shadowOcclusions; + + status = shaveAPI::exportOcclusions(&hairOcclusions, &shadowOcclusions); + + if ((status != MS::kSuccess) && (status != MS::kUnknownParameter)) + { + MGlobal::displayError( + MString(" we got back the following unexpected error: ") + + status.errorString() + ); + } + else + { + if (status == MS::kUnknownParameter) + { + MGlobal::displayWarning( + " motion blur is on but no renderable camera could be found," + " so geometry velocities will all be zero." + ); + } + + displayGeomInfo("Hair occlusions", hairOcclusions); + displayGeomInfo("Shadow occlusions", shadowOcclusions); + } + + // + // Free up all the memory allocated to 'hairInfo'. + // + // This call is provided just as an example. In this particular case + // it is unnecessary because 'hairInfo' will go out of scope at the end + // of the function and be destroyed, along with all of its storage. + // + // However, if you were planning on doing some other processing in this + // function, then it might make sense to clear 'hairInfo's storage + // first so that that memory was available for other uses. + // + hairInfo.clear(); + + return MS::kSuccess; +} + + +MSyntax shaveAPITestCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableQuery(false); + syntax.enableEdit(false); + + return syntax; +} + + +void shaveAPITestCmd::displayGeomInfo( + MString name, shaveAPI::SceneGeom& geom +) const +{ + MGlobal::displayInfo(MString(" ") + name + " consists of..."); + + char msg[200]; + + sprintf( + msg, + " %d faces\n %d vertices\n %d face vertices", + geom.numFaces, + geom.numVertices, + geom.numFaceVertices + ); + + MGlobal::displayInfo(msg); + + // + // Dump out some details for the first three faces, if there are that + // many. + // + int face; + int i; + + for (face = 0; (face < 3) && (face < geom.numFaces); face++) + { + sprintf(msg, " face %d", face); + MGlobal::displayInfo(msg); + + for (i = geom.faceStartIndices[face]; + i < geom.faceEndIndices[face]; + i++) + { + int vert = geom.faceVertices[i]; + + sprintf( + buff, + " vertex %d: position (%f, %f, %f) velocity (%f, %f, %f)", + i-geom.faceStartIndices[face], + geom.vertices[vert].x, + geom.vertices[vert].y, + geom.vertices[vert].z, + geom.velocities[vert].x, + geom.velocities[vert].y, + geom.velocities[vert].z + ); + + MGlobal::displayInfo(buff); + } + } +} + + +void shaveAPITestCmd::displayHairInfo( + shaveAPI::HairInfo* hairInfo, bool instances +) const +{ + MString strandName = (instances ? "face" : "strand"); + char buff[200]; + + sprintf(buff, " %d %ss", hairInfo->numHairs, strandName); + MGlobal::displayInfo(buff); + + sprintf(buff, " %d vertices", hairInfo->numVertices); + MGlobal::displayInfo(buff); + + sprintf(buff, " %d %s vertices", hairInfo->numHairVertices, strandName); + MGlobal::displayInfo(buff); + + // + // Dump out some details for the first three strands, if there are that + // many. + // + int strand; + int i; + + for (strand = 0; (strand < 3) && (strand < hairInfo->numHairs); strand++) + { + sprintf( + buff, + " %s %d: root colour (%f, %f, %f) tip colour (%f, %f, %f)" + " surface normal (%f, %f, %f)", + strandName, + strand, + hairInfo->rootColors[strand].r, + hairInfo->rootColors[strand].g, + hairInfo->rootColors[strand].b, + hairInfo->tipColors[strand].r, + hairInfo->tipColors[strand].g, + hairInfo->tipColors[strand].b, + hairInfo->surfaceNormals[strand].x, + hairInfo->surfaceNormals[strand].y, + hairInfo->surfaceNormals[strand].z + ); + MGlobal::displayInfo(buff); + + for (i = hairInfo->hairStartIndices[strand]; + i < hairInfo->hairEndIndices[strand]; + i++) + { + int vert = hairInfo->hairVertices[i]; + + sprintf( + buff, + " vertex %d: position (%f, %f, %f) velocity (%f, %f, %f)" + " texture coords (%f, %f, %f)", + (i-hairInfo->hairStartIndices[strand]), + hairInfo->vertices[vert].x, + hairInfo->vertices[vert].y, + hairInfo->vertices[vert].z, + hairInfo->velocities[vert].x, + hairInfo->velocities[vert].y, + hairInfo->velocities[vert].z, + hairInfo->uvws[vert].x, + hairInfo->uvws[vert].y, + hairInfo->uvws[vert].z + ); + } + } +} + + +MStatus initializePlugin(MObject obj) +{ + MStatus status; + MFnPlugin plugin(obj, "Epic Games", "1.0", "Any"); + + status = plugin.registerCommand( + "shaveAPITest", + shaveAPITestCmd::creator, + shaveAPITestCmd::createSyntax + ); + + if (!status) + { + status.perror("registering shaveAPITest command"); + return status; + } + + return status; +} + + +MStatus uninitializePlugin(MObject obj) +{ + MStatus status; + MFnPlugin plugin(obj); + + status = plugin.deregisterCommand("shaveAPITest"); + + if (!status) + { + status.perror("deregistering shaveAPITest command"); + return status; + } + + return status; +} diff --git a/mayaPlug/shaveAPIimpl.cpp b/mayaPlug/shaveAPIimpl.cpp new file mode 100644 index 0000000..d93dff9 --- /dev/null +++ b/mayaPlug/shaveAPIimpl.cpp @@ -0,0 +1,514 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MAngle.h> +#include <maya/MAnimControl.h> +#include <maya/MDagPath.h> +#include <maya/MFnCamera.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MObjectArray.h> +#include <maya/MSelectionList.h> +#include <maya/MTime.h> +#include <string.h> + +#include "shaveAPI.h" +#include "shaveAPIimpl.h" +#include "shaveCheckObjectVisibility.h" +#include "shaveConstant.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveIO.h" +#include "shaveMaya.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveSDK.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" + +#include <assert.h> + +std::vector<shaveHairShape*> shaveAPIimpl::mShaveNodes; + + +MStatus shaveAPIimpl::exportAllHair( + shaveAPI::HairInfo* hairInfo, bool renderableOnly +) +{ + MObjectArray shaveNodes; + + shaveRender::saveFrameGlobals(); + + if (renderableOnly) + { + shaveRenderer* renderer = shaveRender::getRenderer(); + + renderer->getRenderableShaveNodes(shaveNodes); + } + else + shaveUtil::getShaveNodes(shaveNodes); + + if (shaveNodes.length() == 0) return MS::kNotFound; + + return doExport(shaveNodes, hairInfo, shaveRender::getFrameGlobals()); +} + + +MStatus shaveAPIimpl::exportDRAFile(MString filename) +{ + MStatus st = MS::kSuccess; + + return st; +} + + +MStatus shaveAPIimpl::exportHair( + MObjectArray& shaveNodes, shaveAPI::HairInfo* hairInfo +) +{ + ////////// + //MGlobal::displayInfo(MString("shaveAPIimpl::exportHair number of nodes :") + shaveNodes.length()); + ///////// + + if (shaveNodes.length() < 1) return MS::kNotFound; + + unsigned int i; + + for (i = 0; i < shaveNodes.length(); i++) + { + MFnDependencyNode nodeFn(shaveNodes[i]); + + ////////////// + //MGlobal::displayInfo(MString("node: ") + nodeFn.name()); + ////////////// + + if (nodeFn.typeId() != shaveHairShape::id) return MS::kInvalidParameter; + } + + shaveRender::saveFrameGlobals(); + + MStatus stat = doExport(shaveNodes, hairInfo, shaveRender::getFrameGlobals()); +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + return stat; +} + + +MStatus shaveAPIimpl::exportOcclusions( + shaveAPI::SceneGeom* hairOcclusions, shaveAPI::SceneGeom* shadowOcclusions +) +{ + MStatus st; + + if ((hairOcclusions == NULL) && (shadowOcclusions == NULL)) + return MS::kSuccess; + + shaveRender::saveFrameGlobals(); + shaveMaya::getRenderGlobals(); + + freeSceneGeom(hairOcclusions); + freeSceneGeom(shadowOcclusions); + + shaveRender::SceneInfo* sceneInfo = shaveRender::getSceneInfo(); + + // + // If the caller only wants shadows and native illumination is on, then + // just return the empty list. + // + if ((hairOcclusions == NULL) && nativeIlluminationGlob) return MS::kSuccess; + + // + // If motion blur is on, get the shutter open and close times. If that + // fails for some reason, then treat it as it motion blur is off but + // return a status indicating what happened. + // + bool doMotionBlur = shaveMaya::doMotionBlur; + float shutterOpen = 0.0f; + float shutterClose = 0.0f; + + if (doMotionBlur) + { + st = shaveRender::getShutterTimes(shutterOpen, shutterClose); + + if (!st) doMotionBlur = false; + } + + if (doMotionBlur) + { + int i; + + shaveUtil::setFrame(shutterOpen); + shaveRender::buildOcclusionLists(shaveConstant::kShutterOpen); + + shaveUtil::setFrame(shutterClose); + shaveRender::buildOcclusionLists(shaveConstant::kShutterClose); + + // + // Calculate velocities. + // + if (hairOcclusions) + { + int numVerts = sceneInfo->shutterOpenCamScene.totalverts; + + if (sceneInfo->shutterCloseCamScene.totalverts < numVerts) + numVerts = sceneInfo->shutterCloseCamScene.totalverts; + + for (i = 0; i < numVerts; i++) + { + sceneInfo->shutterOpenCamScene.velocity[i].x = + sceneInfo->shutterCloseCamScene.v[i].x + - sceneInfo->shutterOpenCamScene.v[i].x; + + sceneInfo->shutterOpenCamScene.velocity[i].y = + sceneInfo->shutterCloseCamScene.v[i].y + - sceneInfo->shutterOpenCamScene.v[i].y; + + sceneInfo->shutterOpenCamScene.velocity[i].z = + sceneInfo->shutterCloseCamScene.v[i].z + - sceneInfo->shutterOpenCamScene.v[i].z; + } + } + + if (shadowOcclusions) + { + int numVerts = sceneInfo->shutterOpenShadowScene.totalverts; + + if (sceneInfo->shutterCloseShadowScene.totalverts < numVerts) + numVerts = sceneInfo->shutterCloseShadowScene.totalverts; + + for (i = 0; i < numVerts; i++) + { + sceneInfo->shutterOpenShadowScene.velocity[i].x = + sceneInfo->shutterCloseShadowScene.v[i].x + - sceneInfo->shutterOpenShadowScene.v[i].x; + + sceneInfo->shutterOpenShadowScene.velocity[i].y = + sceneInfo->shutterCloseShadowScene.v[i].y + - sceneInfo->shutterOpenShadowScene.v[i].y; + + sceneInfo->shutterOpenShadowScene.velocity[i].z = + sceneInfo->shutterCloseShadowScene.v[i].z + - sceneInfo->shutterOpenShadowScene.v[i].z; + } + } + } + else + shaveRender::buildOcclusionLists(shaveConstant::kShutterOpen); + + if (hairOcclusions) + copyWFTYPEToSceneGeom(hairOcclusions, sceneInfo->shutterOpenCamScene); + + if (shadowOcclusions) + { + copyWFTYPEToSceneGeom( + shadowOcclusions, sceneInfo->shutterOpenShadowScene + ); + } + + return st; +} + + +void shaveAPIimpl::freeHairInfo(shaveAPI::HairInfo* hairInfo) +{ + if (hairInfo) + { + SHAVEfree_hairtype((HAIRTYPE*)hairInfo); + SHAVEinit_hairtype((HAIRTYPE*)hairInfo); + } +} + + +void shaveAPIimpl::initHairInfo(shaveAPI::HairInfo* hairInfo) +{ + if (hairInfo) SHAVEinit_hairtype((HAIRTYPE*)hairInfo); +} + + +//****************************************** +// +// SceneGeom Interfaces +// +//****************************************** + + +void shaveAPIimpl::freeSceneGeom(shaveAPI::SceneGeom* sceneGeom) +{ + if (sceneGeom) + { + delete sceneGeom->faceVertices; + delete sceneGeom->faceStartIndices; + delete sceneGeom->faceEndIndices; + delete sceneGeom->vertices; + delete sceneGeom->velocities; + + initSceneGeom(sceneGeom); + } +} + + +void shaveAPIimpl::initSceneGeom(shaveAPI::SceneGeom* sceneGeom) +{ + if (sceneGeom) + { + sceneGeom->numVertices = 0; + sceneGeom->numFaces = 0; + sceneGeom->numFaceVertices = 0; + sceneGeom->faceVertices = NULL; + sceneGeom->faceStartIndices = NULL; + sceneGeom->faceEndIndices = NULL; + sceneGeom->vertices = NULL; + sceneGeom->velocities = NULL; + } +} + + +//****************************************** +// +// Utility Methods +// +//****************************************** + +void shaveAPIimpl::copyWFTYPEToSceneGeom( + shaveAPI::SceneGeom* sceneGeom, WFTYPE& wf +) +{ + // + // 'sceneGeom' MUST have been cleared before calling this method. + // + sceneGeom->numVertices = wf.totalverts; + sceneGeom->numFaces = wf.totalfaces; + sceneGeom->numFaceVertices = wf.totalfverts; + + if (wf.totalverts > 0) + { + // + // For performance, we *could* just move the ptrs from the WFTYPE + // over to the SceneGeom rather than allocating and copying. + // However, that would leave us mixing 'new' and 'malloc' memory + // allocations. We could handle that by adding a flag to SceneInfo + // to tell us where the memory came from, so that we know whether + // to 'delete' or 'free' it. But that's a bit messy, so for now + // we'll just copy the data. If performance becomes an issue then + // we can get fancy later. + // + sceneGeom->vertices = new shaveAPI::Vertex[wf.totalverts]; + sceneGeom->velocities = new shaveAPI::Vertex[wf.totalverts]; + + memcpy( + sceneGeom->vertices, wf.v, sizeof(shaveAPI::Vertex) * wf.totalverts + ); + + memcpy( + sceneGeom->velocities, + wf.velocity, + sizeof(shaveAPI::Vertex) * wf.totalverts + ); + } + + if (wf.totalfaces > 0) + { + sceneGeom->faceStartIndices = new int[wf.totalfaces]; + sceneGeom->faceEndIndices = new int[wf.totalfaces]; + + memcpy( + sceneGeom->faceStartIndices, + wf.face_start, + sizeof(int) * wf.totalfaces + ); + + memcpy( + sceneGeom->faceEndIndices, + wf.face_end, + sizeof(int) * wf.totalfaces + ); + } + + if (wf.totalfverts > 0) + { + sceneGeom->faceVertices = new int[wf.totalfverts]; + + memcpy( + sceneGeom->faceVertices, wf.facelist, sizeof(int) * wf.totalfverts + ); + } +} + + +MStatus shaveAPIimpl::clearHairStack() +{ + SHAVEclear_stack(); + mShaveNodes.clear(); + + return MS::kSuccess; +} + + +MStatus shaveAPIimpl::createHairStack( + MObjectArray& shaveNodes, const shaveGlobals::Globals& globals +) +{ + MStatus st; + MTime curTime = MAnimControl::currentTime(); + + clearHairStack(); + + SHAVEset_tile_limit( + (int)globals.tileMemoryLimit, (int)globals.transparencyDepth + ); + + // + // Save the shaveNode pointers. + // + unsigned int i; + + for (i = 0; i < shaveNodes.length(); i++) + { + MFnDependencyNode nodeFn(shaveNodes[i]); + + mShaveNodes.push_back((shaveHairShape*)nodeFn.userNode()); + } + + shaveMaya::getRenderGlobals(); + + if (shaveMaya::doMotionBlur) + { + float shutterOpen; + float shutterClose; + + st = shaveRender::getShutterTimes(shutterOpen, shutterClose); + + if (st) + { + // + // Do shutter open. + // + shaveUtil::setFrame(shutterOpen); +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveNodes, "", globals.verbose); +#else + initTexInfoLookup(shaveNodes, "", globals.verbose); +#endif + shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterOpen); + + // + // Do shutter close. + // + shaveUtil::setFrame(shutterClose); + shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterClose); + } + else + { + // + // We couldn't find a valid render camera, so go ahead and + // render without motion blur, but return a status code to let + // the caller know what happened. + // + st = MS::kUnknownParameter; +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveNodes, "", globals.verbose); +#else + initTexInfoLookup(shaveNodes, "", globals.verbose); +#endif + shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterBoth); + } + } + else + { + // + // No motion blur, so shutter open and close occur at the same + // time. + // +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveNodes, "", globals.verbose); +#else + initTexInfoLookup(shaveNodes, "", globals.verbose); +#endif + shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterBoth); + } + + return st; +} + + +MStatus shaveAPIimpl::doExport( + MObjectArray& shaveNodes, + shaveAPI::HairInfo* hairInfo, + const shaveGlobals::Globals& globals +) +{ +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + ////////// + //MGlobal::displayInfo(MString("shaveAPIimpl::doExport")); + ///////// + + MStatus st; + + SHAVEset_verbose(globals.verbose ? 1 : 0); + createHairStack(shaveNodes, globals); +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + if (st) + { + ////////// + //MGlobal::displayInfo(MString("stack created")); + ///////// + + freeHairInfo(hairInfo); +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + ////////// + //MGlobal::displayInfo(MString("freeHairInfo")); + ///////// + + int res = SHAVEexport_hairtype((HAIRTYPE*)hairInfo); +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif +#ifdef _DEBUG + MGlobal::displayInfo(MString("SHAVEexport_hairtype result :") + res); + MGlobal::displayInfo(MString("hairInfo->numHairs :") + hairInfo->numHairs); + MGlobal::displayInfo(MString("hairInfo->numHairVertices :") + hairInfo->numHairVertices); + MGlobal::displayInfo(MString("hairInfo->numVertices :") + hairInfo->numVertices); +#endif + SHAVEclear_stack(); +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + ////////// + //MGlobal::displayInfo(MString("SHAVEclear_stack")); + ///////// + + if (shaveMaya::doMotionBlur) + { + ////////// + //MGlobal::displayInfo(MString("shaveUtil::setFrame")); + ///////// + + MTime curTime = MAnimControl::currentTime(); + + shaveUtil::setFrame((float)curTime.value()); + } + ////////// + //MGlobal::displayInfo(MString("done")); + ///////// + } +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + return st; +} + + +shaveHairShape* shaveAPIimpl::getNodeFromStack(unsigned int nodeIndex) +{ + if (nodeIndex >= mShaveNodes.size()) return NULL; + + return mShaveNodes[nodeIndex]; +} diff --git a/mayaPlug/shaveAPIimpl.h b/mayaPlug/shaveAPIimpl.h new file mode 100644 index 0000000..60f3164 --- /dev/null +++ b/mayaPlug/shaveAPIimpl.h @@ -0,0 +1,82 @@ +#ifndef shaveAPIimpl_h +#define shaveAPIimpl_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MStatus.h> +#include <vector> + +#include "libShave.h" +#include "shaveAPI.h" +#include "shaveGlobals.h" +#include "shaveSDK.h" + +#if MAYA_API_VERSION < 20180000 +class MObjectArray; +#endif + +class shaveHairShape; + + +class LIBSHAVE_API shaveAPIimpl +{ +public: + static MStatus clearHairStack(); + + static MStatus createHairStack( + MObjectArray& shaveHairShapes, + const shaveGlobals::Globals& globals + ); + + static MStatus exportAllHair( + shaveAPI::HairInfo* hairInfo, bool renderableOnly + ); + + static MStatus exportDRAFile(MString filename); + + static MStatus exportHair( + MObjectArray& shaveHairShapes, + shaveAPI::HairInfo* hairInfo + ); + + static MStatus exportOcclusions( + shaveAPI::SceneGeom* hairOcclusions, + shaveAPI::SceneGeom* shadowOcclusions + ); + + static void freeHairInfo(shaveAPI::HairInfo* hairInfo); + static void initHairInfo(shaveAPI::HairInfo* hairInfo); + + //****************************************** + // + // SceneGeom Interfaces + // + //****************************************** + static void freeSceneGeom(shaveAPI::SceneGeom* sceneGeom); + static void initSceneGeom(shaveAPI::SceneGeom* sceneGeom); + + //****************************************** + // + // Utility Methods + // + //****************************************** + static void copyWFTYPEToSceneGeom( + shaveAPI::SceneGeom* sceneGeom, WFTYPE& wf + ); + + static shaveHairShape* getNodeFromStack(unsigned int nodeIndex); + +protected: + static std::vector<shaveHairShape*> mShaveNodes; + + static MStatus doExport( + MObjectArray& shaveHairShapes, + shaveAPI::HairInfo* hairInfo, + const shaveGlobals::Globals& globals + ); + + static MStatus doInitHairIterator(MObjectArray& shaveHairShapes); +}; + +#endif diff --git a/mayaPlug/shaveBackgroundShader.cpp b/mayaPlug/shaveBackgroundShader.cpp new file mode 100644 index 0000000..ef45b32 --- /dev/null +++ b/mayaPlug/shaveBackgroundShader.cpp @@ -0,0 +1,585 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDataBlock.h> +#include <maya/MDataHandle.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatVector.h> +#include <maya/MFnCompoundAttribute.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnMatrixAttribute.h> +#include <maya/MFnMessageAttribute.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnNumericData.h> +#include <maya/MPlug.h> +#include <maya/MRenderUtil.h> +#include <maya/MString.h> +#include <maya/MTime.h> +#include <maya/MTypeId.h> + +#include "shaveGlobals.h" +#include "shaveError.h" +#include "shaveIO.h" +#include "shaveSDK.h" +#include "shaveBackgroundShader.h" +#include "shaveUtil.h" + +MTypeId shaveBackgroundShader::id(0x001029A3); + +const MString shaveBackgroundShader::nodeTypeName("shaveBackground"); + +MObject shaveBackgroundShader::aBackgroundSampler; +MObject shaveBackgroundShader::aIncludeBackfacing; + +MObject shaveBackgroundShader::aLightDataArray; +MObject shaveBackgroundShader::aLightDirection; +MObject shaveBackgroundShader::aLightIntensity; +MObject shaveBackgroundShader::aLightAmbient; +MObject shaveBackgroundShader::aLightDiffuse; +MObject shaveBackgroundShader::aLightSpecular; +MObject shaveBackgroundShader::aLightShadowFraction; +MObject shaveBackgroundShader::aPreShadowIntensity; +MObject shaveBackgroundShader::aLightBlindData; +MObject shaveBackgroundShader::aMatrixEyeToWorld; +MObject shaveBackgroundShader::aNormalCamera; +MObject shaveBackgroundShader::aObjectId; +MObject shaveBackgroundShader::aOldShader; +MObject shaveBackgroundShader::aOutColor; +MObject shaveBackgroundShader::aOutMatteOpacity; +MObject shaveBackgroundShader::aOutTransparency; + +#ifdef _DEBUG +MObject shaveBackgroundShader::aPixelCenter; +MObject shaveBackgroundShader::aPixelCenterX; +MObject shaveBackgroundShader::aPixelCenterY; +#endif + +MObject shaveBackgroundShader::aPointWorld; +MObject shaveBackgroundShader::aRayDepth; +MObject shaveBackgroundShader::aRayDirection; +MObject shaveBackgroundShader::aRayOrigin; + + +shaveBackgroundShader::shaveBackgroundShader() +{ +} + + +shaveBackgroundShader::~shaveBackgroundShader() +{ +} + + +void* shaveBackgroundShader::creator() +{ + return new shaveBackgroundShader(); +} + + + +MStatus shaveBackgroundShader::initialize() +{ + MStatus st; + MFnCompoundAttribute cAttr; + MFnMatrixAttribute aMat; + MFnMessageAttribute aMsg; + MFnNumericAttribute nAttr; + + // + // Renderer-Supplied Input Attributes + // + aBackgroundSampler = nAttr.createAddr("backgroundSampler", "rtb"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aBackgroundSampler); + + + aLightDirection = nAttr.createPoint("lightDirection", "ld"); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + + + aLightIntensity = nAttr.createColor("lightIntensity", "li"); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + + + aLightAmbient = nAttr.create( + "lightAmbient", + "la", + MFnNumericData::kBoolean, + true + ); + + aLightDiffuse = nAttr.create( + "lightDiffuse", + "ldf", + MFnNumericData::kBoolean, + true + ); + + aLightSpecular = nAttr.create( + "lightSpecular", + "ls", + MFnNumericData::kBoolean, + false + ); + + aLightShadowFraction = nAttr.create( + "lightShadowFraction", + "lsf", + MFnNumericData::kFloat, + 0.5 + ); + + aPreShadowIntensity = nAttr.create( + "preShadowIntensity", + "psi", + MFnNumericData::kFloat, + 1.0 + ); + + aLightBlindData = nAttr.createAddr("lightBlindData", "lbd", 0); + + aLightDataArray = cAttr.create("lightDataArray", "ltd"); + cAttr.addChild(aLightDirection); + cAttr.addChild(aLightIntensity); + cAttr.addChild(aLightAmbient); + cAttr.addChild(aLightDiffuse); + cAttr.addChild(aLightSpecular); + cAttr.addChild(aLightShadowFraction); + cAttr.addChild(aPreShadowIntensity); + cAttr.addChild(aLightBlindData); + cAttr.setArray(true); + cAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aLightDataArray); + + + aMatrixEyeToWorld = aMat.create( + "matrixEyeToWorld", + "etw", + MFnMatrixAttribute::kFloat + ); + aMat.setHidden(true); + aMat.setStorable(false); + addAttribute(aMatrixEyeToWorld); + + + aNormalCamera = nAttr.createPoint("normalCamera", "n"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aNormalCamera); + + + aObjectId = nAttr.createAddr("objectId", "oi"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aObjectId); + + +#ifdef _DEBUG + aPixelCenterX = nAttr.create( + "pixelCenterX", "pcx", MFnNumericData::kFloat + ); + + aPixelCenterY = nAttr.create( + "pixelCenterY", "pcy", MFnNumericData::kFloat + ); + aPixelCenter = nAttr.create( + "pixelCenter", + "pc", + aPixelCenterX, + aPixelCenterY + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aPixelCenter); +#endif + + + aPointWorld = nAttr.createPoint("pointWorld", "pw"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aPointWorld); + + + aRayDepth = nAttr.create("rayDepth", "rd", MFnNumericData::kInt); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRayDepth); + + + aRayDirection = nAttr.createPoint("rayDirection", "rad"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRayDirection); + + + aRayOrigin = nAttr.createPoint("rayOrigin", "ro"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRayOrigin); + + + // + // Custom Input Attributes + // + aIncludeBackfacing = nAttr.create( + "includeBackfacing", "ibf", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "cannot create 'includeBackfacing' attribute"); + nAttr.setDefault(true); + st = addAttribute(aIncludeBackfacing); + MChkErr(st, "cannot add 'includeBackfacing' attribute"); + + + // + // Standard Renderer Output Attributes + // + aOutColor = nAttr.createColor("outColor", "oc"); + nAttr.setDefault(0.0f, 0.0f, 0.0f); + nAttr.setHidden(true); + nAttr.setWritable(false); + nAttr.setUsedAsColor(true); + addAttribute(aOutColor); + + + aOutMatteOpacity = nAttr.createColor("outMatteOpacity", "omo"); + nAttr.setDefault(0.0f, 0.0f, 0.0f); + nAttr.setHidden(true); + nAttr.setWritable(false); + nAttr.setUsedAsColor(true); + addAttribute(aOutMatteOpacity); + + + aOutTransparency = nAttr.createColor("outTransparency", "ot"); + nAttr.setDefault(0.0f, 0.0f, 0.0f); + nAttr.setHidden(true); + nAttr.setWritable(false); + nAttr.setUsedAsColor(true); + addAttribute(aOutTransparency); + + + // + // Custom Output Attributes + // + aOldShader = aMsg.create("oldShader", "oshd"); + aMsg.setHidden(true); + aMsg.setWritable(false); + aMsg.setHidden(true); + addAttribute(aOldShader); + + + // + // Set up attribute dependencies. + // + attributeAffects(aIncludeBackfacing, aOutColor); + attributeAffects(aBackgroundSampler, aOutColor); + attributeAffects(aLightDataArray, aOutColor); + attributeAffects(aMatrixEyeToWorld, aOutColor); + attributeAffects(aNormalCamera, aOutColor); + attributeAffects(aObjectId, aOutColor); + attributeAffects(aPointWorld, aOutColor); + attributeAffects(aRayDepth, aOutColor); + attributeAffects(aRayDirection, aOutColor); + attributeAffects(aRayOrigin, aOutColor); + + attributeAffects(aIncludeBackfacing, aOutMatteOpacity); + attributeAffects(aBackgroundSampler, aOutMatteOpacity); + attributeAffects(aLightDataArray, aOutMatteOpacity); + attributeAffects(aMatrixEyeToWorld, aOutMatteOpacity); + attributeAffects(aNormalCamera, aOutMatteOpacity); + attributeAffects(aObjectId, aOutMatteOpacity); + attributeAffects(aPointWorld, aOutMatteOpacity); + attributeAffects(aRayDepth, aOutMatteOpacity); + attributeAffects(aRayDirection, aOutMatteOpacity); + attributeAffects(aRayOrigin, aOutMatteOpacity); + + attributeAffects(aIncludeBackfacing, aOutTransparency); + attributeAffects(aBackgroundSampler, aOutTransparency); + attributeAffects(aLightDataArray, aOutTransparency); + attributeAffects(aMatrixEyeToWorld, aOutTransparency); + attributeAffects(aNormalCamera, aOutTransparency); + attributeAffects(aObjectId, aOutTransparency); + attributeAffects(aPointWorld, aOutTransparency); + attributeAffects(aRayDepth, aOutTransparency); + attributeAffects(aRayDirection, aOutTransparency); + attributeAffects(aRayOrigin, aOutTransparency); + +#ifdef _DEBUG + attributeAffects(aPixelCenter, aOutColor); + attributeAffects(aPixelCenter, aOutMatteOpacity); + attributeAffects(aPixelCenter, aOutTransparency); +#endif + + return MS::kSuccess; +} + + +void shaveBackgroundShader::postConstructor( ) +{ +// %%% Can we safely do this, given the use of globals by the Shave +// engine? +// +// setMPSafe(true); +} + + +MStatus shaveBackgroundShader::compute(const MPlug& plug, MDataBlock& datablock) +{ + if ((plug == aOutColor) || (plug.parent() == aOutColor) + || (plug == aOutMatteOpacity) || (plug.parent() == aOutMatteOpacity) + || (plug == aOutTransparency) || (plug.parent() == aOutTransparency)) + { + const MFloatVector& point = datablock + .inputValue(aPointWorld) + .asFloatVector(); + + const MFloatVector& normal = datablock + .inputValue(aNormalCamera) + .asFloatVector(); +#ifdef _DEBUG + float pixelX = datablock.inputValue(aPixelCenterX).asFloat(); + float pixelY = datablock.inputValue(aPixelCenterY).asFloat(); +#endif + + bool includeBackfacing = datablock.inputValue(aIncludeBackfacing).asBool(); + MArrayDataHandle lightArray = + datablock.inputArrayValue(aLightDataArray); + + unsigned int numAffectingLights = 0; + unsigned int numLights = lightArray.elementCount(); + + unsigned int i; + float lightShadow = 0.0f; + float netIllum = 0.0f; + float netShadow = 0.0f; + float preShadowIntensity; + float totalPreShadowIntensity = 0.0f; + + VERT wpoint; + wpoint.x = point.x; + wpoint.y = point.y; + wpoint.z = point.z; + +static unsigned int count = 0; +if (++count == 7453) count = 0; + + for (i = 0; i < numLights; i++) + { + MDataHandle light = lightArray.inputValue(); + + // + // If the preShadowIntensity is zero then the light doesn't + // even point at us (or we're beyond it's range) in which case + // we don't count it. + // + preShadowIntensity = light.child(aPreShadowIntensity).asFloat(); + + if (preShadowIntensity <= 0.0f) continue; + + // + // If the normal is facing away from the light then and we're + // not including backfacing polys in the matte, then we don't want + // to include that light in the calculation. Otherwise, + // backfacing polys are considered to be totally shadowed from + // the light. + // + float hairIllum = 1.0f; + MFloatVector dir = + light.child(aLightDirection).asFloatVector(); + + if (dir * normal < 0.04f) + { + if (includeBackfacing) + lightShadow = 1.0f; + else + continue; + } + else + lightShadow = light.child(aLightShadowFraction).asFloat(); + + numAffectingLights++; + totalPreShadowIntensity += preShadowIntensity; + + // + // If the point is already completely in shadow, then there's + // no point in getting Shave's expensive opinion on the matter. + // + // Similarly, if Shave's shadow density is set to zero, then no + // hair shadows will be displayed so once again we don't need + // to ask Shave if the point is in shadow. + // + if ((lightShadow < 1.0f) && (shaveShadowDensityGlob > 0.0)) + { + // + // We need to get this light's index in the + // globalLightList. + // + // If the light has a non-zero 'lightBlindData' + // attribute value, then that will be its node pointer, + // which is unique, so we can use it to look up the + // light's index. + // + int lightIndex = -1; + void* blindData = light.child(aLightBlindData).asAddr(); + if (blindData != 0) + lightIndex = shaveUtil::getLightIndex(blindData); + + // + // If we didn't find the light through its blind data, + // then try finding it based on its direction. + // + if (lightIndex < 0) + { + const MFloatMatrix& camToWorld = + datablock.inputValue(aMatrixEyeToWorld).asFloatMatrix(); + + dir *= camToWorld; + + lightIndex = shaveUtil::getLightIndex(point, dir); + } + + if (lightIndex >= 0) + { + // + // How much of the light's illumination gets + // through the hair? + // + // Note that internally Shave creates multiple + // sub-lights for some types of lights. They're + // not supposed to overlap but sometimes they do, + // so we only want the brightest one. + // + int id; + float temp; + + hairIllum = 10000.0f; + + for (id = shaveUtil::globalLightList[lightIndex].minId; + id <= shaveUtil::globalLightList[lightIndex].maxId; + id++) + { + temp = SHAVEilluminate_point(wpoint, id); + if (temp < hairIllum) hairIllum = temp; + } + + hairIllum = hairIllum * shaveShadowDensityGlob + + (1.0f - shaveShadowDensityGlob); + } + } + + // + // lightShadow gives the degree to which the point is shadowed + // from that light, with 1.0 being fully shadowed and 0.0 being + // fully lit. + // + // So (preShadowIntensity - lightShadow) gives the amount of + // illumination from the light which hits the point, after + // shadows but before taking hair into account. + // + float lightIllum = preShadowIntensity - lightShadow; + + // + // We multiply that by the amount of illumination that the hair + // will let through to get the final illumination of the point. + // + float finalIllum = lightIllum * hairIllum; + + // + // The final amount of shadowing is (preShadowIntensity - + // finalIllum). + // + lightShadow = preShadowIntensity - finalIllum; + + netShadow += lightShadow; + + if (!lightArray.next()) break; + } + + if (totalPreShadowIntensity > 0.0f) + { + netShadow /= totalPreShadowIntensity; + netIllum = 1.0f - netShadow; + } + else + netShadow = 0.0f; + +#define USE_BG_SAMPLER + +#ifdef USE_BG_SAMPLER + MFloatVector traceColor(0.0f, 0.0f, 0.0f); + + if (netIllum > 0.0f) + { + int rayDepth = datablock.inputValue(aRayDepth).asInt(); + void*& objectId = datablock.inputValue(aObjectId).asAddr(); + void*& backgroundSampler = + datablock.inputValue(aBackgroundSampler).asAddr(); + + const MFloatVector& rayDirection = + datablock.inputValue(aRayDirection).asFloatVector(); + + const MFloatVector& rayOrigin = + datablock.inputValue(aRayOrigin).asFloatVector(); + + MFloatVector traceTransp; + + MRenderUtil::raytrace( + rayOrigin, + rayDirection, + objectId, + backgroundSampler, + (short)rayDepth, + traceColor, + traceTransp, + true + ); + } +#endif + + MFloatVector& outColor = datablock + .outputValue(aOutColor) + .asFloatVector(); + + MFloatVector& outMatteOpacity = datablock + .outputValue(aOutMatteOpacity) + .asFloatVector(); + + MFloatVector& outTransparency = datablock + .outputValue(aOutTransparency) + .asFloatVector(); + +#ifdef USE_BG_SAMPLER + outColor = traceColor * netIllum; +#else + outColor.x = 0.0; + outColor.y = 0.0; + outColor.z = 0.0; +#endif + + outMatteOpacity.x = netShadow; + outMatteOpacity.y = netShadow; + outMatteOpacity.z = netShadow; + +#ifdef USE_BG_SAMPLER + outTransparency.x = 0.0f; + outTransparency.y = 0.0f; + outTransparency.z = 0.0f; +#else + outTransparency.x = netIllum; + outTransparency.y = netIllum; + outTransparency.z = netIllum; +#endif + + datablock.setClean(aOutColor); + datablock.setClean(aOutMatteOpacity); + datablock.setClean(aOutTransparency); + } + else + return MS::kUnknownParameter; + + return MS::kSuccess; +} + + diff --git a/mayaPlug/shaveBackgroundShader.h b/mayaPlug/shaveBackgroundShader.h new file mode 100644 index 0000000..0e32a5a --- /dev/null +++ b/mayaPlug/shaveBackgroundShader.h @@ -0,0 +1,88 @@ +#ifndef shaveBackgroundShader_h +#define shaveBackgroundShader_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxNode.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> + + +class shaveBackgroundShader : public MPxNode +{ + //****************************************************** + // + // Creation & Initialization Methods + // + //****************************************************** +public: + shaveBackgroundShader(); + virtual ~shaveBackgroundShader(); + static void * creator(); + static MStatus initialize(); + + + //****************************************************** + // + // Overloaded MPxNode Methods + // + //****************************************************** +public: + virtual MStatus compute(const MPlug&, MDataBlock&); + virtual void postConstructor(); +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + + //****************************************************** + // + // Member Variables + // + //****************************************************** + public: + static MTypeId id; + static const MString nodeTypeName; + + + //****************************************************** + // + // Attributes + // + //****************************************************** +public: + static MObject aBackgroundSampler; + static MObject aIncludeBackfacing; + + static MObject aLightDataArray; + static MObject aLightDirection; + static MObject aLightIntensity; + static MObject aLightAmbient; + static MObject aLightDiffuse; + static MObject aLightSpecular; + static MObject aLightShadowFraction; + static MObject aPreShadowIntensity; + static MObject aLightBlindData; + + static MObject aMatrixEyeToWorld; + static MObject aNormalCamera; + static MObject aObjectId; + static MObject aOldShader; + static MObject aOutColor; + static MObject aOutMatteOpacity; + static MObject aOutTransparency; + +#ifdef _DEBUG + static MObject aPixelCenter; + static MObject aPixelCenterX; + static MObject aPixelCenterY; +#endif + + static MObject aPointWorld; + static MObject aRayDepth; + static MObject aRayDirection; + static MObject aRayOrigin; +}; + +#endif diff --git a/mayaPlug/shaveBlindData.cpp b/mayaPlug/shaveBlindData.cpp new file mode 100644 index 0000000..deae943 --- /dev/null +++ b/mayaPlug/shaveBlindData.cpp @@ -0,0 +1,360 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MGlobal.h> +#include <string.h> +#include "shaveSDK.h" +#include "shaveBlindData.h" +#include "shaveDebug.h" +#include "shaveUtil.h" + + +MTypeId blindShaveData::dataTypeId(0x83001); +const MString blindShaveData::dataTypeName("blindShaveData"); + + +/* + * Proxy data class implementation + */ + +void* blindShaveData::creator() +{ + + return new blindShaveData; +} + + +blindShaveData::blindShaveData() +{ + init_MEMFILE(&m, 0); +} + + +blindShaveData::~blindShaveData() +{ + free_MEMFILE(&m); +} + + +void blindShaveData::clear() +{ + free_MEMFILE(&m); +} + + +void blindShaveData::copy(const MPxData& other) +// +// Deescription: +// Perform a copy. +// +{ + if (other.typeId() == blindShaveData::dataTypeId) + { + const blindShaveData& otherData = (const blindShaveData&)other; + + copy_MEMFILE(&m, (MEMFILE*)&otherData.m); + } + + return; +} + + +void blindShaveData::resize(int newSize) +{ + if (newSize < 0) newSize = 0; + + if (m.size != newSize) + { + clear(); + + if (newSize > 0) + { + m.data = (char*)malloc(newSize); + m.size = newSize; + } + } +} + + +void blindShaveData::setValue(const void* newData, int newDataLen) +{ + resize(newDataLen); + memcpy(m.data, newData, m.size); +} + + +MTypeId blindShaveData::typeId() const +{ + return blindShaveData::dataTypeId; +} + + +MString blindShaveData::name() const +{ + return blindShaveData::dataTypeName; +} + + +MStatus blindShaveData::readASCII( + const MArgList& args, unsigned& lastParsedElement +) +{ + MStatus status; + MString s; + int datasize; + int argLength = args.length(); + + + if (argLength > 0) + { + datasize = args.asInt(lastParsedElement++, &status); + + resize(datasize); + + // + // Prior to shaveNode version 11, we stored each byte as a separate + // value. From version 11 onward we stored the data as 4-byte + // words, with any leftover bytes stored individually at the end. + // + // So we need to compare the number of remaining arguments against + // the overall datasize to see which format this is. + // + int i; + + if (argLength - (int)lastParsedElement >= datasize) + { + for(i = 0; i < datasize; i++) + m.data[i] = (char)args.asInt(lastParsedElement++, &status); + } + else + { + int intval; +#if defined(IRIX) || defined(__ppc__) + char* p = (char*)&intval; +#endif + int stop = datasize - 3; + + for (i = 0; i < stop; i += 4) + { + intval = args.asInt(lastParsedElement++, &status); +#if defined(IRIX) || defined(__ppc__) + // + // We save values to file in LSB-first order. IRIX and + // PowerPC use MSB_first ordering, so we need to swap the + // byte order. + // + m.data[i+3] = p[0]; + m.data[i+2] = p[1]; + m.data[i+1] = p[2]; + m.data[i] = p[3]; +#else + *(int *)&m.data[i] = intval; +#endif + } + + // + // Grab any leftover bytes one at a time. + // + for (; i < datasize; i++) + m.data[i] = (char)args.asInt(lastParsedElement++, &status); + } + + return MS::kSuccess; + } + + return MS::kFailure; +} + + +MStatus blindShaveData::writeASCII(ostream& out) +{ + int i; + int intval; +#if defined(IRIX) || defined(__ppc__) + char* p = (char*)&intval; +#endif + int stop = 0; + + out << m.size; + + if (m.size > 3) stop = m.size - 3; + + // + // We write the data out as 4-byte ints because that's a bit faster and + // makes the file a bit smaller. + // + for (i = 0; i < stop; i += 4) + { +#if defined(IRIX) || defined(__ppc__) + // + // We save values to file in LSB-first order. IRIX and PowerPC use + // MSB-first ordering, so we need to swap the byte order. + // + p[0] = m.data[i+3]; + p[1] = m.data[i+2]; + p[2] = m.data[i+1]; + p[3] = m.data[i]; +#else + intval = *(int*)&m.data[i]; +#endif + out << ' ' << intval; + + // + // Start a new line after every 10 longwords, just to make viewing + // and editing the .ma files a bit easier. + // + if (i % 40 == 36) out << endl; + } + + // + // If the number of bytes was not divisible by 4, we'll have a few left + // over, so just write them out one byte at a time. + // + for (; i < m.size; i++) + out << ' ' << (int)m.data[i]; + + return out.fail() ? MS::kFailure : MS::kSuccess; +} + + +#ifdef JOETEST +MStatus blindShaveData::writeASCII(ostream& out) +{ + char intdata[10000]; + int position=0; + int start,stop; + + out << m.size; + out << " "; + +// for(int i = 0; i < m.size; i++) +while (position<m.size) +{ + int ii=0; + intdata[0]=0; + start=position; + stop=position+1000; + if (stop>m.size) stop=m.size; + + for(int i = start; i < stop; i++) + { + + sprintf(intdata, "%d ",m.data[i]); + ii++; + + } + out << intdata; + position+=ii; + intdata[0]=0; +} + + return out.fail() ? MS::kFailure : MS::kSuccess; + +} +#endif + + +// +// NOTE: You might have noticed that we read and write the MEMFILE's +// 'pos' member when doing binary I/O but not when doing ASCII +// I/O. Clearly Shave manages to get along fine without it, but +// this was how I inherited the code, and to change it now would +// break a lot of existing scene files, so we let the dichotomy +// stand. +// + + +MStatus blindShaveData::readBinary(istream& in, unsigned length) +{ + ENTER(); + + int expectedSize = length - 2 * sizeof(int); + int storedSize; + bool LSBfirst = false; + + in.read ((char*) &storedSize, sizeof(int)); + +#if defined(IRIX) || defined(__ppc__) + storedSize = shaveUtil::reorderInt(storedSize); +#endif + + if (storedSize != expectedSize) + { + // + // Prior to version 8 of the shaveNode, little-endian systems such + // as IRIX and OSX incorrectly wrote their sizes to the scene file + // in little-endian form, which made the files unreadable on other + // platforms. + // + // From version 8 onward that was corrected so that little-endian + // systems reorder the bytes before writing them to the file. + // + // However, it is possible that the current scene file is an old + // one from a little-endian system, in which case the sizes will + // still be in little-endian format. + // + // So let's flip the ordering and see if we get a match. + // + storedSize = shaveUtil::reorderInt(storedSize); + + if (storedSize != expectedSize) + { + // + // We must have a corrupt file. Just return without an error + // code because the license check later on will fail on the + // empty blind data and display an appropriate message. + // + RETURN(MS::kSuccess); + } + + LSBfirst = true; + } + + resize(storedSize); + + in.read((char*) &(m.pos), sizeof(int)); + +#if defined(IRIX) || defined(__ppc__) + m.pos = shaveUtil::reorderInt(m.pos); +#endif + + if (LSBfirst) m.pos = shaveUtil::reorderInt(m.pos); + +#ifdef DEBUG + char msg[200]; + sprintf(msg, "reading %d bytes of blind data", storedSize); + MGlobal::displayInfo(msg); +#endif + + in.read((char*) m.data, storedSize); + + RETURN(in.fail() ? MS::kFailure : MS::kSuccess); + +} + + +MStatus blindShaveData::writeBinary(ostream& out) +{ + ENTER(); + +#if defined(IRIX) || defined(__ppc__) + int pos = shaveUtil::reorderInt(m.pos); + int size = shaveUtil::reorderInt(m.size); +#else + int pos = m.pos; + int size = m.size; +#endif + + out.write ((char*) &(size), sizeof(int)); + out.write ((char*) &(pos), sizeof(int)); + out.write ((char*) m.data, m.size); + +#ifdef DEBUG + char msg[200]; + sprintf(msg, "wrote %d bytes of blind data", m.size); + MGlobal::displayInfo(msg); +#endif + + RETURN(out.fail() ? MS::kFailure : MS::kSuccess); +} diff --git a/mayaPlug/shaveBlindData.h b/mayaPlug/shaveBlindData.h new file mode 100644 index 0000000..297f21d --- /dev/null +++ b/mayaPlug/shaveBlindData.h @@ -0,0 +1,52 @@ +#ifndef _SHAVEBLINDDATA_H_ +#define _SHAVEBLINDDATA_H_ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include <maya/MPxData.h> +#include <maya/MTypeId.h> +#include <maya/MString.h> +#include <maya/MArgList.h> +#include <string.h> + +#include "shaveSDK.h" + + +class blindShaveData : public MPxData +{ +public: + blindShaveData(); + blindShaveData( const size_t ); + virtual ~blindShaveData(); + + // + // Override methods in MPxData. + virtual MStatus readASCII( const MArgList&, unsigned& lastElement ); + virtual MStatus readBinary(istream& in, unsigned length ); + virtual MStatus writeASCII(ostream& out ); + virtual MStatus writeBinary(ostream& out ); + virtual void copy(const MPxData&); + + void clear(); + MString name() const; + void resize(int newSize); + static void setCurrentNode(MObject node); + void setValue(const void* newData, int newDataLen); + MTypeId typeId() const; + + // + // static methods and data. + static const MString dataTypeName; + static MTypeId dataTypeId; + static void* creator(); + + MEMFILE m; + +protected: +}; + +#endif + diff --git a/mayaPlug/shaveBrushCtx.cpp b/mayaPlug/shaveBrushCtx.cpp new file mode 100644 index 0000000..7dc4518 --- /dev/null +++ b/mayaPlug/shaveBrushCtx.cpp @@ -0,0 +1,62 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <iostream> +using namespace std; + +#include <maya/MPxContext.h> +#include <maya/MString.h> + +#include "shaveBrushCtx.h" +#include "shaveSDK.h" + +const MString shaveBrushCtx::mCtxTypeName = "shaveBrush"; + + +shaveBrushCtx::shaveBrushCtx() +: mBrushMode(kBrushTranslate) +{ + setTitleString("Shave Brush Tool"); + setImage("shaveBrush.xpm", MPxContext::kImage1); +} + + +shaveBrushCtx::~shaveBrushCtx() +{ +} + + +MStatus shaveBrushCtx::strokeBegin( + VERT& eyePoint, VERT& viewDir, VERT& upDir, VERT& screenPos, VERT& worldPos +) +{ + SHAVEsculpt_setup( + mBrushMode, + eyePoint, + viewDir, + upDir, + screenPos, + worldPos + ); + + return MS::kSuccess; +} + + +MStatus shaveBrushCtx::strokeDrag( + VERT& screenPos, VERT& worldPos, VERT& screenDelta, VERT& worldDelta +) +{ + SHAVEsculpt_iterate(screenPos, worldPos, screenDelta, worldDelta); + + return MS::kSuccess; +} + + +MStatus shaveBrushCtx::strokeEnd() +{ + SHAVEsculpt_finish(); + + return MS::kSuccess; +} diff --git a/mayaPlug/shaveBrushCtx.h b/mayaPlug/shaveBrushCtx.h new file mode 100644 index 0000000..77f99ce --- /dev/null +++ b/mayaPlug/shaveBrushCtx.h @@ -0,0 +1,46 @@ +#ifndef shaveBrushCtx_h +#define shaveBrushCtx_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MString.h> + +#include "shaveCursorCtx.h" +#include "shaveSDKTYPES.h" + + +class shaveBrushCtx : public shaveCursorCtx +{ +public: + shaveBrushCtx(); + virtual ~shaveBrushCtx(); + + virtual void getClassName(MString& name) const { name = mCtxTypeName; } + BRUSH_MODE getBrushMode() const { return mBrushMode; } + void setBrushMode(BRUSH_MODE mode) { mBrushMode = mode; } + + virtual MStatus strokeBegin( + VERT& eyePoint, + VERT& viewDir, + VERT& upDir, + VERT& screenPos, + VERT& worldPos + ); + + virtual MStatus strokeDrag( + VERT& screenPos, + VERT& worldPos, + VERT& screenDelta, + VERT& worldDelta + ); + + virtual MStatus strokeEnd(); + + static const MString mCtxTypeName; + +protected: + BRUSH_MODE mBrushMode; +}; + +#endif diff --git a/mayaPlug/shaveBrushCtxCmd.cpp b/mayaPlug/shaveBrushCtxCmd.cpp new file mode 100644 index 0000000..fc3a92c --- /dev/null +++ b/mayaPlug/shaveBrushCtxCmd.cpp @@ -0,0 +1,88 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgParser.h> +#include <maya/MGlobal.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +#include "shaveBrushCtx.h" +#include "shaveBrushCtxCmd.h" +#include "shaveCursorCtxCmd.h" + +const MString shaveBrushCtxCmd::commandName = "shaveBrushCtx"; + +static const char* flMode = "-mode"; +static const char* fsMode = "-m"; + + +shaveBrushCtxCmd::shaveBrushCtxCmd() +{} + + +shaveBrushCtxCmd::~shaveBrushCtxCmd() +{} + + +MStatus shaveBrushCtxCmd::appendSyntax() +{ + MSyntax s = syntax(); + MStatus st = shaveCursorCtxCmd::appendSyntax(); + + if (st) + s.addFlag(fsMode, flMode, MSyntax::kUnsigned); + + return st; +} + + +MStatus shaveBrushCtxCmd::doEditFlags() +{ + MStatus st = shaveCursorCtxCmd::doEditFlags(); + + if (st && mCtx) + { + MArgParser args = parser(); + + if (args.isFlagSet(fsMode)) + { + unsigned mode; + args.getFlagArgument(fsMode, 0, mode); + + if (mode > kBrushClump) + { + MString msg = "Invalid brush mode. Cannot be greater than "; + msg += (int)kBrushClump; + MGlobal::displayError(msg); + return MS::kInvalidParameter; + } + + ((shaveBrushCtx*)mCtx)->setBrushMode((BRUSH_MODE)mode); + } + } + + return st; +} + + +MStatus shaveBrushCtxCmd::doQueryFlags() +{ + MStatus st = shaveCursorCtxCmd::doQueryFlags(); + + if (st && mCtx) + { + MArgParser args = parser(); + + if (args.isFlagSet(fsMode)) + setResult((int)((shaveBrushCtx*)mCtx)->getBrushMode()); + } + + return st; +} + + +shaveCursorCtx* shaveBrushCtxCmd::createContext() +{ + return new shaveBrushCtx; +} diff --git a/mayaPlug/shaveBrushCtxCmd.h b/mayaPlug/shaveBrushCtxCmd.h new file mode 100644 index 0000000..424d5c0 --- /dev/null +++ b/mayaPlug/shaveBrushCtxCmd.h @@ -0,0 +1,26 @@ +#ifndef shaveBrushCtxCmd_h +#define shaveBrushCtxCmd_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxContextCommand.h> + +#include "shaveCursorCtxCmd.h" + +class shaveBrushCtxCmd : public shaveCursorCtxCmd +{ +public: + shaveBrushCtxCmd(); + virtual ~shaveBrushCtxCmd(); + + MStatus appendSyntax(); + static void* createCmd() { return new shaveBrushCtxCmd; } + virtual shaveCursorCtx* createContext(); + virtual MStatus doEditFlags(); + virtual MStatus doQueryFlags(); + + static const MString commandName; +}; + +#endif diff --git a/mayaPlug/shaveBrushManip.cpp b/mayaPlug/shaveBrushManip.cpp new file mode 100644 index 0000000..bc0079b --- /dev/null +++ b/mayaPlug/shaveBrushManip.cpp @@ -0,0 +1,1069 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#if (defined LINUX) || (defined OSMac_) +#include <QtOpenGL/QGLContext> +#include <QtOpenGL/QGLWidget> + +#if QT_VERSION < 0x050000 +# include <QtGui/QWidget> +#else +# include <QtWidgets/QWidget> +#endif +#endif + +#include <maya/M3dView.h> +#include <maya/MDagPath.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnTransform.h> +#include <maya/MHWGeometry.h> +#include <maya/MGlobal.h> +#include <maya/MPoint.h> +#include <maya/MShaderManager.h> +#include <maya/MString.h> +#include <maya/MVector.h> + +#if defined(_WIN32) && (MAYA_API_VERSION >= 201600) +#include <Windows.h> +#endif + +#ifdef OSMac_MachO_ +# include <OpenGL/gl.h> +# include <OpenGL/glu.h> +//# include <AppKit/NSOpenGL.h> +#else +//#define GL_GLEXT_PROTOTYPES +# include <GL/gl.h> +# include <GL/glu.h> +#endif + +#include "shaveBrushManip.h" + +MTypeId shaveBrushManip::id(0x001029B8); +MString shaveBrushManip::nodeTypeName = "shaveBrushManip"; + + +// The 'fast brush' does an XOR draw on top of the view, meaning that it +// can be drawn and erased without having Maya redraw the entire view. +// +// The standard brush is drawn normally during a refresh, which is +// triggered either by moving a proxy transform within the scene to match +// the mouse movement, or by forcing a refresh. +// +// The fast brush is enabled by default in shaveGlobals, but on some +// graphics cards it can cause visual anomalies, in which case the user +// can switch to the standard brush. + +shaveBrushManip::shaveBrushManip() +: mBrushSize(0.0f) +, mClearOld(false) +, mCX(-1) +, mCY(-1) +, mFastBrush(true) +, mIsActive(false) +, mViewHeight(0) +, mViewWidth(0) +, mWindow(0) +{ + aWidget = NULL; +} + + +shaveBrushManip::~shaveBrushManip() +{ + mProxy = MObject::kNullObj; +} + + +MStatus shaveBrushManip::connectToDependNode(const MObject& node) +{ + //thisObj = thisMObject(); + + finishAddingManips(); + MPxManipContainer::connectToDependNode(node); + + if (!mFastBrush) + { + MFnDependencyNode nodeFn(node); + mProxy = node; + } + + return MS::kSuccess; +} + + +MStatus shaveBrushManip::createChildren() +{ + // + // We don't want any manips because their handles, even though not + // drawn, can still capture mouse clicks. + // + return MS::kSuccess; +} + + +void shaveBrushManip::draw( + M3dView& view, + const MDagPath& path, + M3dView::DisplayStyle dStyle, + M3dView::DisplayStatus dStatus +) +{ + if (mFastBrush) + { + // + // If Maya is asking us to redraw in the same view where we last did a + // draw, then it must have already cleared the old image, so we should + // not try to clear it ourselves. + // + if (mClearOld && isSameView(view)) mClearOld = false; + } + else + { + M3dView activeView = M3dView::active3dView(); + + if ((view.window() == activeView.window()) + && (mCX >= 0) && (mCY >= 0) + && (mCX < view.portWidth()) && (mCY < view.portHeight())) + { + prepareView(activeView); + drawBrush(); + restoreView(activeView); + } + } +} + + +void shaveBrushManip::getBrushRadii(int& rx, int& ry) const +{ + rx = mRX; + ry = mRY; +} + + +void shaveBrushManip::leaveView() +{ + //MGlobal::displayInfo("leaveView"); + + /////////////////////////// + //return; + /////////////////////////// + + if (mIsActive) + { + M3dView activeView = M3dView::active3dView(); + + if (mFastBrush) + eraseBrush(activeView); + else + { + if(mCX != -1 || mCY != -1) //to avoid exra redraws + { + // This will let us know not to draw the brush. + mCX = mCY = -1; + + // Force Maya to redraw the view so that the brush will be + // erased. + activeView.refresh(false, true); + } + } + } +} + + +void shaveBrushManip::setActive(bool active) +{ + /////////////////////////// + //return; + /////////////////////////// + + if (mIsActive != active) + { + // + // If we're being made active then clear out the old view + // information. + // + // If we're being made inactive then erase the current brush image. + // + if (active) + { + mWindow = 0; + mViewHeight = 0; + mViewWidth = 0; + } + else + { + M3dView activeView = M3dView::active3dView(); + eraseBrush(activeView, false); + } + + mIsActive = active; + } +} + + +void shaveBrushManip::setBrushPos( + int cx, int cy, bool forceRedraw, bool isMayaDraw +) +{ + /////////////////////////// + //return; + /////////////////////////// + + //printf("set pos %i %i\n",cx,cx);fflush(stdout); + + M3dView activeView = M3dView::active3dView(); + bool isNewView = !isSameView(activeView); + + // If someone is giving us brush positions, then we must be active. + setActive(true); + + if (mFastBrush) + { + // If we were previously inactive or the view has changed, then we + // will need to redraw the brush even if its position is unchanged. + if (isNewView || !mIsActive) + forceRedraw = true; + + // If the brush has moved, or this is a forced redraw, then do the + // draw. + if (forceRedraw || (mCX != cx) || (mCY != cy)) + { + bool activeViewPrepared = eraseBrush(activeView, true); + + if (!activeViewPrepared) prepareView(activeView); + + // If we've changed views, or the view has changed size, then + // we need to recalculate the radii. + if (isNewView) + { + saveView(activeView); + calculateRadii(); + } + + mCX = cx; + mCY = cy; + + if ((mCX >= 0) && (mCY >= 0)) + { + drawBrush(); + mClearOld = true; + } + + if (!isMayaDraw) + swapGLBuffers(activeView); + + restoreView(activeView); + } + } + else + { + // If the brush has moved, move the proxy transform + // correspondingly. + if ((mCX != cx) || (mCY != cy)) + { + // If we've changed views, or the view has changed size, then + // we need to recalculate the radii. + if (isNewView) + { + saveView(activeView); + calculateRadii(); + } + + MPoint nearPlane; + MPoint farPlane; + activeView.viewToWorld(cx, cy, nearPlane, farPlane); + + MVector translation( + (nearPlane.x + farPlane.x) / 2.0, + (nearPlane.y + farPlane.y) / 2.0, + (nearPlane.z + farPlane.z) / 2.0 + ); + MFnTransform transformFn(mProxy); + transformFn.setTranslation(translation, MSpace::kObject); + + mCX = cx; + mCY = cy; + } + } + + MHWRender::MRenderer::setGeometryDrawDirty(thisObj); +} + + +void shaveBrushManip::setBrushSize(float brushSize) +{ + /////////////////////////// + //return; + /////////////////////////// + + if (brushSize != mBrushSize) + { + M3dView view; + bool viewPrepared = false; + bool foundView = findOldView(view); + + if (foundView) + viewPrepared = eraseBrush(view, true); + + mBrushSize = brushSize; + + if (foundView) + { + calculateRadii(); + + if (!viewPrepared) + prepareView(view); + + if ((mCX >= 0) && (mCY >= 0)) + { + drawBrush(); + mClearOld = true; + } + + swapGLBuffers(view); + restoreView(view); + } + } +} + + +//---------------------------------------------------------------------- +// +// Internal Methods +// +//---------------------------------------------------------------------- + +void shaveBrushManip::calculateRadii() +{ + unsigned radius; + + if (mViewHeight < mViewWidth) + radius = (unsigned)((float)mViewHeight * mBrushSize / 2.0f + 0.5); + else + radius = (unsigned)((float)mViewWidth * mBrushSize / 2.0f + 0.5); + + if (radius < 4) radius = 4; + + // + // %%% We eventually need to compensate for non-square pixels by + // assigning X and Y different radii. + // + mRX = radius; + mRY = radius; +} + + +void shaveBrushManip::drawBrush() const +{ + //printf("draw pos %i %i\n",mCX,mCY);fflush(stdout); + // + // Draw the outer circle. We use an ellipse drawing method because + // eventually we want to support displays with non-square pixels, which + // will require different X and Y radii. + // + drawEllipse(mCX, mCY, mRX, mRY); + +#if 0 + // + // Draw a small crosshair in the center. + // + glBegin(GL_LINES); + glVertex2i(mCX-4, mCY); + glVertex2i(mCX+4, mCY); + + glVertex2i(mCX, mCY-4); + glVertex2i(mCX, mCY+4); + glEnd(); +#endif +} + + +// +// This method uses a DDA to draw an ellipse with the given radii into the +// bitmap. +// +// The algorithm is taken almost verbatim from Tim Kientzle's 'Algorithm +// Alley' column in the July, 1994 issue of Dr. Dobb's Journal. +// +void shaveBrushManip::drawEllipse(int cx, int cy, unsigned rx, unsigned ry) +{ + const unsigned rxSquared = rx * rx; + const unsigned rySquared = ry * ry; + const unsigned twoRXSquared = 2 * rxSquared; + const unsigned twoRYSquared = 2 * rySquared; + + glBegin(GL_POINTS); + + // + // Plot the octant from the top of the ellipse to the top-right + // and reflect it in the other four quadrants. + // + int x = 0; + int y = ry; + + int twoXTimesRYSquared = 0; + int twoYTimesRXSquared = (int)(y * twoRXSquared); + int error = -(int)(y * rxSquared); + + while (twoXTimesRYSquared <= twoYTimesRXSquared) + { + drawMirroredVert(cx, cy, x, y); + + x += 1; + twoXTimesRYSquared += (int)twoRYSquared; + error += twoXTimesRYSquared - (int)rySquared; + + if (error >= 0) + { + y -= 1; + twoYTimesRXSquared -= (int)twoRXSquared; + error -= twoYTimesRXSquared; + } + } + + // + // Plot the octant from the right of the ellipse to the top-right + // and reflect it in the other four quadrants. + // + x = rx; + y = 0; + twoXTimesRYSquared = (int)(x * twoRYSquared); + twoYTimesRXSquared = 0; + error = -(int)(x * rySquared); + + while (twoXTimesRYSquared > twoYTimesRXSquared) + { + drawMirroredVert(cx, cy, x, y); + + y += 1; + twoYTimesRXSquared += (int)twoRXSquared; + error += twoYTimesRXSquared - (int)rxSquared; + + if (error >= 0) + { + x -= 1; + twoXTimesRYSquared -= (int)twoRYSquared; + error -= twoXTimesRYSquared; + } + } + + glEnd(); +} + + +void shaveBrushManip::drawMirroredVert(int cx, int cy, int x, int y) +{ + // + // Mirror the vert in the four quadrants around the center point. + // + // Because we are drawing in XOR mode, we don't want to plot the same + // point an even number of times as that will be as if we hadn't + // plotted it at all. So we have special cases for x==0 and y==0 to + // prevent that. + // + if (x == 0) + { + glVertex2i(cx, cy + y); + glVertex2i(cx, cy - y); + } + else if (y == 0) + { + glVertex2i(cx + x, cy); + glVertex2i(cx - x, cy); + } + else + { + glVertex2i(cx + x, cy + y); + glVertex2i(cx + x, cy - y); + glVertex2i(cx - x, cy + y); + glVertex2i(cx - x, cy - y); + } +} + + +bool shaveBrushManip::eraseBrush( + M3dView& currentView, bool dontRestoreCurrentView +) +{ + bool currentViewIsPrepared = false; + + + if (mFastBrush && mClearOld) + { + // + // If the previous brush image was drawn in a different view from + // the one passed to us by the caller, then we must find that view. + // + if (!isSameView(currentView)) + { + M3dView oldView; + + if (findOldView(oldView)) + { + prepareView(oldView); + drawBrush(); + swapGLBuffers(oldView); + restoreView(oldView); + } + } + else + { + prepareView(currentView); + drawBrush(); + + if (dontRestoreCurrentView) + currentViewIsPrepared = true; + else + { + swapGLBuffers(currentView); + restoreView(currentView); + } + } + + mClearOld = false; + } + + return currentViewIsPrepared; +} + + +bool shaveBrushManip::findOldView(M3dView& view) const +{ + if (mIsActive) + { + unsigned numViews = M3dView::numberOf3dViews(); + unsigned i; + + for (i = 0; i < numViews; i++) + { + M3dView::get3dView(i, view); + + if (isSameView(view)) return true; + } + } + + return false; +} + + +bool shaveBrushManip::isSameView(M3dView& view) const +{ + if ((view.window() != mWindow) + || (view.portHeight() != mViewHeight) + || (view.portWidth() != mViewWidth)) + { + return false; + + } + + +#if OSMac_ + QWidget* wgt = view.widget(); + return (wgt == aWidget); +#endif + + return true; +} + +void shaveBrushManip::prepareView(M3dView& view) +{ + view.beginGL(); + + glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D( + 0.0, (GLdouble)view.portWidth(), + 0.0, (GLdouble)view.portHeight() + ); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // This bit of magic is from marqueeTool.cpp in the devkit. + glTranslatef(0.375, 0.375, 0.0); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + if (mFastBrush) + { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + + // Because we're doing XOR, if we set the draw color to 12 (red) + // it won't appear red when over the default grey background of + // Maya's views. By choosing light green XOR will give us + // reddish-orange against a grey background, which is at least + // close to red. + view.setDrawColor(18, M3dView::kActiveColors); + } + else + view.setDrawColor(12, M3dView::kActiveColors); // red +} + + +void shaveBrushManip::restoreView(M3dView& view) +{ + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); + + view.endGL(); +} + + +void shaveBrushManip::saveView(M3dView& view) +{ + mWindow = view.window(); + mViewHeight = view.portHeight(); + mViewWidth = view.portWidth(); + +#ifdef OSMac_ + // + // On OSX the views do not have their own windows but are merely + // regions within the main window. So to know if we have the correct + // view we must compare not just the window but the position of the + // region. + // + aWidget = view.widget(); +#endif +} + + +void shaveBrushManip::swapGLBuffers(M3dView& view) +{ +#if defined(LINUX) + glXSwapBuffers(view.display(), view.window()); +#elif defined(OSMac_) + //there is no good way to swap buffers now + //so we grey out 'fast brush' checkbox for 2011 + + //const QGLContext* qgl = QGLContext::currentContext(); + //if(qgl) + // qgl->swapBuffers(); + //else + // MGlobal::displayInfo("null QGLContext"); + + //NSOpenGLContext* nsgl = view.display(); + //[nsgl flushBuffer]; //cocoa + //nsgl->flushBuffer(); + //CGLContextObj* cgl = nsgl->CGLContextObj(); +#elif defined(_WIN32) + SwapBuffers(view.deviceContext()); +#else + #error Unsupported OS type. +#endif +} + +MString shaveBrushManip::drawDbClassification("drawdb/geometry/shaveBrushManip"); +MString shaveBrushManip::drawRegistrantId("shave_and_haircut_brush"); + +MObject shaveBrushManip::trigger; + +MStatus shaveBrushManip::initialize() +{ + MStatus stat; + MFnNumericAttribute na; + + trigger = na.create("atrigger", "tr", MFnNumericData::kInt,0,&stat); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveBrushManip: can't create attribute"); + } + na.setStorable(false); + na.setHidden(true); + + stat = addAttribute(trigger); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveBrushManip: can't add attribute"); + } + + return MStatus::kSuccess; + //return MPxManipContainer::initialize(); +} + + +// pre-draw callback +static void shaveBrushPreDrawCallback( + MHWRender::MDrawContext& context, + const MHWRender::MRenderItemList& renderItemList, + MHWRender::MShaderInstance *shaderInstance + ) +{ + //printf("Pre-draw callback triggered for render item with name '%s'\n", renderItem->name().asChar()); +} +// post-draw callback +static void shaveBrushPostDrawCallback( + MHWRender::MDrawContext& context, + const MHWRender::MRenderItemList& renderItemList, + MHWRender::MShaderInstance *shaderInstance + ) +{ + //printf("Post-draw callback triggered for render item with name '%s'\n", renderItem->name().asChar()); +} + +shaveBrushOverride::shaveBrushOverride(const MObject& obj) +: MPxGeometryOverride(obj) +, brush(NULL) +{ + MStatus status; + MFnDependencyNode node(obj, &status); + if (status) + { + brush = dynamic_cast<shaveBrushManip*>(node.userNode()); + } +} + +shaveBrushOverride::~shaveBrushOverride() +{ +} + +MHWRender::DrawAPI shaveBrushOverride::supportedDrawAPIs() const +{ + //return MHWRender::kAllDevices; + return MHWRender::kOpenGL; +} + +void shaveBrushOverride::updateDG() +{ + if (brush) + { + //nothing to do at the moment + } +} + +//#define BRUSH_BY_SHADER //geom shaders are + +static const bool debugShader = false;//true; + +void shaveBrushOverride::updateRenderItems(const MDagPath& path, + MHWRender::MRenderItemList& list) +{ + MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) return; + + const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager(); + if (!shaderMgr) return; + + + MHWRender::MRenderItem* item = NULL; + int index = list.indexOf( + "shaveBrush", +//#ifdef BRUSH_BY_SHADER +// MHWRender::MGeometry::kPoints, +//#else + MHWRender::MGeometry::kLines, +//#endif + MHWRender::MGeometry::kAll); + + if (index < 0) + { + item = MHWRender::MRenderItem::Create + ( "shaveBrush", +//#ifdef BRUSH_BY_SHADER +// MHWRender::MGeometry::kPoints, +//#else + MHWRender::MGeometry::kLines, +//#endif + MHWRender::MGeometry::kAll, + false); + list.append(item); + + //MHWRender::MShaderInstance* shader = shaderMgr->getStockShader( + // MHWRender::MShaderManager::k3dSolidShader, + // debugShader ? shaveBrushPreDrawCallback : NULL, + // debugShader ? shaveBrushPostDrawCallback : NULL); + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","Brush"); + if (shader) + { + //static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; + //shader->setParameter("solidColor", theColor); + + // assign shader + item->setShader(shader); + + // sample debug code + if (debugShader) + { + MStringArray params; + shader->parameterList(params); + unsigned int numParams = params.length(); + printf("DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH %d\n", numParams); + for (unsigned int i=0; i<numParams; i++) + { + printf("ParamName='%s', ParamType=", params[i].asChar()); + switch (shader->parameterType(params[i])) + { + case MHWRender::MShaderInstance::kInvalid: + printf("'Invalid', "); + break; + case MHWRender::MShaderInstance::kBoolean: + printf("'Boolean', "); + break; + case MHWRender::MShaderInstance::kInteger: + printf("'Integer', "); + break; + case MHWRender::MShaderInstance::kFloat: + printf("'Float', "); + break; + case MHWRender::MShaderInstance::kFloat2: + printf("'Float2', "); + break; + case MHWRender::MShaderInstance::kFloat3: + printf("'Float3', "); + break; + case MHWRender::MShaderInstance::kFloat4: + printf("'Float4', "); + break; + case MHWRender::MShaderInstance::kFloat4x4Row: + printf("'Float4x4Row', "); + break; + case MHWRender::MShaderInstance::kFloat4x4Col: + printf("'Float4x4Col', "); + break; + case MHWRender::MShaderInstance::kTexture1: + printf("'1D Texture', "); + break; + case MHWRender::MShaderInstance::kTexture2: + printf("'2D Texture', "); + break; + case MHWRender::MShaderInstance::kTexture3: + printf("'3D Texture', "); + break; + case MHWRender::MShaderInstance::kTextureCube: + printf("'Cube Texture', "); + break; + case MHWRender::MShaderInstance::kSampler: + printf("'Sampler', "); + break; + default: + printf("'Unknown', "); + break; + } + printf("IsArrayParameter='%s'\n", shader->isArrayParameter(params[i]) ? "YES" : "NO"); + } + printf("END PARAM LIST\n"); + fflush(stdout); + } + } + } +} + + + +void shaveBrushOverride::populateGeometry( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& itemList, + MHWRender::MGeometry& data + ) +{ + MHWRender::MVertexBuffer* positionBuffer = NULL; + float* positions = NULL; + + const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); + int numVertexReqs = descList.length(); + MHWRender::MVertexBufferDescriptor desc; + +#ifndef BRUSH_BY_SHADER + int nsegs = 24; + int nverts = nsegs*2; +#endif + + for (int reqNum=0; reqNum<numVertexReqs; reqNum++) + { + if (!descList.getDescriptor(reqNum, desc)) + { + continue; + } + + switch (desc.semantic()) + { + case MHWRender::MGeometry::kPosition: + { + positionBuffer = data.createVertexBuffer(desc); + if (positionBuffer) +#ifdef BRUSH_BY_SHADER + positions = (float*)positionBuffer->acquire(2); +#else + positions = (float*)positionBuffer->acquire(nverts); +#endif + } + break; + + case MHWRender::MGeometry::kColor: + case MHWRender::MGeometry::kTexture: + case MHWRender::MGeometry::kNormal: + case MHWRender::MGeometry::kTangent: + case MHWRender::MGeometry::kBitangent: + default: + break; + } + } + + + if (positions) + { +#ifdef BRUSH_BY_SHADER + + M3dView activeView = M3dView::active3dView(); + float H = (float)activeView.portHeight(); + float W = (float)activeView.portWidth(); + + float x = (brush->mCX - 0.5f*W)/(0.5f*W); + float y = (brush->mCY - 0.5f*H)/(0.5f*H); + + //MVector C(0.0f, 0.0f, 0.0f); + MVector C(x, y, 0.0f); + //MVector C(brush->mCX, brush->mCY, 0.0f); + + positions[0] = C.x; + positions[1] = C.y; + positions[2] = 0.0f; + + + float w = brush->mRX/(0.5f*W); + float h = brush->mRY/(0.5f*H); + + MVector P(w, h, 0.0f); + //MVector P(brush->mRX, brush->mRY, 0.0f); + + positions[3] = P.x; + positions[4] = P.y; + positions[5] = 0.0f; +#else + float R = 0.3f; + + M3dView activeView = M3dView::active3dView(); + float H = (float)activeView.portHeight(); + float W = (float)activeView.portWidth(); + + float x = (brush->mCX - 0.5f*W)/(0.5f*W); + float y = (brush->mCY - 0.5f*H)/(0.5f*H); + MVector C(x, y, 0.0f); + + float _w = brush->mRX/(0.5f*W); + float _h = brush->mRY/(0.5f*H); + MVector P(_w, _h, 0.0f); + + const float pi = 3.14159f; + float dstep = 360.0f/nsegs; + float rstep = dstep*pi/(180.0f); + int pid = 0; + for (int h = 0; h < nsegs; h++) + { + int hh = (h+1)%nsegs; + float a1 = rstep*(float)h; + float a2 = rstep*(float)hh; + + //float x1 = C.x + R*sin(a1); + //float y1 = C.y + R*cos(a1); + + //float x2 = C.x + R*sin(a2); + //float y2 = C.y + R*cos(a2); + + float x1 = (float)(C.x + P.x*sin(a1)); + float y1 = (float)(C.y + P.y*cos(a1)); + + float x2 = (float)(C.x + P.x*sin(a2)); + float y2 = (float)(C.y + P.y*cos(a2)); + + positions[pid++] = x1; + positions[pid++] = y1; + positions[pid++] = 0.0f; + + positions[pid++] = x2; + positions[pid++] = y2; + positions[pid++] = 0.0f; + } +#endif + positionBuffer->commit(positions); + } + + // index data + MHWRender::MIndexBuffer* indexBuffer = NULL; + int numItems = itemList.length(); + for (int i=0; i<numItems; i++) + { + const MHWRender::MRenderItem* item = itemList.itemAt(i); + if (!item) continue; + + // Enable to debug vertex buffers that are associated with each render item. + // Can also use to generate indexing better, but we don't need that here. + // Also debugs custom data on the render item. + static const bool debugStuff = false; + if (debugShader) + { + const MHWRender::MVertexBufferDescriptorList& itemBuffers =item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + fflush(stdout); + } + + //same indexing for all + if (item->name() == "shaveBrush") + { + if (!indexBuffer) + { + indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer) + { +#ifdef BRUSH_BY_SHADER + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(2); + if (buffer) + { + buffer[0] = 0; + buffer[1] = 1; + indexBuffer->commit(buffer); + } +#else + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(nsegs*2); + if (buffer) + { + int idx = 0; + for (int h = 0; h < nsegs; h++) + { + buffer[idx] = idx; + buffer[idx+1] = idx+1; + idx += 2; + } + indexBuffer->commit(buffer); + } +#endif + } + } + + // Associate same index buffer with either render item + if (indexBuffer) + { + item->associateWithIndexBuffer(indexBuffer); + + } + } + } + +} + +void shaveBrushOverride::cleanUp() +{ + +} diff --git a/mayaPlug/shaveBrushManip.h b/mayaPlug/shaveBrushManip.h new file mode 100644 index 0000000..19dc49d --- /dev/null +++ b/mayaPlug/shaveBrushManip.h @@ -0,0 +1,148 @@ +#ifndef shaveBrushManip_h +#define shaveBrushManip_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/M3dView.h> +#include <maya/MObject.h> +#include <maya/MPxGeometryOverride.h> +#include <maya/MPxManipContainer.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> +#include <maya/MViewport2Renderer.h> + + +#if MAYA_API_VERSION < 20180000 +class MDagPath; +#endif + +class QWidget; + +class shaveBrushManip : public MPxManipContainer +{ + friend class shaveBrushOverride; + +public: + static MString drawDbClassification; + static MString drawRegistrantId; + + //test need to invoke geometry override to update + static MObject trigger; + + MObject thisObj; + void dirtyDisplay(){MHWRender::MRenderer::setGeometryDrawDirty(thisMObject());} + //void preDrawUI (const M3dView &view) + //{ + // MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + //} + shaveBrushManip(); + virtual ~shaveBrushManip(); + + virtual MStatus connectToDependNode(const MObject& node); + virtual MStatus createChildren(); + static void* creator() { return new shaveBrushManip; } + + virtual void draw( + M3dView& view, + const MDagPath& path, + M3dView::DisplayStyle dStyle, + M3dView::DisplayStatus dStatus + ); + + static MStatus initialize(); + + void getBrushRadii(int& rx, int& ry) const; + void leaveView(); + void redrawBrush() { setBrushPos(mCX, mCY, true); } + +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + void setActive(bool active); + + void setBrushPos( + int cx, + int cy, + bool forceRedraw = false, + bool isMayaDraw = false + ); + + void setBrushSize(float brushSize); + void setFastBrush(bool fastBrush) { mFastBrush = fastBrush; } + + + static MTypeId id; + static MString nodeTypeName; + +protected: + void calculateRadii(); + void drawBrush() const; + static void drawEllipse(int cx, int cy, unsigned rx, unsigned ry); + static void drawMirroredVert(int cx, int cy, int x, int y); + + bool eraseBrush( + M3dView& currentView, + bool dontRestoreCurrentView = false + ); + + bool findOldView(M3dView& view) const; + bool isSameView(M3dView& view) const; + void prepareView(M3dView& view); + void restoreView(M3dView& view); + void saveView(M3dView& view); + static void swapGLBuffers(M3dView& view); + + + float mBrushSize; + bool mClearOld; + int mCX; + int mCY; + bool mFastBrush; + bool mIsActive; + MObject mProxy; + unsigned mRX; + unsigned mRY; + int mViewHeight; + int mViewWidth; + MNativeWindowHdl mWindow; + QWidget* aWidget; + +#ifdef OSMac_ + GLint mViewPosX; + GLint mViewPosY; +#endif +}; + + +class shaveBrushOverride : public MHWRender::MPxGeometryOverride { +public: + static MPxGeometryOverride* Creator(const MObject& obj) + { + return new shaveBrushOverride(obj); + } + virtual ~shaveBrushOverride(); + + virtual MHWRender::DrawAPI supportedDrawAPIs() const; + virtual void updateDG(); + + virtual void updateRenderItems( + const MDagPath& path, + MHWRender::MRenderItemList& list); + + virtual void populateGeometry( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& renderItems, + MHWRender::MGeometry& data + ); + + virtual void cleanUp(); + +protected: + shaveBrushOverride(const MObject& obj); + + shaveBrushManip* brush; +}; + +#endif diff --git a/mayaPlug/shaveCallbacks.cpp b/mayaPlug/shaveCallbacks.cpp new file mode 100644 index 0000000..9ca292b --- /dev/null +++ b/mayaPlug/shaveCallbacks.cpp @@ -0,0 +1,795 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDGMessage.h> +#include <maya/MDGModifier.h> +#include <maya/MEventMessage.h> +#include <maya/MFileIO.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MObjectArray.h> +#include <maya/MPlug.h> +#include <maya/MSceneMessage.h> +#include <maya/MSelectionList.h> + +#include "shaveCallbacks.h" +#include "shaveDebug.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveRender.h" +#include "shaveSDK.h" +#include "shaveUtil.h" + + +//-------------------------------------------------------------------- +// +// External Interface +// +//-------------------------------------------------------------------- + +void shaveCallbacks::endLoad(bool isImport, bool isReference) +{ + ENTER(); + + shaveUtil::setLoadingFile(false); + + // + // The output mesh of a shaveHairShape can depend upon other objects in + // the scene: hair objects, skull objects, shaveGlobals, etc. So the + // shaveHairShape cannot properly initialize its output mesh until + // after the scene is fully loaded. + // + // If we're done loading all files, then run through all the + // shaveHairShapes and initialize any new ones. + // + if (!shaveUtil::isLoadingFile()) initializeHairNodes(); + + //if default renderer is changed in Maya prefs + //we need to make sure that correct MEL callbacks are set + if (!shaveUtil::isLoadingFile()) + MGlobal::executeCommand("shave_selectedRendererChanged"); + + LEAVE(); +} + + +void shaveCallbacks::mayaExiting(void* clientData) +{ + if (mCleanUpMELOnExit) + { + MGlobal::executeCommand("shaveCleanup(true)"); + } +//fprintf (stdout,"why are we calling SHAVEcleanup()?\n");fflush(stdout); + SHAVEcleanup(); + + mCleanUpMELOnExit = false; + + // You might think that we should call removeCallbacks() here, but it's + // possible that this method is executing within one of those callbacks + // and Maya does not behave well when a callback function removes + // itself. So we leave the callback removal to others. + // +} + + +void shaveCallbacks::prepareForNewScene(bool firstTime) +{ + ENTER(); + + SHAVEclear_stack(); + + LEAVE(); +} + + +void shaveCallbacks::registerCallbacks() +{ + // + // Now that we know the plugin has loaded successfully, register + // the callbacks. + // + beforeExportID = MSceneMessage::addCallback( + MSceneMessage::kBeforeExport, beforeExport + ); + + afterExportID = MSceneMessage::addCallback( + MSceneMessage::kAfterExport, afterExport + ); + + beforeImportID = MSceneMessage::addCallback( + MSceneMessage::kBeforeImport, beforeImport + ); + + afterImportID = MSceneMessage::addCallback( + MSceneMessage::kAfterImport, afterImport + ); + + beforeImportRefID = MSceneMessage::addCallback( + MSceneMessage::kBeforeImportReference, + beforeImportReference + ); + + afterImportRefID = MSceneMessage::addCallback( + MSceneMessage::kAfterImportReference, + afterImportReference + ); + + beforeOpenID = MSceneMessage::addCallback( + MSceneMessage::kBeforeOpen, beforeOpen + ); + + afterOpenID = MSceneMessage::addCallback( + MSceneMessage::kAfterOpen, afterOpen + ); + + beforeReferenceID = MSceneMessage::addCallback( + MSceneMessage::kBeforeReference, + beforeReference + ); + + afterReferenceID = MSceneMessage::addCallback( + MSceneMessage::kAfterReference, + afterReference + ); + + beforeSaveID = MSceneMessage::addCallback( + MSceneMessage::kBeforeSave, beforeSave + ); + + afterSaveID = MSceneMessage::addCallback( + MSceneMessage::kAfterSave, afterSave + ); + + afterNewID = MSceneMessage::addCallback( + MSceneMessage::kAfterNew, afterNew + ); + + mayaExitingID = MSceneMessage::addCallback( + MSceneMessage::kMayaExiting, + mayaExiting + ); + + nodeCreatedID = MDGMessage::addNodeAddedCallback(nodeCreated); + + shaveNodeDeletedID= MDGMessage::addNodeRemovedCallback( + shaveNodeDeleted, + shaveHairShape::nodeTypeName + ); + + selectionChangedID= MEventMessage::addEventCallback( + "SelectionChanged", selectionChanged + ); + + // The node added/removed callbacks above keep a count of the number + // of hair nodes in the scene. There may already be some so let's get + // the counter properly initialized. + if (shaveHairShape::initNumShaveNodes() > 0) + { + // There is additional initialization required when a scene + // contains shaveNodes. + shaveCallbacks::firstHairNode(); + } + + // Several of the callbacks above keep track of whether we're in the + // middle of loading a file or not. A file load may already be in + // progress, so let's get the state of that set correctly. + // + // (It would be nice to just use MFileIO::isReadingFile() everywhere, + // instead of having to track it, but Maya sets isReadingFile to false + // before the newly-loaded nodes have had a chance to run their first + // compute cycle, so that's no good.) + // + if (MFileIO::isReadingFile()) + shaveCallbacks::startLoad(); + else + { + // The callbacks above are responsible for initializing any + // existing hair nodes once all file loading has completed. File + // loading may have completed before the callbacks were set up, + // though, so we must initialize any hair nodes which are already + // in the scene. + initializeHairNodes(); + } +} + + +void shaveCallbacks::removeCallbacks() +{ + removeCallback(beforeExportID); + removeCallback(afterExportID); + removeCallback(beforeImportID); + removeCallback(afterImportID); + removeCallback(beforeImportRefID); + removeCallback(afterImportRefID); + removeCallback(beforeOpenID); + removeCallback(afterOpenID); + removeCallback(beforeReferenceID); + removeCallback(afterReferenceID); + removeCallback(beforeSaveID); + removeCallback(afterSaveID); + + removeCallback(afterNewID); + removeCallback(mayaExitingID); + removeCallback(nodeCreatedID); + removeCallback(shaveNodeDeletedID); + removeCallback(selectionChangedID); +} + + +void shaveCallbacks::startLoad() +{ + ENTER(); + shaveUtil::setLoadingFile(true); + LEAVE(); +} + + + +//-------------------------------------------------------------------- +// +// Internal Interface +// +//-------------------------------------------------------------------- + +bool shaveCallbacks::mCleanUpMELOnExit = true; + +// +// Callback IDs +// +MCallbackId shaveCallbacks::beforeExportID = 0; +MCallbackId shaveCallbacks::afterExportID = 0; +MCallbackId shaveCallbacks::beforeImportID = 0; +MCallbackId shaveCallbacks::afterImportID = 0; +MCallbackId shaveCallbacks::beforeImportRefID = 0; +MCallbackId shaveCallbacks::afterImportRefID = 0; +MCallbackId shaveCallbacks::beforeOpenID = 0; +MCallbackId shaveCallbacks::afterOpenID = 0; +MCallbackId shaveCallbacks::beforeReferenceID = 0; +MCallbackId shaveCallbacks::afterReferenceID = 0; +MCallbackId shaveCallbacks::beforeSaveID = 0; +MCallbackId shaveCallbacks::afterSaveID = 0; + +MCallbackId shaveCallbacks::afterNewID = 0; +MCallbackId shaveCallbacks::mayaExitingID = 0; +MCallbackId shaveCallbacks::nodeCreatedID = 0; +MCallbackId shaveCallbacks::shaveNodeDeletedID = 0; +MCallbackId shaveCallbacks::selectionChangedID = 0; + + +void shaveCallbacks::beforeExport(void* clientData) +{ + ENTER(); + + updateNodeVersions(); + + // + // The renderer gets called during some types of export, so make sure + // that it knows about it. + // + shaveRender::exportStart(); + + // + // There's a bug in Maya's ASCII format when saving locked, shared + // nodes. shaveGlobals is a shared node so if this is an ASCII save, + // be sure to unlock it first. + // + // We can't use MFileIO::fileType() because that reflects the type of + // the main scene, not the export file. Instead, we'll look at the + // filename and if ends in anything other than '.mb' we'll unlock + // the shaveGlobals node. + // + MString filename = MFileIO::beforeExportFilename().toLowerCase(); + int len = filename.length(); + + if ((len < 3) || (filename.substring(len-2, len) != ".mb")) + unlockShaveGlobals(); + + LEAVE(); +} + + +void shaveCallbacks::afterExport(void* clientData) +{ + ENTER(); + + // + // The renderer gets called during some types of export, so make sure + // that it knows about it. + // + shaveRender::exportEnd(); + + LEAVE(); +} + + +void shaveCallbacks::beforeImport(void* clientData) +{ + ENTER(); + startLoad(); + LEAVE(); +} + + +void shaveCallbacks::afterImport(void* clientData) +{ + ENTER(); + endLoad(true, false); + LEAVE(); +} + + +void shaveCallbacks::beforeImportReference(void* clientData) +{ + ENTER(); + startLoad(); + LEAVE(); +} + + +void shaveCallbacks::afterImportReference(void* clientData) +{ + ENTER(); + endLoad(true, false); + LEAVE(); +} + + +void shaveCallbacks::afterNew(void* clientData) +{ + ENTER(); + + prepareForNewScene(false); + + MGlobal::executeCommand("shaveSceneCleared"); + MGlobal::executeCommand("shave_resetSelectedRenderer"); + + LEAVE(); +} + + +void shaveCallbacks::beforeOpen(void* clientData) +{ + ENTER(); + + prepareForNewScene(false); + startLoad(); + + LEAVE(); +} + + +void shaveCallbacks::afterOpen(void* clientData) +{ + ENTER(); + + + endLoad(false, false); + + //MObjectArray shaveHairShapes; + //shaveUtil::getShaveNodes(shaveHairShapes); + //for(unsigned int i = 0; i < shaveHairShapes.length(); i++) + //{ + // MFnDependencyNode dFn(shaveHairShapes[i]); + // MPlug plug = dFn.findPlug("clumps"); + // int c; plug.getValue(c); plug.setValue(c); + + // ////does not make any effect + // //shaveHairShape* sh = (shaveHairShape*)dFn.userNode(); + // //sh->mDisplayHairCacheDirty = true; + //} + + LEAVE(); +} + + +void shaveCallbacks::beforeReference(void* clientData) +{ + ENTER(); + startLoad(); + LEAVE(); +} + + +void shaveCallbacks::afterReference(void* clientData) +{ + ENTER(); + endLoad(false, true); + if(shaveRender::rendererIsVray()) + { + MGlobal::executeCommand("shave_removeShaveRenderCallbacks;"); + MGlobal::executeCommand("shaveVraySetRenderCallbacks;"); + } + LEAVE(); +} + + +void shaveCallbacks::beforeSave(void* clientData) +{ + ENTER(); + + // + // If there aren't any shaveHairShapes then get rid of the shaveGlobals + // node as well. + // + bool stillHaveNodes = false; + MObjectArray nodes; + + shaveUtil::getShaveNodes(nodes); + + if (nodes.length() == 0) + { + unsigned int i; + + shaveUtil::getShaveGlobalsNodes(nodes); + + if (nodes.length() > 0) + { + MDGModifier dgMod; + + for (i = 0; i < nodes.length(); i++) + { + MFnDependencyNode nodeFn(nodes[i]); + shaveGlobals* nodePtr = (shaveGlobals*)nodeFn.userNode(); + + if (!nodePtr->deleteMe(dgMod)) stillHaveNodes = true; + } + + nodes.clear(); + + if (!dgMod.doIt()) stillHaveNodes = true; + } + } + else + stillHaveNodes = true; + + if (stillHaveNodes) + { + updateNodeVersions(); + + // + // There's a bug in Maya's ASCII format when saving locked, shared + // nodes. shaveGlobals is a shared node so if this is an ASCII save, + // be sure to unlock it first. + // + if (MFileIO::fileType() == "mayaAscii") unlockShaveGlobals(); + } + + LEAVE(); +} + + +void shaveCallbacks::afterSave(void* clientData) +{ + ENTER(); + MGlobal::executeCommand("shaveSceneWritten"); + LEAVE(); +} + + +void shaveCallbacks::nodeCreated(MObject& node, void* clientData) +{ + // + // This is a bit too expensive to log ENTER/LEAVE on every node + // created, even in debug mode, so we'll leave the macros until we know + // if it's a node we care about. + // + MFnDependencyNode nodeFn(node); + + if (nodeFn.typeId() == shaveHairShape::id) + { + ENTER(); + + // + // If this is the first shaveHairShape to be added to the scene, + // enable various bits of functionality. + // + if (shaveHairShape::getNumShaveNodes() == 0) + firstHairNode(); + + shaveHairShape::nodeAdded(); + + //if default renderer is changed in Maya prefs + //we need to make sure that correct MEL callbacks are set + if (!shaveUtil::isLoadingFile()) + MGlobal::executeCommand("shave_selectedRendererChanged"); + + LEAVE(); + } + else if (node.hasFn(MFn::kField)) + { + // If a new field node has been added, mark the existing list as + // dirty. + shaveUtil::setFieldsDirty(); + } +} + + +void shaveCallbacks::shaveNodeDeleted(MObject& node, void* clientData) +{ + ENTER(); + + shaveHairShape::nodeRemoved(); + + // + // If this is the last shaveHairShape, remove the rendering callbacks + // so that their overhead is not incurred. + // + if (shaveHairShape::getNumShaveNodes() == 0) + { + shaveRender::cleanupCallbacks(); + + // + // Some menu items should be disabled if there are no shave nodes. + // + // The evalDeferred is required because the node is not fully + // removed yet and we don't want the command to execute until it + // is. + // + MGlobal::executeCommand("evalDeferred shave_enableMenus"); + } + else if (shaveHairShape::getNumShaveNodes() < 0) + { + MGlobal::displayWarning( + "Internal Shave problem: negative shaveHairShape count." + ); + } + + LEAVE(); +} + + +void shaveCallbacks::selectionChanged(void* clientData) +{ + // + // If we're supposed to ignore this selection change, then ignore it + // and reset the flag. + // + if (shaveUtil::isIgnoringNextSelectionChange()) + { + shaveUtil::setIgnoringNextSelectionChange(false); + return; + } + + // + // Apply the selections to all the hair shapes in the scene. + // + MObjectArray shaveNodes; + shaveUtil::getShaveNodes(shaveNodes); + + if (shaveNodes.length() > 0) + { + MDagPath currentHair = shaveUtil::getCurrentHairShape(); + unsigned i; + MFnDependencyNode nodeFn; + shaveHairShape* nodePtr; + MSelectionList selections; + + // + // Have the non-current hair nodes clear their selection caches. + // + for (i = 0; i < shaveNodes.length(); i++) + { + if (!currentHair.isValid() || (shaveNodes[i] != currentHair.node())) + { + nodeFn.setObject(shaveNodes[i]); + nodePtr = (shaveHairShape*)nodeFn.userNode(); + nodePtr->clearSelections(); + } + } + + // + // Have the current hair node update its selection cache to match + // the selection list. + // + if (currentHair.isValid()) + { + MGlobal::getActiveSelectionList(selections); + + nodeFn.setObject(currentHair.node()); + nodePtr = (shaveHairShape*)nodeFn.userNode(); + nodePtr->updateSelections(selections); + } + } + + // + // Let the MEL scripts do their stuff. + // + MGlobal::executeCommand("shave_selectionChanged"); +} + + +void shaveCallbacks::firstHairNode() +{ + shaveRender::setupCallbacks(); + + // Some menu items should be enabled if there are now shave nodes. + // + // The evalDeferred is required because the node is not fully + // created yet and we don't want the command to execute until it + // is. + MGlobal::executeCommand("evalDeferred shave_enableMenus"); +} + + +void shaveCallbacks::initializeHairNodes() +{ + bool warningRequired = false; + + // Get the plugin version. + const char* pluginVersionStr = SHAVEquery_version(); + + // If we have any shaveHairShapes in the scene which are from a + // newer version of Shave, delete them and give a warning. + MObjectArray shaveNodes; + shaveUtil::getShaveNodes(shaveNodes); + + MString badSceneVersionStr; + MDGModifier dgMod; + unsigned int i; + unsigned int numShaveNodes = shaveNodes.length(); + + for (i = numShaveNodes; i > 0; --i) + { + MPlug sceneVersionPlug( + shaveNodes[i-1], shaveHairShape::aShaveVersion + ); + MString sceneVersionStr; + + sceneVersionPlug.getValue(sceneVersionStr); + + if (shaveUtil::compareShaveVersions(sceneVersionStr, pluginVersionStr) + > 0) + { + MFnDependencyNode nodeFn(shaveNodes[i-1]); + shaveHairShape* sn = (shaveHairShape*)nodeFn.userNode(); + + sn->deleteMe(dgMod); + shaveNodes.remove(i-1); + warningRequired = true; + badSceneVersionStr = sceneVersionStr; + --numShaveNodes; + } + } + + dgMod.doIt(); + + if (warningRequired) + { + MGlobal::displayError( + MString("Shave: plugin version is ") + pluginVersionStr + + " but scene contains incompatible elements from a" + + " later version of Shave (" + badSceneVersionStr + + "). The incompatible elements have been deleted so" + + " DO NOT SAVE OUT THIS SCENE or you may lose data." + ); + } + + // + // Parameters override + // + //int pluginVerParts[3]; + //splitShaveVersion(pluginVersionStr, pluginVerParts); + for (i = 0; i < numShaveNodes; i++) + { + MString sceneVersionStr; + int sceneVerParts[3]; + MPlug sceneVersionPlug(shaveNodes[i], shaveHairShape::aShaveVersion); + sceneVersionPlug.getValue(sceneVersionStr); + shaveUtil::splitShaveVersion(sceneVersionStr, sceneVerParts); + if(sceneVerParts[0] < 6) + { + float val; + MGlobal::displayInfo("Shave: 'Value Variation' was overriden"); + MPlug valVarPlug(shaveNodes[i], shaveHairShape::shaveParamValueVariation); + valVarPlug.getValue(val); + valVarPlug.setValue(val/3.0f); + } + } + + // + // Let MEL do its stuff. + // + MGlobal::executeCommand("shaveSceneLoaded"); + + // + // If we have any shaveHairShapes in the scene, then make sure that + // we have a shaveGlobals node as well. + // + if (numShaveNodes > 0) MGlobal::executeCommand("shaveGlobals()"); + + // + // Tell the shaveHairShapes that they can calculate their output + // meshes now. + // + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode nodeFn(shaveNodes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + if (!nodePtr->nodeIsInitialized()) + nodePtr->initializeNode(); + } +} + + +void shaveCallbacks::removeCallback(MCallbackId& id) +{ + if (id != 0) + { + MMessage::removeCallback(id); + id = 0; + } +} + + +void shaveCallbacks::unlockShaveGlobals() +{ + MObject node = shaveGlobals::getDefaultNode(); + + if (!node.isNull()) + { + MFnDependencyNode nodeFn(node); + + if (nodeFn.isLocked()) nodeFn.setLocked(false); + } +} + + +void shaveCallbacks::updateNodeVersions() +{ + ENTER(); + + // + // Make sure that the version numbers on all of the shaveGlobals nodes + // are up to date. + // + MObjectArray nodes; + int nodeVersion; + + shaveUtil::getShaveGlobalsNodes(nodes); + + unsigned int numNodes = nodes.length(); + unsigned int i; + + for (i = 0; i < numNodes; i++) + { + MPlug plug(nodes[i], shaveGlobals::aNodeVersion); + + plug.getValue(nodeVersion); + + if (nodeVersion != shaveGlobals::kNodeVersion) + plug.setValue(shaveGlobals::kNodeVersion); + } + + // + // Ditto the shaveHairShapes. + // + MString pluginVersion = SHAVEquery_version(); + MString sceneVersion; + + nodes.clear(); + shaveUtil::getShaveNodes(nodes); + numNodes = nodes.length(); + + for (i = 0; i < numNodes; i++) + { + MPlug plug(nodes[i], shaveHairShape::aNodeVersion); + + plug.getValue(nodeVersion); + + if (nodeVersion != shaveHairShape::kNodeVersion) + plug.setValue(shaveHairShape::kNodeVersion); + + plug.setAttribute(shaveHairShape::aShaveVersion); + plug.getValue(sceneVersion); + + if (sceneVersion != pluginVersion) + plug.setValue(pluginVersion); + } + + nodes.clear(); + + LEAVE(); +} + diff --git a/mayaPlug/shaveCallbacks.h b/mayaPlug/shaveCallbacks.h new file mode 100644 index 0000000..592a6c9 --- /dev/null +++ b/mayaPlug/shaveCallbacks.h @@ -0,0 +1,98 @@ +#ifndef shaveCallbacks_h +#define shaveCallbacks_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MMessage.h> + +#if MAYA_API_VERSION < 20180000 +class MObject; +#endif + +class shaveCallbacks +{ +public: + static void endLoad(bool isImport, bool isReference); + static void mayaExiting(void* clientData = NULL); + static void prepareForNewScene(bool firstTime); + static void registerCallbacks(); + static void removeCallbacks(); + static void setCleanUpMELOnExit(bool cleanUp) + { mCleanUpMELOnExit = cleanUp; } + static void startLoad(); + +private: + // + // Callback Methods + // + static void beforeExport(void* clientData); + static void afterExport(void* clientData); + + static void beforeImport(void* clientData); + static void afterImport(void* clientData); + + static void beforeImportReference(void* clientData); + static void afterImportReference(void* clientData); + + static void afterNew(void* clientData); + + static void beforeOpen(void* clientData); + static void afterOpen(void* clientData); + + static void beforeReference(void* clientData); + static void afterReference(void* clientData); + + static void beforeSave(void* clientData); + static void afterSave(void* clientData); + + static void nodeCreated(MObject& node, void* clientData); + static void shaveNodeDeleted(MObject& node, void* clientData); + + static void selectionChanged(void* clientData); + + // + // Utility Methods + // + static void firstHairNode(); + static void initializeHairNodes(); + static void removeCallback(MCallbackId& id); + static void unlockShaveGlobals(); + static void updateNodeVersions(); + + // + // Member Variables + // + static bool mCleanUpMELOnExit; + + // + // Callback IDs + // + static MCallbackId beforeExportID; + static MCallbackId afterExportID; + + static MCallbackId beforeImportID; + static MCallbackId afterImportID; + + static MCallbackId beforeImportRefID; + static MCallbackId afterImportRefID; + + static MCallbackId beforeOpenID; + static MCallbackId afterOpenID; + + static MCallbackId beforeReferenceID; + static MCallbackId afterReferenceID; + + static MCallbackId beforeSaveID; + static MCallbackId afterSaveID; + + static MCallbackId afterNewID; + + static MCallbackId nodeCreatedID; + static MCallbackId shaveNodeDeletedID; + + static MCallbackId mayaExitingID; + static MCallbackId selectionChangedID; +}; + +#endif diff --git a/mayaPlug/shaveCheckObjectVisibility.cpp b/mayaPlug/shaveCheckObjectVisibility.cpp new file mode 100644 index 0000000..6f725e7 --- /dev/null +++ b/mayaPlug/shaveCheckObjectVisibility.cpp @@ -0,0 +1,250 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagPath.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> + +#include "shaveCheckObjectVisibility.h" +#include "shaveIO.h" +#include "shaveUtil.h" + + +bool isObjectVisible( + const MDagPath& path, + bool andNotTemplated, + bool ignorePrimaryVisibility, + bool ignoreTransparency, + bool ignoreOverrideTemplating, + bool& overridesEnabled +) +// +// Description: +// Check if the given object is visible +// +{ + MStatus status; + MFnDagNode fnDN(path); + MPlug plug; + + // + // Are layer overrides enabled? + // + plug = fnDN.findPlug("overrideEnabled"); + plug.getValue(overridesEnabled); + + // + // If the object isn't visible, then it's not visible. :-) + // + bool isSet; + + plug = fnDN.findPlug("visibility"); + plug.getValue(isSet); + + if (!isSet) return false; + + if (!ignorePrimaryVisibility) + { + plug = fnDN.findPlug("primaryVisibility"); + plug.getValue(isSet); + + if (!isSet) return false; + } + + // + // If any part of the object is being shaded by a shader with non-zero + // transparency, or whose transparency is driven by a connection, then + // we will consider the object to be transparent and return false. + // + if (!ignoreTransparency) + { + // + // Get all of the shading groups to which this instance of this + // object, or any of its components, is assigned. + // + MObjectArray shadingGroups; + + shaveUtil::getShadingGroups(path, shadingGroups); + + // + // Step through each shading group and see if its associated + // shader has non-zero transparency. + // + float transparency; + unsigned int i; + unsigned int j; + unsigned int k; + unsigned int numShadingGroups = shadingGroups.length(); + MPlugArray conns; + char* plugNames[] = { "surfaceShader", "volumeShader" }; + + char* transpNames[] = { + "transparency", + "transparencyR", + "transparencyG", + "transparencyB" + }; + + + for (i = 0; i < numShadingGroups; i++) + { + MFnDependencyNode groupFn(shadingGroups[i]); + + for (j = 0; j < 2; j++) + { + plug = groupFn.findPlug(plugNames[j], &status); + + if (status) + { + // + // If this port on the shading group has a shader + // connected to it, then we need to check the shader's + // transparency. + // + // Unless this is a layered shader, then we ignore + // transparency because layered shaders *always* have + // transparency but are rarely transparent overall. + // + plug.connectedTo(conns, true, false); + + if ((conns.length() > 0) + && !conns[0].node().hasFn(MFn::kLayeredShader)) + { + MFnDependencyNode shaderFn(conns[0].node()); + + // + // Does the shader have a transparency attribute? + // + for (k = 0; k < 4; k++) + { + plug = shaderFn.findPlug(transpNames[k], &status); + + if (status) + { + // + // If the transparency attribute has an + // incoming connection, then the + // transparency value may change during the + // course of a render. So we treat the + // shader as being transparent and return + // true. + // + plug.connectedTo(conns, true, false); + + if (conns.length() > 0) return false; + + // + // If the shader's transparency is anything + // other than zero, we treat it as + // transparent and return true. + // + plug.getValue(transparency); + + if (transparency > 0.0f) return false; + } + } + } + } + } + } + } + + // + // If the object is an intermediate object (e.g. in the middle of a + // chaini of deformations) then it's not visible. + // + plug = fnDN.findPlug("intermediateObject"); + plug.getValue(isSet); + + if (isSet) return false; + + // + // Are we treating templated objects as if they're invisible? + // + if (andNotTemplated) + { + plug = fnDN.findPlug("template"); + plug.getValue(isSet); + + if (isSet) return false; + } + + if (overridesEnabled) + { + // + // Now we do the same checks, but this time for the layer + // overrides. + // + // First, visibility. + // + plug = fnDN.findPlug("overrideVisibility"); + plug.getValue(isSet); + + if (!isSet) return false; + + // + // Templating. + // + if (andNotTemplated && !ignoreOverrideTemplating) + { + int displayMode; + + plug = fnDN.findPlug("overrideDisplayType"); + plug.getValue(displayMode); + + if (displayMode == 1) return false; + } + } + + return true; +} + + +bool areObjectAndParentsVisible( + const MDagPath& path, + bool andNotTemplated, + bool ignorePrimaryVisibility, + bool ignoreTransparency +) +// +// Description: +// Check if this object and all of its parents are visible. In Maya, +// visibility is determined by heirarchy. So, if one of a node's +// parents is invisible, then so is the node. +// +{ + bool ignoreOverrideTemplating = false; + bool isVisible = true; + bool overridesEnabled; + MDagPath searchPath(path); + + while (isVisible) + { + isVisible = isObjectVisible( + searchPath, + andNotTemplated, + ignorePrimaryVisibility, + ignoreTransparency, + ignoreOverrideTemplating, + overridesEnabled + ); + + if (searchPath.length() == 1) break; + + // + // One exception to the hierarchical rule is override templating. + // That's handled by the lowest node which has overrides enabled. + // So once we've hit a node which has overrides enabled, then we + // must ignore override templating in any nodes above that in the + // hierarchy. + // + if (overridesEnabled) ignoreOverrideTemplating = true; + + searchPath.pop(); + } + + return isVisible; +} diff --git a/mayaPlug/shaveCheckObjectVisibility.h b/mayaPlug/shaveCheckObjectVisibility.h new file mode 100644 index 0000000..42ed878 --- /dev/null +++ b/mayaPlug/shaveCheckObjectVisibility.h @@ -0,0 +1,25 @@ +#ifndef _VISCHECK +#define _VISCHECK + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +bool isObjectVisible( + const MDagPath& path, + bool andNotTemplated, + bool ignorePrimaryVisibility, + bool ignoreTransparency, + bool ignoreOverrideTemplating, + bool& overridesEnabled +); + +bool areObjectAndParentsVisible( + const MDagPath& path, + bool andNotTemplated = false, + bool ignorePrimaryVisibility = false, + bool ignoreTransparency = true +); + +#endif + diff --git a/mayaPlug/shaveConstant.h b/mayaPlug/shaveConstant.h new file mode 100644 index 0000000..e06b311 --- /dev/null +++ b/mayaPlug/shaveConstant.h @@ -0,0 +1,51 @@ +#ifndef shaveConstant_h +#define shaveConstant_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +class shaveConstant +{ +public: + enum FileNameFormat + { + kNoFileNameFormat, + kFileExtFrame, + kFileFrameExt, + kFileFrame + }; + + enum RenderMode + { + kBufferRender = 0, + kGeometryRender = 1, + kNoRender = 3 + }; + + enum RibBlurInheritSettings + { + kRibBlurInheritOff, + kRibBlurInheritMaya, + kRibBlurInheritRenderman + }; + + enum ShadowSource + { + kNoShadows, + kShaveShadows, + kMayaGeomShadows + }; + + enum ShutterState + { + kShutterNone, kShutterOpen, kShutterClose, kShutterBoth + }; + + enum TimeBasis + { + kTimeAbsolute, + kTimeRelative + }; +}; + +#endif diff --git a/mayaPlug/shaveCursorCtx.cpp b/mayaPlug/shaveCursorCtx.cpp new file mode 100644 index 0000000..7124cde --- /dev/null +++ b/mayaPlug/shaveCursorCtx.cpp @@ -0,0 +1,1709 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#include <QtCore/QEvent> +#include <QtGui/QMouseEvent> +#include <QtGui/QTabletEvent> +#include <QtGui/QWheelEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include <iostream> +#include <vector> +using namespace std; +#include <limits.h> + +#include <maya/M3dView.h> +#include <maya/MCursor.h> +#include <maya/MDagModifier.h> +#include <maya/MDagPath.h> +#include <maya/MEvent.h> +#include <maya/MEventMessage.h> +#include <maya/MFnCamera.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnTransform.h> +#include <maya/MGlobal.h> +#include <maya/MPoint.h> +#include <maya/MPxContext.h> +#include <maya/MSelectionList.h> +#include <maya/MString.h> +#include <maya/MVector.h> + +#include "shaveSDK.h" +#include "shaveCursorCtx.h" +#include "shaveBrushManip.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveHairUI.h" +#include "shaveUtil.h" +#include "shaveStyleCmd.h" + +#ifdef OSMac_ +#include "shaveMacCarbon.h" +#endif + +#include <vector> + +// +// Qt Mouse Watcher -- vlad|17Mar2010 +// +static bool toolActive = false; +//static bool toolJustMoved = false; //gah, need to set it per node +static bool gMouseDown = false; +static bool eventsHappen = false; +std::vector<shaveHairShape*> shnodes; + +bool IsToolActive() +{ + return toolActive; +} +//bool IsToolJustMoved() +//{ +// return toolJustMoved; +//} +bool GetEventsHappen() +{ + return eventsHappen; +} + +void SetEventsHappen() +{ + eventsHappen = true; + return; +} + + +void ClearEvents() +{ + eventsHappen = false; +} +bool IsMouseDown() +{ + return gMouseDown; +} + +bool shaveQtMouseWatcher::m_busy = false; +bool shaveQtMouseWatcher::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == (QEvent::Type)NotBusyEvent::aType) + { + m_busy = false; + event->accept(); + } + if (event->type() == QEvent::MouseMove || + event->type() == QEvent::TabletMove) + { + //lets try to dirty flag on any mouse move event + + SetEventsHappen(); + event->accept(); + + QPoint sp; + QPoint lp; + //couple words about event: + //LINUX: free move and move events are fired by view + //OSX: drag events are fired by view but free move eviets are fired by + //window that view, so local position (5,6) turns into (5,28) + //in other words in-view test I did (viewLocation == localLocation) below + //is not ok for OSX. + bool tablet = false; + if (event->type() == QEvent::MouseMove) + { + m_busy = false; //just an insurrance + + //if(m_busy) + //{ + // return false; + //} + //if(!m_busy) + //{ + // m_busy = true; + // QEvent* bev = new NotBusyEvent(); + // QApplication::postEvent(qApp->activeWindow(), bev); + //} + + SetEventsHappen(); + + QMouseEvent* me = (QMouseEvent*)event; + sp = me->globalPos(); + lp = me->pos(); + + //printf("--- pos %i %i\n",sp.x(),sp.y());fflush(stdout); + } + else if (event->type() == QEvent::TabletMove) + { + SetEventsHappen(); + if(m_busy) + { + return false; + } + if(!m_busy) + { + m_busy = true; + QEvent* bev = new NotBusyEvent(); + QApplication::postEvent(qApp->activeWindow(), bev); + } + + tablet = true; + + QTabletEvent* te = (QTabletEvent*)event; + sp = te->globalPos(); + lp = te->pos(); + + //there is pretty consistent + //printf("get pos %i %i\n",lp.x(),lp.y());fflush(stdout); + //printf("get pos %i %i\n",sp.x(),sp.y());fflush(stdout); + } + //char buf[200]; + //sprintf(buf,"lp: %i %i",lp.x(),lp.y()); + //MGlobal::displayInfo(buf); + + bool inview = false; + //shave tracks only in active view, so no need to iterate all + //unsigned int nv = M3dView::numberOf3dViews(); + //for(unsigned int i = 0; i < nv; i++) + { + //M3dView theview; + //MStatus stat = M3dView::get3dView(i,theview); + + MStatus stat; + M3dView theview = M3dView::active3dView(&stat); + if(stat == MStatus::kSuccess) + { + //unsigned int vw = theview.portWidth(); + //unsigned int vh = theview.portHeight(); + //if(vw == 0 || vh == 0) + // continue; + + QWidget* qw = theview.widget(&stat); + if(stat == MStatus::kSuccess && qw != NULL) + { + QPoint wp = qw->mapFromGlobal(sp); + + //char buf[200]; + //sprintf(buf,"wp%i: %i %i",i,wp.x(),wp.y()); + //MGlobal::displayInfo(buf); + + // not good for OSX, see note above + // additional tests needed + //if(lp == wp) + //{ + // inview = true; + // //MGlobal::displayInfo(MString("view:") + i); + // m_ctx->pointerMoved(theview.window(),lp.x(),lp.y()); + // break; + //} + if(wp.x() >= 0 && wp.y() >= 0 && + wp.x() < theview.portWidth() && + wp.y() < theview.portHeight()) + { + inview = true; + m_ctx->pointerMoved(theview.window(),wp.x(),wp.y()); + //printf("set pos %i %i\n",wp.x(),wp.y());fflush(stdout); + //break; + } + } + } + } + if(!inview) + { + m_ctx->leaveWindow(NULL,0,0); //x,y are not used in leaveWindow + } + } + + if(incrementaldraw ) + return false; + + return QObject::eventFilter(obj, event); +} + +static shaveQtMouseWatcher* mv = NULL; + +static globalQtViewport20Tracker* vp20t = NULL; +void CheckGlobalQtViewport20Tracker() +{ + if(vp20t == NULL) + { + vp20t = new globalQtViewport20Tracker(); + qApp->installEventFilter(vp20t); + } + +} +bool globalQtViewport20Tracker::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::MouseMove || + event->type() == QEvent::TabletMove ) + { + //MGlobal::displayInfo("mouse moved"); + M3dView view = M3dView::active3dView(); + + MDagPath camPath; + MStatus stat = MStatus::kSuccess; + stat = view.getCamera(camPath); + if(stat == MStatus::kSuccess) + { + MFnCamera camFn(camPath); + MMatrix tm = camPath.inclusiveMatrixInverse(); + if(worldToCam != tm) + { + + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + //MGlobal::displayInfo("vp2.0 dirtied"); + MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + worldToCam = tm; + } + } +#ifdef OSMac_ + //need to diryt vp2 on OSX ( for the case of growth obj moved) + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + //MGlobal::displayInfo("vp2.0 dirtied"); + MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } +#endif + + } + if (event->type() == QEvent::MouseButtonRelease) + { + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + + return QObject::eventFilter(obj, event); +} + +#ifdef GLOBAL_FALLBACK + +//static LARGE_INTEGER last={ static_cast<LONGLONG>(0) }; +//LARGE_INTEGER GetLastMoveTime() {return last;} + +static qint64 last; +qint64 GetLastMoveTime() {return last;} +void SetLastMoveTime(qint64 t) {last = t;} + +bool globalQtMouseWatcher::eventFilter(QObject* obj, QEvent* event) +{ + //printf("event type %i\n",event->type());fflush(stdout); +if (event->type() == QEvent::Wheel )eventsHappen = true; + + if (event->type() == QEvent::MouseMove || + event->type() == QEvent::TabletMove ) +// event->type() == QEvent::Wheel ) + { +// printf("mouse move (pressed: %s)\n",gMouseDown?"yes":"no"); + + if(gMouseDown) + { + if(!eventsHappen) + { +// printf("flush 01\n");fflush(stdout); +// qApp->flush(); + } + eventsHappen = true; + } + + + if(!incrementaldraw ) + event->accept(); + + shaveGlobals::getGlobals(); + + if((toolActive && !gMouseDown && doFallbackGlob)||(event->type() == QEvent::Wheel && doFallbackGlob )) + { + eventsHappen = true; + //gah, hungs. probably need to make the list of nodes in tool init + + // MFnDagNode nodeFn; + // MDagPathArray paths; + // shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + // for (unsigned int i = 0; i < paths.length(); i++) + // { + // nodeFn.setObject(paths[i].node()); + + // if (nodeFn.typeId() == shaveHairShape::id) + // { + // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + // shape->dirties.BRUSH_JUST_MOVED = 1; + // } + // } + //QueryPerformanceCounter(&last); + if(!incrementaldraw ) + last = GetQTimer().elapsed(); + + if(!incrementaldraw ) + for(unsigned int i = 0; i < shnodes.size(); i++) + { + if(shnodes[i]->dirties.BRUSH_JUST_MOVED == 0) + { + shnodes[i]->dirtyDisplay(); //its idiocy + shnodes[i]->dirties.BRUSH_JUST_MOVED = 1; + } + } + //if(!incrementaldraw ) + //if(shaveStyleCmd::redrawOnIdlDone) + //{ + // printf("redrawOnIdle fired\n");fflush(stdout); + + // shaveStyleCmd::redrawOnIdlDone = false; + // MGlobal::executeCommandOnIdle("shaveStyle -redrawOnIdle"); + //} +// MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + qint64 t = GetQTimer().elapsed(); + SetLastMoveTime(t); + //////////////////////// + if(incrementaldraw ) + { + + if(shaveStyleCmd::redrawOnIdlDone) + { + //printf("redrawOnIdle fired\n");fflush(stdout); + + shaveStyleCmd::redrawOnIdlDone = false; + MGlobal::executeCommandOnIdle("shaveStyle -redrawOnIdle"); + + //nope. does not make better on tex assigment + //MGlobal::executeCommandOnIdle("shaveStyle -fullRedrawOnIdle"); + } + } + } + if (event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::TabletPress|| + event->type() == /*QEvent::KeyPress*/ 6) + { + if(!incrementaldraw ) + event->accept(); + + +// printf("flush 00\n");fflush(stdout); +// qApp->flush(); + + gMouseDown = true; + eventsHappen = true; + + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); + if(!incrementaldraw ) + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + //////////////////////// + //printf("MOUSE DOWN\n"); fflush(stdout); + //////////////////////// + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirties.GLOBAL_MOUSE_DOWN = 1; + + MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + } + + //////////////////////////////////////// + //MSelectionList selection; + //MGlobal::getActiveSelectionList(selection); + + //MObject comp; + //bool componentMode = (MGlobal::selectionMode()== MGlobal::kSelectComponentMode); + //unsigned i; + //MDagPath path; + //MFnDagNode nodeFn; + + //for (i = 0; i < selection.length(); i++) + //{ + // if (selection.getDagPath(i, path, comp) && path.isValid()) + // { + // path.extendToShape(); + + // // + // // If we're in component selection mode then ignore any + // // nodes which don't have components selected. + // // + // if (!componentMode || !comp.isNull()) + // { + // nodeFn.setObject(path.node()); + + // if (nodeFn.typeId() == shaveHairShape::id) + // { + // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + // shape->dirties.GLOBAL_MOUSE_DOWN = 1; + // break; + // } + // } + // } + //} + //////////////////////////////////////// + } + if (event->type() == QEvent::MouseButtonRelease || + event->type() == QEvent::TabletRelease|| + event->type() == /*QEvent::KeyRelease*/ 7) + { + if(!incrementaldraw ) + event->accept(); + + gMouseDown = false; + //eventsHappen = true; + + //shaveGlobals::Globals globals; + //shaveGlobals::getGlobals(globals); + + //////////////////////////////////////// + //MFnDagNode nodeFn; + //MDagPathArray paths; + //shaveUtil::getShaveNodes(paths); + //for (unsigned int i = 0; i < paths.length(); i++) + //{ + // nodeFn.setObject(paths[i].node()); + + // if (nodeFn.typeId() == shaveHairShape::id) + // { + + // //////////////////////// + // //printf("MOUSE UP\n"); fflush(stdout); + // //////////////////////// + + // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + // shape->dirties.GLOBAL_MOUSE_DOWN = 0; + // shape->dirties.DIRTY_TEXTURE = 1; + // shape->dirties.DIRTY_TEXTURE_JOE = 1; + // shape->dirtyDisplay(); + + // //how to avoid it? + // float trigger; + // MPlug triggerPlug = nodeFn.findPlug("trigger"); + // triggerPlug.getValue(trigger); + // triggerPlug.setValue(trigger+1.0f); + // } + //} + ///////////////////aaaaaahh/////////////////// + //MFnDagNode nodeFn; + //MDagPathArray paths; + //shaveUtil::getShaveNodes(paths); + //for (unsigned int i = 0; i < paths.length(); i++) + //{ + // nodeFn.setObject(paths[i].node()); + // if (nodeFn.typeId() == shaveHairShape::id) + // { + // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + // shape->updateTexLookups(); + // } + //} + //////////////////////////////////////// + //if(shaveStyleCmd::redrawOnIdlDone) + eventsHappen = true; + //if(!incrementaldraw ) + if(doFallbackGlob) + { + //printf("fullRedrawOnIdle fired\n");fflush(stdout); + + shaveStyleCmd::redrawOnIdlDone = false; + MGlobal::executeCommandOnIdle("shaveStyle -fullRedrawOnIdle"); + } + + //////////////////////////////////// + } + if(incrementaldraw ) + return false; + + return QObject::eventFilter(obj, event); +} + +static globalQtMouseWatcher* gmv = NULL; + +void CheckAndSetGlobalQtWatcher() +{ + if(gmv == NULL) + { + gmv = new globalQtMouseWatcher(); + qApp->installEventFilter(gmv); + } + StartQTimer(); +} + +static QElapsedTimer qTimer; +static bool qTimerStarted = false; +void StartQTimer() +{ + if(!qTimerStarted) + { + qTimer.start(); + qTimerStarted = true; + } +} +QElapsedTimer& GetQTimer() +{ + return qTimer; +} +#endif + + +#ifdef _WIN32 +MCursor shaveCursorCtx::mDoubleArrowCursor("shaveDoubleArrowCursor.cur"); +#else +# ifdef OSMac_ +# include "icons/shaveDoubleArrowCursorOSX.xbm" +# else +# include "icons/shaveDoubleArrowCursor.xbm" +# endif + +MCursor shaveCursorCtx::mDoubleArrowCursor( + shaveDoubleArrowCursor_width, + shaveDoubleArrowCursor_height, + 8, + 4, + (unsigned char*)shaveDoubleArrowCursor_bits, + (unsigned char*)shaveDoubleArrowCursor_bits + ); +#endif + +shaveCursorCtx* shaveCursorCtx::mActiveCtx = 0; + + +shaveCursorCtx::shaveCursorCtx() +: mActiveViewChangedId(0) +, mBrushSize(0.2f) +, mBrushStren(1.0f) +, mDoingStroke(false) +, mEventWindow(0) +, mFalloffEnabled(true) +, mFastBrush(true) +, mIsActive(false) +, mIsResizing(false) +, mManip(0) +, mResizeKeyPressed(false) +#ifdef _WIN32 +, mTrackingLeaveWindow(false) +#endif +{ +} + + +shaveCursorCtx::~shaveCursorCtx() +{ +} + + +MStatus shaveCursorCtx::doDrag(MEvent& event) +{ +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveCursorCtx::doDrag", NULL); +#endif + + MStatus st = MS::kSuccess; + + if (!mManip) return st; + + short x; + short y; + + event.getPosition(x, y); + + if ((x != mStrokeLastX) || (y != mStrokeLastY)) + { + mStrokeLastX = x; + mStrokeLastY = y; +#ifdef DO_PROFILE + Profile::ProfileDump("doDrag 01", NULL); +#endif + M3dView view = M3dView::active3dView(); + + if (mIsResizing || !mDoingStroke) + { + if (!mFastBrush) + { + // Maya doesn't do refreshes during a drag so we must + // force a refresh so that the brush is redrawn at its new + // position. (That's not a problem for the fast brush + // because it draws itself independent of Maya's refresh.) + view.refresh(false, true); + } + + return st; + } +#ifdef DO_PROFILE + Profile::ProfileDump("doDrag 02", NULL); +#endif + MPoint worldMouse = getWorldPt(view, x, y); + + float maxX = (float)(view.portWidth() - 1); + float maxY = (float)(view.portHeight() - 1); + VERT screenDelta; + VERT screenPos; + VERT worldDelta; + VERT worldPos; + + screenDelta.x = (float)(x - mStrokeStartX) / maxX; + screenDelta.y = (float)(y - mStrokeStartY) / maxY; + screenDelta.z = 0.0f; + + screenPos.x = (float)x / maxX; + screenPos.y = (float)y / maxY; + screenPos.z = 0.0f; + + worldDelta.x = (float)worldMouse.x - mStrokeStartWorld.x; + worldDelta.y = (float)worldMouse.y - mStrokeStartWorld.y; + worldDelta.z = (float)worldMouse.z - mStrokeStartWorld.z; + + worldPos.x = (float)worldMouse.x; + worldPos.y = (float)worldMouse.y; + worldPos.z = (float)worldMouse.z; + + ////////////////////// + screenDelta.x *= getBrushStren(); + screenDelta.y *= getBrushStren(); + + screenPos.x = mStrokeStartX / maxX + screenDelta.x; + screenPos.y = mStrokeStartY / maxY + screenDelta.y; + + worldDelta.x *= getBrushStren(); + worldDelta.y *= getBrushStren(); + worldDelta.z *= getBrushStren(); + + worldPos.x = mStrokeStartWorld.x + worldDelta.x; + worldPos.y = mStrokeStartWorld.y + worldDelta.y; + worldPos.z = mStrokeStartWorld.z + worldDelta.z; + ////////////////////// + st = strokeDrag(screenPos, worldPos, screenDelta, worldDelta); + if (st) + { + // + // Let the hair node know that there have been changes. + // + mTargetShape->guidesChanged(); + // + // The call above will have let Maya know that a redraw is + // required, but Maya won't do one while we're dragging, so we have + // to force it. + // + view.refresh(false, true); + //what it try do not force + //view.refresh(false, false); //nothing changes + } + else + cleanupStroke(); + + mTargetShape->dirtyDisplay(); + +#if 0 + ////////////////////// + //mTargetShape->applyEdits(false); + ////////////////////// + if(mTargetShape) + { + //SHAVEsculpt_finish(); + mTargetShape->updateParams(); + mTargetShape->applyEdits(true); + //mTargetShape->getHairNode(); + + MSelectionList selection; + MGlobal::getActiveSelectionList(selection); + + MObject comp; + bool componentMode = (MGlobal::selectionMode() + == MGlobal::kSelectComponentMode); + unsigned i; + MDagPath path; + MFnDagNode nodeFn; + + for (i = 0; i < selection.length(); i++) + { + if (selection.getDagPath(i, path, comp) && path.isValid()) + { + path.extendToShape(); + + // + // If we're in component selection mode then ignore any + // nodes which don't have components selected. + // + if (!componentMode || !comp.isNull()) + { + nodeFn.setObject(path.node()); + + + float trigger; + MPlug triggerPlug = nodeFn.findPlug("trigger"); + triggerPlug.getValue(trigger); + triggerPlug.setValue(trigger+1.0f); + } + } + } + + M3dView::active3dView().refresh(); + } + ////////////////////// +#endif + } +#ifdef DO_PROFILE + Profile::ProfileDump("shaveCursorCtx::doDrag - done", NULL); +#endif + return st; +} + + +MStatus shaveCursorCtx::doPress(MEvent& event) +{ + MStatus st = MS::kSuccess; + + mDoingStroke = false; + + if (!mManip) return MS::kSuccess; + + // Save the start of the stroke. + // + event.getPosition(mStrokeStartX, mStrokeStartY); + + mStrokeLastX = mStrokeStartX; + mStrokeLastY = mStrokeStartY; + + // + // If the resizing hotkey is pressed, then go into resize mode. + // + if (mResizeKeyPressed) + { + mStrokeStartBrushSize = mBrushSize; + mIsResizing = true; + mDoingStroke = true; + } + else + { + // + // The first hair node on the selection list is the one we'll be + // modifying. There shouldn't be any others but if there are we'll + // ignore them because Shave can only handle editing on one hair + // node at a time. + // + MSelectionList selection; + MGlobal::getActiveSelectionList(selection); + + MObject comp; + bool componentMode = (MGlobal::selectionMode() + == MGlobal::kSelectComponentMode); + unsigned i; + MDagPath path; + MFnDagNode nodeFn; + + for (i = 0; i < selection.length(); i++) + { + if (selection.getDagPath(i, path, comp) && path.isValid()) + { + path.extendToShape(); + + // + // If we're in component selection mode then ignore any + // nodes which don't have components selected. + // + if (!componentMode || !comp.isNull()) + { + nodeFn.setObject(path.node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + mTargetShape = (shaveHairShape*)nodeFn.userNode(); + break; + } + } + } + } + + if (i == selection.length()) return MS::kSuccess; + + int rx; + int ry; + + mManip->getBrushRadii(rx, ry); + + float aspect = (float)rx / (float)ry; + float rSquared = (float)(rx * rx); + + M3dView view = M3dView::active3dView(); + + // + // Processing the selection list is slow. Let's get the + // already-processed selections from the hair node instead. + // + mTargetShape->makeCurrent(); //maybe better to place it in tool init? + + const shaveHairShape::Guides& cachedGuides = mTargetShape->getGuides().guides; + + SOFTGUIDE guide; + int g; + int v; + MPoint centroid; + unsigned numVerts = 0; + + for (g = 0; SHAVEfetch_guide(g, &guide) != -1; g++) + { + if (!guide.hidden) + { + float maxWeight = -1.0f; + + // + // If we're not in component mode, then we treat every vert as + // being selected. + // + for (v = 1; v < SHAVE_VERTS_PER_GUIDE; v++) + { + if (!componentMode || (cachedGuides[g].select & (1 << v))) + { + // + // Get the position of the vertex in window coords. + // + MPoint p(guide.guide[v].x, guide.guide[v].y, guide.guide[v].z); + short px; + short py; + + view.worldToView(p, px, py); + + // + // How far is this vertex from the center of the brush? + // + float dx = (float)(px - mStrokeStartX); + float dy = aspect * (float)(py - mStrokeStartY); + float dSquared = dx * dx + dy * dy; + + if (dSquared >= rSquared) + guide.select[v] = 0; + else + { + guide.select[v] = 1; + + if (mFalloffEnabled) + { + guide.weight[v] = 1.0f - dSquared / rSquared; + + if (guide.weight[v] > maxWeight) + maxWeight = guide.weight[v]; + } + else + { + guide.weight[v] = 1.0f; + maxWeight = 1.0f; + } + + centroid.x += p.x; + centroid.y += p.y; + centroid.z += p.z; + numVerts++; + } + } + else + guide.select[v] = 0; + } + + // + // If any verts on the guide were selected then select the root + // vert as well and give it a weight identical to the max + // assigned to any of the other verts. + // + if (maxWeight >= 0.0f) + { + guide.select[0] = 1; + guide.weight[0] = maxWeight; + } + else + guide.select[0] = 0; + + SHAVEput_guideNOCALC(g, &guide); + } + } + + if (numVerts > 0) + { + centroid = centroid / (double)numVerts; + + // + // How far along the eye vector, between the near and far clipping + // planes, does the centroid lie? + // + short centroidX; + short centroidY; + MPoint farClip; + MPoint nearClip; + + view.worldToView(centroid, centroidX, centroidY); + view.viewToWorld(centroidX, centroidY, nearClip, farClip); + + double totalDist = (farClip - nearClip).length(); + + // + // We should never get a totalDist of zero, but let's be safe. + // + if (totalDist > 0.0) + { + double centroidDist = (centroid - nearClip).length(); + + mCentroidFraction = centroidDist / totalDist; + + MPoint worldMouse = getWorldPt( + view, mStrokeStartX, mStrokeStartY + ); + + float maxX = (float)(view.portWidth() - 1); + float maxY = (float)(view.portHeight() - 1); + VERT screenPos; + VERT worldPos; + + screenPos.x = (float)mStrokeStartX / maxX; + screenPos.y = (float)mStrokeStartY / maxY; + screenPos.z = 0.0f; + + worldPos.x = (float)worldMouse.x; + worldPos.y = (float)worldMouse.y; + worldPos.z = (float)worldMouse.z; + + mStrokeStartWorld = worldPos; + + // + // Get the camera's position and viewing vector. + // + MDagPath cameraPath; + view.getCamera(cameraPath); + + MFnCamera cameraFn(cameraPath); + MPoint eyePoint = cameraFn.eyePoint(MSpace::kWorld); + MVector upDir = cameraFn.upDirection(MSpace::kWorld); + MVector viewDir = cameraFn.viewDirection(MSpace::kWorld); + + VERT eyePointAsVERT; + eyePointAsVERT.x = (float)eyePoint.x; + eyePointAsVERT.y = (float)eyePoint.y; + eyePointAsVERT.z = (float)eyePoint.z; + + VERT viewDirAsVERT; + viewDirAsVERT.x = (float)viewDir.x; + viewDirAsVERT.y = (float)viewDir.y; + viewDirAsVERT.z = (float)viewDir.z; + + VERT upDirAsVERT; + upDirAsVERT.x = (float)upDir.x; + upDirAsVERT.y = (float)upDir.y; + upDirAsVERT.z = (float)upDir.z; + + st = strokeBegin( + eyePointAsVERT, + viewDirAsVERT, + upDirAsVERT, + screenPos, + worldPos + ); + + if (st) + { + mDoingStroke = true; + + // + // To speed up interaction, we hide a lot of + // unnecessary stuff during the stroke, so that redraws + // are fast. + // + //hideHair(); not higging, but decreasing display haircount + + if(mTargetShape) + { + //mTargetShape->setBrushDown(true); //currently used for higing hair in vp2.0 + mTargetShape->dirties.BRUSH_MOUSE_DOWN = 1; + M3dView::active3dView().refresh(false, true); + } + } + else + { + // + // If beginStroke() returns kEndOfFile it means that + // the method succeeded, but the rest of the stroke + // is to be ignored. Typically used for instantaneous + // actions which occur on mouse-down. + // + if (st == MS::kEndOfFile) + { + cleanupStroke(); + st = MS::kSuccess; + } + } + } + } + } + + + return st; +} + + +MStatus shaveCursorCtx::doRelease(MEvent& event) +{ + MStatus st; + + if (!mManip || !mDoingStroke) return MS::kSuccess; + + // + // It may be possible for the release to occur at a different position + // from the last drag, so let's do a drag update first. + // + st = doDrag(event); + + if (mIsResizing) + { + mIsResizing = false; + + // + // If the resizing hotkey is no longer pressed, reset the cursor. + // + if (!mResizeKeyPressed) setCursor(MCursor::defaultCursor); + + // + // Update the property sheet to reflect the new brush size. + // + MGlobal::executeCommand("shaveCursorCtx_updateCommonPropertySheet"); + } + else + { + if (st) strokeEnd(); + eventsHappen = false; + cleanupStroke(); + } + + mDoingStroke = false; + // come back + + if(mTargetShape) + { + //mTargetShape->setBrushDown(false); //in vp2.0 + mTargetShape->dirties.BRUSH_MOUSE_DOWN = 0; + M3dView::active3dView().refresh(); + } + return st; +} + + +void shaveCursorCtx::resizeKeyPressed(bool isPressed) +{ + if (mResizeKeyPressed != isPressed) + { + mResizeKeyPressed = isPressed; + + // + // If we're not currently doing a stroke, then set the cursor to + // the double-headed arrow when the key pressed, and back to normal + // when it is released. + // + if (!mDoingStroke) + { + if (isPressed) + setCursor(mDoubleArrowCursor); + else + setCursor(MCursor::defaultCursor); + } + } +} + + +void shaveCursorCtx::setBrushSize(float size) +{ + if (size != mBrushSize) + { + mBrushSize = size; + + if (mManip) + { + mManip->setBrushSize(mBrushSize); + + if (mIsActive) mManip->redrawBrush(); + } + } +} + + +void shaveCursorCtx::toolOffCleanup() +{ + // + // Let the hair nodes know that the brush is no longer active. + // + toolActive = false; + + shaveHairUI::setBrushActive(false); + //for vp2.0 + if (mTargetShape != NULL) mTargetShape->setBrushActive(false); + + + mIsActive = false; + + MGlobal::executeCommand("shaveBrush_setHotkeys off"); + uncatchActiveViewEvents(); + MMessage::removeCallback(mActiveViewChangedId); + + if (mManip) + { + mManip->setActive(false); + mManip = NULL; + } + + deleteManipulators(); + mActiveCtx = 0; + + if (!mFastBrush) + { + MSelectionList list; + MObject dummy; + list.add("shaveBrushProxy"); + + if (list.length() > 0) + { + list.getDependNode(0, dummy); + + MDagModifier dagMod; + dagMod.deleteNode(dummy); + dagMod.doIt(); + } + } +} + + +void shaveCursorCtx::toolOnSetup(MEvent& event) +{ + //////////////////////////////////// + toolActive = true; +#ifdef GLOBAL_FALLBACK + shaveStyleCmd::redrawOnIdlDone = true; +#endif + MFnDagNode nodeFn; + MDagPathArray paths; + shnodes.clear(); + eventsHappen = true; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + shnodes.push_back(shape); + } + } + ////////////////////////////////// + + shaveGlobals::Globals g; + shaveGlobals::getGlobals(g); + + mFastBrush = g.fastBrush; + + MObject manipObj; + + mActiveCtx = this; + + mManip = (shaveBrushManip*)shaveBrushManip::newManipulator( + shaveBrushManip::nodeTypeName, + manipObj + ); + + if (mManip) + { + mManip->thisObj = manipObj; + mManip->setFastBrush(mFastBrush); + + // + // %%% We should really find the current pointer position relative + // the active view and pass that to the manip as well then + // call setActive(). For now we have a kludge that the first + // call to setBrushPos() will do the setActive() call for us. + // + mManip->setBrushSize(mBrushSize); + addManipulator(manipObj); + + if (!mFastBrush) + { + // Create a dummy transform which we can connect the manip to and + // move around in response to mouse movements. + MStatus st; + MDagModifier dagMod; + MObject dummy; + + dummy = dagMod.createNode("transform", MObject::kNullObj, &st); + if (!st) + { + MGlobal::displayError( + MString("Shave: cannot create proxy transform for brush: ") + + st.errorString() + ); + } + else + { + dagMod.renameNode(dummy, "shaveBrushProxy"); + + st = dagMod.doIt(); + + if (!st) + { + MGlobal::displayError( + MString("Shave: cannot commit proxy transform for brush: ") + + st.errorString() + ); + } + else + { + MSelectionList list; + list.add("shaveBrushProxy"); + list.getDagPath(0, mProxyPath); + mManip->connectToDependNode(mProxyPath.node()); + } + } + } + } + + // + // Prior to Maya 7.0 there was no ModelPanelSetFocus event, so instead + // we watch for whenever the busy state goes false since that happens + // whenever the user pops between single-view and four-view. + // + MString eventName = "ModelPanelSetFocus"; + + mActiveViewChangedId = MEventMessage::addEventCallback( + eventName, activeViewChanged, this + ); + + catchActiveViewEvents(); + MGlobal::executeCommand("shave_prepareForBrushing"); + MGlobal::executeCommand("shaveBrush_setHotkeys on"); + + mIsActive = true; + + // + // Let the hair nodes know that a brush is active. + // + shaveHairUI::setBrushActive(true); + //for vp2.0 + mTargetShape = NULL; + { + MSelectionList selection; + MGlobal::getActiveSelectionList(selection); + + MObject comp; + bool componentMode = (MGlobal::selectionMode() + == MGlobal::kSelectComponentMode); + unsigned i; + MDagPath path; + MFnDagNode nodeFn; + + for (i = 0; i < selection.length(); i++) + { + if (selection.getDagPath(i, path, comp) && path.isValid()) + { + path.extendToShape(); + + // + // If we're in component selection mode then ignore any + // nodes which don't have components selected. + // + if (!componentMode || !comp.isNull()) + { + nodeFn.setObject(path.node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + mTargetShape = (shaveHairShape*)nodeFn.userNode(); + break; + } + } + } + } + } + if(mTargetShape) + { + mTargetShape->setBrushActive(true); + //mTargetShape->makeCurrent(); //here to avoid delays on mouse press + } +} + + +//---------------------------------------------------------------- +// +// Internal Methods +// +//---------------------------------------------------------------- + +void shaveCursorCtx::activeViewChanged(void* clientData) +{ + shaveCursorCtx* brushCtx = (shaveCursorCtx*)clientData; + + brushCtx->uncatchActiveViewEvents(); + brushCtx->catchActiveViewEvents(); +} + + +void shaveCursorCtx::catchActiveViewEvents() +{ + M3dView view = M3dView::active3dView(); + + if(qApp) + { + mv = new shaveQtMouseWatcher(this); + qApp->installEventFilter(mv);; + } + else + { + printf("shaveCursorCtx: qApp is null\n"); + } +} + + +void shaveCursorCtx::cleanupStroke() +{ + // + // Joe says that we don't need to reset the rest pose when brushing, + // thus the 'false' in the call below. + // + if(mTargetShape) + { + mTargetShape->updateParams(); + mTargetShape->applyEdits(false); + } + //unhideHair(); //we do not show/hide hair but decrese haircount + mDoingStroke = false; +} + + +MPoint shaveCursorCtx::getWorldPt( + const M3dView& view, short viewX, short viewY +) const +{ + // + // Calculate the point along the ray passing through the screen coords + // which is at the same depth, relative to the clipping planes, as + // the centroid. + // + MPoint farClip; + MPoint nearClip; + + view.viewToWorld(viewX, viewY, nearClip, farClip); + + MVector mouseRay = farClip - nearClip; + double mouseDist = mouseRay.length() * mCentroidFraction; + + mouseRay.normalize(); + + return nearClip + mouseRay * mouseDist; +} + + +void shaveCursorCtx::hideHair() +{ + // + // Completely hide all hairnodes except the target, so that they don't + // draw at all. + // + MDagPathArray paths; + + shaveUtil::getShaveNodes(paths); + mHiddenHairNodes.clear(); + + unsigned i; + + for (i = 0; i < paths.length(); i++) + { + if (paths[i].node() != mTargetShape->thisMObject()) + { + MFnDagNode nodeFn(paths[i]); + bool isVisible; + MPlug plug = nodeFn.findPlug("visibility"); + + plug.getValue(isVisible); + + if (isVisible) + { + plug.setValue(false); + mHiddenHairNodes.append(paths[i]); + } + } + } + + // + // Turn off hair display on the target. + // +#if 0 + // + // %%% This code *should* work but doesn't, the reason being that + // that setting dspyMode dirties both triggerAttr and outputMesh. + // Retrieving either one of those will call computeOutputMesh() + // which will in turn call SHAVExform, even if the current node + // is already in the engine and nothing has changed. + // + // To get around it shaveHairShape will have to be more clever + // about determining whether it needs to do an xform. In the + // meantime we use the workaround in the 'else' portion of this + // ifdef, which uses a purpose-built method for temporarily + // disabling hair display. + // + short hairDisplayMode; + MPlug plug(mTargetShape->thisMObject(), shaveHairShape::dspyMode); + + plug.getValue(hairDisplayMode); + mTargetDisplayMode = (shaveHairShape::HairDisplayMode)hairDisplayMode; + + if (mTargetDisplayMode != shaveHairShape::kHairDisplayNone) + plug.setValue((short)shaveHairShape::kHairDisplayNone); +#else + mTargetShape->enableHairDisplay(false); +#endif +} + +void shaveCursorCtx::leaveWindow(MNativeWindowHdl window, int x, int y) +{ + M3dView view = M3dView::active3dView(); + + if (window == NULL || // vlad|17Mar2010 + window == view.window()) + mManip->leaveView(); +} + + +void shaveCursorCtx::motionEventHandler(shaveCursorCtx::EventData* event) +{ + switch (event->type) + { +#if defined(_WIN32) + case kMouseLeftWindow: + mTrackingLeaveWindow = false; + case kMouseMoved: + { + M3dView activeView = M3dView::active3dView(); + HWND viewWin = activeView.window(); + HWND viewParentWin = GetParent(viewWin); + + // + // For some reason mouse events are delivered to the parent + // of the view's window. + // + if (event->window == viewWin) + { + if (event->type == kMouseLeftWindow) + leaveWindow(viewWin, event->x, event->y); + else + { + // + // Have Windows send a WM_MOUSELEAVE message when the + // pointer leaves the view. + // + if (!mTrackingLeaveWindow) + { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = viewParentWin; + TrackMouseEvent(&tme); + mTrackingLeaveWindow = true; + } + + pointerMoved(viewWin, event->x, event->y); + } + } + } + break; +#else + case kMouseLeftWindow: + leaveWindow(event->window, event->x, event->y); + break; + + case kMouseMoved: + pointerMoved(event->window, event->x, event->y); + break; +#endif + + default: + break; + } + + delete event; +} + +void shaveCursorCtx::pointerMoved(MNativeWindowHdl window, int x, int y) +{ + //////////////////////// + //mManip->dirtyDisplay(); + //return; + //////////////////////// + M3dView view = M3dView::active3dView(); + + if (window == view.window()) + { + if (mIsResizing) + { + // + // Calculate the per-pixel scaling factor along X. + // + int rx; + int ry; + + mManip->getBrushRadii(rx, ry); + + float scalePerPixel = mBrushSize / (float)rx; + float minSize = scalePerPixel * 4.5f; + + // + // Determine what the new scaling factor must be for the radius + // to grow in the X direction by the same number of pixels as + // the mouse was dragged. + // + float newSize = mStrokeStartBrushSize + + scalePerPixel * (float)(x - mStrokeStartX); + + if (newSize < minSize) + newSize = minSize; + else if (newSize > 1.0f) + newSize = 1.0f; + + setBrushSize(newSize); + } + else + { + mManip->setBrushPos(x, view.portHeight() - 1 - y); + view.refresh(false,true); + //view.refresh(true,true); //why do we need it here? + } + } + +} + + +void shaveCursorCtx::uncatchActiveViewEvents() +{ + if(mv) + { + delete mv ; + mv = NULL; + } +} + + +void shaveCursorCtx::unhideHair() +{ + unsigned i; + + for (i = 0; i < mHiddenHairNodes.length(); i++) + { + MFnDagNode nodeFn(mHiddenHairNodes[i]); + MPlug plug = nodeFn.findPlug("visibility"); + + plug.setValue(true); + } + + mHiddenHairNodes.clear(); + + // + // Restore the target's hair display. + // +#if 0 + // + // See hideHair() for the reason for this ifdef. + // + if (mTargetDisplayMode != shaveHairShape::kHairDisplayNone) + { + MPlug plug(mTargetShape->thisMObject(), shaveHairShape::dspyMode); + plug.setValue((short)mTargetDisplayMode); + } +#else + mTargetShape->enableHairDisplay(true); +#endif +} + + +#ifdef _WIN32 +LRESULT CALLBACK shaveCursorCtx::WinEventHandler( + int nCode, WPARAM wParam, LPARAM lParam +) +{ + shaveCursorCtx* brushCtx = getActiveCtx(); + + if (brushCtx && (nCode == HC_ACTION)) + { + MSG* msgInfo = (MSG*)lParam; + int msgType = LOWORD(msgInfo->message); + + switch (msgType) + { + case WM_MOUSELEAVE: + case WM_MOUSEMOVE: + { + EventData* event = new EventData; + + if (msgType == WM_MOUSEMOVE) + event->type = kMouseMoved; + else + event->type = kMouseLeftWindow; + + event->window = msgInfo->hwnd; + + RECT rect; + GetWindowRect(event->window, &rect); + + event->x = msgInfo->pt.x - rect.left; + event->y = msgInfo->pt.y - rect.top; + + brushCtx->motionEventHandler(event); + } + break; + + default: + break; + } + } + + return CallNextHookEx(0, nCode, wParam, lParam); +} +#endif + + +#ifdef LINUX +void shaveCursorCtx::XEventHandler( + Widget widget, + XtPointer clientData, + XEvent* event, + Boolean* continueDispatch) +{ + EventData* eventData = new EventData; + + switch (event->type) + { + case LeaveNotify: + eventData->type = kMouseLeftWindow; + eventData->window = event->xcrossing.window; + eventData->x = event->xcrossing.x; + eventData->y = event->xcrossing.y; + break; + + case MotionNotify: + eventData->type = kMouseMoved; + eventData->window = event->xmotion.window; + eventData->x = event->xmotion.x; + eventData->y = event->xmotion.y; + break; + + default: + delete eventData; + return; + } + + shaveCursorCtx* brushCtx = (shaveCursorCtx*)clientData; + + brushCtx->motionEventHandler(eventData); +} +#endif diff --git a/mayaPlug/shaveCursorCtx.h b/mayaPlug/shaveCursorCtx.h new file mode 100644 index 0000000..d262b84 --- /dev/null +++ b/mayaPlug/shaveCursorCtx.h @@ -0,0 +1,262 @@ +#ifndef shaveCursorCtx_h +#define shaveCursorCtx_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#include <QtCore/QEvent> +#include <QtCore/QElapsedTimer> +#include <QtGui/QMouseEvent> +#include <QtGui/QTabletEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include <vector> + +#include <maya/M3dView.h> +#include <maya/MCursor.h> +#include <maya/MDagPathArray.h> +#include <maya/MEventMessage.h> +#include <maya/MGlobal.h> +#include <maya/MPxContext.h> +#include <maya/MString.h> +#include <maya/MMatrix.h> + +#include "shaveHairShape.h" +#include "shaveSDKTYPES.h" + +#if MAYA_API_VERSION < 20180000 +class MEvent; +#endif + +class shaveBrushManip; +class shaveCursorCtx; + +//Qt mouse test -- vlad|17Mar2010 +class shaveQtMouseWatcher : public QObject +{ +public: + shaveQtMouseWatcher(shaveCursorCtx* ctx) + { + m_ctx = ctx; + m_busy = false; + } + virtual ~shaveQtMouseWatcher(){} + + static bool IsBusy() {return m_busy;} + +protected: + + bool eventFilter(QObject* obj, QEvent* event); + +private: + shaveCursorCtx* m_ctx; + static bool m_busy; + +}; + +void CheckGlobalQtViewport20Tracker(); + +class globalQtViewport20Tracker : public QObject +{ +public: + globalQtViewport20Tracker(){} + virtual ~globalQtViewport20Tracker(){} + +protected: + bool eventFilter(QObject* obj, QEvent* event); + MMatrix worldToCam; +}; + +//#define GLOBAL_FALLBACK +#ifdef GLOBAL_FALLBACK +void CheckAndSetGlobalQtWatcher(); + +void SetEventsHappen(); +bool GetEventsHappen(); +void ClearEvents(); +bool IsMouseDown(); +bool IsToolActive(); + + +class globalQtMouseWatcher : public QObject +{ +public: + globalQtMouseWatcher(){} + virtual ~globalQtMouseWatcher(){} + +protected: + bool eventFilter(QObject* obj, QEvent* event); +}; + +void StartQTimer(); +QElapsedTimer& GetQTimer(); +qint64 GetLastMoveTime(); +void SetLastMoveTime(qint64 t); + +#endif + +class NotBusyEvent : public QEvent { +public: + enum {aType = 4500}; + NotBusyEvent():QEvent((QEvent::Type)aType){}; +}; + +//LARGE_INTEGER GetLastMoveTime(); + +class shaveCursorCtx : public MPxContext +{ + friend class shaveQtMouseWatcher; +public: + shaveCursorCtx(); + virtual ~shaveCursorCtx(); + + virtual void deleteAction() {} + void enableFalloff(bool enable) { mFalloffEnabled = enable; } + float getBrushSize() const { return mBrushSize; } + float getBrushStren() const { return mBrushStren; } + virtual void getClassName(MString& name) const = 0; + bool isFalloffEnabled() const { return mFalloffEnabled; } + bool isResizing() const { return mIsResizing; } + void resizeKeyPressed(bool isPressed); + void setBrushSize(float size); + void setBrushStren(float stren) { mBrushStren = stren; } + + // + // 'screenPos' is normalized to the range 0.0 to 1.0 for both view + // dimensions. Thus (0, 0) is the lower left corner of the view and + // (1, 1) is the upper right. + // + // If a derived class is only interested in mouse clicks, not strokes, + // then it should perform its click operation in this method and then + // return MS::kEndOfFile to indicate that the remainder of the stroke + // can be ignored. Otherwise it should return MS::kSuccess on success + // or any of the other failure codes for failure. + // + virtual MStatus strokeBegin( + VERT& eyePoint, + VERT& viewDir, + VERT& upDir, + VERT& screenPos, + VERT& worldPos + ) { return MS::kSuccess; } + + // + // 'screenPos' and 'screenDelta' are normalized to the range 0.0 to 1.0 + // for both view dimensions. Thus (0, 0) is the lower left corner of + // the view and (1, 1) is the upper right. If the drag extends beyond + // the edges of the view then the values passed in may lie outside that + // range. + // + // Derived classes are guaranteed that the position of the cursor in + // the final strokeDrag() will be identical to that when strokeEnd() is + // called. + // + virtual MStatus strokeDrag( + VERT& screenPos, + VERT& worldPos, + VERT& screenDelta, + VERT& worldDelta + ) { return MS::kSuccess; } + + virtual MStatus strokeEnd() { return MS::kSuccess; } + + +protected: + typedef enum + { + kMouseMoved, + kMouseLeftWindow + } MouseEventType; + + typedef struct + { + MouseEventType type; + MNativeWindowHdl window; + int x; + int y; + } EventData; + + // + // Internal Methods + // + static void activeViewChanged(void* clientData); + void catchActiveViewEvents(); + void cleanupStroke(); + + static shaveCursorCtx* + getActiveCtx() { return mActiveCtx; } + MPoint getWorldPt( + const M3dView& view, short viewX, short viewY + ) const; + + void hideHair(); + void leaveWindow(MNativeWindowHdl window, int x, int y); + void pointerMoved(MNativeWindowHdl window, int x, int y); + void motionEventHandler(EventData* event); + void uncatchActiveViewEvents(); + void unhideHair(); +#if defined _WIN32 + static LRESULT CALLBACK + WinEventHandler(int nCode, WPARAM wParam, LPARAM lParam); +#elif defined LINUX + static void XEventHandler( + Widget widget, + XtPointer clientData, + XEvent* event, + Boolean* continueDispatch + ); +#elif defined OSMac_ + //Qt stuff should be used +#endif + + static shaveCursorCtx* mActiveCtx; + MCallbackId mActiveViewChangedId; + float mBrushSize; + float mBrushStren; + double mCentroidFraction; + bool mDoingStroke; + static MCursor mDoubleArrowCursor; + MNativeWindowHdl mEventWindow; + bool mFalloffEnabled; + bool mFastBrush; + MDagPathArray mHiddenHairNodes; + bool mIsActive; + bool mIsResizing; + shaveBrushManip* mManip; + MDagPath mProxyPath; + bool mResizeKeyPressed; + short mStrokeLastX; + short mStrokeLastY; + float mStrokeStartBrushSize; + VERT mStrokeStartWorld; + short mStrokeStartX; + short mStrokeStartY; + shaveHairShape::HairDisplayMode + mTargetDisplayMode; + shaveHairShape* mTargetShape; +#ifdef _WIN32 + HHOOK mMouseHookId; + bool mTrackingLeaveWindow; +#endif + +private: + virtual MStatus doPress (MEvent& event, MHWRender::MUIDrawManager& drawMgr, const MHWRender::MFrameContext& context){ return doPress(event);} + virtual MStatus doDrag (MEvent& event, MHWRender::MUIDrawManager& drawMgr, const MHWRender::MFrameContext& context){ return doDrag(event);} + virtual MStatus doRelease (MEvent& event, MHWRender::MUIDrawManager& drawMgr, const MHWRender::MFrameContext& context){ return doRelease(event);} + virtual MStatus doDrag(MEvent& event); + virtual MStatus doPress(MEvent& event); + virtual MStatus doRelease(MEvent& event); + virtual void toolOffCleanup(); + virtual void toolOnSetup(MEvent& event); +}; + +#endif diff --git a/mayaPlug/shaveCursorCtxCmd.cpp b/mayaPlug/shaveCursorCtxCmd.cpp new file mode 100644 index 0000000..8a0032b --- /dev/null +++ b/mayaPlug/shaveCursorCtxCmd.cpp @@ -0,0 +1,106 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgParser.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +#include "shaveCursorCtx.h" +#include "shaveCursorCtxCmd.h" + + +static const char* flBrushSize = "-brushSize"; +static const char* fsBrushSize = "-bs"; +static const char* flBrushStren = "-brushStren"; +static const char* fsBrushStren = "-br"; +static const char* flEnableFalloff = "-enableFalloff"; +static const char* fsEnableFalloff = "-ef"; +static const char* flInteractiveResize = "-interactiveResize"; +static const char* fsInteractiveResize = "-ir"; + + +shaveCursorCtxCmd::shaveCursorCtxCmd() +: mCtx(0) +{} + + +shaveCursorCtxCmd::~shaveCursorCtxCmd() +{} + + +MStatus shaveCursorCtxCmd::appendSyntax() +{ + MSyntax s = syntax(); + + s.addFlag(fsBrushSize, flBrushSize, MSyntax::kDouble); + s.addFlag(fsBrushStren, flBrushStren, MSyntax::kDouble); + s.addFlag(fsEnableFalloff, flEnableFalloff, MSyntax::kBoolean); + s.addFlag(fsInteractiveResize, flInteractiveResize, MSyntax::kBoolean); + + return MS::kSuccess; +} + + +MStatus shaveCursorCtxCmd::doEditFlags() +{ + if (mCtx) + { + MArgParser args = parser(); + + if (args.isFlagSet(fsBrushSize)) + { + double size; + args.getFlagArgument(fsBrushSize, 0, size); + mCtx->setBrushSize((float)size); + } + if (args.isFlagSet(fsBrushStren)) + { + double s; + args.getFlagArgument(fsBrushStren, 0, s); + mCtx->setBrushStren((float)s); + } + else if (args.isFlagSet(fsEnableFalloff)) + { + bool enable; + args.getFlagArgument(fsEnableFalloff, 0, enable); + mCtx->enableFalloff(enable); + } + else if (args.isFlagSet(fsInteractiveResize)) + { + bool enable; + args.getFlagArgument(fsInteractiveResize, 0, enable); + mCtx->resizeKeyPressed(enable); + } + } + + return MS::kSuccess; +} + + +MStatus shaveCursorCtxCmd::doQueryFlags() +{ + if (mCtx) + { + MArgParser args = parser(); + + if (args.isFlagSet(fsBrushSize)) + setResult(mCtx->getBrushSize()); + if (args.isFlagSet(fsBrushStren)) + setResult(mCtx->getBrushStren()); + else if (args.isFlagSet(fsEnableFalloff)) + setResult(mCtx->isFalloffEnabled()); + else if (args.isFlagSet(fsInteractiveResize)) + setResult(mCtx->isResizing()); + } + + return MS::kSuccess; +} + + +MPxContext* shaveCursorCtxCmd::makeObj() +{ + mCtx = createContext(); + + return mCtx; +} diff --git a/mayaPlug/shaveCursorCtxCmd.h b/mayaPlug/shaveCursorCtxCmd.h new file mode 100644 index 0000000..3a1b3ba --- /dev/null +++ b/mayaPlug/shaveCursorCtxCmd.h @@ -0,0 +1,36 @@ +#ifndef shaveCursorCtxCmd_h +#define shaveCursorCtxCmd_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxContextCommand.h> + +class shaveCursorCtx; + + +// +// A shaveCursorCtxCmd acts as a base class for context commands for +// contexts derived from shaveCursorCtx. +// +class shaveCursorCtxCmd : public MPxContextCommand +{ +public: + shaveCursorCtxCmd(); + virtual ~shaveCursorCtxCmd(); + + MStatus appendSyntax(); + virtual shaveCursorCtx* createContext() = 0; + virtual MStatus doEditFlags(); + virtual MStatus doQueryFlags(); + + static const MString commandName; + +protected: + shaveCursorCtx* mCtx; + +private: + MPxContext* makeObj(); +}; + +#endif diff --git a/mayaPlug/shaveCutCtx.cpp b/mayaPlug/shaveCutCtx.cpp new file mode 100644 index 0000000..3729886 --- /dev/null +++ b/mayaPlug/shaveCutCtx.cpp @@ -0,0 +1,46 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <iostream> +using namespace std; + +#include <maya/MPxContext.h> +#include <maya/MString.h> + +#include "shaveCutCtx.h" +#include "shaveSDK.h" + +const MString shaveCutCtx::mCtxTypeName = "shaveCut"; + + +shaveCutCtx::shaveCutCtx() +{ + setTitleString("Shave Cut Tool"); + setImage("shaveCut.xpm", MPxContext::kImage1); +} + + +shaveCutCtx::~shaveCutCtx() +{ +} + + +MStatus shaveCutCtx::strokeBegin( + VERT& eyePoint, VERT& viewDir, VERT& upDir, VERT& screenPos, VERT& worldPos +) +{ + SHAVEcut( + eyePoint, + viewDir, + screenPos, + worldPos + ); + + // + // We don't care about the rest of the stroke, so let the caller know + // this by returning kEndOfFile. + // + return MS::kEndOfFile; +} + diff --git a/mayaPlug/shaveCutCtx.h b/mayaPlug/shaveCutCtx.h new file mode 100644 index 0000000..dcd1503 --- /dev/null +++ b/mayaPlug/shaveCutCtx.h @@ -0,0 +1,32 @@ +#ifndef shaveCutCtx_h +#define shaveCutCtx_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MString.h> + +#include "shaveCursorCtx.h" +#include "shaveSDKTYPES.h" + + +class shaveCutCtx : public shaveCursorCtx +{ +public: + shaveCutCtx(); + virtual ~shaveCutCtx(); + + virtual void getClassName(MString& name) const { name = mCtxTypeName; } + + virtual MStatus strokeBegin( + VERT& eyePoint, + VERT& viewDir, + VERT& upDir, + VERT& screenPos, + VERT& worldPos + ); + + static const MString mCtxTypeName; +}; + +#endif diff --git a/mayaPlug/shaveCutCtxCmd.cpp b/mayaPlug/shaveCutCtxCmd.cpp new file mode 100644 index 0000000..5969095 --- /dev/null +++ b/mayaPlug/shaveCutCtxCmd.cpp @@ -0,0 +1,29 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgParser.h> +#include <maya/MGlobal.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +#include "shaveCutCtx.h" +#include "shaveCutCtxCmd.h" +#include "shaveCursorCtxCmd.h" + +const MString shaveCutCtxCmd::commandName = "shaveCutCtx"; + + + +shaveCutCtxCmd::shaveCutCtxCmd() +{} + + +shaveCutCtxCmd::~shaveCutCtxCmd() +{} + + +shaveCursorCtx* shaveCutCtxCmd::createContext() +{ + return new shaveCutCtx; +} diff --git a/mayaPlug/shaveCutCtxCmd.h b/mayaPlug/shaveCutCtxCmd.h new file mode 100644 index 0000000..5af106f --- /dev/null +++ b/mayaPlug/shaveCutCtxCmd.h @@ -0,0 +1,23 @@ +#ifndef shaveCutCtxCmd_h +#define shaveCutCtxCmd_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxContextCommand.h> + +#include "shaveCursorCtxCmd.h" + +class shaveCutCtxCmd : public shaveCursorCtxCmd +{ +public: + shaveCutCtxCmd(); + virtual ~shaveCutCtxCmd(); + + static void* createCmd() { return new shaveCutCtxCmd; } + virtual shaveCursorCtx* createContext(); + + static const MString commandName; +}; + +#endif diff --git a/mayaPlug/shaveDebug.cpp b/mayaPlug/shaveDebug.cpp new file mode 100644 index 0000000..d135308 --- /dev/null +++ b/mayaPlug/shaveDebug.cpp @@ -0,0 +1,174 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#ifdef _DEBUG + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <maya/MGlobal.h> +#include <maya/MString.h> + +#if defined(OSMac_) && !defined(OSMac_MachO_) +#include "shaveMacCarbon.h" +#endif + +#include "shaveDebug.h" + +bool shaveDebug::mInitialized = false; +FILE* shaveDebug::mLogFile = NULL; +MStringArray shaveDebug::mStack; +unsigned int shaveDebug::mStackDepth = 0; + + +void shaveDebug::enter( + MString method, unsigned int lineNumber, const char* file +) +{ + init(); + + if (mLogFile) + { + outputHeader("ENTER", method, lineNumber, file); + fputc('\n', mLogFile); + fflush(mLogFile); + + if (mStackDepth < mStack.length()) + mStack[mStackDepth] = method; + else + mStack.append(method); + } + + mStackDepth++; +} + + +void shaveDebug::indent() +{ + init(); + + if (mLogFile) + { + unsigned int i; + + for (i = 0; i < mStackDepth; i++) + fputs(" ", mLogFile); + + fflush(mLogFile); + } +} + + +void shaveDebug::init() +{ + if (!mInitialized) + { + const char* logFilePath; + + mInitialized = true; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + MString temp; + + MGlobal::executeCommand("getenv SHAVE_DEBUG_LOG", temp); + temp = shaveMacCarbon::makeMacFilename(temp); + logFilePath = temp.asChar(); +#else + logFilePath = getenv("SHAVE_DEBUG_LOG"); +#endif + + if (logFilePath) + mLogFile = fopen(logFilePath, "w"); + } +} + + +void shaveDebug::leave( + MString method, unsigned int lineNumber, const char* file +) +{ + bool unbalanced = (mStackDepth == 0); + + init(); + + if (mStackDepth > 0) mStackDepth--; + + if (mLogFile) + { + if (!unbalanced && (method != mStack[mStackDepth])) + fprintf(mLogFile, "ERROR: enter/leave method mismatch!\n"); + + outputHeader("LEAVE", method, lineNumber, file); + fputc('\n', mLogFile); + + if (unbalanced) + { + fprintf( + mLogFile, "ERROR: leaving more levels than were entered!\n" + ); + } + + fflush(mLogFile); + } +} + + +void shaveDebug::logMsg( + MString msg, unsigned int lineNumber, const char* file +) +{ + init(); + + if (mLogFile) + { + MString method = "???"; + + if ((mStackDepth > 0) && (mStackDepth <= mStack.length())) + method = mStack[mStackDepth - 1]; + + outputHeader("INFO", method, lineNumber, file); + fprintf(mLogFile, "%s\n", msg.asChar()); + fflush(mLogFile); + } +} + + +void shaveDebug::outputHeader( + const char* tag, + MString method, + unsigned int lineNumber, + const char* file +) +{ + indent(); + + // + // Get rid of the directory path from the filename. + // +#ifdef _WIN32 + const char* nameOnly = strrchr(file, '\\'); +#else +#ifdef OSMac_ + const char* nameOnly = strrchr(file, '/'); +#else + const char* nameOnly = rindex(file, '/'); +#endif +#endif + + if (nameOnly == NULL) + nameOnly = file; + else + nameOnly++; + + fprintf( + mLogFile, + "%s [%s, %s:%d]: ", + tag, + method.asChar(), + nameOnly, + lineNumber + ); +} + +#endif diff --git a/mayaPlug/shaveDebug.h b/mayaPlug/shaveDebug.h new file mode 100644 index 0000000..79b72f7 --- /dev/null +++ b/mayaPlug/shaveDebug.h @@ -0,0 +1,77 @@ +#ifndef shaveDebug_h +#define shaveDebug_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#ifndef _DEBUG + +#define INFO(msg) +#define ENTER() +#define LEAVE() return +#define RETURN(value) return (value) + +#else + +#ifdef __GNUG__ +# define DEBUGFUNC __PRETTY_FUNCTION__ +#else +# ifdef _WIN32 +# define DEBUGFUNC __FUNCTION__ +# else +# define DEBUGFUNC __func__ +# endif +#endif + +#define ENTER() shaveDebug::enter(DEBUGFUNC, __LINE__, __FILE__) +#define LEAVE() { shaveDebug::leave(DEBUGFUNC, __LINE__, __FILE__); return; } +#define RETURN(value) { shaveDebug::leave(DEBUGFUNC, __LINE__, __FILE__); return (value); } +#define INFO(msg) shaveDebug::logMsg(msg, __LINE__, __FILE__) + +#include <stdio.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> + + +class shaveDebug +{ +public: + static void enter( + MString method, + unsigned int lineNumber, + const char* file + ); + + static void indent(); + static void init(); + + static void leave( + MString method, + unsigned int lineNumber, + const char* file + ); + + static void logMsg( + MString msg, + unsigned int lineNumber, + const char* file + ); + + static void outputHeader( + const char* tag, + MString method, + unsigned int lineNumber, + const char* file + ); + +private: + static bool mInitialized; + static FILE* mLogFile; + static MStringArray mStack; + static unsigned int mStackDepth; +}; + +#endif + +#endif diff --git a/mayaPlug/shaveDefaultSwatch.png b/mayaPlug/shaveDefaultSwatch.png Binary files differnew file mode 100644 index 0000000..b9c494c --- /dev/null +++ b/mayaPlug/shaveDefaultSwatch.png diff --git a/mayaPlug/shaveDefaultSwatch.xpm b/mayaPlug/shaveDefaultSwatch.xpm new file mode 100644 index 0000000..5072686 --- /dev/null +++ b/mayaPlug/shaveDefaultSwatch.xpm @@ -0,0 +1,135 @@ +/* XPM */ +static char *shaveDefaultSwatch[] = { +/* columns rows colors chars-per-pixel */ +"100 100 29 1", +" c #1c1c1c", +". c #232323", +"X c #2a2a2a", +"o c #333333", +"O c #3c3c3c", +"+ c #434343", +"@ c #4b4b4b", +"# c #535353", +"$ c #5c5c5c", +"% c #636363", +"& c #6a6a6a", +"* c #747474", +"= c #7c7c7c", +"- c #848484", +"; c #8c8c8c", +": c #949494", +"> c #9c9c9c", +", c #a2a2a2", +"< c #ababab", +"1 c #b2b2b2", +"2 c #bababa", +"3 c #c3c3c3", +"4 c #cbcbcb", +"5 c #d3d3d3", +"6 c #d9d9d9", +"7 c #e3e3e3", +"8 c #ededed", +"9 c #f2f2f2", +"0 c #fefefe", +/* pixels */ +"0090799988877877777777777777776777776776776777776776777777776776776776776777776776767776767875878700", +"0988<<>,<<<<<<<<<<<<<<<1<<<<<<1<<<1<<1<1<<1<<<1<<1<1<<<<<<1<<1<11<<1<11<1<<<1<1<<<<<<<1<1<<1<2788800", +"9997<>><><,<<<<11<<2111112112111111111112111111111111111111111112<1112<112111111211111112<1<<<788800", +"8875,>>,,,,<,<<<1<<<<1<1<<1<<1<1<1<1<1<1<1<1<1<1<1<11<11<1<1<1<1<1<11<1<<<1<<1<1<<<1<1<<<1<1<<678800", +"9752,>,><<<,<<,<<<<<<<<<<<1<<<<<1<<<<<<1<<<<<1<<<<<<<1<<<<<<<<<<1<<<1<<<<<<1<<<<<11<<<<11<<1<1787800", +"742,>>>,>>,<,<,<,<<<<<<<1<<<<1<<<<<<1<<<<<<1<<<<<1<<<<<<1<<<<1<1<<<1<<<<<<<<1<<<<<<1<1<<1<<<<<678800", +"64<:>>>>>>,,,,<,<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<111677800", +"62:=>>>>,,>,,<,,,<,,<<<<<<<,<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,<<<<<<<<<<<<<<<<<<<<<1<<<<1<,678800", +"42;*>>>>>>>,,,,,,<,,<<,<<<,<,<<,,,<,<,<<,<<,<<,<<,,<,<<,,<,<<,<<,<<<,<,<<<<,,<<,<,<<<<<<<<<<<<578800", +"52;*>:>>>>>>>,,,,<,<,,<,,,<<<,<<<<<<<,,<<,,<,<<,,<<<<,<<,<<<,<<,<<,<<,<<,,<<<<,<<<<,<,,<<<<<<,578800", +"52;*>::>>>>>>,,>,,,<<,<<,<<,,<<,<,,,,<,<,,<,<,<,,<<,,<<,,,<,,<,,,,,<,<,,,<<,,,,<,<,<<<<<<<,<<<577800", +"51:*:>>>>>>>>,>,,,,,,,,,,,,,,,,<,,<,<,,,<<,,,,,<,,,,,,,<,<,,,,,<<,,,,,,<,,,,<,,<,,<,,<,<,<,<<<678800", +"52:*>::::>>>>>,>>,,,,,,,,,,,,,,,>,,,,,,,>,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,<,,,<<<<,<678800", +"42:*:::>>>:>>>>>,,>,,,>,,,,>,,,>,,,,,,,,,,,,,>,,,,,>,,,>,,,,>,,,,,,,,,,,>,,,,,>,,,,<,,<,,<,,,<578800", +"42:*:::>::>:>>>>>>>,>>,,,>>,>,>,>,>,>>,,>,>,,>,>,>,>>,>,>,,>>,>,>,>,>,,>>,>,,,,>,,,,,<,,,,,<<<588800", +"52:*:::::>>>>>>>,>>>>,>,>,>,,>,,,>,>>,,>>,,>>,>,>,>,,>,>,>,>,>,>,>,>,>>,,>,>>,>,,,>,,,,,<<<,,<679800", +"52:=:::::>::>:>>>>>,>,>>,>>>>>>>>>,>>,>>,>,>>>>>>>>>>>>>>>>>>>>,,>>,>>>>>>>>>>>>>>>,,>,,,,,,,<678800", +"52:=:::::>>>>>>::>>>>>>,>>>,,>>,,>>>>>>,>>>>>,>>>,,>,>>,,>>,,>>>>>>>,>>,,>>,>,,>,,>,,,,,,,<<<,679800", +"52:=:>::::>:::>>,>>>>>,>>>>>>,>>>>,>>,>>>>>>,>>,>>>>>>>>>,>>,>>>>>>,>>,>>>>>>>>>>,>,>,,,><,,,,588800", +"52:=::::>::>>>>>>>>>>>,>,>>,>>>>>>>>>>>>,>,>>>>>>>,>,>>,>>>>>>>,,>>>>>>>,>>,>,>>>>,>,,,,><,<,<678800", +"52:=;:::>::::>>>>,>><:>>>>,>>,>,,>,>>,>,>>,>,>>,,>,>>>>>,>,>>,>>>>,>,>>,>>>>>,>,,,>,,,,,,,<,,<689800", +"52>=::::::>>>>:>>:*&<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,>,>>>>>>>>>>>>>>>>,>,>>>>>>>>,>,,,><<,,<678800", +"52:=:::::>>::>:>>*XX>,>,>,,>>>>>>,>,,>>,>,,>>>>>,>>>>,>>>>>>>>,>>,>>>>>>>>,>>>>,,>,,,,,,><,,<,679800", +"52:=:::::>:>:>>>>=..>>><>>>>>,>>>>>>>>>>>>>>,>>>>>,>>>>>,>,>>,>>>>,>>,>>>>>>>,>>>>,>,>,,,,,<,,688800", +"52:=::::::>:>>>>>-X.>>>,>>>>>>,>>>>>>>>,,,>>><>>>>>,>>>>,>>,>>,>>>,>>,:,>>>>>>>><>>,>>,,><,,<,679800", +"52:=:::::>:>:>:>>=XX,>>>,:>>>>>>,,><>>,,>>>,>>>>>>>>>>>>>>::>>>>>>>>>,>>,:>>><,>>>>,,,,<,,,<,<678800", +"52:=::::::>::>>>>-XX>>>,$%:-%@@$;2;&@+#*>,>>>:*%++@%-><>><>*#O#;;&$><:>>:&@@@%-<>>>,,,,,,<,,,<679800", +"52:=::::>>:>>:>>>-oo,>>>.o%@X...+:$X X.X%,>>-#X..X O*,>>-@. XX.#XX>>>>-OX.XX.O=,<>>,>,,><,<<,588800", +"52:=:::::::>:>>:>-Oo>>>,X.O@%**Xo+o&-&O.o:<>#.+%=*%o @<>,#.O&-*o. .>>>-OX#**&+.o-,>>,>>,><,,,,579800", +"52>=:::::>>::>>>>-OO>>>>oo#-<<>@XO*>>,-.o=>;+o=>>>>$.X>>=o+=>>>-O.X,>>%X%:,>>>% $><><>>,,,,<<<688800", +"52:=::::::>>>>>>>-+O>>>>++*>>:>*+@>,:>>oX=><=-<>>>>&oo>>*O#>>><>&oX,,:++;,>>><=XX;<><>,<,<,,,<679800", +"52:=::::>::>:>>:>-OO,>>>++=,><>*+$>>,>>OO->,,>>--*%+oO>:$@*,>>>>*+o>>:O@%$$%$#$o.-,>>>,,><,<,,588800", +"52:=::::>::>>>>>>-O+>,>>@@;>>>,*@$,>>,>@+=>,:%+#@#@++O>>#@*>>,,>-+o>>-@@@#@@@+Ooo=<><,,,,<,,,<679800", +"52:=::::::>:::>:>=OO>>>>@@->,>>=@$,>>>>O+-,>%+@$&*=*#O,,%+*>>>>>-@+>>;@@---:;---=>>>,>><>,,<,<678800", +"52:=:::::>:>>>>>,-++>>>>@+->>>>=@$>>>,>@+=>=+#->>>>=#+>>&+%>>:><*@+>>;##;>>>>>>>>>>>,>,,,<,<,<589800", +"52:=::::::>:>:>>>=O+>,>>OO->,>>*O#>>,>>++=>*+$>,>1>&@#><=++=,,>>#@@>,>%@&:<>>,;$*:>,><<>,,,,<,678800", +"52:=::::>>:>:>>:>-++>>:>@+->>>>*+$,>>,,@@=>=@#->>:*$@$:>>$o@->-&@@@>,>=$#*>,>;$X#:<>><<>,<,,,<679800", +"52>=:::::::>>>>>,-OO>>>>Oo=>,>>*o@>>>>>@@;,:#O#&&##&#@->>=+oO+@@$#+>>>>*@@@%$+O@-><>,,<>><,<,,688800", +"52:=:::::>:>>:>:>=@@>>,>OO->,>>=+#>>,>:@#=>,=%@##%->%@=,>,;&@+%**#@>>,,:*#O+++%-,,>,,>,,,,,<<,679800", +"52:=:::::::>:>>>>>>:<:>>>:<>>>,>>:<:>>1:>,>,,>:-;>>,>:>>>,>>>>>>:@#>>>>>>>;-->>>>>><,,<>><,,,<678800", +"52>=:::::>:>>:>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>,>>>>,>*#&>>>>>*@$>>>>>>>>>,>>,>>,>,,,,,<,,<,679800", +"52:=:::::>>:>>>:>>>>>>>>,>,>>,>>>>>,>,,>>,>,,>>,>>>,,>>>=@@=>>>-@@*>>>>>>,>>>>>>>,,,>,,,,,,<,,688800", +"52:=::::::>::>>>>>>>>>>>>>>>>>>,>>>>>>>>>>>>>>>>>,>>>>,>>&@@###@@&:,>>>>>,>,>>>>>,>,,,,,>,,<,<579800", +"52:=::::>::>>:>>>>>>>>>>>,>>>,>>,>>,>,>>>>,>,>>,>>>>,>>>,:=&@@$&-:<>>>,>>>>>,>,>>>>,>,,,,<,,,<688800", +"52>=::::>::::>>>>>>>>>>>>,>,>>>>>>>>>,>,,>>>>>>>>>>>>>>>>,<>>>>,>,>>>>>>>>>>>>,>,>,>,,,,,<,<,<679800", +"52:=;::::>>>>>:>>>>>>,>,>>>>>,>>,>,>>>>>>>,>>,>>>,>,>,,>>>>>>,>>>>>>,,>,,>>,>,>>,>>,,,,,>,,<,,678800", +"52:=::::::>::>>>>>>>>>>>,>>,>,>>>>,>>>>,,>>>>,>,>>>>>>>>>>>>>>>,>>>>,>>>>>>>>>>>>>,,>,,,,,,,<,689800", +"52:*:::>>::>:>>>>>>>>>,>>>>>>>>>>>>>>,>>>>,>>>>>>>,>>>>>>>>>>>>>>>,>>>,>,>>,>>>>,>>,,,,,,<<,,<678800", +"52:=::::>:>>>>>>:>>>,:>>>>>>>,>>>>>,>>><>>>,>>>>>>,>>>>>>,,>><>>>,>>,>>,>>>>>>>>,>><>>,>><,,,,579800", +"52:=::::::::::>>>>>>>><>>>><>,>,>,>>,>>>>>>>,>>>,>>>>>>>>>>>>>>,>,>>>,>>>>>>>>,,>>,,>,<,,,<,<<688800", +"52:=:::::>==**>>>>>:>,>***;>>>==->>>>>>>>>>>>,>,,>>>>,>>>>>:=:>>>,>>>>>>,>,,>:>,>>,,,,<>><,<,<679800", +"52>=::::>:+..o;>>>>>>>*o.X%>,>@.&>,,>>>,>>,>>>>>>,>,,>>>><:+.&>>>>>>,>>,>>>>>>>>>,>>><,>,,,<,,678800", +"52:=::::;>+X..*>>:>>>>% X.&><:@.&:>>>>>>>>>>>>>>,>>>>>>,>,:@X&>>>>>,,,>>>>>>>>>,>>>>,<,,,<,,,<589800", +"52:=:::::>@Xo.$>>>>>,:@XOX%>><>>><>:>>,><>>>>,,>>>>>>>>>>>>>>>>>>>,>>>,>,>>>>,>,><,>>>><,,,<<,678800", +"52:=:::::>#O#oo:>>>>>-O@@o&>:>>>>>>>>>>>>>,>>>>>,>>>,,,>,>>,>>>>,>><,>>,>>,>>>>>>>>><<>,>,,<,<679800", +"52:=::::::$@&+.=>>,>>=+&$O&>>>*&->>>=#@+$*>,>>>>*%@+$*:,>,>*&->>-%&:-@O#*:>>,>>,&#+$-:*%,<,,,<588800", +"52:=::::;>$#**X$>>>>>$$:$+*>>>@X&,<$.X.X. @:>>>%.X.XX.@>>>>#X&,>-.o$o X .#>>:<;+..X..@o ,<,,<,679800", +"52:=:::::>%#*-oO>>>>:$$>%+&>>>#X&>=oo%*=&X &,,=oo%==$..&,>>#X&>>*X.+$=*+ o=>>:$X+&=*+. .>,,<,<678800", +"52:=::::::&$*:+X->>>=$*>$+&>>>@o*>&X#>,,:$O%>>*o#:<>:%O%>>,#o&>>-+o&;<,:o *>>-OX=>,,=O.X,<,,,<579800", +"52:=::::::&#*>%o$>>>%$-,$+*>>,#O&>*O@->>>>>>>>*++->>,>:<>>>#o&>,-++->>>,@X&>,&O#>,><>&oX><,<<,688800", +"52:=:::::>&#*,-O+>>;#$>>$+*>>,$+*>:%O+#&=><<,>-$@@@$=>>>,>>#O*>>-+@>>>>>#o&>>$+*,>>,>=Oo,<,,,<679800", +"52>=::::>>%#=>:+O;>=#&>>$+*>>>$+*>>-%@++O+$=>,>-$@@+O+&;,>>$+*>>;@@>>><>#+&>>#@*>><>>;oo>,,<<,678800", +"52:=::::>:&#*>>$O*>%#->>$+*>>,$@*>>,>;&#+Oo#;>>,>-&#Ooo+;>,$@*>>;++>>:>>$+*>>%@*<>>>,;+o,<,,,<589800", +"52:=::::::%#*>,*+#:@#>>>%+*,>>%#*>>>>,>>-*+O*,>>><>>:&+O*>,$@*,>-@+><>>>%+*>>*O%>>><>*+o><,<,,588800", +"52>=::::>:%@*>>>+@&@&>>>%O*>>>%@=>=@*>,>>,#O*>=@&>,>,>$O*>>%+*,>;+o>>>>>%+=>,-OO;<><>$+o,<,,<,688800", +"52:=:::::;$@*>>:%@$@*>>>$+*>>>%+*>-@#->>,-++-,-+$->>>=@O->>%+*>>-+O>>>,>%+=>,:$o@=>:*+oO>,,<,<679800", +"52:=::::::%@*>>>-@@@-<>>$+*>>>%+*>>%@@#%$+O%:<:%+#$%$++$>>>%@*>>;Oo>,>>>$+*>>>=+OoO+##Oo,,,,,,588800", +"52:=::::>:%@&::>;$#%:<>>#@*>>:%+=>>-*#@#@$&:,>,:&$@@##*:,>:%#*>,=++:>>>>%#=>>,,:%@@#=-@o,<,<<<579800", +"52>=:::::>;:>>>>,:>:>,>>,:>,><>>>>>>,>-;::>,>:,,>:--;:><>>,>:>>><>>,>>,>>:>>>>:<,>>>,-O+>,,<,<588800", +"52:=;:::>::>>>:>>>,>>,>>>>><>>>>>>>>>,>>>>>>>>>>,,,,><,>>>>>>>>,>,>>>>,>,,>>:*#%>><>>*+%,<,,<,679800", +"52:=::::>::>>>>>>>>>,,>>>>>>>>>>>,,,>>,>>>>>,,>>>>>>>>>>>>>>>,>>>>>>,>>>>>>>,-@+=:>>-@O*,<<,,,588800", +"52:*:>::::::>:>:>>>>>>>>>>>>>>>>>>>>>>,,>,>>>>>>>>>,>>>>,>,>>,>>>>>>>>>>>>,>>>&@@###$@%,>,,,<<579800", +"52:*;::::>:>:>:>>>>>>,>>>>>>,>>,>,>>,>,>,>,>>>>>>>>>,>>,>,>>>,>,>>>>>,>,>>>>,>>-$##$&->,,<,<,,688800", +"52:=::::::::>:>>>>>>>>>,,>>,>>>>>>>>>>>>>>>>>,>>>>,>>>>>>>>>>>>>>>>>>>>>>>>>>>>:<,>>><,,><,,,<678900", +"52:=;:;:>:>:>>>:>>>>>>>>>,>>,>>>>,,>,>>>>>>>>,>,>>>>>>>,>,,>,>,>>>>>,>>,>>>>><,>>>,,,,>>,<>,,,688900", +"52:=:::::::>::>>>>>>>>>,>>>>>>>,>>>>>>,>,>,>>>>>>,>>,>>>>>>>>>>>>,>>>>>>,>>>>>>>>>>,>,<<,<,<,<578800", +"52:=:::::::>:>>>>>>>>,>>,>>,>,>>>>>>>>,>>>,>,>>,>,,>>>>>>,>,>>>>>,>,>>>>>>,>>>>>,,<>>,,,><,,<,688900", +"52:=::::>::>:>:>>>>>>>>>>>>>>>>>,>,>,>>>>>>>>>>>>>>>,>>,>>>>>>,>>>>>,>,>>>>>>>>>>>,,,>>,,<,,,<678900", +"42:=;::::>::>:>>>>>>>>>>>>>>>>,>>>>>>>,>>,,>>>>>>>>>>>,>>>>>,>,>>>>>>>,>>>>>,>>,,>>>>,><,<,<,<588800", +"52:*:::::::>:>>:>>>>>>,>,>,>>,>>>,>>,>>>>>>>>>>>,>>,>>>>>,>,>>>>>,>,>>>>,>>>>>>>>>>,,,>>,,,,,,678900", +"52:=:::;:::::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>:>>:>>>>>>>>>>>>>>>>>>>>>>>>>>>>>:>>>>>>>,>,,,>,,<,778900", +"52:=::;:>::>>::::>>:>>:>>>>>>:>>>>>:>>>>>>>>>>>>>>>>>>>>>:>>>>>>>>:>>>>>>>>>>>>>>>>>>,>,,,,,,,689900", +"52:=;:;::::::::>>::>>>>::>>>:>>:>>>>>:>>>>>:>>>>:>>>>:>:>:>>:>:>>:>>:>:>:>>:>>>>>>>>>>>>>>,,,,789900", +"52>=;::;::::::>:::>:::>>>::>>>:>:>:>:>>:::>>:>:>>::>:>>::>:>>:>>:>>:>:>>>:>:>::>>>>>>>>>,>,>>>689900", +"52>=;:;:;::::::::::::>>:>:>::>::>:>::>:>:>>::>>::>:::>:>>:>:>:>:>:>:>::::::>:>:>>:>>>:>>>>,>,,789900", +"52>=:;;::;:;:;:::::::::::::::::>:::::::::::::::::::>>:::::::::::::::::>::>::::::>::>:>:>>>>>>>789000", +"62>=:;;;:;;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::>:>>>>,>>>699900", +"62>=;;::;;:;:;::::;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::>::>:>:>,>>>699900", +"62>=;;:;:;;;;:;::;;:;:;::::;::::::;;:::;:::::;:;::;::::;::;:;::;::::;:;:;:::::;:::::>::>:>>>>>799000", +"62>-:::;;:;:;;;:;:;;;:;;;::::;:;::::;::::;:::::::;::;:::::::;:::::::::;::::;:;:::::::::::>>>>>799000", +"62>=;-;-;;;;;;;;:;::::::;;:;;;;;;;;;;:;;;;;;;;;:;;:;;;;;;;;;;:;;;;;;:;;::;;:;;:;:::::::::>::>>890900", +"62>=::::;;;::;;;;;;;;-;;:;;:::;::;:::;;::;:;;::::;;:::;::;:::;:;;::;;;:;;:;::;;:;-;;:;:;::>:>>799000", +"52>-;;;;-;--;-;-;:;;:;;;-;-;--;;;;;;-;-;--;;;;--;-;---;;-;;;-;-;;-;-;;;-;-;-;-;-;;:::::::::>>>790000", +"52>-;;:-;;:;;;;;-;;-;--;:;:;:;:;;;;;:;:;:;:;;;:;:;::;:;;:;;;:;:;;:;:;;;:;::;;:;::;:;::::::::::800000", +"52,-;;;;-;;-;--;;-;;;;;--;;;;--;;;-;-;;;;--;;;;--;;-;-;;-;;;-;;;;;-;;;;-;-;-;;-;;:-:;;;;;:::>>790000", +"72>=*&&%&&&&&&&&&&%&%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%$%%%&=>22090000", +"63<:=**********=**=*=***=*=**************************=*****=*=***********************&*&*=;,26900000", +"641>:-------;-;-------------;--;-;;-;-;--;--;--;--;-----;------;--;-;-;--;--;--;--=-=====;>136900000", +"8522<>>>>>>>,>>,>>>>,>>>>>,,>>>>>>>,>>>>,>>>>>,>>,,>,>>>>>,,>>>>>>,>>>>>,>>>>>,>>>>>>>>>><1257990000", +"8653222121212112112121212112112121121121212121121<212121211211212112112121212112121222<1223579900000" +}; diff --git a/mayaPlug/shaveError.h b/mayaPlug/shaveError.h new file mode 100644 index 0000000..5102742 --- /dev/null +++ b/mayaPlug/shaveError.h @@ -0,0 +1,19 @@ +#ifndef shaveError_h +#define shaveError_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MGlobal.h> +#include <maya/MString.h> + +#define MRptErr(st,msg) MGlobal::displayError(MString("Shave: ")+msg + ": " + st.errorString()) + +#define MRetErr(st,msg) { MRptErr(st,msg); return st; } + +#define MChkErr(st,msg) if (!st) MRetErr(st,msg) + +#define MChkErrVoid(st,msg) if (!st) { MRptErr(st,msg); return; } + +#endif diff --git a/mayaPlug/shaveExportGame.cpp b/mayaPlug/shaveExportGame.cpp new file mode 100644 index 0000000..349362a --- /dev/null +++ b/mayaPlug/shaveExportGame.cpp @@ -0,0 +1,151 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveExportGame.h" +#include "shaveHairShape.h" + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDagPath.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MItDependencyNodes.h> + +#include "shaveSDKFUNCS.h" + +#include "shaveUtil.h" +#include "shaveTextureStore.h" + +//Syntax: shaveExportGame <file> -node <...> + +const MString shaveExportGame::cmd("shaveGameExport"); + +//const char* shaveExportGame::filename_long("file"); +//const char* shaveExportGame::filename_short("f"); +const char* shaveExportGame::nodename_long("node"); +const char* shaveExportGame::nodename_short("n"); + +//void SHAVEwrite_nodeDISK(char *filename); + +MStatus shaveExportGame::doIt(const MArgList &args) +{ + MStatus stat = MStatus::kSuccess; + + MArgDatabase parser(syntax(),args,&stat); + if(stat != MS::kSuccess) + { + MGlobal::displayError("shaveGameExport: can not parse arguments."); + return stat; + } + + MString filename; + //stat = parser.getFlagArgument( filename_short, 0, filename ); + stat = parser.getCommandArgument(0,filename); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveGameExport: can not parse the 'file' flag."); + return stat; + } + shaveHairShape* sn = NULL; + if(parser.isFlagSet(nodename_short)) + { + MString nodename; + stat = parser.getFlagArgument( nodename_short, 0, nodename ); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveGameExport: can not parse the 'node' flag."); + return stat; + } + MObject node; + if(!findNodeByName(nodename,node)) + { + MGlobal::displayError(MString("shaveGameExport: can not find ") + nodename + " node"); + return MStatus::kFailure; + } + MFnDependencyNode dFn(node); + if(dFn.typeId() != shaveHairShape::id) + { + MGlobal::displayError(MString("shaveGameExport: the node ") + nodename + " is not a shaveHairShape"); + return MStatus::kFailure; + } + sn = (shaveHairShape*)dFn.userNode(); + } + else //pull shavenode from selection + { + MDagPath node = shaveUtil::getCurrentHairShape(); + if(!node.isValid()) + { + MGlobal::displayError("shaveGameExport: can't find current shaveHairShape"); + return MStatus::kFailure; + } + MFnDagNode dFn(node); + if(dFn.typeId() != shaveHairShape::id) + { + MGlobal::displayError(MString("shaveGameExport: the node ") +dFn.name() + " is not a shaveHairShape"); + return MStatus::kFailure; + } + //MGlobal::displayInfo(MString("Exporting for node ")+dFn.name()); + sn = (shaveHairShape*)dFn.userNode(); + } + if(!sn) + { + MGlobal::displayError("shaveGameExport: can not find shaveHairShape - select or spesify node name"); + return MStatus::kFailure; + } + ///// do whatever you want /////// + //MGlobal::displayInfo(MString("game export file ")+filename + " node " + (unsigned int)&sn->hairnode); + + //// update texture cache ///// + MObjectArray shaveHairShapes; + shaveUtil::getShaveNodes(shaveHairShapes); + + if(shaveHairShapes.length() > 0) + { +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapes, "", false/*mFrameGlobals.verbose*/); +#else + initTexInfoLookup(shaveHairShapes, "", false/*mFrameGlobals.verbose*/); +#endif + } + + //use &sn->hairnode to pass shavenode to your params + sn->makeCurrent(); + SHAVEwrite_nodeDISK((char*)filename.asChar()); + ////////////////////////////////// + return MStatus::kSuccess; + + + +} + +MSyntax shaveExportGame::newSyntax() +{ + MSyntax syn; + + //syn.addFlag(filename_short, filename_long, MSyntax::kString); + syn.addArg(MSyntax::kString); + syn.addFlag(nodename_short, nodename_long, MSyntax::kString); + + return syn; +} + +bool shaveExportGame::findNodeByName(const MString& name, MObject& thenode) const +{ + if(name == "") + return false; + + MItDependencyNodes iter; + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + MFnDependencyNode dFn(node); + if(dFn.name() == name) + { + thenode = node; + return true; + } + } + return false; +} diff --git a/mayaPlug/shaveExportGame.h b/mayaPlug/shaveExportGame.h new file mode 100644 index 0000000..09bf385 --- /dev/null +++ b/mayaPlug/shaveExportGame.h @@ -0,0 +1,42 @@ +#ifndef _shaveExportGameCmd_h_ +#define _shaveExportGameCmd_h_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> +#include <maya/MObject.h> + + +class shaveExportGame : public MPxCommand +{ +public: + static const MString cmd; + +// static const char* filename_long; +// static const char* filename_short; + static const char* nodename_long; + static const char* nodename_short; + + + shaveExportGame(){} + virtual ~shaveExportGame(){} + + MStatus doIt( const MArgList& args ); + bool isUndoable() const {return false;} + + static void* creator() {return new shaveExportGame;} + static MSyntax newSyntax(); + +protected: + bool findNodeByName(const MString& name, MObject& thenode) const; + +}; + + +#endif + diff --git a/mayaPlug/shaveGeometryOverride.cpp b/mayaPlug/shaveGeometryOverride.cpp new file mode 100644 index 0000000..fb29392 --- /dev/null +++ b/mayaPlug/shaveGeometryOverride.cpp @@ -0,0 +1,3840 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/M3dView.h> +#include <maya/MBoundingBox.h> +#include <maya/MDagPath.h> +#include <maya/MDrawContext.h> +#include <maya/MDrawRegistry.h> +#include <maya/MFloatVector.h> +#include <maya/MFnCamera.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnDoubleIndexedComponent.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MGlobal.h> +#include <maya/MHWGeometry.h> +#include <maya/MHWGeometryUtilities.h> +#include <maya/MObject.h> +#include <maya/MPlug.h> +#if MAYA_API_VERSION >= 201600 +#include <maya/MPxComponentConverter.h> +#include <maya/MSelectionContext.h> +#endif +#include <maya/MVector.h> +#include <maya/MViewport2Renderer.h> + +#include "shaveGeometryOverride.h" +#include "shaveGlobals.h" + + +// Names of render items. +// +const MString shaveGeometryOverride::kHairShadedItemName = "shaveShaded"; +const MString shaveGeometryOverride::kHairShadedSelectedItemName = "shaveShadedSelection"; +const MString shaveGeometryOverride::kHairWireItemName = "shaveWire"; +const MString shaveGeometryOverride::kHairWireSelectedItemName = "shaveWireSelection"; +const MString shaveGeometryOverride::kInstanceShadedItemName = "shaveInstSolid"; +const MString shaveGeometryOverride::kInstanceWireItemName = "shaveInstWire"; +const MString shaveGeometryOverride::kInstanceWireSelectedItemName = "shaveInstWireSelection"; +const MString shaveGeometryOverride::kGuidesItemName = "shaveGuides"; +const MString shaveGeometryOverride::kKnotsItemName = "shaveKnots"; + +// pre-draw callback +//MHWRender::MShaderInstance* gShader = NULL; +#if 0 +static void shavePreDrawCallback( + MHWRender::MDrawContext& context, + const MHWRender::MRenderItemList& renderItemList, + MHWRender::MShaderInstance *shaderInstance + ) +{ + //printf("Pre-draw callback triggered for render item with name '%s'\n", renderItem->name().asChar()); +} +// post-draw callback +static void shavePostDrawCallback( + MHWRender::MDrawContext& context, + const MHWRender::MRenderItemList& renderItemList, + MHWRender::MShaderInstance *shaderInstance + ) +{ + //printf("Post-draw callback triggered for render item with name '%s'\n", renderItem->name().asChar()); +} +#endif + + +#if MAYA_API_VERSION >= 201600 +// Custom component converter for selecting guide vertices. +// +class guideVertComponentConverter : public MHWRender::MPxComponentConverter +{ +public: + guideVertComponentConverter() : MHWRender::MPxComponentConverter() {} + virtual ~guideVertComponentConverter() {} + + virtual void initialize(const MHWRender::MRenderItem& renderItem) + { + mComponent = MObject::kNullObj; + mPrimIdxToVertIdx = NULL; + mSelectType = ""; + + shaveGeometryOverride::SelectionData* itemData = dynamic_cast<shaveGeometryOverride::SelectionData*>(renderItem.customData()); + + if (itemData) + { + mSelectType = itemData->mShaveSelectType; + mPrimIdxToVertIdx = &itemData->mPrimIdxToVertIdx; + + // If the selection type is anything other than 'vert' or 'tip' + // then we are not selecting verts, we're using verts to select + // guides. + // + if ((mSelectType == "vert") || (mSelectType == "tip")) + { + mComponent = mVertComponentFn.create(shaveHairShape::kShaveGuideVertComponent); + } + else + { + mComponent = mGuideComponentFn.create(shaveHairShape::kShaveGuideComponent); + } + } + } + + virtual void addIntersection(MHWRender::MIntersection& intersection) + { + int primIdx = intersection.index(); + + if (mPrimIdxToVertIdx != NULL) + { + if ((primIdx >= 0) && (primIdx < mPrimIdxToVertIdx->size())) + { + int vertIdx = mPrimIdxToVertIdx->at(primIdx); + int guideIdx = vertIdx / SHAVE_VERTS_PER_GUIDE; + + if ((mSelectType == "vert") || (mSelectType == "tip")) + { + vertIdx = vertIdx % SHAVE_VERTS_PER_GUIDE; + mVertComponentFn.addElement(guideIdx, vertIdx); + } + else + { + mGuideComponentFn.addElement(guideIdx); + } + } + } + } + + virtual MObject component() + { + return mComponent; + } + + virtual MSelectionMask selectionMask() const + { + MSelectionMask mask; + + if ((mSelectType == "vert") || (mSelectType == "tip")) + { + mask.addMask(MSelectionMask::kSelectCVs); + + // Allow the converter to be used for snapping as well. + // + mask.addMask(MSelectionMask::kSelectPointsForGravity); + } + else + { + mask.addMask(MSelectionMask::kSelectMeshEdges); + } + + return mask; + } + + static MPxComponentConverter* create() + { + return new guideVertComponentConverter(); + } + +private: + MObject mComponent; + MFnSingleIndexedComponent mGuideComponentFn; + MFnDoubleIndexedComponent mVertComponentFn; + const std::vector<unsigned int>* mPrimIdxToVertIdx; + MString mSelectType; +}; + + +// Custom component converter for selecting guides. +// +class guideComponentConverter : public MHWRender::MPxComponentConverter +{ +public: + guideComponentConverter() : MHWRender::MPxComponentConverter() {} + virtual ~guideComponentConverter() {} + + virtual void initialize(const MHWRender::MRenderItem& renderItem) + { + mComponent = MObject::kNullObj; + mPrimIdxToGuideIdx = NULL; + mSelectType = ""; + + shaveGeometryOverride::SelectionData* itemData = dynamic_cast<shaveGeometryOverride::SelectionData*>(renderItem.customData()); + + if (itemData) + { + mSelectType = itemData->mShaveSelectType; + + // Only use guides for selection if we're in 'guide' mode. + // + if (mSelectType == "guide") + { + mComponent = mComponentFn.create(shaveHairShape::kShaveGuideComponent); + mPrimIdxToGuideIdx = &itemData->mPrimIdxToGuideIdx; + } + } + } + + virtual void addIntersection(MHWRender::MIntersection& intersection) + { + int primIdx = intersection.index(); + if (mPrimIdxToGuideIdx != NULL) + { + if ((primIdx >= 0) && (primIdx < mPrimIdxToGuideIdx->size())) + { + int guideIdx = mPrimIdxToGuideIdx->at(primIdx); + + mComponentFn.addElement(guideIdx); + } + } + } + + virtual MObject component() + { + return mComponent; + } + + virtual MSelectionMask selectionMask() const + { + MSelectionMask mask; + + if (mSelectType == "guide") + { + mask.addMask(MSelectionMask::kSelectMeshEdges); + } + + return mask; + } + + static MPxComponentConverter* create() + { + return new guideComponentConverter(); + } + +private: + MObject mComponent; + MFnSingleIndexedComponent mComponentFn; + const std::vector<unsigned int>* mPrimIdxToGuideIdx; + MString mSelectType; +}; +#endif + + +shaveGeometryOverride::shaveGeometryOverride(const MObject& obj) +: MPxGeometryOverride(obj) +, shave(NULL) +, instex (NULL) +{ + MStatus status; + shobj = obj; + MFnDependencyNode node(obj, &status); + if (status) + { + shave = dynamic_cast<shaveHairShape*>(node.userNode()); + } + else + { + MGlobal::displayError("can not attach MFnDependencyNode function set to the shave node"); + } + + MFnDagNode dag(obj,&status); + if (status) + { + MObject tm = dag.parent(0,&status); + if(!tm.isNull() && status == MStatus::kSuccess) + { + MFnDependencyNode tmfn(tm, &status); + //if(!tmfn.isLocked()) + { + //does not have any effect + //status = tmfn.setLocked(true); + //if(status != MStatus::kSuccess) + // MGlobal::displayError("can not lock transfrom"); + + MPlug plug = tmfn.findPlug("translate", &status); + status = plug.setLocked(true); // lock the attribute + + plug = tmfn.findPlug("rotate", &status); + status = plug.setLocked(true); // lock the attribute + + plug = tmfn.findPlug("scale", &status); + status = plug.setLocked(true); // lock the attribute + } + } + else + MGlobal::displayError("can not get parent for shave node"); + } + else + MGlobal::displayError("can not attach MFnDagNode function set to the shave node"); +} + +shaveGeometryOverride::~shaveGeometryOverride() +{ +} + +MHWRender::DrawAPI shaveGeometryOverride::supportedDrawAPIs() const +{ + //return MHWRender::kAllDevices; + //return (MHWRender::kOpenGL | MHWRender::kDirectX11); + return MHWRender::kOpenGL; +} + +#define POLY_HAIR + +static const bool debugShader = false; + +void shaveGeometryOverride::updateDG() +{ + MStatus st; + + if (shave) + { + mNumHairsToDraw = shave->getNumDisplayHairs(false); + + // TODO: + // + // We assume that the "active" 3d view (i.e. the one the mouse is in) + // is also the one currently being drawn. That is not necessarily + // true, especially when there are multiple views. + // + // We can determine the camera being rendered from the MFrameContext + // passed to addUIDrawables() (which we don't currently implement) but + // that won't be called until *after* updateDG() when it will be too + // late to pull data from the shaveHairShape's attrs. + // + // addUIDrawables() is called *before* populateGeometry(), so one + // workaround would be to cache data for *all* available views here, + // then select the appropriate set of data in populateGeometry(), based + // on the camera determined in addUIDrawables(). However, it would make + // sense to first check with ADesk to see if there is a way to get the + // necessary cam info (viewport, matrix, etc) here in updateDG(). + // + M3dView view = M3dView::active3dView(); + + mLightMode = M3dView::kLightDefault; + view.getLightingMode(mLightMode); + + MDagPath camPath; + st = view.getCamera(camPath); + + if (st) + { + MFnCamera camFn(camPath); + + mViewDir = camFn.viewDirection(MSpace::kWorld); + mViewDir.normalize(); + + mUpDir = camFn.upDirection(MSpace::kWorld); + mUpDir.normalize(); + + mRightDir = camFn.rightDirection(MSpace::kWorld); + mRightDir.normalize(); + + mWorldToCam = camPath.inclusiveMatrixInverse(); + } + else + { + mViewDir = MVector::zAxis; + mUpDir = MVector::xAxis; + mRightDir = MVector::yAxis; + } + +#if 1 + mHairCache = &shave->getDisplayHairs(view, true); + + while (mNumHairsToDraw != mHairCache->displayHair.size()) + { +#ifdef GLOBAL_FALLBACK + if((IsMouseDown() || GetEventsHappen()) && !liveModeGlob && fallback) + { + shave->invalidateDisplayHairs(); + break; + } +#endif + mHairCache = &shave->getDisplayHairs(view, false); + } +#ifdef GLOBAL_FALLBACK + ClearEvents(); +#endif +#else + mHairCache = &shave->getDisplayHairs(); +#endif + + if (debugShader) + { + printf("cache size: %zi\n", mHairCache->displayHair.size()); + } + + if (mNumHairsToDraw > mHairCache->displayHair.size()) + { + mNumHairsToDraw = (unsigned int)mHairCache->displayHair.size(); + } + + mHairDisplayMode = shave->getDisplayMode(); + mSpecularTint = shave->getSpecularTint(); + mSpecularTint2 = shave->getSpecularTint2(); + mGloss = shave->getGloss(); + mSpecular = shave->getSpecular(); + mTransparency = shave->getHairXparency(); + mDoTipFade = shave->getDoTipfade(); + mPasses = shave->getNumPasses(); + + mInstanceCache = &shave->getInstanceDisplay(); + mGuideCache = &shave->getGuides().guides; + + mGuideExtent = shave->getGuideExt(); + + // If the guide extent is zero then it hasn't yet been calculated, so do it now. + // + if (mGuideExtent == 0.0f) //compute once + { + MBoundingBox bbox; + bbox.clear(); + + shaveHairShape::Guides::const_iterator iter; + + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + MPoint v = guide.verts[SHAVE_VERTS_PER_GUIDE-1];//tip only + bbox.expand(v); + } + } + + mGuideExtent = (float)(bbox.max() - bbox.min()).length(); + shave->setGuideExt(mGuideExtent); + } + + if (!glisntGlob) + { + shave->updateTexLookups(); + } + } +} +#if 0 +/////////////////////////////////////////////////////////////////////////////////// +#ifdef _WIN32 +#else +# include <string.h> +# define MAX_PATH 1024 +#endif +#ifndef _CGFX_PLUGIN_MAX_COMPILER_ARGS_ +#define _CGFX_PLUGIN_MAX_COMPILER_ARGS_ 20 +#endif + +MString cgfxFindFile(const MString& name, const MString &searchpath) +{ + MString file = name; + struct stat statBuf; + char path[MAX_PATH]; + + const char * psearchpath = searchpath.asChar(); + + OutputDebugString("File = "); + OutputDebugString(file.asChar()); + OutputDebugString("\n"); + + // First we check if it is a fully qualified path... + if (stat(file.asChar(), &statBuf) == -1) + { + bool found = false; + + while (found == false && psearchpath < searchpath.asChar() + searchpath.length()) + { + const char * endpath = strchr(psearchpath,';'); + if (endpath) + { + strncpy(path,psearchpath, endpath - psearchpath); + path[endpath - psearchpath] = '\0'; + } + else + { + strcpy(path,psearchpath); + } + + psearchpath += strlen(path)+1; + + bool fullPath = (path[0] == '/' || + path[0] == '\\'); + + if (strlen(path) > 2) + { + fullPath = fullPath || + (path[1] == ':' && + (path[2] == '/' || + path[2] == '\\')); + } + + // Add the path and the filename together to get the full path + file = MString(path) + "/" + name; + + OutputDebugString("Try File = "); + OutputDebugString(file.asChar()); + OutputDebugString("\n"); + + if (stat(file.asChar(), &statBuf) != -1) + found = true; + else + file = ""; + } + } + + OutputDebugString("Returning: "); + OutputDebugString(file.asChar()); + OutputDebugString("\n"); + + + return file; +} + +MString cgfxFindFile(const MString& name, bool projectRelative) { + // Our result + MString fileName; + + // Do we have an image to look for? + if (name.asChar() != NULL && strcmp(name.asChar(), "")) + { + // Build a list of places we'll look for textures + // Start with the current working directory + static MString texturePath( "."); + + // Add the standard Maya project paths + MString workspace; + MStatus status = MGlobal::executeCommand(MString("workspace -q -rd;"), + workspace); + if ( status == MS::kSuccess) + { + texturePath += ";"; + texturePath += workspace; + texturePath += ";"; + texturePath += workspace; + texturePath += "/textures;"; + texturePath += workspace; + texturePath += "/images;"; + texturePath += workspace; + } + + // Finally, see if any CgFX environment variable paths are set + char * cgfxPath = getenv("CGFX_TEXTURE_PATH"); + if (cgfxPath) + { + texturePath += ";"; + texturePath += cgfxPath; + } + else + { + char * cgfxRoot = getenv("CGFX_ROOT"); + if (cgfxRoot) + { + texturePath += ";"; + texturePath += cgfxRoot; + texturePath += "/textures/2D;"; + texturePath += cgfxRoot; + texturePath += "/textures/cubemaps;"; + texturePath += cgfxRoot; + texturePath += "/textures/3D;"; + texturePath += cgfxRoot; + texturePath += "/textures/rectangles;"; + texturePath += cgfxRoot; + texturePath += "/CgFX_Textures;"; + texturePath += cgfxRoot; + texturePath += "/CgFX"; + } + } + + OutputDebugString("CgFX texture path is: "); + OutputDebugString(texturePath.asChar()); + OutputDebugString("\n"); + + fileName = cgfxFindFile(name, texturePath); + + int hasFile = fileName.asChar() != NULL && strcmp(fileName.asChar(), ""); + + if (hasFile == 0) + { + // lets extract the filename and try it again... + int idx = name.rindex('/'); + if (idx == -1) + idx = name.rindex('\\'); + if (idx != -1) + { + MString filename = name.substring(idx+1,name.length()-1); + fileName = cgfxFindFile(filename, texturePath); + hasFile = fileName.asChar() != NULL && strcmp(fileName.asChar(), ""); + } + } + + // If we found the file and the user wants project relative, try + // to strip the project directory off the front of our result + if( hasFile && projectRelative) + { + if( fileName.length() > workspace.length() && + workspace.length() > 0 && + fileName.substring( 0, workspace.length() - 1) == workspace) + // Strip the project path off the front INCLUDING the + // separating '/' (otherwise we'd create an absolute path) + fileName = fileName.substring( workspace.length() + 1, fileName.length() - 1); + } + + if (hasFile == 0) + OutputDebugString("Error: file not found.\n"); + } + return fileName; +} + +void cgfxGetFxIncludePath( const MString &fxFile, MStringArray &pathOptions ) +{ + // Append the path of the cgfx file as a possible include search path + // + MString option; + if (fxFile.length()) + { + MFileObject fobject; + fobject.setRawFullName( fxFile ); + option = MString("-I") + fobject.resolvedPath(); + pathOptions.append( option ); + } + + // Add in "standard" cgfx search for cgfx files as a possible include + // search path + // + char * cgfxRoot = getenv("CGFX_ROOT"); + if (cgfxRoot) + { + option = MString("-I") + MString(cgfxRoot); + pathOptions.append( option ); + option = MString("-I") + MString(cgfxRoot) + MString("/CgFX"); + pathOptions.append( option ); + } + + // Add in Maya's Cg directory + char * mayaLocation = getenv("MAYA_LOCATION"); + if (mayaLocation) + { + MString mayaCgLocation(MString(mayaLocation) + MString("/bin/Cg/")); + option = MString("-I") + mayaCgLocation; + pathOptions.append( option ); + } +} +/////////////////////////////////////////////////////////////////////////////////// +#endif + +static bool vp20dlgFired = false; + +void shaveGeometryOverride::updateRenderItems(const MDagPath& path, + MHWRender::MRenderItemList& list) +{ + if (mLightMode == M3dView::kLightActive || + mLightMode == M3dView::kLightAll) + { + if(!vp20dlgFired) + { + vp20dlgFired = true; + MGlobal::executeCommand("shave_showVP2lightWarning"); + } + } + else + vp20dlgFired = false; + + if(debugShader) printf("updateRenderItems\n"); + + if(!shave) + return; + + MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) return; + + const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager(); + if (!shaderMgr) return; + + MHWRender::MTextureManager* textureMgr = renderer->getTextureManager(); + if(!textureMgr) return; + + if(debugShader) + { + MStringArray paths; + shaderMgr->shaderPaths(paths); + printf("Cgfx shader paths:\n"); + for(unsigned int i = 0; i < paths.length(); i++) + printf("%s\n",paths[i].asChar()); + } + + // This is the type of primitive we use for drawing anything + // which isn't an instance. + // +#ifdef POLY_HAIR + MHWRender::MGeometry::Primitive nonInstPrim = MHWRender::MGeometry::kTriangles; +#else + MHWRender::MGeometry::Primitive nonInstPrim = MHWRender::MGeometry::kLines; +#endif + + if (mHairDisplayMode == shaveHairShape::kHairDisplayNone) + { + // Disable all render items. + // + disableRenderItem(list, kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); + disableRenderItem(list, kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); + disableRenderItem(list, kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe); + disableRenderItem(list, kHairWireSelectedItemName, nonInstPrim, MHWRender::MGeometry::kWireframe/*::kAll*/); + + disableRenderItem( + list, + kInstanceShadedItemName, + MHWRender::MGeometry::kTriangles, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + + disableRenderItem( + list, + kHairShadedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + + disableRenderItem( + list, + kHairShadedSelectedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + } + else if (mHairDisplayMode == shaveHairShape::kHairDisplayHair) + { + // disable instances + disableRenderItem( + list, + kInstanceWireItemName, + MHWRender::MGeometry::kLines, + MHWRender::MGeometry::kWireframe + ); + + disableRenderItem( + list, + kInstanceWireSelectedItemName, + MHWRender::MGeometry::kLines, + MHWRender::MGeometry::kWireframe + ); + + disableRenderItem( + list, + kInstanceShadedItemName, + MHWRender::MGeometry::kTriangles, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + + ///////////// wirewrame not selected ///////// + // + int index ; + // add wire frame item if it's not there + MHWRender::MRenderItem* wireItem = NULL; + index = list.indexOf(kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe); + + if (index < 0) + { + wireItem = MHWRender::MRenderItem::Create + (kHairWireItemName, + nonInstPrim, + MHWRender::MGeometry::kWireframe, + false); + + //wireItem->castsShadows(true); + + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx", + "StrandLines"/*, + 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/); + if (shader) + { + list.append(wireItem); + + static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; + shader->setParameter("gUseColor", true); + shader->setParameter("gColor", theColor); + + // assign shader + wireItem->setShader(shader); + + // sample debug code + if (debugShader) + { + MStringArray params; + shader->parameterList(params); + unsigned int numParams = params.length(); + printf("DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH %d\n", numParams); + for (unsigned int i=0; i<numParams; i++) + { + printf("ParamName='%s', ParamType=", params[i].asChar()); + switch (shader->parameterType(params[i])) + { + case MHWRender::MShaderInstance::kInvalid: + printf("'Invalid', "); + break; + case MHWRender::MShaderInstance::kBoolean: + printf("'Boolean', "); + break; + case MHWRender::MShaderInstance::kInteger: + printf("'Integer', "); + break; + case MHWRender::MShaderInstance::kFloat: + printf("'Float', "); + break; + case MHWRender::MShaderInstance::kFloat2: + printf("'Float2', "); + break; + case MHWRender::MShaderInstance::kFloat3: + printf("'Float3', "); + break; + case MHWRender::MShaderInstance::kFloat4: + printf("'Float4', "); + break; + case MHWRender::MShaderInstance::kFloat4x4Row: + printf("'Float4x4Row', "); + break; + case MHWRender::MShaderInstance::kFloat4x4Col: + printf("'Float4x4Col', "); + break; + case MHWRender::MShaderInstance::kTexture1: + printf("'1D Texture', "); + break; + case MHWRender::MShaderInstance::kTexture2: + printf("'2D Texture', "); + break; + case MHWRender::MShaderInstance::kTexture3: + printf("'3D Texture', "); + break; + case MHWRender::MShaderInstance::kTextureCube: + printf("'Cube Texture', "); + break; + case MHWRender::MShaderInstance::kSampler: + printf("'Sampler', "); + break; + default: + printf("'Unknown', "); + break; + } + printf("IsArrayParameter='%s'\n", shader->isArrayParameter(params[i]) ? "YES" : "NO"); + } + printf("END PARAM LIST\n"); + fflush(stdout); + } + } + else //let's try to get debug info + { +#if 0 + ////////////////////////////////////////////////////// + MString fileName = cgfxFindFile("shaveHair.cgfx",false); + + if(fileName.asChar() != NULL && strcmp(fileName.asChar(), "")) + { + // Compile and create the effect. + MStringArray fileOptions; + cgfxGetFxIncludePath( fileName, fileOptions ); + const char *opts[_CGFX_PLUGIN_MAX_COMPILER_ARGS_]; + unsigned int numOpts = fileOptions.length(); + if (numOpts) + { + numOpts = (numOpts > _CGFX_PLUGIN_MAX_COMPILER_ARGS_) ? _CGFX_PLUGIN_MAX_COMPILER_ARGS_ : numOpts; + for (unsigned int i=0; i<numOpts; i++) + opts[i] = fileOptions[i].asChar(); + opts[numOpts] = NULL; + } + CGeffect cgEffect = cgCreateEffectFromFile(sCgContext, fileName.asChar(), opts); + + if (cgEffect) + { + } + } + + ////////////////////////////////////////////////////// +#endif + } + } + else + { + wireItem = list.itemAt(index); + } + ///////////// wirewrame selected ///////// + // + MHWRender::MRenderItem* selectItem = NULL; + index = list.indexOf(kHairWireSelectedItemName, nonInstPrim, MHWRender::MGeometry::kWireframe/*::kAll*/); + if (index < 0) + { + selectItem = MHWRender::MRenderItem::Create + (kHairWireSelectedItemName, + nonInstPrim, + MHWRender::MGeometry::kWireframe/*::kAll*/, + true); + + //selectItem->castsShadows(true); + + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx", + "StrandLines" /*, + 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/ ); + + if (shader) + { + list.append(selectItem); + + static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; + shader->setParameter("gUseColor", true); + shader->setParameter("gColor", selColor); + + //shader->setIsTransparent(true);//crap, why it does not change anything? + + selectItem->setShader(shader); + + /////////////////// + //printf("transparent %s\n", shader->isTransparent()?"yes":"no");fflush(stdout); + /////////////////// + } + } + else + { + selectItem = list.itemAt(index); + } + + // Update selection item, disable by default + bool brushDown = shave->getBrushDown(); + selectItem->enable(false && !brushDown /*&& !hideHairGlob*/); + wireItem->enable(true && !brushDown /*&& !hideHairGlob*/); + { + MStatus status; + MSelectionList selectedList; + status = MGlobal::getActiveSelectionList(selectedList); + if (status) + { + MDagPath pathCopy = path; + do + { + if (selectedList.hasItem(pathCopy)) + { + selectItem->enable(true && !brushDown/* && !hideHairGlob*/); + wireItem->enable(false && !brushDown /*&& !hideHairGlob*/); + break; + } + status = pathCopy.pop(); + } while(status); + } + } + + ///////////// solid not selected ///////// + // + MHWRender::MShaderInstance* shader = NULL; + MHWRender::MRenderItem* shadedItem = NULL; + index = list.indexOf( + kHairShadedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured)); + if (index < 0) + { + shadedItem = MHWRender::MRenderItem::Create + (kHairShadedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured), + false); + /*MHWRender::MShaderInstance* */ shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx", + "StrandLines" /*, + 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/); + + //shadedItem->castsShadows(true); + + if (shader) + { + list.append(shadedItem); + + // assign shader + shader->setParameter("gUseColor", false); + shader->setParameter("gDimmed", true); + + shadedItem->setShader(shader); + } + else + MGlobal::displayError("Can't load 'shaveHair.cgfx' shader"); + } + else + { + shadedItem = list.itemAt(index); + shader = shadedItem->getShader(); + } + + if(shader) + { + shader->setParameter("gSpecular", mGloss); + shader->setParameter("gSpecularAmt", mSpecular); + + float tint[3]; + mSpecularTint.get(tint); + shader->setParameter("gSpecularTint", tint); + + mSpecularTint2.get(tint); + shader->setParameter("gSpecularTint2", tint); + } + + + ///////////// solid selected ///////// + // + MHWRender::MShaderInstance* shader2 = NULL; + MHWRender::MRenderItem* shadedItemSel = NULL; + index = list.indexOf( + kHairShadedSelectedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured)); + if (index < 0) + { +// shadedItemSel = MHWRender::MRenderItem::Create +// (kHairShadedSelectedItemName, +// MHWRender::MGeometry::kLines, +// MHWRender::MGeometry::kShaded, /*false*/ true); + +//#if MAYA_API_VERSION >= 201500 +// shadedItemSel = MHWRender::MRenderItem::Create +// (kHairShadedSelectedItemName, +// MHWRender::MRenderItem::MaterialSceneItem, +// MHWRender::MGeometry::kLines); +// shadedItemSel->setDrawMode(MHWRender::MGeometry::kShaded); +//#else +// shadedItemSel = MHWRender::MRenderItem::Create +// (kHairShadedSelectedItemName, +// MHWRender::MGeometry::kLines, +// MHWRender::MGeometry::kShaded, /*false*/ true); +//#endif + shadedItemSel = MHWRender::MRenderItem::Create + (kHairShadedSelectedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured), + false); + + //shadedItemSel->castsShadows(true); + + /*MHWRender::MShaderInstance* */ shader2 = shaderMgr->getEffectsFileShader("shaveHair.cgfx", + "StrandLines"/*, + 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/); + + if (shader2) + { + list.append(shadedItemSel); + + // assign shader + shader2->setParameter("gUseColor", false); + shader2->setParameter("gDimmed", false); + shader2->setParameter("gSpecular", mGloss); + shader2->setParameter("gSpecularAmt", mSpecular); + + float tint[3]; + mSpecularTint.get(tint); + shader2->setParameter("gSpecularTint", tint); + + mSpecularTint2.get(tint); + shader2->setParameter("gSpecularTint2", tint); + + shadedItemSel->setShader(shader2); + + // once assigned, no need to hold on to shader instance + //shaderMgr->releaseShader(shader); + } + else + MGlobal::displayError("Can't load 'shaveHair.cgfx' shader"); + + + /* + //let's try stock shader - its ok + MHWRender::MShaderInstance* shader = shaderMgr->getStockShader( + MHWRender::MShaderManager::k3dSolidShader, NULL, NULL); + if (shader) + { + // assign shader + list.append(shadedItemSel); + shadedItemSel->setShader(shader); + + // once assigned, no need to hold on to shader instance + shaderMgr->releaseShader(shader); + } + */ + + } + else + { + shadedItemSel = list.itemAt(index); + shader2 = shadedItemSel->getShader(); + } + if(shader2) + { + shader2->setParameter("gSpecular", mGloss); + shader2->setParameter("gSpecularAmt", mSpecular); + + float tint[3]; + mSpecularTint.get(tint); + shader2->setParameter("gSpecularTint", tint); + + mSpecularTint2.get(tint); + shader2->setParameter("gSpecularTint2", tint); + } + + // Update selection item, disable by default + shadedItemSel->enable(false && !brushDown /*&& !hideHairGlob*/); + shadedItem->enable(true && !brushDown /*&& !hideHairGlob*/); + { + MStatus status; + MSelectionList selectedList; + status = MGlobal::getActiveSelectionList(selectedList); + if (status) + { + MDagPath pathCopy = path; + do + { + if (selectedList.hasItem(pathCopy)) + { + shadedItemSel->enable(true && !brushDown /*&& !hideHairGlob*/); + shadedItem->enable(false && !brushDown/* && !hideHairGlob*/); + break; + } + status = pathCopy.pop(); + } while(status); + } + } + + if (mNumHairsToDraw == 0) + { + shadedItemSel->enable(false); + shadedItem->enable(false); + selectItem->enable(false); + wireItem->enable(false); + } + else if(!doFallbackGlob) + { + shadedItemSel->enable(true); + shadedItem->enable(false); + } + } + else if(mHairDisplayMode == shaveHairShape::kHairDisplayGeom && glisntGlob /*false*/) + { + // disable regular hair items + disableRenderItem(list, kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe); + disableRenderItem(list, kHairWireSelectedItemName, nonInstPrim, MHWRender::MGeometry::kWireframe/*::kAll*/); + + disableRenderItem( + list, + kHairShadedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + + disableRenderItem( + list, + kHairShadedSelectedItemName, + nonInstPrim, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + + ///////////// wirewrame not selected ///////// + // + MHWRender::MRenderItem* wireItem = NULL; + { + int index = list.indexOf( + kInstanceWireItemName, + MHWRender::MGeometry::kLines, + MHWRender::MGeometry::kWireframe); + + if (index < 0) + { + wireItem = MHWRender::MRenderItem::Create + (kInstanceWireItemName, + MHWRender::MGeometry::kLines, + MHWRender::MGeometry::kWireframe, + false); + + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","InstanceWire"/*, + 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/); + + //wireItem->castsShadows(true); + + if (shader) + { + list.append(wireItem); + + static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; + shader->setParameter("gColor", theColor); + + + // assign shader + wireItem->setShader(shader); + + // sample debug code + if (debugShader) + { + MStringArray params; + shader->parameterList(params); + unsigned int numParams = params.length(); + printf("DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH %d\n", numParams); + for (unsigned int i=0; i<numParams; i++) + { + printf("ParamName='%s', ParamType=", params[i].asChar()); + switch (shader->parameterType(params[i])) + { + case MHWRender::MShaderInstance::kInvalid: + printf("'Invalid', "); + break; + case MHWRender::MShaderInstance::kBoolean: + printf("'Boolean', "); + break; + case MHWRender::MShaderInstance::kInteger: + printf("'Integer', "); + break; + case MHWRender::MShaderInstance::kFloat: + printf("'Float', "); + break; + case MHWRender::MShaderInstance::kFloat2: + printf("'Float2', "); + break; + case MHWRender::MShaderInstance::kFloat3: + printf("'Float3', "); + break; + case MHWRender::MShaderInstance::kFloat4: + printf("'Float4', "); + break; + case MHWRender::MShaderInstance::kFloat4x4Row: + printf("'Float4x4Row', "); + break; + case MHWRender::MShaderInstance::kFloat4x4Col: + printf("'Float4x4Col', "); + break; + case MHWRender::MShaderInstance::kTexture1: + printf("'1D Texture', "); + break; + case MHWRender::MShaderInstance::kTexture2: + printf("'2D Texture', "); + break; + case MHWRender::MShaderInstance::kTexture3: + printf("'3D Texture', "); + break; + case MHWRender::MShaderInstance::kTextureCube: + printf("'Cube Texture', "); + break; + case MHWRender::MShaderInstance::kSampler: + printf("'Sampler', "); + break; + default: + printf("'Unknown', "); + break; + } + printf("IsArrayParameter='%s'\n", shader->isArrayParameter(params[i]) ? "YES" : "NO"); + } + printf("END PARAM LIST\n"); + fflush(stdout); + } + } + } + else + { + wireItem = list.itemAt(index); + } + } + ///////////// wirewrame selected ///////// + // + MHWRender::MRenderItem* wireItemSel = NULL; + { + int index = list.indexOf( + kInstanceWireSelectedItemName, + MHWRender::MGeometry::kLines, + MHWRender::MGeometry::kWireframe); + + if (index < 0) + { + wireItemSel = MHWRender::MRenderItem::Create + (kInstanceWireSelectedItemName, + MHWRender::MGeometry::kLines, + MHWRender::MGeometry::kWireframe, + false); + + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","InstanceWire"/*, 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/); + + //wireItemSel->castsShadows(true); + + if (shader) + { + list.append(wireItemSel); + + static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; + shader->setParameter("gColor", selColor); + + // assign shader + wireItemSel->setShader(shader); + } + } + else + { + wireItemSel = list.itemAt(index); + } + } + + + // Update selection item, disable by default + bool brushDown = shave->getBrushDown(); + wireItemSel->enable(false && !brushDown /*&& !hideHairGlob*/); + wireItem->enable(true && !brushDown /*&& !hideHairGlob*/); + { + MStatus status; + MSelectionList selectedList; + status = MGlobal::getActiveSelectionList(selectedList); + if (status) + { + MDagPath pathCopy = path; + do + { + if (selectedList.hasItem(pathCopy)) + { + wireItemSel->enable(true && !brushDown /*&& !hideHairGlob*/); + wireItem->enable(false && !brushDown /*&& !hideHairGlob*/); + break; + } + status = pathCopy.pop(); + } while(status); + } + } + + MHWRender::MRenderItem* solidItem = NULL; + MHWRender::MShaderInstance* shader = NULL; + { + int index = list.indexOf( + kInstanceShadedItemName, + MHWRender::MGeometry::kTriangles, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured)); + + if (index < 0) + { + solidItem = MHWRender::MRenderItem::Create + (kInstanceShadedItemName, + MHWRender::MGeometry::kTriangles, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured), + false); + + shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","InstanceSolid" /*, 0,0,true, + debugShader ? shavePreDrawCallback : NULL, + debugShader ? shavePostDrawCallback : NULL*/); + + //solidItem->castsShadows(true); + + if (shader) + { + list.append(solidItem); + solidItem->setShader(shader); + } + } + else + { + solidItem = list.itemAt(index); + shader = solidItem->getShader(); + } + if(shader) + { + static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; + shader->setParameter("gColor", theColor); + + if (mInstanceCache->diffuse != 0) + { + shader->setParameter("gUseTexture", true); + if(!instex) + { + MHWRender::MTextureDescription desc; + { + desc.setToDefault2DTexture(); + desc.fWidth = mInstanceCache->eTexW; + desc.fHeight = mInstanceCache->eTexH; + desc.fDepth = 1; + desc.fBytesPerRow = 4*mInstanceCache->eTexW; + desc.fBytesPerSlice = 4*mInstanceCache->eTexW*mInstanceCache->eTexH; + desc.fMipmaps = 0; + desc.fArraySlices = 0; + desc.fFormat = MHWRender::kR8G8B8A8_UNORM; + desc.fTextureType = MHWRender::kImage2D; + } + + MString texName("Diffuse"); + instex = textureMgr->acquireTexture(texName, desc, mInstanceCache->img.pixels()); + } + + MHWRender::MTextureAssignment tas; + tas.texture = instex; + shader->setParameter("gTexture",tas); + } + else + { + shader->setParameter("gUseTexture", false); + } + } + } + solidItem->enable(true); + + if (mNumHairsToDraw == 0) + { + //solidItemSel->enable(false); + solidItem->enable(false); + wireItemSel->enable(false); + wireItem->enable(false); + } + + if (!shave->getInstancingStatus() /*|| hideHairGlob*/) + { + solidItem->enable(false); + wireItemSel->enable(false); + wireItem->enable(false); + } + + } + else if (!glisntGlob) + { + // disable instances + disableRenderItem(list, kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); + disableRenderItem(list, kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); + + disableRenderItem( + list, + kInstanceShadedItemName, + MHWRender::MGeometry::kTriangles, + (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) + ); + } + //end of hair + + /////////////////////////////// + MHWRender::DisplayStatus displayStatus = MHWRender::MGeometryUtilities::displayStatus(path); + bool componentSelectionMode = (displayStatus == MHWRender::kHilite ); + + MString selectType; + MGlobal::executeCommand( + "optionVar -q shaveBrushSelectMode", selectType + ); + + mSelectionData.mShaveSelectType = selectType; + + ///// guides display + MHWRender::MRenderItem* guidesItem = NULL; + { + int index = list.indexOf(kGuidesItemName, nonInstPrim, MHWRender::MGeometry::kAll); + + if (index < 0) + { + guidesItem = MHWRender::MRenderItem::Create + (kGuidesItemName, + nonInstPrim, + MHWRender::MGeometry::kAll, + false); + guidesItem->setCustomData(&mSelectionData); + + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","Guides"); + if (shader) + { + list.append(guidesItem); + + //static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; + //shader->setParameter("gColor", selColor); + + // assign shader + guidesItem->setShader(shader); + } + } + else + { + guidesItem = list.itemAt(index); + } + + bool displayGuides = shave->getDisplayGuides(); + bool brushActive = shave->getBrushActive(); + if (displayGuides || brushActive || + (componentSelectionMode && (selectType == "root" || selectType == "tip" || selectType == "vert" || selectType == "guide" )) + ) + guidesItem->enable(true); + else + guidesItem->enable(false); + + { + MHWRender::MShaderInstance* shader = NULL; + shader = guidesItem->getShader(); + //assert(shader); + if(shader) + { + float w = shave->getGuideThinkness(); + if(w < 0.01f) w=0.01f; + shader->setParameter("gGuideWidth",w); + } + } + } + //vertices display + MHWRender::MRenderItem* knotsItem = NULL; + { + int index = list.indexOf(kKnotsItemName, nonInstPrim, MHWRender::MGeometry::kAll); + + if (index < 0) + { + knotsItem = MHWRender::MRenderItem::Create + (kKnotsItemName, + nonInstPrim, + MHWRender::MGeometry::kAll, + false); + knotsItem->setCustomData(&mSelectionData); + + MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","Knots"); + //assert(shader); + if (shader) + { + list.append(knotsItem); + + //static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; + //shader->setParameter("gColor", selColor); + knotsItem->depthPriority( MHWRender::MRenderItem::sActiveWireDepthPriority /*sDormantWireDepthPriority*/ ); + + // assign shader + knotsItem->setShader(shader); + } + } + else + { + knotsItem = list.itemAt(index); + } + + bool displayGuides = shave->getDisplayGuides(); + bool brushActive = shave->getBrushActive(); + + //can not access it here its a part of getDrawRequests + //bool componentSelectionMode = (!noActiveComponents && (dStatus == M3dView::kHilite)); + + if (/*displayGuides || brushActive*/ + componentSelectionMode && + (selectType == "root" || selectType == "tip" || selectType == "vert" )) + { + knotsItem->enable(true); + } + else + { + knotsItem->enable(false); + } + + { + MHWRender::MShaderInstance* shader = NULL; + shader = knotsItem->getShader(); + + //assert(shader); + if(shader) + { + float w = shave->getGuideThinkness(); + if(w < 0.01f) w=0.01f; + shader->setParameter("gGuideWidth",w); + } + } + + } +} + + + +void shaveGeometryOverride::populateGeometry( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& itemList, + MHWRender::MGeometry& data + ) + +{ + if (mHairDisplayMode == shaveHairShape::kHairDisplayHair) + { +#ifdef POLY_HAIR + populateHairPolys(requirements, itemList, data); +#else + populateHairLines(requirements, itemList, data); +#endif + } + else if (mHairDisplayMode == shaveHairShape::kHairDisplayGeom) + { + populateHairInstances(requirements, itemList, data); + } +#if 0 + bool displayGuides = shave->getDisplayGuides(); + if (/*displayGuides || mBrushIsActive*/ true) + { + populateHairGuides( + requirements,itemList,data + ); + } +#endif +} + +void shaveGeometryOverride::populateHairInstances( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& itemList, + MHWRender::MGeometry& data +) +{ + cacheGuidePolys(); + cacheKnotPolys(); + + unsigned int guideTriangles = (unsigned int)guidePolys.size(); + unsigned int knotTriangles = (unsigned int)knotPolys.size(); + unsigned int instTriangles = mInstanceCache->faceCounts.length(); + unsigned int totalTriangles = guideTriangles+ instTriangles + knotTriangles; + + unsigned int totalLines = instTriangles*3; + unsigned int instVerts = instTriangles*3; + unsigned int guideVerts = guideTriangles*3; + unsigned int knotVerts = knotTriangles*3; + unsigned int totalVerts = totalTriangles*3; + + if(debugShader) + { + printf("triangles: %i verts: %i\n",totalTriangles, totalVerts); + } + + MHWRender::MVertexBuffer* positionBuffer = NULL; + float* positions = NULL; + + MHWRender::MVertexBuffer* colorBuffer = NULL; + float* colors = NULL; + + MHWRender::MVertexBuffer* normalBuffer = NULL; + float* normals = NULL; + + MHWRender::MVertexBuffer* uvBuffer = NULL; + float* uvs = NULL; + + const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); + + int numVertexReqs = descList.length(); + MHWRender::MVertexBufferDescriptor desc; + + for (int reqNum=0; reqNum<numVertexReqs; reqNum++) + { + if (!descList.getDescriptor(reqNum, desc)) + { + continue; + } + + switch (desc.semantic()) + { + case MHWRender::MGeometry::kPosition: + { + positionBuffer = data.createVertexBuffer(desc); + if (positionBuffer) + positions = (float*)positionBuffer->acquire(totalVerts); + } + break; + + case MHWRender::MGeometry::kColor: + { + colorBuffer = data.createVertexBuffer(desc); + if (colorBuffer) + colors = (float*)colorBuffer->acquire(totalVerts, true); + } + break; + + + //let's use texture to pass radius as uv.x + case MHWRender::MGeometry::kTexture: + { + uvBuffer = data.createVertexBuffer(desc); + if (uvBuffer) + uvs = (float*)uvBuffer->acquire(totalVerts, true); + + } + break; + + case MHWRender::MGeometry::kNormal: + { + normalBuffer = data.createVertexBuffer(desc); + if (normalBuffer) + normals = (float*)normalBuffer->acquire(totalVerts, true); + } + break; + + + case MHWRender::MGeometry::kTangent: + case MHWRender::MGeometry::kBitangent: + break; + + default: + // do nothing for stuff we don't understand + break; + } + } + + if(positions) + { + int vid = 0; + int pid = 0; + unsigned int nf = mInstanceCache->faceCounts.length(); + for(unsigned int i = 0; i < nf; i++) + { + int fc = mInstanceCache->faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = mInstanceCache->faceStart[i]; + int fe = mInstanceCache->faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int k = mInstanceCache->faceVerts[j]; + const MFloatPoint& p = mInstanceCache->points[k]; + positions[pid] = p.x; + positions[pid+1] = p.y; + positions[pid+2] = p.z; + + pid += 3; + + + } + } + //////////////////////////// + for (unsigned int i = 0; i < guidePolys.size(); i++) + { + const GuidePoly& p = guidePolys[i]; + for(int j=0; j<3; j++) + { + positions[pid++] = (float)p.v[j].x; + positions[pid++] = (float)p.v[j].y; + positions[pid++] = (float)p.v[j].z; + } + } + + //////////////////////////// + + for (unsigned int i = 0; i < knotPolys.size(); i++) + { + const KnotPoly& p = knotPolys[i]; + for(int j=0; j<3; j++) + { + positions[pid++] = (float)p.v[j].x; + positions[pid++] = (float)p.v[j].y; + positions[pid++] = (float)p.v[j].z; + } + } + + positionBuffer->commit(positions); + } + + if (colors) + { + unsigned int nf = mInstanceCache->faceCounts.length(); + int cid = nf*3*4; + //int cid = 0; + //for(unsigned int i = 0; i < nf; i++) + //{ + // int fc = mInstanceCache->faceCounts[i]; + // if(fc != 3) + // continue; //trianglular faces expected + + // int fs = mInstanceCache->faceStart[i]; + // int fe = mInstanceCache->faceEnd[i]; + // for(int j = fs; j < fe; j++) + // cid += 3; + //} + + ///////////////////////////////////////////////// + + for (unsigned int i = 0; i < guidePolys.size(); i++) + { + const GuidePoly& p = guidePolys[i]; + for(int j=0; j<3; j++) + { + colors[cid] = p.active?1.0f:0.0f; + colors[cid+1] = 0.0f; + colors[cid+2] = 0.0f; + colors[cid+3] = 0.0f; + cid+=4; + } + } + + //////////////////////////////////////////////// + + for (unsigned int i = 0; i < knotPolys.size(); i++) + { + const KnotPoly& p = knotPolys[i]; + for(int j=0; j<3; j++) + { + colors[cid] = p.active?1.0f:0.0f; + colors[cid+1] = 0.0f; + colors[cid+2] = 0.0f; + colors[cid+3] = 0.0f; + cid+=4; + } + } + + colorBuffer->commit(colors); + } + + if(normals) + { + int nid = 0; + unsigned int nf = mInstanceCache->faceCounts.length(); + for(unsigned int i = 0; i < nf; i++) + { + int fc = mInstanceCache->faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = mInstanceCache->faceStart[i]; + int fe = mInstanceCache->faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int k = mInstanceCache->faceVerts[j]; + //something is weird with this, normals are inverted... + const MFloatPoint& n = mInstanceCache->normals[k]; + normals[nid] = n.x; + normals[nid+1] = n.y; + normals[nid+2] = n.z; + nid += 3; + } + } + normalBuffer->commit(normals); + } + + if(uvs) + { + + int uid = 0; + int oo = 0; + unsigned int nf =mInstanceCache->faceCounts.length(); + for(unsigned int i = 0; i < nf; i++) + { + int fc = mInstanceCache->faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = mInstanceCache->faceStart[i]; + int fe = mInstanceCache->faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int k = mInstanceCache->faceVerts[j]; + const MFloatPoint& t = mInstanceCache->uvs[oo /*k*/]; + uvs[uid] = t.x; + uvs[uid+1] = t.y; + uid += 2; + oo++; + } + } + + uvBuffer->commit(uvs); + } + + // index data + int numItems = itemList.length(); + for (int i=0; i<numItems; i++) + { + const MHWRender::MRenderItem* item = itemList.itemAt(i); + if (!item) continue; + + // Enable to debug vertex buffers that are associated with each render item. + // Can also use to generate indexing better, but we don't need that here. + // Also debugs custom data on the render item. + if (debugShader) + { + const MHWRender::MVertexBufferDescriptorList& itemBuffers = + item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + fflush(stdout); + } + //same indexing for all + if (item->name() == kInstanceWireItemName || item->name() == kInstanceWireSelectedItemName) + { + MHWRender::MIndexBuffer* indexBuffer = NULL; + if (!indexBuffer) + { + indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer) + { + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(totalLines*2); + if (buffer) + { + int idx = 0; + int vid = 0; + unsigned int nf = mInstanceCache->faceCounts.length(); + for(unsigned int i = 0; i < nf; i++) + { + int fc = mInstanceCache->faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + //buffer[idx] = idx; + //buffer[idx+1] = idx+1; + //buffer[idx+2] = idx+2; + + ////printf("f %i [%i %i %i]\n",i, idx, idx+1, idx+2);fflush(stdout); + + //idx += 3; + + int fs = mInstanceCache->faceStart[i]; + int fe = mInstanceCache->faceEnd[i]; + int k=0; + for(int j = fs; j < fe; j++) + { + buffer[idx] = vid; + unsigned int vid2 = (k < 2) ? vid+1 : vid-2; + buffer[idx+1] = vid2; + idx += 2; + vid++; + k++; + } + } + indexBuffer->commit(buffer); + } + } + } + + // Associate same index buffer with either render item + if (indexBuffer) + { + item->associateWithIndexBuffer(indexBuffer); + } + } + + if (item->name() == kInstanceShadedItemName ) + { + MHWRender::MIndexBuffer* indexBuffer = NULL; + if (!indexBuffer) + { + indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer) + { + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(instTriangles*3); + if (buffer) + { + int idx = 0; + int vid = 0; + unsigned int nf = mInstanceCache->faceCounts.length(); + for(unsigned int i = 0; i < nf; i++) + { + int fc = mInstanceCache->faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + buffer[idx] = idx; + buffer[idx+1] = idx+1; + buffer[idx+2] = idx+2; + + //printf("f %i [%i %i %i]\n",i, idx, idx+1, idx+2);fflush(stdout); + + idx += 3; + + //int fs = mInstanceCache->faceStart[i]; + //int fe = mInstanceCache->faceEnd[i]; + //int k=0; + //for(int j = fs; j < fe; j++) + //{ + // buffer[idx] = vid; + // unsigned int vid2 = (k < 2) ? vid+1 : vid-2; + // buffer[idx+1] = vid2; + // idx += 2; + // vid++; + // k++; + //} + } + indexBuffer->commit(buffer); + } + } + } + + // Associate same index buffer with either render item + if (indexBuffer) + { + item->associateWithIndexBuffer(indexBuffer); + } + } + + //////////////////////////////////////// + //// guide indexing + if (item->name() == kGuidesItemName) + { + MHWRender::MIndexBuffer* indexBuffer2 = NULL; + // Wireframe index buffer is same for both wireframe and selected render item + // so we only compute and allocate it once, but reuse it for both render items + if (!indexBuffer2) + { + indexBuffer2 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + //indexBuffer2 = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer2) + { + unsigned int* buffer = (unsigned int*)indexBuffer2->acquire(guideVerts); + if (buffer) + { + for (unsigned int i = 0; i < guideVerts; i++) + { + //simple indexing + buffer[i] = i+instVerts; + } + + indexBuffer2->commit(buffer); + } + } + } + if(indexBuffer2) + item->associateWithIndexBuffer(indexBuffer2); + } + + ////////////////////////////// + // knot indexing + if (item->name() == kKnotsItemName) + { + MHWRender::MIndexBuffer* indexBuffer3 = NULL; + if (!indexBuffer3) + { + indexBuffer3 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer3) + { + unsigned int* buffer = (unsigned int*)indexBuffer3->acquire(knotVerts); + if (buffer) + { + for (unsigned int i = 0; i < knotVerts; i++) + { + //simple indexing + buffer[i] = i+instVerts+guideVerts; + } + indexBuffer3->commit(buffer); + } + } + } + if(indexBuffer3) + item->associateWithIndexBuffer(indexBuffer3); + } + } + +} + +void shaveGeometryOverride::populateHairLines( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& itemList, + MHWRender::MGeometry& data +) +{ + // + // Determine the number of hairs to be drawn. + // + bool doxpar = shave->getDoHairXparency(); + bool fallback = shave->getDoFallback(); + float ipasses = 1.0f/(float)mPasses; + + if(debugShader) + { + printf("numHairsToDraw: %i\n",mNumHairsToDraw); + } + + unsigned int numGuidesToDraw = (unsigned int)mGuideCache->size(); + if(debugShader) + printf("numGuidesToDraw: %i\n",numGuidesToDraw); + + // + // compute number of elements + // + unsigned int totalLines = 0; + unsigned int hairLines = 0; + unsigned int guideLines = 0; + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + unsigned int strandlen = strand.verts.length(); + hairLines += (strandlen-1); + } + } + +#if 1 + { + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + + if (!guide.hidden) + guideLines += (SHAVE_VERTS_PER_GUIDE-1); + + } + } +#endif + totalLines = guideLines + hairLines; + unsigned int hairVerts = hairLines*2; + unsigned int guideVerts = guideLines*2; + unsigned int totalVerts = totalLines*2; + + + // vertex data + MHWRender::MVertexBuffer* positionBuffer = NULL; + float* positions = NULL; + + MHWRender::MVertexBuffer* colorBuffer = NULL; + float* colors = NULL; + + MHWRender::MVertexBuffer* uvBuffer = NULL; + float* uvs = NULL; + + MHWRender::MVertexBuffer* tangBuffer = NULL; + float* tang = NULL; + + MHWRender::MVertexBuffer* guidePositionBuffer = NULL; + float* guidepositions = NULL; + + ///////////////////////////////////////////////////////////// + //int numItems = itemList.length(); + //for (int i=0; i<numItems; i++) + //{ + // const MHWRender::MRenderItem* item = itemList.itemAt(i); + // if (!item) continue; + // + ///////////////////////////////////////////////////////////// + + const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); +// const MHWRender::MVertexBufferDescriptorList& descList = item->requiredVertexBuffers(); + int numVertexReqs = descList.length(); + MHWRender::MVertexBufferDescriptor desc; + + if(debugShader) + { + printf("numVertexReqs: %i\n",numVertexReqs); + /* + const MHWRender::MVertexBufferDescriptorList& itemBuffers = item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + */ + } + + for (int reqNum=0; reqNum<numVertexReqs; reqNum++) + { + if (!descList.getDescriptor(reqNum, desc)) + { + continue; + } + + switch (desc.semantic()) + { + case MHWRender::MGeometry::kPosition: + { + positionBuffer = data.createVertexBuffer(desc); + if (positionBuffer) + positions = (float*)positionBuffer->acquire(totalVerts, true); + } + break; + + case MHWRender::MGeometry::kColor: + { + colorBuffer = data.createVertexBuffer(desc); + if (colorBuffer) + colors = (float*)colorBuffer->acquire(totalVerts, true); + + + } + break; + + //let's use texture to pass radius as uv.x + case MHWRender::MGeometry::kTexture: + { + uvBuffer = data.createVertexBuffer(desc); + if (uvBuffer) + uvs = (float*)uvBuffer->acquire(totalVerts, true); + + } + break; + + //case MHWRender::MGeometry::kNormal: + // break; +#if 0 + case MHWRender::MGeometry::kNormal: + { + guidePositionBuffer = data.createVertexBuffer(desc); + if (guidePositionBuffer) + guidepositions = (float*)guidePositionBuffer->acquire(totalVerts,true); + } + break; +#endif + case MHWRender::MGeometry::kTangent: + { + tangBuffer = data.createVertexBuffer(desc); + if (tangBuffer) + tang = (float*)tangBuffer->acquire(totalVerts, true); + } + break; + case MHWRender::MGeometry::kBitangent: + break; + + default: + // do nothing for stuff we don't understand + break; + } + } + if(debugShader) fflush(stdout); + + if (positions) + { + int pid = 0; + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + int numS = strand.verts.length()-1; + + //////////////////////// + //printf("strand%i \n",s); + //////////////////////// + + for (int v = 0; v < numS; v++) + { + int vv = v+1; + positions[pid++] = (float)strand.verts[v].x; + positions[pid++] = (float)strand.verts[v].y; + positions[pid++] = (float)strand.verts[v].z; + + positions[pid++] = (float)strand.verts[vv].x; + positions[pid++] = (float)strand.verts[vv].y; + positions[pid++] = (float)strand.verts[vv].z; + + //////////////////////// + //printf("v%i [%f %f %f] v%i [%f %f %f]\n",v,strand.verts[v].x,strand.verts[v].y,strand.verts[v].z,vv,strand.verts[vv].x,strand.verts[vv].y,strand.verts[vv].z); + //////////////////////// + } + } + } +#if 1 + ///////////////////////////////////////////////// + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) + { + int vv = v+1; + positions[pid++] = guide.verts[v].x; + positions[pid++] = guide.verts[v].y; + positions[pid++] = guide.verts[v].z; + + positions[pid++] = guide.verts[vv].x; + positions[pid++] = guide.verts[vv].y; + positions[pid++] = guide.verts[vv].z; + + } + } + } +#endif + positionBuffer->commit(positions); + } + + if (colors) + { + + int cid = 0; + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + int numS = strand.verts.length()-1; + + for (int v = 0; v < numS; v++) + { + int vv = v+1; + + MColor col0 = strand.colors[v]; + MColor col1 = strand.colors[vv]; + //we do get 1.0 in WFTYPE.alpha[] at the moment, lets fake it with tipfade + if(doxpar ) + { + if(mDoTipFade) + { + col0.a = 1.0f - v/(float)numS; + col1.a = 1.0f - vv/(float)numS; + } + if(mPasses > 1) + { + col0.a *= ipasses; + col1.a *= ipasses; + } + if(mTransparency != 1.0f) + { + col0.a *= mTransparency; + col1.a *= mTransparency; + } + } + else + col1.a = col0.a = 1.0f; + + + colors[cid++] = col0.r; + colors[cid++] = col0.g; + colors[cid++] = col0.b; + colors[cid++] = col0.a; + + colors[cid++] = col1.r; + colors[cid++] = col1.g; + colors[cid++] = col1.b; + colors[cid++] = col1.a; + } + + } + } + colorBuffer->commit(colors); + } + + if (uvs) + { + int uid = 0; + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + float r0, r1; + int numS = strand.verts.length()-1; + for (int v = 0; v < numS; v++) + { + + int vv = v+1; + int vvv = v+2; + MVector dv = strand.verts[v]-strand.verts[vv]; + float dvl = (float)dv.length(); + + float t1 = vv/(float)numS; + if(v == 0) //root + { + r0 = strand.rootrad; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + else if(vv == numS) //tip + { + r0 = r1; + r1 = strand.tiprad; + } + else + { + r0 = r1; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + } + uvs[uid++] = r0; + uvs[uid++] = dvl; + + uvs[uid++] = r1; + uvs[uid++] = 0.0f; + } + } + } + uvBuffer->commit(uvs); + } + + if (tang) + { + int pid = 0; + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + int numS = strand.verts.length()-1; + + //////////////////////// + //printf("strand%i \n",s); + //////////////////////// + + for (int v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + if(v == 0) //root + { + p0.x = strand.verts[0].x; + p0.y = strand.verts[0].y; + p0.z = strand.verts[0].z; + p1.x = strand.verts[1].x; + p1.y = strand.verts[1].y; + p1.z = strand.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + + tangent1 = tangent0; + + } + else if(vv == numS) //tip + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + } + else + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + if(vvv < (int)strand.verts.length()) + { + p2.x = strand.verts[vvv].x; + p2.y = strand.verts[vvv].y; + p2.z = strand.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + } + tang[pid++] = (float)tangent0.x; + tang[pid++] = (float)tangent0.y; + tang[pid++] = (float)tangent0.z; + + tang[pid++] = (float)tangent1.x; + tang[pid++] = (float)tangent1.y; + tang[pid++] = (float)tangent1.z; + + } + } + } + tangBuffer->commit(tang); + } + + + // index data + MHWRender::MIndexBuffer* indexBuffer = NULL; + MHWRender::MIndexBuffer* indexBuffer2 = NULL; + MHWRender::MIndexBuffer* indexBuffer3 = NULL; + int numItems = itemList.length(); + + for (int i=0; i<numItems; i++) + { + const MHWRender::MRenderItem* item = itemList.itemAt(i); + if (!item) continue; + + // Enable to debug vertex buffers that are associated with each render item. + // Can also use to generate indexing better, but we don't need that here. + // Also debugs custom data on the render item. + static const bool debugStuff = false; + if (debugShader) + { + const MHWRender::MVertexBufferDescriptorList& itemBuffers = + item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + fflush(stdout); + } + + //same indexing for all hair + if (item->name() == kHairWireItemName || item->name() == kHairWireSelectedItemName || + item->name() == kHairShadedItemName || item->name() == kHairShadedSelectedItemName) + { + // Wireframe index buffer is same for both wireframe and selected render item + // so we only compute and allocate it once, but reuse it for both render items + if (!indexBuffer) + { + indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer) + { + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(hairLines*2); + if (buffer) + { + int idx = 0; + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + int numS = strand.verts.length()-1; + for (int v = 0; v < numS; v++) + { + //simple indexing + buffer[idx] = idx; + buffer[idx+1] = idx+1; + idx += 2; + } + } + } + indexBuffer->commit(buffer); + } + } + } + + // Associate same index buffer with either render item + if (indexBuffer) + { + item->associateWithIndexBuffer(indexBuffer); + } + } + ////////////////////////////////////////// + // guide indexing + if (item->name() == kGuidesItemName) + { + + // Wireframe index buffer is same for both wireframe and selected render item + // so we only compute and allocate it once, but reuse it for both render items + if (!indexBuffer2) + { + indexBuffer2 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + //indexBuffer2 = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer2) + { + unsigned int* buffer = (unsigned int*)indexBuffer2->acquire(guideLines*2); + if (buffer) + { + int idx0 = 0; + int idx1 = hairVerts; + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) + { + //simple indexing + buffer[idx0] = idx1; + buffer[idx0+1] = idx1+1; + idx0 += 2; + idx1 += 2; + } + } + } + indexBuffer2->commit(buffer); + //data.addIndexBuffer(indexBuffer2); + + item->associateWithIndexBuffer(indexBuffer2); + + } + } + } + } + ////////////////////////////// + // guide indexing + if (item->name() == kKnotsItemName) + { + if (!indexBuffer3) + { + indexBuffer3 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer3) + { + unsigned int* buffer = (unsigned int*)indexBuffer3->acquire(guideVerts/2); + if (buffer) + { + int idx0 = 0; + int idx1 = hairVerts; + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) + { + //simple indexing + buffer[idx0] = idx1; + idx0 += 1; + idx1 += 2; + } + } + } + indexBuffer3->commit(buffer); + item->associateWithIndexBuffer(indexBuffer3); + } + } + } + } + /////////////////////////////// + } + + ////////////////////////////////////// + //} + ////////////////////////////////////// +} + +///////////////// cache polys & z-sorty strands/////////////////////////////////// + +void shaveGeometryOverride::cacheHairPolys() +{ + bool doxpar = shave->getDoHairXparency(); + bool fallback = shave->getDoFallback(); + float ipasses = 1.0f/(float)mPasses; + + // far/near + MPoint centerPt(mHairCache->center); + MPoint nearPt = centerPt - mViewDir*mHairCache->radius; + MPoint farPt = centerPt + mViewDir*mHairCache->radius; + + MPoint nearSc = nearPt*mWorldToCam; + MPoint farSc = farPt*mWorldToCam; + + //sort to groups + ingroups = 0; + int numS = 1; + + groups.resize(400); + for(int g = (int)groups.size()-1; g >= 0; g--) + { + groups[g].p.clear(); + groups[g].p.reserve(300); + } + + for (unsigned int h = 0; h < mNumHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + float r0; + float r1; + numS = strand.verts.length()-1; + float inumS = 1.0f/(float)numS; + + for (int v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + float t1 = vv*inumS; + if(v == 0) //root polygon + { + p0.x = strand.verts[0].x; + p0.y = strand.verts[0].y; + p0.z = strand.verts[0].z; + p1.x = strand.verts[1].x; + p1.y = strand.verts[1].y; + p1.z = strand.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + + tangent1 = tangent0; + + r0 = strand.rootrad; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + else if(vv == numS) //tip poly + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + + + r0 = r1; + r1 = strand.tiprad; + } + else + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + if(vvv < (int)strand.verts.length()) + { + p2.x = strand.verts[vvv].x; + p2.y = strand.verts[vvv].y; + p2.z = strand.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + + r0 = r1; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + if(p0 == p1) + continue; + + + MVector d0 = tangent0^mViewDir; + MVector d1 = tangent1^mViewDir; + d0.normalize(); + d1.normalize(); + + MVector v0 = p0 + d0*r0; + MVector v1 = p0 - d0*r0; + MVector v2 = p1 - d1*r1; + MVector v3 = p1 + d1*r1; + + MColor col0 = strand.colors[v]; + + //we do get 1.0 in WFTYPE.alpha[] at the moment, lets fake it with tipfade + //col.a = 1.0f - vv*inumS; + col0.a = 1.0f; + if(mDoTipFade) + col0.a -= v*inumS; + if(mPasses) + col0.a *= ipasses; + + MColor col1 = strand.colors[vv]; + col1.a = 1.0f; + if(mDoTipFade) + col1.a -= vv*inumS; + if(mPasses) + col1.a *= ipasses; + + + Poly P1; + + P1.v[0] = v0; + P1.c[0] = col0; + P1.t[0] = tangent0; + + P1.v[1] = v1; + P1.c[1] = col0; + P1.t[1] = tangent0; + + + P1.v[2] = v2; + P1.c[2] = col1; + P1.t[2] = tangent1; + + Poly P2; + + P2.v[0] = v2; + P2.c[0] = col1; + P2.t[0] = tangent1; + + P2.v[1] = v3; + P2.c[1] = col1; + P2.t[1] = tangent1; + + P2.v[2] = v0; + P2.c[2] = col0; + P2.t[2] = tangent0; + + //for Joe: what I am doing wrong with z-sort? + MPoint V(0.5f*(p0.x+p1.x), 0.5f*(p0.y+p1.y), 0.5f*(p0.z+p1.z));//a middle of poly + MPoint S = V*mWorldToCam; //to camera space + float d = (float)((S.z-nearSc.z)/(farSc.z-nearSc.z)); + //if(d > 1.0f) d = 1.0f; + //if(d < 0.0f) d = 0.0f; + int pos = (int)(d*(float)groups.size()); + if(pos >= groups.size()) pos = (int)groups.size()-1; + if(pos < 0) pos = 0; + + groups[pos].p.push_back(P1); + groups[pos].p.push_back(P2); + + ingroups+=2; + } + + } + } + +}; + +void shaveGeometryOverride::cacheGuidePolys() +{ + int numS = SHAVE_VERTS_PER_GUIDE-1; + shaveHairShape::Guides::const_iterator iter; + float w = shave->getGuideThinkness(); + + w *= (4.0f*mGuideExtent)/1500.0f; + + int nvisible = 0; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + nvisible++; + } + guidePolys.clear(); + guidePolys.reserve(numS*2*nvisible); + + mSelectionData.mPrimIdxToGuideIdx.clear(); + mSelectionData.mPrimIdxToGuideIdx.reserve(numS*2*nvisible); + + int v; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + unsigned int guideIdx = (unsigned int)(iter - mGuideCache->begin()); + bool isActive = (guide.select != 0); + + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + int numS = SHAVE_VERTS_PER_GUIDE-1; + + for (v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + if(v == 0) //root polygon + { + p0.x = guide.verts[0].x; + p0.y = guide.verts[0].y; + p0.z = guide.verts[0].z; + p1.x = guide.verts[1].x; + p1.y = guide.verts[1].y; + p1.z = guide.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + tangent1 = tangent0; + } + else if(vv == numS) //tip poly + { + p0 = p1; + p1.x = guide.verts[vv].x; + p1.y = guide.verts[vv].y; + p1.z = guide.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + } + else + { + p0 = p1; + p1.x = guide.verts[vv].x; + p1.y = guide.verts[vv].y; + p1.z = guide.verts[vv].z; + + tangent0 = tangent1; + if(vvv < SHAVE_VERTS_PER_GUIDE) + { + p2.x = guide.verts[vvv].x; + p2.y = guide.verts[vvv].y; + p2.z = guide.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + } + if(p0 == p1) + continue; + + + MVector d0 = tangent0^mViewDir; + MVector d1 = tangent1^mViewDir; + d0.normalize(); + d1.normalize(); + + MVector v0 = p0 + d0*(float)w; + MVector v1 = p0 - d0*(float)w; + MVector v2 = p1 - d1*(float)w; + MVector v3 = p1 + d1*(float)w; + + + GuidePoly P1; + P1.v[0] = v0; + P1.v[1] = v1; + P1.v[2] = v2; + P1.active = isActive; + + GuidePoly P2; + P2.v[0] = v2; + P2.v[1] = v3; + P2.v[2] = v0; + P2.active = isActive; + + guidePolys.push_back(P1); + guidePolys.push_back(P2); + + // Both polys are on the same guide so we store + // the same index in them. + // + mSelectionData.mPrimIdxToGuideIdx.push_back(guideIdx); + mSelectionData.mPrimIdxToGuideIdx.push_back(guideIdx); + } + } + } +} + +void shaveGeometryOverride::cacheKnotPolys() +{ + int numS = SHAVE_VERTS_PER_GUIDE-1; + shaveHairShape::Guides::const_iterator iter; + float w = shave->getGuideThinkness(); + + w *= (4.0f*mGuideExtent)/1500.0f; + w *= 2.0f; + + /////////////////////////////////////// + bool drawActive = false; + int startVert = 0; + int endVert = SHAVE_VERTS_PER_GUIDE - 1; + + MString selectType; + MGlobal::executeCommand( + "optionVar -q shaveBrushSelectMode", selectType + ); + + if(selectType == "root") + { + startVert = endVert = 0; + } + else if(selectType == "tip") + { + startVert = endVert = SHAVE_VERTS_PER_GUIDE - 1; + } + else if(selectType == "vert") + { + startVert = 0; + endVert = SHAVE_VERTS_PER_GUIDE - 1; + } + + const unsigned int numDrawnVertsPerGuide = endVert - startVert + 1; + + /////////////////////////////////////// + + int nvisible = 0; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + nvisible++; + } + knotPolys.clear(); + knotPolys.reserve(numDrawnVertsPerGuide*2*nvisible); + + mSelectionData.mPrimIdxToVertIdx.clear(); + mSelectionData.mPrimIdxToVertIdx.reserve(numDrawnVertsPerGuide*2*nvisible); + + MVector p0; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + unsigned int guideIdx = (unsigned int)(iter - mGuideCache->begin()); + + for (int i = startVert; i <= endVert ; i++) + { + // + // Is this vert active? + // + bool isActive = ((guide.select & (1 << i)) != 0); + + p0.x = guide.verts[i].x; + p0.y = guide.verts[i].y; + p0.z = guide.verts[i].z; + + MVector v0 = p0 + (mUpDir+mRightDir)*(float)w; + MVector v1 = p0 + (-mUpDir+mRightDir)*(float)w; + MVector v2 = p0 + (-mUpDir-mRightDir)*(float)w; + MVector v3 = p0 + (mUpDir-mRightDir)*(float)w; + + + KnotPoly P1; + P1.v[0] = v0; + P1.v[1] = v1; + P1.v[2] = v2; + P1.active = isActive; + + KnotPoly P2; + P2.v[0] = v2; + P2.v[1] = v3; + P2.v[2] = v0; + P2.active = isActive; + + knotPolys.push_back(P1); + knotPolys.push_back(P2); + + // Both polys represent the same guide vert so we store the same + // index for each of them. + // + mSelectionData.mPrimIdxToVertIdx.push_back(guideIdx * SHAVE_VERTS_PER_GUIDE + i); + mSelectionData.mPrimIdxToVertIdx.push_back(guideIdx * SHAVE_VERTS_PER_GUIDE + i); + } + } + } +} + + +////////////////////// draw polyginal hair /////////////////////////////////// +void shaveGeometryOverride::populateHairPolys( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& itemList, + MHWRender::MGeometry& data +) +{ + bool doxpar = shave->getDoHairXparency(); + bool fallback = shave->getDoFallback(); + float ipasses = 1.0f/(float)mPasses; + + cacheHairPolys(); + cacheGuidePolys(); + cacheKnotPolys(); + +#if 0 //skip guides for now + unsigned int numGuidesToDraw = mGuideCache->size(); +#endif + + // + // compute number of elements + // + + unsigned int hairPolys = ingroups; + unsigned int nguidePolys = (unsigned int)guidePolys.size(); + unsigned int nknotPolys = (unsigned int)knotPolys.size(); + unsigned int totalPolys = nguidePolys + hairPolys + nknotPolys; + + unsigned int hairVerts = hairPolys*3; + unsigned int guideVerts = nguidePolys*3; + unsigned int knotVerts = nknotPolys*3; + unsigned int totalVerts = totalPolys*3; + + // vertex data + MHWRender::MVertexBuffer* positionBuffer = NULL; + float* positions = NULL; + + MHWRender::MVertexBuffer* colorBuffer = NULL; + float* colors = NULL; + + MHWRender::MVertexBuffer* uvBuffer = NULL; + float* uvs = NULL; + + MHWRender::MVertexBuffer* tangBuffer = NULL; + float* tang = NULL; + + MHWRender::MVertexBuffer* guidePositionBuffer = NULL; + float* guidepositions = NULL; + + const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); + int numVertexReqs = descList.length(); + MHWRender::MVertexBufferDescriptor desc; + + if(debugShader) + { + printf("numVertexReqs: %i\n",numVertexReqs); + /* + const MHWRender::MVertexBufferDescriptorList& itemBuffers = item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + */ + } + + for (int reqNum=0; reqNum<numVertexReqs; reqNum++) + { + if (!descList.getDescriptor(reqNum, desc)) + { + continue; + } + + switch (desc.semantic()) + { + case MHWRender::MGeometry::kPosition: + { + if(debugShader) printf("POSITION BUFFER\n"); + positionBuffer = data.createVertexBuffer(desc); + if (positionBuffer) + positions = (float*)positionBuffer->acquire(totalVerts,true); + } + break; + + case MHWRender::MGeometry::kColor: + { + if(debugShader) printf("COLOR BUFFER\n"); + colorBuffer = data.createVertexBuffer(desc); + if (colorBuffer) + colors = (float*)colorBuffer->acquire(totalVerts,true); + } + break; + + //let's use texture to pass radius as uv.x + case MHWRender::MGeometry::kTexture: + { + if(debugShader) printf("TEXTURE BUFFER\n"); + uvBuffer = data.createVertexBuffer(desc); + if (uvBuffer) + uvs = (float*)uvBuffer->acquire(totalVerts,true); + } + break; + + + //case MHWRender::MGeometry::kTangent: + case MHWRender::MGeometry::kNormal: + { + if(debugShader) printf("TANTGET/NORMAL BUFFER\n"); + tangBuffer = data.createVertexBuffer(desc); + if (tangBuffer) + tang = (float*)tangBuffer->acquire(totalVerts,true); + } + break; + case MHWRender::MGeometry::kBitangent: + break; + + default: + // do nothing for stuff we don't understand + break; + } + } + if(debugShader) fflush(stdout); + + if (positions) + { + int pid = 0; + for(int g = (int)groups.size()-1; g >= 0; g--) + { + const PolyGroup& gr = groups[g]; + unsigned int gs = (unsigned int)gr.p.size(); + for(unsigned int i = 0; i < gs; i++) + { + const Poly& p = gr.p[i]; + + for(int j=0; j<3; j++) + { + positions[pid] = (float)p.v[j].x; + positions[pid+1] = (float)p.v[j].y; + positions[pid+2] = (float)p.v[j].z; + pid+=3; + } + } + } + + ///////////////////////////////////////////////// + + for (unsigned int i = 0; i < guidePolys.size(); i++) + { + const GuidePoly& p = guidePolys[i]; + for(int j=0; j<3; j++) + { + positions[pid++] = (float)p.v[j].x; + positions[pid++] = (float)p.v[j].y; + positions[pid++] = (float)p.v[j].z; + } + } + + //////////////////////////////////////////////// + + for (unsigned int i = 0; i < knotPolys.size(); i++) + { + const KnotPoly& p = knotPolys[i]; + + for(int j=0; j<3; j++) + { + positions[pid++] = (float)p.v[j].x; + positions[pid++] = (float)p.v[j].y; + positions[pid++] = (float)p.v[j].z; + } + } + + positionBuffer->commit(positions); + } + + if (colors) + { + int cid = 0; + for(int g = (int)groups.size()-1; g >= 0; g--) + { + const PolyGroup& gr = groups[g]; + unsigned int gs = (unsigned int)gr.p.size(); + for(unsigned int i = 0; i < gs; i++) + { + const Poly& p = gr.p[i]; + for(int j=0; j<3; j++) + { + colors[cid] = p.c[j].r; + colors[cid+1] = p.c[j].g; + colors[cid+2] = p.c[j].b; + colors[cid+3] = p.c[j].a; + cid+=4; + } + } + } + ///////////////////////////////////////////////// + + for (unsigned int i = 0; i < guidePolys.size(); i++) + { + const GuidePoly& p = guidePolys[i]; + for(int j=0; j<3; j++) + { + colors[cid] = p.active?1.0f:0.0f; + colors[cid+1] = 0.0f; + colors[cid+2] = 0.0f; + colors[cid+3] = 0.0f; + cid+=4; + } + } + + //////////////////////////////////////////////// + + for (unsigned int i = 0; i < knotPolys.size(); i++) + { + const KnotPoly& p = knotPolys[i]; + for(int j=0; j<3; j++) + { + colors[cid] = p.active?1.0f:0.0f; + colors[cid+1] = 0.0f; + colors[cid+2] = 0.0f; + colors[cid+3] = 0.0f; + cid+=4; + } + } + + colorBuffer->commit(colors); + } + + if (uvs) + { + int uid = 0; + for(int g = (int)groups.size()-1; g >= 0; g--) + { + const PolyGroup& gr = groups[g]; + unsigned int gs = (unsigned int)gr.p.size(); + for(unsigned int i = 0; i < gs; i++) + { + const Poly& p = gr.p[i]; + for(int j=0; j<3; j++) + { + uvs[uid] = (float)p.uv[j].x; + uvs[uid+1] = (float)p.uv[j].y; + uid+=2; + } + } + } + uvBuffer->commit(uvs); + } + + if (tang) + { + int tid = 0; + for(int g = (int)groups.size()-1; g >= 0; g--) + { + const PolyGroup& gr = groups[g]; + unsigned int gs = (unsigned int)gr.p.size(); + for(unsigned int i = 0; i < gs; i++) + { + const Poly& p = gr.p[i]; + for(int j=0; j<3; j++) + { + tang[tid] = (float)p.t[j].x; + tang[tid+1] = (float)p.t[j].y; + tang[tid+2] = (float)p.t[j].z; + tid+=3; + } + } + } + tangBuffer->commit(tang); + } + + + // index data + MHWRender::MIndexBuffer* indexBuffer = NULL; + MHWRender::MIndexBuffer* indexBuffer2 = NULL; + MHWRender::MIndexBuffer* indexBuffer3 = NULL; + int numItems = itemList.length(); + for (int i=0; i<numItems; i++) + { + const MHWRender::MRenderItem* item = itemList.itemAt(i); + if (!item) continue; + + // Enable to debug vertex buffers that are associated with each render item. + // Can also use to generate indexing better, but we don't need that here. + // Also debugs custom data on the render item. + static const bool debugStuff = false; + if (debugShader) + { + const MHWRender::MVertexBufferDescriptorList& itemBuffers = + item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + fflush(stdout); + } + + //same indexing for all hair + if (item->name() == kHairWireItemName || item->name() == kHairWireSelectedItemName || + item->name() == kHairShadedItemName || item->name() == kHairShadedSelectedItemName) + { + // Wireframe index buffer is same for both wireframe and selected render item + // so we only compute and allocate it once, but reuse it for both render items + if (!indexBuffer) + { + indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer) + { + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(hairVerts); + if (buffer) + { + for(unsigned int k = 0; k < hairVerts; k++) + buffer[k] = k; + + indexBuffer->commit(buffer); + } + } + } + + // Associate same index buffer with either render item + if (indexBuffer) + { + item->associateWithIndexBuffer(indexBuffer); + } + } + + ////////////////////////////////////////// + // guide indexing + if (item->name() == kGuidesItemName) + { + + // Wireframe index buffer is same for both wireframe and selected render item + // so we only compute and allocate it once, but reuse it for both render items + if (!indexBuffer2) + { + indexBuffer2 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + //indexBuffer2 = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer2) + { + unsigned int* buffer = (unsigned int*)indexBuffer2->acquire(guideVerts); + if (buffer) + { + for (unsigned int i = 0; i < guideVerts; i++) + { + //simple indexing + buffer[i] = i+hairVerts; + } + + indexBuffer2->commit(buffer); + } + } + } + if(indexBuffer2) + item->associateWithIndexBuffer(indexBuffer2); + } + ////////////////////////////// + // knot indexing + if (item->name() == kKnotsItemName) + { + if (!indexBuffer3) + { + indexBuffer3 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer3) + { + unsigned int* buffer = (unsigned int*)indexBuffer3->acquire(knotVerts); + if (buffer) + { + for (unsigned int i = 0; i < knotVerts; i++) + { + //simple indexing + buffer[i] = i+hairVerts+guideVerts; + } + indexBuffer3->commit(buffer); + } + } + } + if(indexBuffer3) + { + item->associateWithIndexBuffer(indexBuffer3); + } + } + /////////////////////////////// + } + + ////////////////////////////////////// + //} + ////////////////////////////////////// +} + +/////////// not used ///////////////////// +void shaveGeometryOverride::populateHairGuides( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& itemList, + MHWRender::MGeometry& data + ) +{ + float w = shave->getGuideThinkness(); + + unsigned int numGuidesToDraw = (unsigned int)mGuideCache->size(); + if(debugShader) + printf("numGuidesToDraw: %i\n",numGuidesToDraw); + + // + // compute number of elements + // + unsigned int totalLines = 0; + { + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + + if (!guide.hidden) + totalLines += (SHAVE_VERTS_PER_GUIDE-1); + + } + } + unsigned int totalVerts = totalLines*2; + + + // vertex data + int positionStart = 0; + MHWRender::MVertexBuffer* positionBuffer = NULL; + float* positions = NULL; + + MHWRender::MVertexBuffer* tangBuffer = NULL; + float* tang = NULL; + + const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); + int numVertexReqs = descList.length(); + MHWRender::MVertexBufferDescriptor desc; + + if(debugShader) + printf("numVertexReqs: %i\n",numVertexReqs); + + + for (int reqNum=0; reqNum<numVertexReqs; reqNum++) + { + if (!descList.getDescriptor(reqNum, desc)) + { + continue; + } +#if 0 + switch (desc.semantic()) + { + case MHWRender::MGeometry::kPosition: + { + for (int k = 0; k < data.vertexBufferCount(); k++) + { + MHWRender::MVertexBuffer* vb = data.vertexBuffer(k); + if(vb) + { + if(vb->descriptor().semantic() == desc.semantic()) + { + positionBuffer = vb; + positionStart = positionBuffer->vertexCount(); + positions = (float*)malloc(totalVerts*3*sizeof(float)); + + if(debugShader) + printf("startPosVertex: %i\n",positionStart); + } + } + + } + + //positionBuffer = data.createVertexBuffer(desc); + //if (positionBuffer) + // positions = (float*)positionBuffer->acquire(totalVerts); + } + break; + + case MHWRender::MGeometry::kTangent: + { + tangBuffer = data.createVertexBuffer(desc); + if (tangBuffer) + tang = (float*)tangBuffer->acquire(totalVerts); + } + break; + + case MHWRender::MGeometry::kNormal: + { + positionBuffer = data.createVertexBuffer(desc); + if (positionBuffer) + positions = (float*)positionBuffer->acquire(totalVerts); + } + break; + + default: + // do nothing for stuff we don't understand + break; + } +#endif + } + if(debugShader) fflush(stdout); + + if (positions ) + { + int pid = 0; + int g = 0; + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) + { + int vv = v+1; + positions[pid++] = guide.verts[v].x; + positions[pid++] = guide.verts[v].y; + positions[pid++] = guide.verts[v].z; + + positions[pid++] = guide.verts[vv].x; + positions[pid++] = guide.verts[vv].y; + positions[pid++] = guide.verts[vv].z; + + } + } + } + positionBuffer->commit(positions); + //positionBuffer->update(positions,positionStart,totalVerts,false); + //free(positions); + } + + + if (tang) + { + int pid = 0; + MFloatVector tangent0; + MFloatVector tangent1; + MFloatVector p0; + MFloatVector p1; + MFloatVector p2; + + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-2; v++) + { + int vv = v+1; + int vvv = v+2; + if(v == 0) //root + { + p0.x = guide.verts[0].x; + p0.y = guide.verts[0].y; + p0.z = guide.verts[0].z; + p1.x = guide.verts[1].x; + p1.y = guide.verts[1].y; + p1.z = guide.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + + tangent1 = tangent0; + + } + else if(vv == (int)SHAVE_VERTS_PER_GUIDE-1) //tip + { + p0 = p1; + p1.x = guide.verts[vv].x; + p1.y = guide.verts[vv].y; + p1.z = guide.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + } + else + { + p0 = p1; + p1.x = guide.verts[vv].x; + p1.y = guide.verts[vv].y; + p1.z = guide.verts[vv].z; + + tangent0 = tangent1; + if(vvv < (int)SHAVE_VERTS_PER_GUIDE) + { + p2.x = guide.verts[vvv].x; + p2.y = guide.verts[vvv].y; + p2.z = guide.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + } + tang[pid++] = tangent0.x; + tang[pid++] = tangent0.y; + tang[pid++] = tangent0.z; + + tang[pid++] = tangent1.x; + tang[pid++] = tangent1.y; + tang[pid++] = tangent1.z; + + } + } + } + tangBuffer->commit(tang); + } + + + // index data + MHWRender::MIndexBuffer* indexBuffer = NULL; + int numItems = itemList.length(); + for (int i=0; i<numItems; i++) + { + const MHWRender::MRenderItem* item = itemList.itemAt(i); + if (!item) continue; + + // Enable to debug vertex buffers that are associated with each render item. + // Can also use to generate indexing better, but we don't need that here. + // Also debugs custom data on the render item. + static const bool debugStuff = false; + if (debugShader) + { + const MHWRender::MVertexBufferDescriptorList& itemBuffers = + item->requiredVertexBuffers(); + int numBufs = itemBuffers.length(); + MHWRender::MVertexBufferDescriptor desc; + for (int bufNum=0; bufNum<numBufs; bufNum++) + { + if (itemBuffers.getDescriptor(bufNum, desc)) + { + printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar()); + printf("\tBufferName: %s\n", desc.name().asChar()); + printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); + printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); + printf("\n"); + } + } + fflush(stdout); + } + + //indexing + if (item->name() == kGuidesItemName) + { + + // Wireframe index buffer is same for both wireframe and selected render item + // so we only compute and allocate it once, but reuse it for both render items + if (!indexBuffer) + { + indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + //indexBuffer = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); + if (indexBuffer) + { + unsigned int* buffer = (unsigned int*)indexBuffer->acquire(totalLines*2); + if (buffer) + { + int idx0 = 0; + int idx1 = 0;//positionStart; // 0; + shaveHairShape::Guides::const_iterator iter; + for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) + { + //simple indexing + buffer[idx0] = idx1; + buffer[idx0+1] = idx1+1; + idx0 += 2; + idx1 += 2; + } + } + } + indexBuffer->commit(buffer); + //data.addIndexBuffer(indexBuffer); + + item->associateWithIndexBuffer(indexBuffer); + + } + } + } + } + } +} + +#if MAYA_API_VERSION >= 201600 +void shaveGeometryOverride::updateSelectionGranularity( + const MDagPath& path, MHWRender::MSelectionContext& selectCtx + ) +{ + // The selection level defaults to object, so we only have to set + // it if we want to select components. + // + MHWRender::DisplayStatus displayStatus = MHWRender::MGeometryUtilities::displayStatus(path); + + if (displayStatus == MHWRender::kHilite) + { + MSelectionMask mayaSelectionMask; + + if (MGlobal::selectionMode() == MGlobal::kSelectComponentMode) + { + mayaSelectionMask = MGlobal::componentSelectionMask(); + } + else + { + mayaSelectionMask = MGlobal::objectSelectionMask(); + } + + MSelectionMask supportedComponents(MSelectionMask::kSelectCVs); + supportedComponents.addMask(MSelectionMask::kSelectMeshEdges); + + if (mayaSelectionMask.intersects(supportedComponents)) + { + selectCtx.setSelectionLevel(MHWRender::MSelectionContext::kComponent); + } + } +#if MAYA_API_VERSION >= 201650 + // pointSnappingActive() actually became available in API version 201610 + // (2016 Extension 1), but we don't want to force our 2016 users to update + // to the extension if they don't have to. + // + else if (pointSnappingActive()) + { + selectCtx.setSelectionLevel(MHWRender::MSelectionContext::kComponent); + } +#endif +} +#endif + + +void shaveGeometryOverride::cleanUp() +{ + +} + + +#if MAYA_API_VERSION >= 201600 +MStatus shaveGeometryOverride::registerComponentConverters() +{ + MStatus st = MHWRender::MDrawRegistry::registerComponentConverter(kKnotsItemName, guideVertComponentConverter::create); + + if (st) + { + st = MHWRender::MDrawRegistry::registerComponentConverter(kGuidesItemName, guideComponentConverter::create); + } + + return st; +} + +MStatus shaveGeometryOverride::deregisterComponentConverters() +{ + MStatus st = MHWRender::MDrawRegistry::deregisterComponentConverter(kKnotsItemName); + + if (st) + { + st = MHWRender::MDrawRegistry::deregisterComponentConverter(kGuidesItemName); + } + + return st; +} +#endif + + +bool shaveGeometryOverride::disableRenderItem( + MHWRender::MRenderItemList& list, + const MString& itemName, + MHWRender::MGeometry::Primitive primType, + MHWRender::MGeometry::DrawMode drawMode + ) +{ + int index = list.indexOf(itemName, primType, drawMode); + + if (index >= 0) + { + list.itemAt(index)->enable(false); + } + + return (index >= 0); +} diff --git a/mayaPlug/shaveGeometryOverride.h b/mayaPlug/shaveGeometryOverride.h new file mode 100644 index 0000000..2499bd8 --- /dev/null +++ b/mayaPlug/shaveGeometryOverride.h @@ -0,0 +1,203 @@ +#ifndef shaveGeometryOverride_h +#define shaveGeometryOverride_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <vector> + +#include <maya/M3dView.h> +#include <maya/MColor.h> +#include <maya/MGeometryRequirements.h> +#include <maya/MGeometry.h> +#include <maya/MHWGeometry.h> +#include <maya/MMatrix.h> +#include <maya/MObject.h> +#include <maya/MPxGeometryOverride.h> +#include <maya/MShaderManager.h> +#include <maya/MTextureManager.h> +#include <maya/MUserData.h> +#include <maya/MVector.h> +#include <maya/MViewport2Renderer.h> + +#include "shaveHairShape.h" + +#if MAYA_API_VERSION < 20180000 +class MDagPath; +#endif + +class shaveInstanceDisplay; + + +class shaveGeometryOverride : public MHWRender::MPxGeometryOverride { +public: + static MPxGeometryOverride* Creator(const MObject& obj) + { + return new shaveGeometryOverride(obj); + } + virtual ~shaveGeometryOverride(); + + virtual MHWRender::DrawAPI supportedDrawAPIs() const; + virtual void updateDG(); + + virtual void updateRenderItems( + const MDagPath& path, + MHWRender::MRenderItemList& list); + + virtual void populateGeometry( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& renderItems, + MHWRender::MGeometry& data + ); + +#if MAYA_API_VERSION >= 201600 + virtual void updateSelectionGranularity(const MDagPath& path, MHWRender::MSelectionContext& selectCtx); +#endif + virtual void cleanUp(); + +#if MAYA_API_VERSION >= 201600 + static MStatus registerComponentConverters(); + static MStatus deregisterComponentConverters(); +#endif + + // Custom user data to attach to render items + // for doing selection. + // + class SelectionData : public MUserData + { + public: + SelectionData() + : MUserData(false) + {} + + virtual ~SelectionData() + {} + + MString mShaveSelectType; + + // Holds the encoded (guide, vert) value for each + // primitive used to draw guide verts. + // + std::vector<unsigned int> mPrimIdxToVertIdx; + + // Holds the guide index for each primitive used to + // draw a guide. + // + std::vector<unsigned int> mPrimIdxToGuideIdx; + }; + + // RenderItem names. + // + static const MString kHairShadedItemName; + static const MString kHairShadedSelectedItemName; + static const MString kHairWireItemName; + static const MString kHairWireSelectedItemName; + static const MString kInstanceShadedItemName; + static const MString kInstanceWireItemName; + static const MString kInstanceWireSelectedItemName; + static const MString kGuidesItemName; + static const MString kKnotsItemName; + +protected: + shaveGeometryOverride(const MObject& obj); + + bool disableRenderItem( + MHWRender::MRenderItemList& list, + const MString& itemName, + MHWRender::MGeometry::Primitive primType, + MHWRender::MGeometry::DrawMode drawMode + ); + + virtual void populateHairInstances( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& renderItems, + MHWRender::MGeometry& data + ); + + + //uses geometry shader + virtual void populateHairLines( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& renderItems, + MHWRender::MGeometry& data + ); + + + //polygonal hair + virtual void populateHairPolys( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& renderItems, + MHWRender::MGeometry& data + ); + + + //uses geometry shader + virtual void populateHairGuides( + const MHWRender::MGeometryRequirements& requirements, + const MHWRender::MRenderItemList& renderItems, + MHWRender::MGeometry& data + ); + + void cacheHairPolys(); + void cacheGuidePolys(); + void cacheKnotPolys(); + + shaveHairShape* shave; + MObject shobj; + + struct Poly { + MVector t[3]; + MVector v[3]; + MColor c[3]; + MVector uv[3]; + }; + + class PolyGroup { + public: + std::vector<Poly> p; + }; + std::vector<PolyGroup> groups; + unsigned int ingroups; + + struct GuidePoly { + MVector v[3]; + bool active; + }; + std::vector<GuidePoly> guidePolys; + + struct KnotPoly { + MVector v[3]; + bool active; + }; + std::vector<KnotPoly> knotPolys; + + MHWRender::MTexture* instex; + + SelectionData mSelectionData; + + // We're only allowed to access the DG during updateDG(). Below + // are the DG-dependent data that we need to cache for use in subsequent + // methods. + // + unsigned int mNumHairsToDraw; + const shaveHairShape::DisplayHairCache* mHairCache; + M3dView::LightingMode mLightMode; + int mHairDisplayMode; + MColor mSpecularTint; + MColor mSpecularTint2; + float mGloss; + float mSpecular; + const shaveInstanceDisplay* mInstanceCache; + MMatrix mWorldToCam; + MVector mViewDir; + MVector mUpDir; + MVector mRightDir; + float mTransparency; + bool mDoTipFade; + int mPasses; + const shaveHairShape::Guides* mGuideCache; + float mGuideExtent; +}; + +#endif + diff --git a/mayaPlug/shaveGlobals.cpp b/mayaPlug/shaveGlobals.cpp new file mode 100644 index 0000000..65c5bec --- /dev/null +++ b/mayaPlug/shaveGlobals.cpp @@ -0,0 +1,1807 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDGModifier.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnEnumAttribute.h> +#include <maya/MFnMeshData.h> +#include <maya/MFnMessageAttribute.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MFnUnitAttribute.h> +#include <maya/MGlobal.h> +#include <maya/MItSelectionList.h> +#include <maya/MMessage.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> +#include <maya/MSelectionList.h> +#include <maya/MFnStringData.h> + +#include "shaveConstant.h" +#include "shaveError.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveRender.h" +#include "shaveSDK.h" +#include "shaveUtil.h" + +#include <time.h> + + +MTypeId shaveGlobals::id(0x00106501); + +const MString shaveGlobals::defaultNodeName("shaveGlobals"); +const MString shaveGlobals::nodeTypeName("shaveGlobals"); +const int shaveGlobals::kNodeVersion = 11; + + +// Attribute Objects +MObject shaveGlobals::aAddress; +MObject shaveGlobals::aGlInst; +MObject shaveGlobals::aComposite2d; +MObject shaveGlobals::aDisplayGuides; +MObject shaveGlobals::aDisplayGuideThick; +MObject shaveGlobals::aDisplaySegmentLimit; +MObject shaveGlobals::aDoCompositing; +MObject shaveGlobals::aDoHairShadows; +MObject shaveGlobals::aEnableInstanceGeometry; +MObject shaveGlobals::aGravity; +MObject shaveGlobals::aHairFilename; +MObject shaveGlobals::aHairOcclusionObjects; +MObject shaveGlobals::aHideHair; +MObject shaveGlobals::aInfo1; +MObject shaveGlobals::aInfo2; +MObject shaveGlobals::aKeepHairRenderFiles; +MObject shaveGlobals::aLiveMode; +MObject shaveGlobals::aLiveModeRecord; +MObject shaveGlobals::aLiveModePlayback; +MObject shaveGlobals::aMaxThreads; +MObject shaveGlobals::aNativeIllumination; +MObject shaveGlobals::aNodeVersion; +MObject shaveGlobals::aNormalRenderMode; +MObject shaveGlobals::aPrimCameraVis; +MObject shaveGlobals::aPrimGiVis; +MObject shaveGlobals::aPrimLightVis; +MObject shaveGlobals::aRenderQuality; +MObject shaveGlobals::aShadowDensity; +MObject shaveGlobals::aShadowMatte; +MObject shaveGlobals::aShadowMatteIncludeBackfacing; +MObject shaveGlobals::aStatFileDir; +MObject shaveGlobals::aThreadPerProcessor; +MObject shaveGlobals::aTileMemoryLimit; +MObject shaveGlobals::aTransparencyDepth; +MObject shaveGlobals::aUseAllLights; +MObject shaveGlobals::aUseDRA; +MObject shaveGlobals::aVisibleInReflections; +MObject shaveGlobals::aVerbose; +MObject shaveGlobals::aVoxelResolution; +MObject shaveGlobals::aVoxelScaling; + +// Renderman defaults +MObject shaveGlobals::aRibBinary; +MObject shaveGlobals::aRibBlurEnable; +MObject shaveGlobals::aRibBlurInheritSettings; +MObject shaveGlobals::aRibBlurRestoreFrame; +MObject shaveGlobals::aRibBlurShutterOpenOffset; +MObject shaveGlobals::aRibBlurShutterCloseOffset; +MObject shaveGlobals::aRibBlurTimeBasis; +MObject shaveGlobals::aRibCompress; +MObject shaveGlobals::aRibKeepRibFiles; +MObject shaveGlobals::aRibLibOverride; +MObject shaveGlobals::aRibNormals; +MObject shaveGlobals::aRibOpacities; +MObject shaveGlobals::aRibPrimitiveType; +MObject shaveGlobals::aRibRootPositions; +MObject shaveGlobals::aRibRootTipColors; +MObject shaveGlobals::aRibStuff; +MObject shaveGlobals::aRibTimeUnits; +MObject shaveGlobals::aRibUVs; +MObject shaveGlobals::aRibUVSet; +MObject shaveGlobals::aRibWCoords; +MObject shaveGlobals::aRibVertexColors; +MObject shaveGlobals::aRibVoxelEnable; +MObject shaveGlobals::aRibVoxelResolution; +MObject shaveGlobals::aRibVoxelFullPaths; +MObject shaveGlobals::displayDoFallbackAttr; +MObject shaveGlobals::displayFallbackRatioAttr; +MObject shaveGlobals::displayHairRatioAttr; +MObject shaveGlobals::displayHiarDoXparency; + + +// +// Semi-Deprecated Attributes +// + +MObject shaveGlobals::aUseGeomForShadows; +bool shaveUseGeomShadowsGlob; + +MObject shaveGlobals::aShadowHairRatio; + + +// +// Deprecated Attributes +// +MObject shaveGlobals::aActiveShaveNode; +MObject shaveGlobals::aCurrentShaveNode; +MObject shaveGlobals::aDisplayDensity; +MObject shaveGlobals::aDisplayHairDoSsao; +MObject shaveGlobals::aFastBrush; +MObject shaveGlobals::aGenerateHairGeometry; +MObject shaveGlobals::aInstanceRenderMode; +MObject shaveGlobals::aMREnableIrradiance; +MObject shaveGlobals::aMRIrradiance; +MObject shaveGlobals::aMROcclusionCollection; +MObject shaveGlobals::aMRSafeMode; +MObject shaveGlobals::aMRSatelliteSupport; +MObject shaveGlobals::aMRUseRenderGlobalsCallbacks; +MObject shaveGlobals::aPrmanCurvesSupported; +MObject shaveGlobals::aRibBlurUseMayaDefaults; +MObject shaveGlobals::aRibUseRenderGlobalsCallbacks; +MObject shaveGlobals::aShadowAntiAlias; +MObject shaveGlobals::aShadowQuality; +MObject shaveGlobals::aShaveLightList; +MObject shaveGlobals::aShavePrmanMode; +MObject shaveGlobals::aShaveShadowFuzz; +MObject shaveGlobals::aShaveShadowRes; +MObject shaveGlobals::aShaveShadowSamples; +MObject shaveGlobals::aTileCount; +MObject shaveGlobals::aTrigger; +MObject shaveGlobals::aUseDeepShadows; +MObject shaveGlobals::aVrayDraFile; + + +// Global Vars For Attr Values +// +// We're slowly deprecating these in favour of passing values using +// shaveGlobals::Globals structs, so don't add any new ones here. +bool doHairShadowsGlob; +bool doShaveComp2dGlob; +bool doShaveCompsGlob; +bool enableInstanceGeometryGlob; +float gravityGlob; +int glisntGlob; +MString hairFileNameGlob; +bool keepHairRenderFilesGlob; +bool liveModeGlob; +bool liveModeRecord; +bool liveModePlayback; +float displayRatioGlob; +float fallbackRatioGlob; +bool displayGuidesGlob = false; +float guideThickGlob; +bool doFallbackGlob = true; +//bool hideHairGlob = false; +float displaySegmentLimitGlob; +bool doHairXparencyGlob; +int maxThreadsGlob = 0; +bool nativeIlluminationGlob; +shaveConstant::RenderMode normalRenderModeGlob; +short renderQualityGlob; +MString shaveSceneObjectsGlob; +float shaveShadowDensityGlob; +float shaveShadowHairRatioGlob; +bool shaveShadowMatteGlob; +MString tmpDirGlob; +bool threadPerProcessorGlob = true; +bool useAllLightsGlob; +bool useDRAGlob = true; +bool visibleInReflectionsGlob; +int voxelResolutionGlob; + + +MCallbackId idleEventId; + +bool shadowRender; + +float motionBlurSamplesGlob; + + +shaveGlobals::shaveGlobals() +: mHideHair(false) +, mNodeVersion(0) +, mNormalRenderMode(shaveConstant::kBufferRender) +{} + + +/* + * Create all of the attributes of our shaveGlobals Dependency Node. + */ +MStatus shaveGlobals::initialize() +{ + MStatus st; + + MFnEnumAttribute eAttr; + MFnMessageAttribute mAttr; + MFnNumericAttribute nAttr; + MFnTypedAttribute tAttr; + MFnUnitAttribute unitAttr; + + aInfo1 = nAttr.create("in1", "i1", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setHidden(true); + addAttribute(aInfo1); + + aInfo2 = nAttr.create("in2", "i2", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setHidden(true); + addAttribute(aInfo2); + + aNormalRenderMode = eAttr.create( + "renderMode", + "rndm", + shaveConstant::kBufferRender + ); + + eAttr.addField( + shaveRender::getRenderModeName(shaveConstant::kBufferRender), + shaveConstant::kBufferRender + ); + eAttr.addField( + shaveRender::getRenderModeName(shaveConstant::kGeometryRender), + shaveConstant::kGeometryRender + ); + eAttr.addField( + shaveRender::getRenderModeName(shaveConstant::kNoRender), + shaveConstant::kNoRender + ); + + eAttr.setInternal(true); + eAttr.setStorable(true); + addAttribute(aNormalRenderMode); + + aEnableInstanceGeometry = nAttr.create("enableInstanceGeometry", "eig", MFnNumericData::kBoolean, true); + addAttribute(aEnableInstanceGeometry); + + aLiveMode = nAttr.create ("liveMode", "slive", MFnNumericData::kBoolean, false); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute (aLiveMode); + + aLiveModeRecord = nAttr.create ("liveModeRec", "rec", MFnNumericData::kBoolean, false); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute (aLiveModeRecord); + + aLiveModePlayback = nAttr.create ("liveModePlay", "pla", MFnNumericData::kBoolean, false); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute (aLiveModePlayback); + + aNativeIllumination = nAttr.create ("nativeIllumination", "nil", MFnNumericData::kBoolean, false); + nAttr.setStorable(true); + addAttribute (aNativeIllumination); + + aDoCompositing = nAttr.create ("doCompositing", "dsc", MFnNumericData::kBoolean, true); + addAttribute (aDoCompositing); + + aComposite2d = nAttr.create ("composite2d", "c2d", MFnNumericData::kBoolean, true); + addAttribute (aComposite2d); + + aDoHairShadows = nAttr.create ("doHairShadows", "dhs", MFnNumericData::kBoolean, true); + addAttribute (aDoHairShadows); + + aUseAllLights = nAttr.create ("useAllLights", "ual", MFnNumericData::kBoolean, true); + addAttribute(aUseAllLights); + + aShadowMatte = nAttr.create ("shadowMatte", "sm", MFnNumericData::kBoolean, false); + addAttribute (aShadowMatte); + + aShadowMatteIncludeBackfacing = nAttr.create( + "shadowMatteIncludeBackfacing", + "smbf", + MFnNumericData::kBoolean, + true, + &st + ); + MChkErr(st, "cannot create 'shadowMatteIncludeBackfacing' attribute."); + st = addAttribute(aShadowMatteIncludeBackfacing); + MChkErr(st, "cannot add 'shadowMatteIncludeBackfacing' attribute."); + + aKeepHairRenderFiles = nAttr.create ("keepHairPassPics", "khrf", MFnNumericData::kBoolean, false); + addAttribute (aKeepHairRenderFiles); + + aRenderQuality = eAttr.create ("renderQuality", "rq", 1); + eAttr.addField("Draft",0); + eAttr.addField("Low",1); + eAttr.addField("Medium", 2); + eAttr.addField("High",3); + eAttr.addField("Duuuude...",4); + eAttr.setStorable(true); + addAttribute (aRenderQuality); + + aHairFilename = tAttr.create("hairFilenamePrefix", "hfn", MFnData::kString, &st); + addAttribute (aHairFilename); + + // + // 'tmpDir' holds the user-defined path to the directory where the + // scene's stat (dynamics) files will be stored. The unfortunate name + // of the attribute is the result of history: originally it was viewed + // as being a place to store temporary files, but the stat files are + // less temporary than others. + // + // 'tmpDir' can hold either an absolute path, or a relative path which + // is interpreted relative to the current project directory. + // + aStatFileDir = tAttr.create("tmpDir", "tpd", MFnData::kString, &st); + tAttr.setStorable(true); + addAttribute (aStatFileDir); + + aHairOcclusionObjects = tAttr.create( + "hairOcclusionObjects", + "sso", + MFnData::kString, + &st + ); + addAttribute (aHairOcclusionObjects); + + aShadowDensity = nAttr.create("shadowDensity", "sd", MFnNumericData::kFloat, 1.); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + addAttribute (aShadowDensity); + + aGravity = nAttr.create ("gravity", "grav", MFnNumericData::kFloat, 1.0); + nAttr.setStorable(true); + nAttr.setSoftMin(-2); + nAttr.setSoftMax(2); + addAttribute (aGravity); + + aGlInst = nAttr.create ("glInstances", "gli", MFnNumericData::kBoolean, /*0*/ 1); + nAttr.setStorable(true); + addAttribute (aGlInst); + + aNodeVersion = nAttr.create("nodeVersion", "nv", MFnNumericData::kLong); + nAttr.setStorable(true); + nAttr.setHidden(true); + nAttr.setInternal(true); + nAttr.setDefault(0); + addAttribute(aNodeVersion); + + aUseDRA = nAttr.create( + "useDRA", "dra", MFnNumericData::kBoolean, true + ); +#if !DRA_OPTIONAL + nAttr.setInternal(true); +#endif + nAttr.setStorable(true); + addAttribute(aUseDRA); + + aVoxelResolution = eAttr.create ("voxelResolution", "vxrs", 9); + eAttr.addField("1x1x1",1); + eAttr.addField("2x2x2",2); + eAttr.addField("3x3x3",3); + eAttr.addField("5x5x5",5); + eAttr.addField("9x9x9",9); + eAttr.addField("15x15x15",15); + eAttr.addField("20x20x20",20); + eAttr.addField("25x25x25",25); + eAttr.setStorable(true); + addAttribute(aVoxelResolution); + + aVisibleInReflections = nAttr.create( + "visibleInReflections", "vrfl", MFnNumericData::kBoolean, false + ); + nAttr.setStorable(true); + addAttribute(aVisibleInReflections); + + aHideHair = nAttr.create("hideHair", "hh", MFnNumericData::kBoolean, false); + nAttr.setInternal(true); + nAttr.setStorable(true); + st = addAttribute(aHideHair); + MChkErr(st, "Cannot add 'hideHair' attribute."); + + aMaxThreads = nAttr.create( + "maxThreads", "mth", MFnNumericData::kLong, 0, &st + ); + MChkErr(st, "Cannot create 'maxThreads' attribute."); + nAttr.setStorable(true); + nAttr.setHidden(true); + nAttr.setInternal(true); + nAttr.setDefault(0); + nAttr.setMin(0); + nAttr.setMax(20); + st = addAttribute(aMaxThreads); + MChkErr(st, "Cannot add 'maxThreads' attribute."); + + aThreadPerProcessor = nAttr.create( + "threadPerProcessor", "tpp", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'threadPerProcessor' attribute."); + nAttr.setInternal(true); + nAttr.setDefault(true); + st = addAttribute(aThreadPerProcessor); + MChkErr(st, "Cannot add 'threadPerProcessor' attribute."); + + aVerbose = nAttr.create("verbose", "verb", MFnNumericData::kBoolean, true); + nAttr.setStorable(true); + st = addAttribute(aVerbose); + MChkErr(st, "Cannot add 'verbose' attribute."); + + aVoxelScaling = nAttr.create( + "voxelScaling", "vs", MFnNumericData::kFloat, 1.1, &st + ); + MChkErr(st, "Cannot create 'voxelScaling' attribute."); + nAttr.setMin(0.1); + nAttr.setSoftMax(2.0); + st = addAttribute(aVoxelScaling); + MChkErr(st, "Cannot add 'voxelScaling' attribute."); + + // Maya doesn't supply an MPlug method for reading the value of + // a kAddr plug. So we use this attribute to allow us to read it + // through an MDataHandle. See shaveUtil::getAddrPlugValue() + aAddress = nAttr.createAddr("address", "addr", NULL, &st); + MChkErr(st, "Cannot create 'address' attribute."); + nAttr.setHidden(true); + st = addAttribute(aAddress); + MChkErr(st, "Cannot add 'address' attribute."); + + aTileMemoryLimit = nAttr.create( + "tileMemoryLimit", "tml", MFnNumericData::kInt, /*90.0*/ 180.0, &st + ); + MChkErr(st, "Cannot create 'tileMemoryLimit' attribute."); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(1000); + st = addAttribute(aTileMemoryLimit); + MChkErr(st, "Cannot add 'tileMemoryLimit' attribute."); + + aTransparencyDepth = nAttr.create( + "transparencyDepth", "td", MFnNumericData::kInt, 30.0, &st + ); + MChkErr(st, "Cannot create 'transparencyDepth' attribute."); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(100); + st = addAttribute(aTransparencyDepth); + MChkErr(st, "Cannot add 'transparencyDepth' attribute."); + + // RIB-Related Attributes + + aRibBinary = nAttr.create( + "ribBinary", "rbin", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribBinary' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibBinary); + MChkErr(st, "Cannot add 'ribBinary' attribute."); + + aRibBlurTimeBasis = eAttr.create( + "ribBlurTimeBasis", "rbtb", shaveConstant::kTimeRelative, &st + ); + MChkErr(st, "Cannot create 'ribBlurTimeBasis' attribute."); + eAttr.addField("Absolute", shaveConstant::kTimeAbsolute); + eAttr.addField("Relative", shaveConstant::kTimeRelative); + st = addAttribute(aRibBlurTimeBasis); + MChkErr(st, "Cannot add 'ribBlurTimeBasis' attribute."); + + aRibBlurEnable = nAttr.create( + "ribBlurEnable", "rben", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribBlurEnable' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibBlurEnable); + MChkErr(st, "Cannot add 'ribBlurEnable' attribute."); + + aRibBlurInheritSettings = eAttr.create( + "ribBlurInheritSettings", + "rbis", + shaveConstant::kRibBlurInheritMaya, + &st + ); + MChkErr(st, "Cannot create 'ribBlurInheritSettings' attribute."); + eAttr.addField("Off", shaveConstant::kRibBlurInheritOff); + eAttr.addField("Maya Globals", shaveConstant::kRibBlurInheritMaya); + eAttr.addField("RfM Studio", shaveConstant::kRibBlurInheritRenderman); + eAttr.setStorable(true); + st = addAttribute(aRibBlurInheritSettings); + MChkErr(st, "Cannot add 'ribBlurInheritSettings' attribute."); + + aRibBlurRestoreFrame = nAttr.create( + "ribBlurRestoreFrame", "rbrf", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribBlurRestoreFrame' attribute."); + nAttr.setDefault(true); + st = addAttribute(aRibBlurRestoreFrame); + MChkErr(st, "Cannot add 'ribBlurRestoreFrame' attribute."); + + aRibBlurShutterCloseOffset = nAttr.create( + "ribBlurShutterCloseOffset", + "rbsc", + MFnNumericData::kFloat, + 0.3, + &st + ); + MChkErr(st, "Cannot create 'ribBlurShutterCloseOffset' attribute."); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + st = addAttribute(aRibBlurShutterCloseOffset); + MChkErr(st, "Cannot add 'ribBlurShutterCloseOffset' attribute."); + + aRibBlurShutterOpenOffset = nAttr.create( + "ribBlurShutterOpenOffset", + "rbso", + MFnNumericData::kFloat, + -0.3, + &st + ); + MChkErr(st, "Cannot create 'ribBlurShutterOpenOffset' attribute."); + nAttr.setSoftMin(-1.0); + nAttr.setSoftMax(0.0); + st = addAttribute(aRibBlurShutterOpenOffset); + MChkErr(st, "Cannot add 'ribBlurShutterOpenOffset' attribute."); + + aRibCompress = nAttr.create( + "ribCompress", "rcmp", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribCompress' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibCompress); + MChkErr(st, "Cannot add 'ribCompress' attribute."); + + aRibKeepRibFiles = nAttr.create( + "ribKeepRibFiles", + "rkrf", + MFnNumericData::kBoolean, + 0.0, + &st + ); + MChkErr(st, "Cannot create 'ribKeepRibFiles' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibKeepRibFiles); + MChkErr(st, "Cannot add 'ribKeepRibFiles' attribute."); + + aRibLibOverride = tAttr.create( + "ribLibOverride", "rlo", MFnData::kString, MObject::kNullObj, &st + ); + MChkErr(st, "Cannot create 'ribLibOverride' attribute."); + tAttr.setUsedAsFilename(true); + st = addAttribute(aRibLibOverride); + MChkErr(st, "Cannot add 'ribLibOverride' attribute."); + + aRibNormals = nAttr.create( + "ribNormals", "rnrm", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribNormals' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibNormals); + MChkErr(st, "Cannot add 'ribNormals' attribute."); + + aRibOpacities = nAttr.create( + "ribOpacities", "ropa", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribOpacities' attribute."); + nAttr.setDefault(true); + st = addAttribute(aRibOpacities); + MChkErr(st, "Cannot add 'ribOpacities' attribute."); + + aRibPrimitiveType = eAttr.create("hairPrimitiveType", "prhpt", 1, &st); + MChkErr(st, "Cannot create 'hairPrimitiveType' attribute."); + eAttr.addField("Linear Curve", 0); + eAttr.addField("Cubic Curve", 1); + st = addAttribute(aRibPrimitiveType); + MChkErr(st, "Cannot add 'hairPrimitiveType' attribute."); + + aRibRootPositions = nAttr.create( + "ribRootPositions", + "rrp", + MFnNumericData::kBoolean, + 0.0, + &st + ); + MChkErr(st, "Cannot create 'ribRootPositions' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibRootPositions); + MChkErr(st, "Cannot add 'ribRootPositions' attribute."); + + aRibRootTipColors = nAttr.create( + "ribRootTipColors", + "rrtc", + MFnNumericData::kBoolean, + 0.0, + &st + ); + MChkErr(st, "Cannot create 'ribRootTipColors' attribute."); + nAttr.setDefault(true); + st = addAttribute(aRibRootTipColors); + MChkErr(st, "Cannot add 'ribRootTipColors' attribute."); + + // See the createShaveGlobals() proc in shaveUI.mel for the setting of + // this attribute's default value. + aRibStuff = tAttr.create( + "ribStuff", "rst", MFnData::kString, MObject::kNullObj, &st + ); + MChkErr(st, "Cannot create 'ribStuff' attribute."); + st = addAttribute(aRibStuff); + MChkErr(st, "Cannot add 'ribStuff' attribute."); + + aRibVertexColors = nAttr.create( + "ribVertexColors", "rvc", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribVertexColors' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibVertexColors); + MChkErr(st, "Cannot add 'ribVertexColors' attribute."); + + aRibTimeUnits = eAttr.create("ribTimeUnits", "rtu", kFrames, &st); + MChkErr(st, "Cannot create 'ribTimeUnits' attribute."); + eAttr.addField("frames", kFrames); + eAttr.addField("seconds", kSeconds); + st = addAttribute(aRibTimeUnits); + MChkErr(st, "Cannot add 'ribTimeUnits' attribute."); + + aRibUVs= nAttr.create("ribUVs", "ruv", MFnNumericData::kBoolean, 0.0, &st); + MChkErr(st, "Cannot create 'ribUVs' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibUVs); + MChkErr(st, "Cannot add 'ribUVs' attribute."); + + aRibUVSet= tAttr.create( + "ribUVSet", "ruvs", MFnData::kString, MObject::kNullObj, &st + ); + MChkErr(st, "Cannot create 'ribUVSet' attribute."); + st = addAttribute(aRibUVSet); + MChkErr(st, "Cannot add 'ribUVSet' attribute."); + + aRibWCoords = nAttr.create( + "ribWCoords", "rwc", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribWCoords' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibWCoords); + MChkErr(st, "Cannot add 'ribWCoords' attribute."); + + aRibVoxelEnable= nAttr.create( + "ribVoxelEnable", "rvxe", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribVoxelEnable' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibVoxelEnable); + MChkErr(st, "Cannot add 'ribVoxelEnable' attribute."); + + aRibVoxelFullPaths= nAttr.create( + "ribVoxelFullPaths", "rvxp", MFnNumericData::kBoolean, 0.0, &st + ); + MChkErr(st, "Cannot create 'ribVoxelFullPaths' attribute."); + nAttr.setDefault(false); + st = addAttribute(aRibVoxelFullPaths); + MChkErr(st, "Cannot add 'ribVoxelFullPaths' attribute."); + + aRibVoxelResolution= eAttr.create("ribVoxelResolution", "rvxr", 20, &st); + MChkErr(st, "Cannot create 'ribVoxelResolution' attribute."); + eAttr.addField("1x1x1",1); + eAttr.addField("2x2x2",2); + eAttr.addField("3x3x3",3); + eAttr.addField("5x5x5",5); + eAttr.addField("9x9x9",9); + eAttr.addField("15x15x15",15); + eAttr.addField("20x20x20",20); + eAttr.addField("40x40x40",40); + eAttr.setStorable(true); + st = addAttribute(aRibVoxelResolution); + MChkErr(st, "Cannot add 'ribVoxelResolution' attribute."); + + displayHairRatioAttr = nAttr.create( + "displayHairRatio", + "dhr", + MFnNumericData::kFloat, + 50.0f + ); + nAttr.setMin(0.0f); + nAttr.setMax(100.0f); + st = addAttribute(displayHairRatioAttr); + MChkErr(st, "can't add 'displayHairRatio' attribute."); + + + displayFallbackRatioAttr = nAttr.create( + "displayFallbackRatio", + "dfr", + MFnNumericData::kFloat, + 10.0f + ); + nAttr.setMin(0.0f); + nAttr.setMax(100.0f); + st = addAttribute(displayFallbackRatioAttr); + MChkErr(st, "can't add 'displayRatio' attribute."); + + + displayDoFallbackAttr = nAttr.create( + "doFallback", + "dof", + MFnNumericData::kBoolean, + 1 + ); + nAttr.setStorable(false); + st = addAttribute(displayDoFallbackAttr); + MChkErr(st, "can't add 'doFallback' attribute."); + + aDisplayGuides = nAttr.create( + "displayGuides", + "dg", + MFnNumericData::kBoolean, + 0, + &st + ); + MChkErr(st, "can't create 'displayGuides' attribute."); + nAttr.setDefault(false); + st = addAttribute(aDisplayGuides); + MChkErr(st, "can't add 'displayGuides' attribute."); + + aDisplaySegmentLimit = nAttr.create( + "displaySegmentLimit", + "dsl", + MFnNumericData::kFloat, + 50.0f, + &st + ); + MChkErr(st, "can't create 'displaySegmentLimit' attribute."); + nAttr.setHidden(true); // So it doesn't get saved as a preset. + nAttr.setMin(1.0f); + nAttr.setSoftMax(100.0f); + st = addAttribute(aDisplaySegmentLimit); + MChkErr(st, "can't add 'displaySegmentLimit' attribute."); + + aDisplayGuideThick = nAttr.create( + "displayGuideThick", + "dgt", + MFnNumericData::kFloat, + 0.2, + &st + ); + MChkErr(st, "can't create 'displayGuideThick' attribute."); + nAttr.setHidden(true); // So it doesn't get saved as a preset. + nAttr.setMin(0.001f); + nAttr.setSoftMax(1.0f); + nAttr.setDefault(/*0.7f*/ 0.2f); + st = addAttribute(aDisplayGuideThick); + MChkErr(st, "can't add 'displayGuideThick' attribute."); + + displayHiarDoXparency = nAttr.create( + "displayHairTransprency", + "dht", + MFnNumericData::kBoolean, + true + ); + + + st = addAttribute(displayHiarDoXparency); + + + + //******************************************************* + // + // Semi-Deprecated Attributes + // + // (Functionality hidden but still present.) + // + //******************************************************* + + aUseGeomForShadows = nAttr.create( + "useGeomForShadows", "ugs", MFnNumericData::kBoolean, false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aUseGeomForShadows); + + aShadowHairRatio = nAttr.create( + "shadowHairRatio", "shr", MFnNumericData::kFloat, 0.15 + ); + nAttr.setStorable(true); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + nAttr.setHidden(true); + addAttribute(aShadowHairRatio); + + + //******************************************************* + // + // Deprecated Attributes + // + //******************************************************* + + aActiveShaveNode = mAttr.create("activeShaveNode", "asn"); + mAttr.setArray(true); + mAttr.setHidden(true); + mAttr.setWritable(true); + mAttr.setReadable(false); // Don't allow outgoing connections. + mAttr.setStorable(false); + mAttr.setIndexMatters(false); + addAttribute(aActiveShaveNode); + + aCurrentShaveNode = mAttr.create("currentShaveNode", "csn"); + mAttr.setHidden(true); + mAttr.setStorable(false); + addAttribute(aCurrentShaveNode); + + aDisplayHairDoSsao = nAttr.create( + "displayHairSsao", + "dho", + MFnNumericData::kBoolean, + false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + st = addAttribute(aDisplayHairDoSsao); + + aDisplayDensity = eAttr.create("displayLod", "dns", 1); + eAttr.addField("Off", -1); + eAttr.addField("Low", 0); + eAttr.addField("Medium", 1); + eAttr.addField("High", 2); + eAttr.setHidden(true); + eAttr.setStorable(false); + addAttribute(aDisplayDensity); + + aGenerateHairGeometry = nAttr.create( + "doHairGeometry", + "mhg", + MFnNumericData::kBoolean, + false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aGenerateHairGeometry); + + aFastBrush = nAttr.create( + "fastBrush", "fb", MFnNumericData::kBoolean, 0.0, &st + ); + nAttr.setDefault(false); + nAttr.setHidden(true); + nAttr.setStorable(false); + st = addAttribute(aFastBrush); + + aInstanceRenderMode = eAttr.create( + "instanceRenderMode", + "irnm", + shaveConstant::kGeometryRender + ); + eAttr.addField( + shaveRender::getRenderModeName(shaveConstant::kBufferRender), + shaveConstant::kBufferRender + ); + eAttr.addField( + shaveRender::getRenderModeName(shaveConstant::kGeometryRender), + shaveConstant::kGeometryRender + ); + eAttr.addField( + shaveRender::getRenderModeName(shaveConstant::kNoRender), + shaveConstant::kNoRender + ); + eAttr.setHidden(true); + eAttr.setStorable(false); + addAttribute(aInstanceRenderMode); + + aMREnableIrradiance = nAttr.create( + "mrEnableIrradiance", "mrei", MFnNumericData::kBoolean, false, &st + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setDefault(false); + addAttribute(aMREnableIrradiance); + + aMRIrradiance = nAttr.create( + "mrIrradiance", "mrir", MFnNumericData::kFloat, 1.0, &st + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aMRIrradiance); + + aMROcclusionCollection = nAttr.create( + "mrOcclusionCollection", "mroc", MFnNumericData::kBoolean, false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aMROcclusionCollection); + + aMRSafeMode = nAttr.create( + "mrSafeMode", "mrsm", MFnNumericData::kBoolean, false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aMRSafeMode); + + aMRSatelliteSupport = nAttr.create( + "mrSatelliteSupport", "mss", MFnNumericData::kBoolean, false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aMRSatelliteSupport); + + aMRUseRenderGlobalsCallbacks = nAttr.create( + "mrUseRenderGlobalsCallbacks", "rgcb", MFnNumericData::kBoolean, true + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aMRUseRenderGlobalsCallbacks); + + aPrmanCurvesSupported = nAttr.create( + "prmanCurvesSupported", + "pmcs", + MFnNumericData::kBoolean, + true + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aPrmanCurvesSupported); + + aRibBlurUseMayaDefaults = nAttr.create( + "ribBlurUseMayaDefaults", + "rbum", + MFnNumericData::kBoolean, + 0.0, + &st + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + st = addAttribute(aRibBlurUseMayaDefaults); + + aRibUseRenderGlobalsCallbacks = nAttr.create( + "ribUseRenderGlobalsCallbacks", "rbrg", MFnNumericData::kBoolean, true + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRibUseRenderGlobalsCallbacks); + + aShadowAntiAlias = eAttr.create ("shadowAntialiasing", "shadaa", 1); + eAttr.addField("Draft", 1); + eAttr.addField("Low",3); + eAttr.addField("Medium",5); + eAttr.addField("High", 10); + eAttr.setHidden(true); + eAttr.setStorable(false); + addAttribute(aShadowAntiAlias); + + aShadowQuality = eAttr.create("shadowQuality", "sq", kHighShadowQuality); + eAttr.addField("Low", kLowShadowQuality); + eAttr.addField("Medium", kMediumShadowQuality); + eAttr.addField("High", kHighShadowQuality); + eAttr.setHidden(true); + eAttr.setStorable(false); + addAttribute(aShadowQuality); + + aShaveLightList = tAttr.create("shaveLightList", "sll", MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(aShaveLightList); + + aShavePrmanMode = nAttr.create( + "prmanMode", "sprmm", MFnNumericData::kBoolean, false + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aShavePrmanMode); + + aShaveShadowFuzz = nAttr.create("shaveShadFuzz", "ssfuzz", MFnNumericData::kFloat, 8.0); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aShaveShadowFuzz); + + aShaveShadowRes = nAttr.create("shadowResolution", "ssr", MFnNumericData::kShort, 1400); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aShaveShadowRes); + + aShaveShadowSamples = nAttr.create( + "shaveShadSamp", + "sssamp", + MFnNumericData::kShort, + 0 + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aShaveShadowSamples); + + aTrigger = tAttr.create("trigger", "trg", MFnData::kMesh); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(aTrigger); + + aTileCount = eAttr.create("tileCount", "srtc", 3); + eAttr.addField("1x1",1); + eAttr.addField("2x2", 2); + eAttr.addField("3x3",3); + eAttr.addField("5x5", 5); + eAttr.addField("9x9", 9); + eAttr.addField("15x15", 15); + eAttr.addField("25x25", 25); + eAttr.addField("40x40", 40); + eAttr.setHidden(true); + eAttr.setStorable(false); + addAttribute(aTileCount); + + aUseDeepShadows = nAttr.create( + "useDeepShadows", "uds", MFnNumericData::kBoolean + ); + nAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(aUseDeepShadows); + + //default dra file name, + //artist can edit it to resolve confilt when rendering + //multiple scenes on same machine -- dub|23-09-2010 + MStatus stat; + //char buf[200]; + //time_t t; + //time(&t); + //srand((unsigned int)t); + //int rsuf = rand(); + //sprintf(buf,"vrayHair_%i.dra",rsuf); + //MFnStringData strData; + //strData.create(buf,&stat); + + MFnStringData strData; + strData.create("",&stat); + if(stat != MStatus::kSuccess) + MGlobal::displayError("ShaveGlobals: can not create MFnStringData."); + + aVrayDraFile = tAttr.create( "vrayDraFile", "vrd", MFnData::kString,strData.object(), &stat); + if(stat != MStatus::kSuccess) + MGlobal::displayError("ShaveGlobals: can not create 'vrayDraFile' string attribute."); + else + { + tAttr.setHidden ( false ); + tAttr.setKeyable ( false ); + tAttr.setStorable( true ); + } + addAttribute(aVrayDraFile); + + + aPrimCameraVis = nAttr.create("primCameraVis", "pcv", MFnNumericData::kBoolean, true); + nAttr.setStorable(true); + st = addAttribute(aPrimCameraVis); + MChkErr(st, "Cannot add 'primCameraVis' attribute."); + + aPrimLightVis = nAttr.create("primLightVis", "plv", MFnNumericData::kBoolean, true); + nAttr.setStorable(true); + st = addAttribute(aPrimLightVis); + MChkErr(st, "Cannot add 'primGiVis' attribute."); + + aPrimGiVis = nAttr.create("primGiVis", "pgv", MFnNumericData::kBoolean, true); + nAttr.setStorable(true); + st = addAttribute(aPrimGiVis); + MChkErr(st, "Cannot add 'primGiVis' attribute."); + + return MS::kSuccess; +} + + +MStatus shaveGlobals::shouldSave(const MPlug& plug, bool& saveIt) +{ + // + // If it's a dynamic attribute, or one of the standard MPxNode + // attributes, then do the default handling. + // + MFnDependencyNode nodeFn(thisMObject()); + MFnDependencyNode::MAttrClass attrClass; + + attrClass = nodeFn.attributeClass(plug.attribute()); + + if ((attrClass == MFnDependencyNode::kLocalDynamicAttr) + || (plug == MPxNode::message) + || (plug == MPxNode::isHistoricallyInteresting) + || (plug == MPxNode::caching) + || (plug == MPxNode::state)) + { + return MS::kInvalidParameter; + } + + // + // Everything else we will save all the time. That way if we decide to + // change defaults at some later time, we won't screw up old scene + // files. + // + saveIt = true; + + return MS::kSuccess; +} + + +void* shaveGlobals::creator() +{ + return new shaveGlobals; +} + + +bool shaveGlobals::deleteMe(MDGModifier& dgMod) +{ + MFnDependencyNode nodeFn(thisMObject()); + + // + // We can't delete the node if it's from a referenced file. + // + if (nodeFn.isFromReferencedFile()) return false; + + if (nodeFn.isLocked()) nodeFn.setLocked(false); + + MStatus st = dgMod.deleteNode(thisMObject()); + + return st; +} + + +void* shaveGlobals::getAddress() +{ + MDataBlock block = forceCache(); + + return block.inputValue(aAddress).asAddr(); +} + + +MObject shaveGlobals::getDefaultNode() +{ + MObject node = getDefaultNodeInternal(); + + // + // If no shaveGlobals node was found, try to create one. + // + if (node.isNull()) + { + MGlobal::executeCommand("shaveGlobals"); + node = getDefaultNodeInternal(); + } + + return node; +} + +MObject shaveGlobals::getDefaultNodeInternal() +{ + MSelectionList list; + list.add(defaultNodeName); + + MObject node; + list.getDependNode(0, node); + + return node; +} + + +MStatus shaveGlobals::getGlobals() +{ + // + // Get the shave globals node + // + MObject shaveGlobalsNode = getDefaultNode(); + + if (shaveGlobalsNode.isNull()) return MS::kFailure; + + // + // This method gets called fairly often (e.g. 4 during render init & + // cleanup, plus 4 per frame rendered) so let's make sure it's fast by + // grabbing a datablock and getting the values from that. + // + MFnDependencyNode nodeFn(shaveGlobalsNode); + shaveGlobals* nodePtr = (shaveGlobals*)nodeFn.userNode(); + + if (!nodePtr) return MS::kFailure; + + return nodePtr->getGlobalsInternal(); +} + + +// Deprecated +// +// This version is being phased out in favour of the one which copies its +// values into a Globals struct. Do not add any new attrs here. +MStatus shaveGlobals::getGlobalsInternal() +{ + MDataBlock block = forceCache(); + + doHairShadowsGlob = block.inputValue(aDoHairShadows).asBool(); + doShaveCompsGlob = block.inputValue(aDoCompositing).asBool(); + doShaveComp2dGlob = block.inputValue(aComposite2d).asBool(); + gravityGlob = block.inputValue(aGravity).asFloat(); + glisntGlob = block.inputValue(aGlInst).asInt(); + //if(gi != glisntGlob) + //{ + // glisntGlob = gi; + + //MFnDagNode nodeFn; + //MDagPathArray paths; + //shaveUtil::getShaveNodes(paths); + //for (unsigned int i = 0; i < paths.length(); i++) + //{ + // nodeFn.setObject(paths[i].node()); + + // if (nodeFn.typeId() == shaveHairShape::id) + // { + // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + // shape->dirtyDisplay(); + + // float trigger; + // MPlug triggerPlug = nodeFn.findPlug("trigger"); + // triggerPlug.getValue(trigger); + // triggerPlug.setValue(trigger+1.0f); + // } + //} + //} + hairFileNameGlob = block.inputValue(aHairFilename).asString(); + keepHairRenderFilesGlob = block.inputValue(aKeepHairRenderFiles).asBool(); + liveModeGlob = block.inputValue(aLiveMode).asBool(); + displayRatioGlob = 0.01f*block.inputValue(displayHairRatioAttr).asFloat(); +// doFallbackGlob = block.inputValue(displayDoFallbackAttr).asBool(); //just a trigger + displayGuidesGlob = block.inputValue(aDisplayGuides).asBool(); + fallbackRatioGlob = 0.01f*block.inputValue(displayFallbackRatioAttr).asFloat(); + displaySegmentLimitGlob = block.inputValue(aDisplaySegmentLimit).asFloat(); + guideThickGlob = block.inputValue(aDisplayGuideThick).asFloat(); + doHairXparencyGlob = block.inputValue(displayHiarDoXparency).asBool(); + nativeIlluminationGlob = block.inputValue(aNativeIllumination).asBool(); + renderQualityGlob = block.inputValue(aRenderQuality).asShort(); + shaveShadowMatteGlob = block.inputValue(aShadowMatte).asBool(); + shaveSceneObjectsGlob = + block.inputValue(aHairOcclusionObjects).asString(); + shaveShadowDensityGlob = block.inputValue(aShadowDensity).asFloat(); + shaveUseGeomShadowsGlob = block.inputValue(aUseGeomForShadows).asBool(); + shaveShadowHairRatioGlob= block.inputValue(aShadowHairRatio).asFloat(); + tmpDirGlob = block.inputValue(aStatFileDir).asString(); + useAllLightsGlob = block.inputValue(aUseAllLights).asBool(); +#if DRA_OPTIONAL + useDRAGlob = block.inputValue(aUseDRA).asBool(); +#endif + visibleInReflectionsGlob= block.inputValue(aVisibleInReflections).asBool(); + + voxelResolutionGlob = (int)block.inputValue(aVoxelResolution).asShort(); + + enableInstanceGeometryGlob = + block.inputValue(aEnableInstanceGeometry).asBool(); + + // + // Internal Attributes + // ------------------- + // + // Internal attributes need special handling. A value which was set + // via 'setAttr' or an MPlug will not appear in the datablock, which + // means that we need to get it from our internal storage. However, if + // there is a connection to the attr and it is dirty, the new value + // won't be in our internal storage yet, so we need to build its data + // handle to force the update. + // + // Put it all together and it means that we must first build the + // attribute's handle, to ensure that dirty connections are evaluated, + // but then we must ignore the handle and retrieve the value from our + // internal storage. + // + MDataHandle hdl = block.inputValue(aNormalRenderMode); + normalRenderModeGlob = mNormalRenderMode; + + // + // For the following internal attrs, the global var *is* our internal + // storage, so there's no value to copy. + // + hdl = block.inputValue(aMaxThreads); + hdl = block.inputValue(aThreadPerProcessor); +#if !DRA_OPTIONAL + hdl = block.inputValue(aUseDRA); +#endif + + return MS::kSuccess; +} + + +MStatus shaveGlobals::getGlobals(shaveGlobals::Globals& g) +{ + // + // Get the shave globals node + // + MObject shaveGlobalsNode = getDefaultNode(); + + if (shaveGlobalsNode.isNull()) return MS::kFailure; + + // + // This method gets called fairly often (e.g. 4 times during render + // init & cleanup, plus 4 per frame rendered) so let's make sure it's + // fast by grabbing a datablock and getting the values from that. + // + MFnDependencyNode nodeFn(shaveGlobalsNode); + shaveGlobals* nodePtr = (shaveGlobals*)nodeFn.userNode(); + + if (!nodePtr) return MS::kFailure; + + return nodePtr->getGlobalsInternal(g); +} + + +MStatus shaveGlobals::getGlobalsInternal(shaveGlobals::Globals& g) +{ + MDataBlock block = forceCache(); + + glisntGlob = block.inputValue(aGlInst).asInt(); + //if(gi != glisntGlob) + //{ + // glisntGlob = gi; + + /* MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + + float trigger; + MPlug triggerPlug = nodeFn.findPlug("trigger"); + triggerPlug.getValue(trigger); + triggerPlug.setValue(trigger+1.0f); + } + }*/ + //} + + g.composite2d = block.inputValue(aComposite2d).asBool(); + g.doCompositing = block.inputValue(aDoCompositing).asBool(); + g.doHairShadows = block.inputValue(aDoHairShadows).asBool(); + g.enableInstanceGeometry= + block.inputValue(aEnableInstanceGeometry).asBool(); + g.gravity = block.inputValue(aGravity).asFloat(); + g.glinst = block.inputValue(aGlInst).asInt(); + g.hairFileNamePrefix = block.inputValue(aHairFilename).asString(); + g.keepHairRenderFiles = block.inputValue(aKeepHairRenderFiles).asBool(); + g.liveMode = block.inputValue(aLiveMode).asBool(); + g.occlusionObjects = + block.inputValue(aHairOcclusionObjects).asString(); + g.renderQuality = block.inputValue(aRenderQuality).asShort(); + g.shadowDensity = block.inputValue(aShadowDensity).asFloat(); + g.shadowMatte = block.inputValue(aShadowMatte).asBool(); + g.shadowMatteIncludeBackfacing = + block.inputValue(aShadowMatteIncludeBackfacing).asBool(); + g.tileMemoryLimit = + (unsigned int)block.inputValue(aTileMemoryLimit).asInt(); + g.transparencyDepth = + (unsigned int)block.inputValue(aTransparencyDepth).asInt(); + g.useGeomShadows = block.inputValue(aUseGeomForShadows).asBool(); + g.shadowHairRatio = block.inputValue(aShadowHairRatio).asFloat(); + g.statFileDir = block.inputValue(aStatFileDir).asString(); + g.useAllLights = block.inputValue(aUseAllLights).asBool(); +#if DRA_OPTIONAL + g.useDRA = block.inputValue(aUseDRA).asBool(); +#endif + g.useNativeIllumination = block.inputValue(aNativeIllumination).asBool(); + g.verbose = block.inputValue(aVerbose).asBool(); + g.visibleInReflections = block.inputValue(aVisibleInReflections).asBool(); + g.voxelResolution = (int)block.inputValue(aVoxelResolution).asShort(); + g.voxelScaling = block.inputValue(aVoxelScaling).asFloat(); + + g.cameraVis = block.inputValue(aPrimCameraVis).asBool(); + g.lightVis = block.inputValue(aPrimLightVis).asBool(); + g.giVis = block.inputValue(aPrimGiVis).asBool(); + + // RIB Attributes + g.rib.binary = block.inputValue(aRibBinary).asBool(); + g.rib.compress = block.inputValue(aRibCompress).asBool(); + g.rib.filePreamble = block.inputValue(aRibStuff).asString(); + g.rib.libOverride = block.inputValue(aRibLibOverride).asString(); + g.rib.keepRibFiles = block.inputValue(aRibKeepRibFiles).asBool(); + g.rib.normals = block.inputValue(aRibNormals).asBool(); + g.rib.opacities = block.inputValue(aRibOpacities).asBool(); + g.rib.primitiveType = block.inputValue(aRibPrimitiveType).asShort(); + g.rib.rootPositions = block.inputValue(aRibRootPositions).asBool(); + g.rib.rootTipColors = block.inputValue(aRibRootTipColors).asBool(); + g.rib.timeUnits = + (RibTimeUnits)block.inputValue(aRibTimeUnits).asShort(); + g.rib.uvCoords = block.inputValue(aRibUVs).asBool(); + g.rib.uvSet = block.inputValue(aRibUVSet).asString(); + g.rib.vertexColors = block.inputValue(aRibVertexColors).asBool(); + g.rib.wCoords = block.inputValue(aRibWCoords).asBool(); + + g.rib.blur.enable = block.inputValue(aRibBlurEnable).asBool(); + g.rib.blur.inheritSettings = + block.inputValue(aRibBlurInheritSettings).asShort(); + g.rib.blur.restoreFrame = block.inputValue(aRibBlurRestoreFrame).asBool(); + g.rib.blur.shutterCloseOffset = + block.inputValue(aRibBlurShutterCloseOffset).asFloat(); + g.rib.blur.shutterOpenOffset = + block.inputValue(aRibBlurShutterOpenOffset).asFloat(); + g.rib.blur.timeBasis = block.inputValue(aRibBlurTimeBasis).asShort(); + + g.rib.voxels.enable = block.inputValue(aRibVoxelEnable).asBool(); + g.rib.voxels.fullPaths = block.inputValue(aRibVoxelFullPaths).asBool(); + g.rib.voxels.resolution = block.inputValue(aRibVoxelResolution).asShort(); + + // Preferences Stored In optionVars + bool exists = false; + int iValue; + + iValue = MGlobal::optionVarIntValue("shave_fastBrush", &exists); + if (!exists) iValue = 0; + g.fastBrush = (iValue != 0); + + // + // Internal Attributes + // ------------------- + // + // Internal attributes need special handling. A value which was set + // via 'setAttr' or an MPlug will not appear in datablock, which means + // that we need to get it from our internal storage. However, if there + // is a connection to the attr and it is dirty, the new value won't be + // in our internal storage yet, so we need to build its data handle to + // force the update. + // + // Put it all together and it means that we must first build the + // attribute's handle, to ensure that dirty connections are evaluated, + // but then we must ignore the handle and retrieve the value from our + // internal storage. + // + MDataHandle hdl = block.inputValue(aNormalRenderMode); + g.normalRenderMode = mNormalRenderMode; + + hdl = block.inputValue(aMaxThreads); + g.maxThreads = maxThreadsGlob; + + hdl = block.inputValue(aThreadPerProcessor); + g.threadPerProcessor = threadPerProcessorGlob; + +#if !DRA_OPTIONAL + hdl = block.inputValue(aUseDRA); + g.useDRA = true; +#endif + + return MS::kSuccess; +} + + +MString getSceneName() +{ + MString sceneName; + MStringArray splitPath; + MGlobal::executeCommand ("file -q -sn", sceneName); + if(sceneName.length() != 0) + { + splitPath.clear(); + sceneName.split('/',splitPath); + sceneName = splitPath[splitPath.length()-1]; + splitPath.clear(); + sceneName.split('.',splitPath); + sceneName = splitPath[0]; + } + else + sceneName = "untitled"; + return sceneName; +} + + +short buildh(char* id) +{ + short h = 1; + short i; + for(i = 0; i < (short)(strlen(id)); i++) + { + h = h + (short)id[i]; + } + return h; +} + + +short buildk(short val) +{ + short h; + h = (short)(2.5*val)-(short)(4.5*val); + return h; +} + + +#if MAYA_API_VERSION >= 20180000 +bool shaveGlobals::getInternalValue(const MPlug &plug, MDataHandle &hdl) +#else +bool shaveGlobals::getInternalValueInContext( + const MPlug& plug, MDataHandle& hdl, MDGContext& +) +#endif +{ + if (plug == aNormalRenderMode) + hdl.set((short)mNormalRenderMode); + else if (plug == aNodeVersion) + { + // + // If the version is zero then it has not yet been explicitly set, + // so we must determine an appropriate value. + // + if (mNodeVersion == 0) + { + // + // If we're in the middle of file loading, we might still be + // awaiting the set command so we should continue to return 0. + // Otherwise we assume that this is a newly-created node and + // return the latest version. + // + // I used to do this in the postConstructor() but that was + // causing random crashes within Maya, so we do it here now, + // instead. + // + if (!shaveUtil::isLoadingFile()) mNodeVersion = kNodeVersion; + } + + hdl.set(mNodeVersion); + } + else if (plug == aHideHair) + hdl.set(mHideHair); + else if (plug == aMaxThreads) + hdl.set(maxThreadsGlob); + else if (plug == aThreadPerProcessor) + hdl.set(threadPerProcessorGlob); +#if !DRA_OPTIONAL + else if (plug == aUseDRA) + hdl.set(true); +#endif + else + return false; + + return true; +} + + +#if MAYA_API_VERSION >= 20180000 +bool shaveGlobals::setInternalValue(const MPlug &plug, const MDataHandle &hdl) +#else +bool shaveGlobals::setInternalValueInContext( + const MPlug& plug, const MDataHandle& hdl, MDGContext& +) +#endif +{ + if (plug == aNormalRenderMode) + { + mNormalRenderMode = (shaveConstant::RenderMode)hdl.asShort(); + } + else if (plug == aNodeVersion) + { + mNodeVersion = hdl.asInt(); + } + else if (plug == aHideHair) + { + bool val = hdl.asBool(); + + if (val != mHideHair) + { + mHideHair = val; + hideHair(); + } + } + else if (plug == aMaxThreads) + { + maxThreadsGlob = hdl.asInt(); + + if (!threadPerProcessorGlob) + SHAVEset_max_threads(maxThreadsGlob); + } + else if (plug == aThreadPerProcessor) + { + threadPerProcessorGlob = hdl.asBool(); + + if (threadPerProcessorGlob) + SHAVEset_max_threads(-1); + else + SHAVEset_max_threads(maxThreadsGlob); + } +#if !DRA_OPTIONAL + else if (plug == aUseDRA) + { + } +#endif + else + return false; + + return true; +} + + MStatus shaveGlobals::setDependentsDirty( + const MPlug& dirty, + MPlugArray& affected + ) + { + if(dirty == aDisplayGuides || + dirty == aDisplayGuideThick) + { + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + + shape->dirtyGuides(); + + MPlug triggerPlug = nodeFn.findPlug("trigger"); + float value; + triggerPlug.getValue(value); + triggerPlug.setValue(value+1.0f); + MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + } + //M3dView::active3dView().refresh(); + + } + if(dirty == displayDoFallbackAttr) + { + //bool e; + //dirty.getValue(e); + //doFallbackGlob = !e; + doFallbackGlob = !doFallbackGlob; + MGlobal::displayInfo(MString("Shave display fallback: ") + (doFallbackGlob ? "enabled":"disabled")); + + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + + shape->dirties.DIRTY_DISPLAY_COUNT = 1; + shape->dirties.DIRTY_TEXTURE = 1; //need to update cache as well + + MPlug triggerPlug = nodeFn.findPlug("trigger"); + float value; + triggerPlug.getValue(value); + triggerPlug.setValue(value+1.0f); +// MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + + } + } + + } + if(dirty == aHideHair) + { + //hideHairGlob = !hideHairGlob; + + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + + shape->dirties.DIRTY_DISPLAY_COUNT = 1; + shape->dirties.DIRTY_TEXTURE = 1; //need to update cache as well + + MPlug triggerPlug = nodeFn.findPlug("trigger"); + float value; + triggerPlug.getValue(value); + triggerPlug.setValue(value+1.0f); +// MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + + } + } + + } + if(dirty == aDisplaySegmentLimit) + { + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + + shape->dirties.DIRTY_DISPLAYSEGS = 1; + + MPlug triggerPlug = nodeFn.findPlug("trigger"); + float value; + triggerPlug.getValue(value); + triggerPlug.setValue(value+1.0f); + +// MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + } + } + if(dirty == aLiveMode || + dirty == displayFallbackRatioAttr || + dirty == displayHairRatioAttr || + dirty == aGlInst || + dirty == displayHiarDoXparency) + { + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + + shape->dirties.DIRTY_DISPLAY_COUNT = 1; + shape->dirties.DIRTY_TEXTURE = 1; //need to update cache as well + + + //MPlug instPlug = nodeFn.findPlug("instancingStatus"); + //if(!instPlug.isNull()) + { + //bool s; + //instPlug.getValue(s); + //if(s) + { + MPlug triggerPlug = nodeFn.findPlug("trigger"); + float value; + triggerPlug.getValue(value); + triggerPlug.setValue(value+1.0f); + + //M3dView::active3dView().refresh(true,true); + + //MPlug countPlug = nodeFn.findPlug("hairCount"); + //int c; + //countPlug.getValue(c); + //countPlug.setValue(c); + } + } +// MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + } + //M3dView::active3dView().refresh(); + } + return MPxNode::setDependentsDirty(dirty,affected); + } + + //works for legacy +void shaveGlobals::hideHair() +{ + MObjectArray hairNodes; + unsigned i; + + shaveUtil::getShaveNodes(hairNodes); + + for (i = 0; i < hairNodes.length(); i++) + { + MPlug dspyModePlug(hairNodes[i], shaveHairShape::dspyMode); + short displayMode; + + dspyModePlug.getValue(displayMode); + + if (mHideHair && (displayMode != shaveHairShape::kHairDisplayNone)) + dspyModePlug.setValue(shaveHairShape::kHairDisplayNone); + else if (!mHideHair && (displayMode == shaveHairShape::kHairDisplayNone)) + { + MFnDependencyNode dFn(hairNodes[i]); + shaveHairShape* sh = (shaveHairShape*)dFn.userNode(); + if(sh->getInstancingStatus()) + dspyModePlug.setValue(shaveHairShape::kHairDisplayGeom); + else + dspyModePlug.setValue(shaveHairShape::kHairDisplayHair); + } + // Hide the display node as well, otherwise the hair node will + // continue to evaluate because the display mesh will keep + // requesting mesh updates. + // + // %%% shaveHairShape should really do this in response to changes + // its display mode. + MPlugArray conns; + MPlug displayNodePlug(hairNodes[i], shaveHairShape::displayNodeAttr); + + displayNodePlug.connectedTo(conns, true, false); + + if ((conns.length() > 0) && conns[0].node().hasFn(MFn::kDagNode)) + { + MPlug displayNodeVisibilityPlug( + conns[0].node(), MPxSurfaceShape::visibility + ); + bool visibility; + displayNodeVisibilityPlug.getValue(visibility); + + if (visibility == mHideHair) + { + visibility = !visibility; + displayNodeVisibilityPlug.setValue(visibility); + } + } + } +} diff --git a/mayaPlug/shaveGlobals.h b/mayaPlug/shaveGlobals.h new file mode 100644 index 0000000..8a1cfb6 --- /dev/null +++ b/mayaPlug/shaveGlobals.h @@ -0,0 +1,355 @@ +#ifndef _SHAVEGLOBALS_H_ +#define _SHAVEGLOBALS_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagPathArray.h> +#include <maya/MPxNode.h> + +#define DRA_OPTIONAL 0 + +#include "shaveConstant.h" +#include "shaveSDK.h" + +#define _FILEVERSION 1 +#define kStringLength 512 + +#if MAYA_API_VERSION < 20180000 +class MDataBlock; +class MDataHandle; +class MDGContext; +class MDGModifier; +class MPlug; +#endif + +class shaveGlobals : public MPxNode +{ +public: + typedef enum + { + kFrames, + kSeconds + } RibTimeUnits; + + enum ShadowQuality + { + kLowShadowQuality = 1, + kMediumShadowQuality = 2, + kHighShadowQuality = 3 + }; + + typedef struct + { + bool composite2d; + bool doCompositing; + bool doHairShadows; + bool enableInstanceGeometry; + bool fastBrush; + float gravity; + int glinst; + MString hairFileNamePrefix; + bool keepHairRenderFiles; + bool liveMode; + int maxThreads; + shaveConstant::RenderMode normalRenderMode; + MString occlusionObjects; + short renderQuality; + float shadowDensity; + bool shadowMatte; + bool shadowMatteIncludeBackfacing; + MString statFileDir; + bool threadPerProcessor; + unsigned int tileMemoryLimit; + unsigned int transparencyDepth; + bool useAllLights; + bool useDeepShadows; + bool useDRA; + bool useNativeIllumination; + bool verbose; + bool visibleInReflections; + int voxelResolution; + float voxelScaling; + + struct + { + bool binary; + bool compress; + MString filePreamble; + bool keepRibFiles; + MString libOverride; + bool normals; + bool opacities; + short primitiveType; + bool rootPositions; + bool rootTipColors; + RibTimeUnits timeUnits; + bool uvCoords; + MString uvSet; + bool vertexColors; + bool wCoords; + + struct + { + bool enable; + short inheritSettings; + bool restoreFrame; + float shutterCloseOffset; + float shutterOpenOffset; + short timeBasis; + } blur; + + struct + { + bool enable; + bool fullPaths; + short resolution; + } voxels; + } rib; + + bool cameraVis; + bool lightVis; + bool giVis; + + // + // Semi-Deprecated + // + float shadowHairRatio; + bool useGeomShadows; + } Globals; + + shaveGlobals(); + virtual ~shaveGlobals() {}; + static void* creator(); + + bool deleteMe(MDGModifier& dgMod); + static MStatus initialize(); + void* getAddress(); + static MObject getDefaultNode(); + static MStatus getGlobals(); + static MStatus getGlobals(Globals& globals); + int getNodeVersion() const { return mNodeVersion; } + +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + MStatus shouldSave(const MPlug& plug, bool& saveIt); + +#if MAYA_API_VERSION >= 2018000 + virtual bool getInternalValue( + const MPlug &plug, + MDataHandle &hdl + ); + + virtual bool setInternalValue( + const MPlug &plug, + const MDataHandle &hdl + ); +#else + virtual bool getInternalValueInContext( + const MPlug& plug, + MDataHandle& hdl, + MDGContext& context + ); + + virtual bool setInternalValueInContext( + const MPlug& plug, + const MDataHandle& hdl, + MDGContext& context + ); +#endif + + virtual MStatus setDependentsDirty( + const MPlug& dirty, + MPlugArray& affected + ); + + static MTypeId id; + static const MString nodeTypeName; + static const MString defaultNodeName; + static const int kNodeVersion; + + // + // Attributes + // + static MObject aAddress; + static MObject aComposite2d; + static MObject aDisplayGuides; + static MObject aDisplayGuideThick; + static MObject aDisplaySegmentLimit; + static MObject aDoCompositing; + static MObject aDoHairShadows; + static MObject aEnableInstanceGeometry; + static MObject aGravity; + static MObject aGlInst; + static MObject aHairFilename; + static MObject aHairOcclusionObjects; + static MObject aHideHair; + static MObject aInfo1; + static MObject aInfo2; + static MObject aKeepHairRenderFiles; + static MObject aLiveMode; + static MObject aLiveModeRecord; + static MObject aLiveModePlayback; + static MObject aMaxThreads; + static MObject aNativeIllumination; + static MObject aNodeVersion; + static MObject aNormalRenderMode; + static MObject aPrimCameraVis; + static MObject aPrimGiVis; + static MObject aPrimLightVis; + static MObject aRenderQuality; + static MObject aRibBinary; + static MObject aRibBlurEnable; + static MObject aRibBlurInheritSettings; + static MObject aRibBlurRestoreFrame; + static MObject aRibBlurShutterCloseOffset; + static MObject aRibBlurShutterOpenOffset; + static MObject aRibBlurTimeBasis; + static MObject aRibCompress; + static MObject aRibKeepRibFiles; + static MObject aRibLibOverride; + static MObject aRibNormals; + static MObject aRibOpacities; + static MObject aRibPrimitiveType; + static MObject aRibRootPositions; + static MObject aRibRootTipColors; + static MObject aRibStuff; + static MObject aRibTimeUnits; + static MObject aRibVertexColors; + static MObject aRibUVs; + static MObject aRibUVSet; + static MObject aRibWCoords; + static MObject aRibVoxelEnable; + static MObject aRibVoxelFullPaths; + static MObject aRibVoxelResolution; + static MObject aShadowMatte; + static MObject aShadowMatteIncludeBackfacing; + static MObject aShadowDensity; + static MObject aStatFileDir; + static MObject aThreadPerProcessor; + static MObject aTileMemoryLimit; + static MObject aTransparencyDepth; + static MObject aUseAllLights; + static MObject aUseDRA; + static MObject aVerbose; + static MObject aVisibleInReflections; + static MObject aVoxelResolution; + static MObject aVoxelScaling; + + //default dra file name, + //artist can edit it to resolve confilt when rendering + //multiple scenes on same machine -- dub|23-09-2010 + static MObject aVrayDraFile; + + static MObject displayHairRatioAttr; + static MObject displayHiarDoXparency; + static MObject displayDoFallbackAttr; + static MObject displayFallbackRatioAttr; + + + // + // Semi-Deprecated Attributes + // + // (Functionality present but hidden in case we change our minds.) + // + static MObject aShadowHairRatio; + static MObject aUseGeomForShadows; + + + // + // Fully Deprecated Attributes + // + static MObject aActiveShaveNode; + static MObject aCurrentShaveNode; + static MObject aDisplayDensity; + static MObject aDisplayHairDoSsao; + static MObject aFastBrush; + static MObject aGenerateHairGeometry; + static MObject aInstanceRenderMode; + static MObject aMREnableIrradiance; + static MObject aMRIrradiance; + static MObject aMROcclusionCollection; + static MObject aMRSafeMode; + static MObject aMRSatelliteSupport; + static MObject aMRUseRenderGlobalsCallbacks; + static MObject aPrmanCurvesSupported; + static MObject aRibBlurUseMayaDefaults; + static MObject aRibUseRenderGlobalsCallbacks; + static MObject aShadowAntiAlias; + static MObject aShadowQuality; + static MObject aShaveLightList; + static MObject aShavePrmanMode; + static MObject aShaveShadowFuzz; + static MObject aShaveShadowRes; + static MObject aShaveShadowSamples; + static MObject aTileCount; + static MObject aTrigger; + static MObject aUseDeepShadows; + +protected: + static MObject getDefaultNodeInternal(); + MStatus getGlobalsInternal(); + MStatus getGlobalsInternal(Globals& globals); + void hideHair(); + + bool mHideHair; + int mNodeVersion; + shaveConstant::RenderMode mNormalRenderMode; +}; + + +extern bool doHairShadowsGlob; +extern bool doShaveCompsGlob; +extern bool doShaveComp2dGlob; +extern bool enableInstanceGeometryGlob; +extern float gravityGlob; +extern int glisntGlob; +extern MString hairFileNameGlob; +extern bool keepHairRenderFilesGlob; +extern bool nativeIlluminationGlob; +extern bool liveModeGlob; +extern bool displayGuidesGlob; +extern float guideThickGlob; +extern bool doFallbackGlob; +//extern bool hideHairGlob; +extern float displayRatioGlob; +extern float fallbackRatioGlob; +extern float displaySegmentLimitGlob; +extern bool doHairXparencyGlob; +extern bool liveModeRecord; +extern bool liveModePlayback; +extern int maxThreadsGlob; +extern short renderQualityGlob; +extern bool shadowRender; +extern MString shaveSceneObjectsGlob; +extern float shaveShadowDensityGlob; +extern bool shaveShadowMatteGlob; +extern MString tmpDirGlob; +extern bool threadPerProcessorGlob; +extern bool useAllLightsGlob; +extern bool useDeepShadowsGlob; +extern bool useDRAGlob; +extern bool visibleInReflectionsGlob; +extern int voxelResolutionGlob; + +// +// Semi-Deprecated +// +extern float shaveShadowHairRatioGlob; +extern bool shaveUseGeomShadowsGlob; + +// +// These is the raw render mode. Normally you should not use this +// directly but use the mode returned by a call to the getRenderMode() +// method of an instance of shaveRenderer. +// +extern shaveConstant::RenderMode normalRenderModeGlob; + + +short buildh(char*); +short buildk(short); +#endif + diff --git a/mayaPlug/shaveHairGeomIt.cpp b/mayaPlug/shaveHairGeomIt.cpp new file mode 100644 index 0000000..8ce71a0 --- /dev/null +++ b/mayaPlug/shaveHairGeomIt.cpp @@ -0,0 +1,247 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagPath.h> +#include <maya/MFnComponent.h> +#include <maya/MFnDoubleIndexedComponent.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MMatrix.h> +#include <maya/MObjectArray.h> +#include <maya/MPoint.h> +#include <maya/MPxGeometryIterator.h> + +#include "shaveHairGeomIt.h" +#include "shaveHairShape.h" +#include "shaveSDKTYPES.h" + +static const int mid = SHAVE_VERTS_PER_GUIDE / 2; + + +shaveHairGeomIt::shaveHairGeomIt(void* geom, MObjectArray& components) +: MPxGeometryIterator(geom, components) +, mComponents(components) +, mHairShape((shaveHairShape*)geom) +{ + mWholeGeom = (components.length() == 0); + reset(); +} + +shaveHairGeomIt::shaveHairGeomIt(void* geom, MObject& components) +: MPxGeometryIterator(geom, components) +, mHairShape((shaveHairShape*)geom) +{ + mWholeGeom = components.isNull(); + + if (!mWholeGeom) mComponents.append(components); + + reset(); +} + + +void shaveHairGeomIt::component(MObject& comp) +{ + if (mIsDone) + comp = MObject::kNullObj; + else + { + MFnDoubleIndexedComponent compFn; + + comp = compFn.create(shaveHairShape::kShaveGuideVertComponent); + compFn.addElement(mCurGuide, mCurVert); + } +} + + +int shaveHairGeomIt::index() const +{ + return mIterationIndex; +} + + +int shaveHairGeomIt::indexUnsimplified() const +{ + return mCurGuide * SHAVE_VERTS_PER_GUIDE + mCurVert; +} + + +int shaveHairGeomIt::iteratorCount() const +{ + return mNumGuides * SHAVE_VERTS_PER_GUIDE; +} + + +void shaveHairGeomIt::next() +{ + if (!mIsDone) + { + if (mWholeGeom) + { + if (++mCurVert >= SHAVE_VERTS_PER_GUIDE) + { + mCurVert = 0; + mIsDone = (++mCurGuide >= mNumGuides); + } + } + else + { + MFnComponent compFn(mComponents[mObjIndex]); + + if (++mElementIndex >= compFn.elementCount()) + { + mElementIndex = 0; + + // + // I don't know if we need to worry about the + // possibility of empty component objects, but if so, + // this should take care of them. + // + while (++mObjIndex < mComponents.length()) + { + compFn.setObject(mComponents[mObjIndex]); + + if (compFn.elementCount() > 0) break; + } + + mIsDone = (mObjIndex >= mComponents.length()); + } + + if (!mIsDone) + { + if (mComponents[mObjIndex].hasFn(MFn::kSingleIndexedComponent)) + { + MFnSingleIndexedComponent guideComp(mComponents[mObjIndex]); + + mCurGuide = guideComp.element(mElementIndex); + mCurVert = mid; + } + else + { + MFnDoubleIndexedComponent vertComp(mComponents[mObjIndex]); + + vertComp.getElement(mElementIndex, mCurGuide, mCurVert); + } + } + } + + mIterationIndex++; + } +} + + +MPoint shaveHairGeomIt::point() const +{ + MPoint p; + + if (!mIsDone && mHairShape) + { + // + // Load the hairShape into the Shave engine. + // + mHairShape->makeCurrent(); + + SOFTGUIDE guide; + + if (SHAVEfetch_guide(mCurGuide, &guide) != -1) + { + VERT& vert = guide.guide[mCurVert]; + + p.x = (double)vert.x; + p.y = (double)vert.y; + p.z = (double)vert.z; + + // + // Transform the point into local space. + // + MDagPath shapePath; + + MDagPath::getAPathTo(mHairShape->thisMObject(), shapePath); + + p *= shapePath.inclusiveMatrixInverse(); + } + } + + return p; +} + + +void shaveHairGeomIt::reset() +{ + MPxGeometryIterator::reset(); + setCurrentPoint(0); + + mIterationIndex = 0; + + if (mHairShape) + { + mNumGuides = (int)mHairShape->getGuideCount(); + setMaxPoints(mNumGuides * SHAVE_VERTS_PER_GUIDE); + } + else + { + setMaxPoints(0); + mNumGuides = 0; + } + + mCurGuide = 0; + mCurVert = 0; + + if (mWholeGeom) + { + mIsDone = (mNumGuides == 0); + } + else + { + // + // I don't know if we have to worry about getting empty component + // objects, but let's check, just to be safe. + // + for (mObjIndex = 0; mObjIndex < mComponents.length(); mObjIndex++) + { + if (mComponents[mObjIndex].hasFn(MFn::kSingleIndexedComponent)) + { + MFnSingleIndexedComponent compFn(mComponents[mObjIndex]); + + if (compFn.elementCount() > 0) + { + mCurGuide = compFn.element(0); + mCurVert = mid; + break; + } + } + else if (mComponents[mObjIndex].hasFn(MFn::kDoubleIndexedComponent)) + { + MFnDoubleIndexedComponent compFn(mComponents[mObjIndex]); + + if (compFn.elementCount() > 0) + { + compFn.getElement(0, mCurGuide, mCurVert); + break; + } + } + } + + mIsDone = (mObjIndex >= mComponents.length()); + mElementIndex = 0; + } +} + + +void shaveHairGeomIt::setPoint(const MPoint& newPoint) const +{ +cout << "shaveHairGeomIt::setPoint" << endl; + // %%% not yet implemented +} + + +int shaveHairGeomIt::setPointGetNext(MPoint& pointInOut) +{ + // %%% 'set' portion not yet implemented + +cout << "shaveHairGeomIt::setPointGetNext" << endl; + next(); + pointInOut = point(); + + return index(); +} + diff --git a/mayaPlug/shaveHairGeomIt.h b/mayaPlug/shaveHairGeomIt.h new file mode 100644 index 0000000..cba6f67 --- /dev/null +++ b/mayaPlug/shaveHairGeomIt.h @@ -0,0 +1,51 @@ +#ifndef shaveHairGeomIt_h +#define shaveHairGeomIt_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxGeometryIterator.h> + +#if MAYA_API_VERSION < 20180000 +class MObjectArray; +class MPoint; +#endif + +class shaveHairShape; + + +class shaveHairGeomIt : public MPxGeometryIterator +{ +public: + shaveHairGeomIt(void* geom, MObjectArray& components); + shaveHairGeomIt(void* geom, MObject& components); + + virtual void component(MObject& comp); + virtual bool hasNormals() const { return false; } + virtual bool hasPoints() const { return true; } + virtual bool isDone() const { return mIsDone; } + virtual int index() const; + virtual int indexUnsimplified() const; + virtual int iteratorCount() const; + virtual void next(); + virtual MPoint point() const; + virtual void reset(); + virtual void setPoint(const MPoint& newPoint) const; + virtual int setPointGetNext(MPoint& point); + + +protected: + MObjectArray mComponents; + int mCurGuide; + int mCurVert; + int mElementIndex; + bool mIsDone; + shaveHairShape* mHairShape; + int mIterationIndex; + int mNumGuides; + unsigned mObjIndex; + bool mWholeGeom; +}; + +#endif diff --git a/mayaPlug/shaveHairShape.cpp b/mayaPlug/shaveHairShape.cpp new file mode 100644 index 0000000..54988d3 --- /dev/null +++ b/mayaPlug/shaveHairShape.cpp @@ -0,0 +1,8379 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +int dprint=0; // debug printfs + +//Qt headers must be included before any others !!! +# include <QtCore/QEvent> +# include <QtCore/QElapsedTimer> +# include <QtGui/QMouseEvent> +# include <QtGui/QTabletEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include <map> +#include <math.h> +#include <stdlib.h> +#include <time.h> +#include <vector> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "shaveIO.h" + +#include <maya/M3dView.h> +#include <maya/MAttributeIndex.h> +#include <maya/MAttributeSpec.h> +#include <maya/MAttributeSpecArray.h> +#include <maya/MDagPath.h> +#include <maya/MDGContext.h> +#include <maya/MDGModifier.h> +#include <maya/MDoubleArray.h> +#include <maya/MFileIO.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFnAttribute.h> +#include <maya/MFnCamera.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnDoubleIndexedComponent.h> +#include <maya/MFnField.h> +#include <maya/MFnLight.h> +#include <maya/MFnMeshData.h> +#include <maya/MFnNonExtendedLight.h> +#include <maya/MFnNurbsCurve.h> +#include <maya/MFnNurbsCurveData.h> +#include <maya/MFnPluginData.h> +#include <maya/MFnSet.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MGlobal.h> +#include <maya/MIntArray.h> +#include <maya/MItDependencyGraph.h> +#include <maya/MItSelectionList.h> +#include <maya/MMatrix.h> +#include <maya/MModelMessage.h> +#include <maya/MObjectArray.h> +#include <maya/MPlug.h> +#include <maya/MPoint.h> +#include <maya/MPointArray.h> +#include <maya/MPxObjectSet.h> +#include <maya/MPxTransform.h> +#include <maya/MTime.h> +#include <maya/MTransformationMatrix.h> +#include <maya/MRenderUtil.h> +#include <maya/MImage.h> +#include <maya/MAnimControl.h> +#include <maya/MFileObject.h> + +#ifdef _WIN32 +# include <process.h> +# include <stdio.h> +# define unlink _unlink +#else +# ifdef OSMac_ +# include <sys/types.h> +# include <sys/sysctl.h> +# endif +# include <unistd.h> +#endif + +#include "shaveCursorCtx.h" +#include "shaveBlindData.h" +#include "shaveConstant.h" +#include "shaveDebug.h" +#include "shaveError.h" +#include "shaveGlobals.h" +#include "shaveHairGeomIt.h" +#include "shaveHairShape.h" +#include "shaveMaya.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveSDK.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" +#include "shaveHairUI.h" +#include "shaveStyleCmd.h" +#ifdef USE_PROCEDURAL +# include "shaveProcedural.h" +#endif + +#include <maya/MHWGeometryUtilities.h> + +#if defined(OSMac_) && !defined(OSMac_MachO_) +#include "shaveMacCarbon.h" +#endif + + +#ifdef REUSABLE_THREADS + +#ifdef _WIN32 +LEvent::LEvent( bool signalled, LPSECURITY_ATTRIBUTES sa) +#else +LEvent::LEvent( bool signalled) +#endif +{ +#ifdef _WIN32 + handle = CreateEvent(sa,TRUE,signalled ? TRUE : FALSE,NULL); + if(!handle) + printf("CreateEvent failed.\n"); +#else + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); + triggered = false; + if(signalled) + Set(); +#endif +} +LEvent::~LEvent() +{ +#ifdef _WIN32 + if(handle) + CloseHandle(handle); +#else + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); + triggered = false; +#endif +} +void LEvent::Set() +{ +#ifdef _WIN32 + if(handle) + { + if(!SetEvent(handle)) + { + printf("SetEvent failed.\n"); + return; + } + } +#else + pthread_mutex_lock(&mutex); + triggered = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); +#endif +} +void LEvent::Reset() +{ +#ifdef _WIN32 + if(handle) + { + if(!ResetEvent(handle)) + { + printf("ResetEvent failed.\n"); + return; + } + } +#else + pthread_mutex_lock(&mutex); + triggered = false; + pthread_mutex_unlock(&mutex); +#endif +} +void LEvent::Wait() +{ +#ifdef _WIN32 + if(handle) + { + WaitForSingleObject(handle,INFINITE); + } +#else + pthread_mutex_lock(&mutex); + while (!triggered) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); +#endif +} + +LThread::LThread(THPROC* pThreadFunc, + void* pThreadFuncParameter, + unsigned int exeFlags, + unsigned int sSize, + bool inheritable) +{ + _state() = eAvilable; +#ifdef _WIN32 + _sa() = (LPSECURITY_ATTRIBUTES)HeapAlloc(GetProcessHeap(),0,sizeof(SECURITY_ATTRIBUTES)); + _sa()->nLength = sizeof(SECURITY_ATTRIBUTES); + _sa()->lpSecurityDescriptor = NULL; + _sa()->bInheritHandle = inheritable; + + _exitCode() = 0xFFFFFFFF; +#endif + _thFn() = pThreadFunc; + _thArg() = pThreadFuncParameter; + + _exeFlags() = exeFlags; + _stackSize() = sSize; + + _th() = 0; + _estart() = NULL; + _efinish() = NULL; + +} + + +void LThread::Create() +{ + //run thread but hit should hit event in non signeled stat + //and get into wait state untill ::Execute is called + if( !th() ) + { +#ifdef WIN32 + _estart() = new LEvent(false,sa()); + _efinish() = new LEvent(true,sa()); + if ( (_th() = CreateThread(sa(),stackSize(),thFnEx,this,exeFlags(),&_thId())) == NULL) + { + //error + printf("CreateThread failed.\n"); + return; + } + if(!SetThreadPriority(th(),THREAD_PRIORITY_HIGHEST))//does not change much + { + printf("SetThreadPriority failed.\n"); + } +#else + _estart() = new LEvent(false); + _efinish() = new LEvent(true); + if ( pthread_create( &_th(), NULL, thFnEx, this ) != 0) + { + printf("pthread_create failed.\n"); + return; + } +#endif + } +} + +void LThread::Execute(THPROC* pThreadFunc, void* pThreadData) +{ + if(pThreadData != NULL) + _thArg() = pThreadData; + + if(pThreadFunc != NULL) + _thFn() = pThreadFunc; + + if(estart()) + { + if(efinish()) + _efinish()->Reset(); + + _estart()->Set(); + } +} + +//each thread immediately runs for execution +void LThread::execute() +{ + //wait for an event + while(true) + { + _estart()->Wait(); + + if(thFn()) + { + _state() = eRunning; + _thFn()(thArg()); + _state() = eWaiting; + } + _estart()->Reset(); + _efinish()->Set(); + } +} + +#ifdef _WIN32 +DWORD /*unsigned int*/ APIENTRY LThread::thFnEx(void* param) +#else +void* APIENTRY LThread::thFnEx( void* param) +#endif +{ + if(param) + { + LThread* tth = (LThread*)param; + tth->execute(); + } +#ifdef _WIN32 + return 0; +#else + return NULL; +#endif +} + + +LThread::~LThread() +{ +#ifdef _WIN32 + if(th() != 0) + { + TerminateThread(th(),0); + CloseHandle(th()); + _th() = 0; + } + if (sa()) + { + HeapFree(GetProcessHeap(),0,sa()); + _sa() = NULL; + } + +#else + pthread_cancel(th()); + pthread_join(th(),NULL); +#endif + if(estart()) + delete estart(); + + if(efinish()) + delete efinish(); +} + +void LThread::WaitToFinish() +{ +#ifdef WIN32 + WaitForSingleObject(th(),INFINITE); +#else + pthread_join( th(), NULL ); +#endif +} + +LThreadGroup::LThreadGroup(THPROC *thProc) +{ + unsigned int ncpus = GetNumCPUs(); + _ths().resize(ncpus); + for(unsigned int i = 0; i < ncpus; i++) + _th(i) = NULL; + + for(unsigned int i = 0; i < ncpus; i++) + _th(i) = new LThread(thProc); + + for(unsigned int i = 0; i < ncpus; i++) + { + _th(i)->Create(); +#ifdef _WIN32 + SetThreadIdealProcessor(th(i)->GetHandle(),i);//does not do much +#endif + } +} + +LThreadGroup::~LThreadGroup() +{ + for(unsigned int i = 0; i < ths().size(); i++) + if(th(i)) + delete _th(i); + + _ths().clear(); +} + +unsigned int LThreadGroup::GetNumCPUs() +{ +#if defined(_WIN32) + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwNumberOfProcessors; +#elif defined(__linux__) + int nc = sysconf(_SC_NPROCESSORS_ONLN); + return nc > 1 ? (unsigned int)nc : 1; +#elif defined(OSMac_) + int nc = 0; + size_t resultSize = sizeof(int); + sysctlbyname("hw.logicalcpu", &nc, &resultSize, NULL, 0); + return nc; +#else +# error Unsupported OS. + return 1; +#endif +} + +bool LThreadGroup::Start(unsigned int i, THPROC* pThreadFunc, void *thData) +{ + if(th(i)) + { + _th(i)->Execute(pThreadFunc,thData); + return true; + } + return false; +} + +void LThreadGroup::WaitToFinish(int ms) +{ + unsigned int ntotal = (unsigned int)ths().size(); + if(ntotal > 0) + { +#ifdef _WIN32 + + HANDLE* handles = (HANDLE*)malloc(ntotal*sizeof(HANDLE)); + memset(handles,0,ntotal*sizeof(HANDLE)); + + unsigned int nrunning = 0; + for(unsigned int i = 0; i < ntotal; i++) + { + if(th(i)) + { + //handles[nrunning] = th(i)->GetHandle(); + //handles[nrunning] = th(i)->GetFinish(); + handles[nrunning] = th(i)->GetFinish()->handle; + nrunning++; + } + } + if(nrunning > 0) + { + DWORD wait_interval = (ms == -1) ? INFINITE : ms; + WaitForMultipleObjects(nrunning,handles,TRUE,wait_interval); + } +#else + for(unsigned int i = 0; i < ntotal; i++) + { + if(th(i)/* && th(i)->GetState() == LThread::eRunning*/) + { + //th(i)->WaitToFinish(); + th(i)->GetFinish()->Wait(); + } + } + +#endif + } +} + +#endif + +const MString shaveHairShape::nodeTypeName("shaveHair"); +const int shaveHairShape::kNodeVersion = 5; + +const char* shaveHairShape::kShapeSelectionMaskName = "shaveHair"; +const char* shaveHairShape::kGuideSelectionMaskName = "shaveGuide"; +const char* shaveHairShape::kStrandSelectionMaskName = "shaveStrand"; +const char* shaveHairShape::kVertSelectionMaskName = "shaveGuideVert"; + +int shaveHairShape::numShaveNodes = 0; +int shaveHairShape::mNextShaveID = 0; + +MString shaveHairShape::drawDbClassification("drawdb/geometry/shaveHair"); +MString shaveHairShape::drawRegistrantId("shave_and_haircut"); + + + +MTypeId shaveHairShape::id(0x001029B7); + + + +/* + * The shaveHairShape constructor. Set the initial values of Shave data. + */ +shaveHairShape::shaveHairShape() +: mAfterDuplicateCB(0) +, mBeforeDuplicateCB(0) +, mBrushIsActive(false) +, mBrushIsDown(false) +, mDisplayHairCacheDirty(true) +, mDisplayInstCacheDirty(true) +, mFirstTime(true) +, mGuideCacheDirty(true) +, mGuideCountDirty(true) +, mHairDisplayEnabled(true) +, mHaveSelections(false) +, mIsInstanced(false) +, mNodeInitialized(false) +, mNodeVersion(0) +, mXplantRequired(false) +//, mDirtyDisplaycount(true) +//, mDirtyTexture(false) +//, mDirtyHaircount(false) +{ + ENTER(); + + mShaveID = mNextShaveID++; + + // + // %%% SHAVEinit_node() should initialize this to zero, but currently + // does not. + // + + SHAVEinit_node(&hairnode, mShaveID); + + hairnode.shavep.tipfade = 1; + hairnode.shavep.squirrel = 0; + hairnode.shavep.flyaway_percent = 0.0f; + hairnode.shavep.clumps = 0; + + + doDyn = kDynamicsOff; + mShaveFrame = 0; + + + // initialize a few flags we'll use down the road. + beenHereBefore = false; + blindInit = false; + doneSelections = false; + shaveRenderCancelled = true; + exportData.lastUSubdiv = 0; + exportData.lastVSubdiv = 0; + exportData.lastSubdDepth = 0; + exportData.lastSubdSamples = 0; + availableMem = 0; + + // initialize the shave data objects this node will be using. + init_geomWF(&MAYAdata); + init_geomWF(&memShaveObj); + init_geomWF(&growthCollisionUVs); + init_geomWF(&mLockSplines); + + // Let's find a unique filename we can use for our OBJ export file. + shaveObjFile = shaveUtil::makeUniqueTempFileName("", "shave_", ".obj"); +#if defined(OSMac_) && !defined(OSMac_MachO_) + shaveObjFileHFS = shaveMacCarbon::makeMacFilename(shaveObjFile); +#endif + + MStatus status = MGlobal::executeCommand("shaveEnsureVrayRenderCallbacksSet"); + if(status != MStatus::kSuccess) + MGlobal::displayWarning("shaveNode: can not ensure V-Ray callbacks are set."); + +#ifdef REUSABLE_THREADS + _lthGrp() = new LThreadGroup(); +#endif + + LEAVE(); +} + + +shaveHairShape::~shaveHairShape() +{ + ENTER(); + + if (mBeforeDuplicateCB != 0) MMessage::removeCallback(mBeforeDuplicateCB); + if (mAfterDuplicateCB != 0) MMessage::removeCallback(mAfterDuplicateCB); + + if (shaveObjFile.length() != 0) + { + // Get rid of our temp file, if it exists. + // + + // Maya can crash if a node deletion gets pushed off the undo queue + // and then its destructor tries to execute a MEL command. + // + // shaveUtil::fileDelete() executes MEL commands, so we can't use + // it here. Instead, we execute an equivalent MEL script on idle. + // + MGlobal::executeCommandOnIdle( + MString("shave_deleteFile \"") + shaveObjFile + "\"" + ); + } + + free_geomWF(&MAYAdata); // come back here + free_geomWF(&memShaveObj); + free_geomWF(&growthCollisionUVs); + free_geomWF(&mLockSplines); + +#ifdef REUSABLE_THREADS + if(lthGrp()) + delete lthGrp(); +#endif + + LEAVE(); +} + + +void* shaveHairShape::creator() +{ + return new shaveHairShape; +} + + +void shaveHairShape::postConstructor() +{ + ENTER(); + + setMPSafe(false); + setRenderable(true); + + // Maya's 'duplicate' command can mess up our geometry sets, so we + // need to keep an eye on them. + mBeforeDuplicateCB = MModelMessage::addBeforeDuplicateCallback( + beforeDuplication, (void*)this + ); + mAfterDuplicateCB = MModelMessage::addAfterDuplicateCallback( + afterDuplication, (void*)this + ); + + LEAVE(); +} + + +MStatus shaveHairShape::setInstanceObject( + MObject instanceMesh, MObject shader, MDataBlock* block + ) +{ + ENTER(); + + // + // The defaults used for growing uninstanced hair are not appropriate + // for instanced hair. For example, it makes no sense to use multiple + // transparency passes, and the default hair count should be much + // lower. + // + // So if this is the first time that this node has been instanced, then + // give it some more appropriate defaults. + // + MPlug plug(thisMObject(), neverBeenInstanced); + bool hasNeverBeenInstanced; + + if (block != NULL) + { + hasNeverBeenInstanced = block->inputValue(neverBeenInstanced).asBool(); + } + else + { + plug.getValue(hasNeverBeenInstanced); + } + + if (hasNeverBeenInstanced) + { + for(int m = 0; m < 5; m++) + { + // + // Make the tip colour white. + // + hairnode.shavep.slider_val[9][m] = 255; + hairnode.shavep.slider_val[10][m] = 255; + hairnode.shavep.slider_val[11][m] = 255; + + // + // Make the mutant hair colour white. + // + hairnode.shavep.slider_val[13][m] = 255; + hairnode.shavep.slider_val[14][m] = 255; + hairnode.shavep.slider_val[15][m] = 255; + + // + // Make the root colour white. + // + hairnode.shavep.slider_val[17][m] = 255; + hairnode.shavep.slider_val[18][m] = 255; + hairnode.shavep.slider_val[19][m] = 255; + + // + // No root frizz or kink. + // + hairnode.shavep.slider_val[0][m] = 0; + hairnode.shavep.slider_val[38][m] = 0; + + // + // Only one pass: no transparency required. + // + hairnode.shavep.passes[m] = 1; + + // + // If there are more than 100 hairs, reduce it to just 100, + // otherwise we could end up with a ton of geometry. + // + if (hairnode.shavep.haircount[m] > 100) + { + hairnode.shavep.haircount[m] = 100; + setShadowHaircount(m); + } + } + + // It's been instanced now! + // + if (block != NULL) + { + block->outputValue(neverBeenInstanced).set(false); + } + else + { + plug.setValue(false); + } + } + + MString fileName = shaveUtil::expandTempFileName( + MString("shaveInstance_") + nodeName() + ".obj" + ); + + shaveObjTranslator objDump; + objDump.exportInstanceObj(instanceMesh, shader, fileName); + +#if defined(OSMac_) && !defined(OSMac_MachO_) + fileName = shaveMacCarbon::makeMacFilename(fileName); +#endif + SHAVEget_instance((char*)fileName.asChar(), &hairnode); + + mIsInstanced = true; + + updateBlindData(block); + + // Set the display mode to geometry. + // + if (block != NULL) + { + block->outputValue(dspyMode).set(kHairDisplayGeom); + } + else + { + plug.setAttribute(dspyMode); + plug.setValue(kHairDisplayGeom); + } + + RETURN(MS::kSuccess); +} + + +MStatus shaveHairShape::clearInstanceObject(MDataBlock& block) +{ + ENTER(); + + SHAVEclear_instance(&hairnode); + mIsInstanced = false; + updateBlindData(&block); + + // Set the display mode to geometry. + // + block.outputValue(dspyMode).set(kHairDisplayHair); + + RETURN(MS::kSuccess); +} + +bool shaveHairShape::getInstancingStatus() +{ + return mIsInstanced; +} + + +MStatus shaveHairShape::shouldSave(const MPlug& plug, bool& saveIt) +{ + ENTER(); + + // + // If it's a dynamic attribute then do the default handling. + // + MFnDependencyNode nodeFn(thisMObject()); + MFnDependencyNode::MAttrClass attrClass; + + attrClass = nodeFn.attributeClass(plug.attribute()); + + if ((attrClass == MFnDependencyNode::kLocalDynamicAttr)) + { + RETURN(MS::kInvalidParameter); + } + + // + // If it's one of the standard MPxNode attributes, or a deprecated + // attribute, then do the default handling. + // + if ((plug == MPxNode::message) + || (plug == MPxNode::isHistoricallyInteresting) + || (plug == MPxNode::caching) + || (plug == MPxNode::state)) + { + RETURN(MS::kInvalidParameter); + } + + // + // For all other attributes, if they're marked storable, then store + // them. We don't want Maya's store-only-if-not-default functionality + // because that screws up scene files if we subsequently change + // defaults. + // + MFnAttribute attr(plug.attribute()); + + saveIt = attr.isStorable(); + + RETURN(MS::kSuccess); +} + + +/***************************************************************** + * NAME: COMPUTE + * + * DESCRIPTON: This is our main compute funtion. This gets called + * when any of the "Affects" attributes are changedin the GUI. Most + * importantly, it gets called when there is a frame (time) change. + * + *****************************************************************/ + + +MStatus shaveHairShape::compute (const MPlug& plug, MDataBlock& data) +{ + //MGlobal::displayInfo("compute"); + //printf("::compute\n");fflush(stdout); + + ENTER(); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::compute", NULL); +#endif + MStatus status = MS::kSuccess; + + CheckGlobalQtViewport20Tracker(); + //////////////////////////////////// +#ifdef GLOBAL_FALLBACK + CheckAndSetGlobalQtWatcher(); +#endif + //////////////////////////////////// + if(dirties.DIRTY_HAIRCOUNT) + dirties.this_haircount = data.inputValue(shaveParamHaircount).asInt(); + else if(dirties.DIRTY_PASSES) + dirties.this_passes = data.inputValue(shaveParamPasses).asInt(); + else if(dirties.DIRTY_DISPLAY_COUNT) + { + int totalCount = data.inputValue(shaveParamHaircount).asInt(); + unsigned displayCount = (unsigned)(displayRatioGlob * (float)totalCount + 0.5); + if (displayCount > (unsigned)totalCount) + { + displayCount = (unsigned)totalCount; + } + + //unsigned int c = passes*displayCount; + +#ifdef GLOBAL_FALLBACK + bool fallback = doFallbackGlob; + if(liveModeGlob || (fallback && (liveModeGlob || dirties.BRUSH_MOUSE_DOWN || dirties.GLOBAL_MOUSE_DOWN || (dirties.BRUSH_JUST_MOVED && IsToolActive())))) +#else + if(doFallbackGlob) +#endif + { + + float fallback_ratio = fallbackRatioGlob; + + if (fallback_ratio < 0.0f) + fallback_ratio = 0.0f; + else if (fallback_ratio > 1.0f) + fallback_ratio = 1.0f; + + displayCount *= fallback_ratio; + } + + dirties.this_display_count = displayCount; + } + else if(dirties.DIRTY_DISPLAYSEGS) + dirties.this_display_segs = data.inputValue(aDisplaySegmentLimit).asInt(); + else if(dirties.DIRTY_GUIDE_SEGS) + dirties.this_guide_segs = SHAVE_VERTS_PER_GUIDE-1; + //////////////////////////////////// + + //not sure a good place + //updateTexLookups(); + + ////////////////////////////////////// + //bool doingCreate = false; + //MString cmdFlag = data.inputValue(aEvalOption, &status).asString(); + //if(cmdFlag != "") + //{ + // MStringArray cmdOptions; + // cmdFlag.split(' ', cmdOptions); + // cmdFlag = cmdOptions[0]; + // cmdOptions.remove(0); + + // if(cmdFlag == "create") doingCreate = true; + //} + //short hairGroup = data.inputValue(aHairGroup).asShort(); + //if(!doingCreate) + // updateParamsFromDatablock(data, hairGroup); + + ////////////////////////////////////// + + if ((plug == aCachedBBox) + || (plug == aCachedBBoxMin) + || (plug == aCachedBBoxMax)) + { + MBoundingBox bbox; + + computeBoundingBox(bbox, data); + + MPoint bboxMin = bbox.min(); + MPoint bboxMax = bbox.max(); + MDataHandle hdl = data.outputValue(aCachedBBoxMin); + double3& bboxMinData = hdl.asDouble3(); + + bboxMinData[0] = bboxMin.x; + bboxMinData[1] = bboxMin.y; + bboxMinData[2] = bboxMin.z; + + hdl = data.outputValue(aCachedBBoxMax); + double3& bboxMaxData = hdl.asDouble3(); + + bboxMaxData[0] = bboxMax.x; + bboxMaxData[1] = bboxMax.y; + bboxMaxData[2] = bboxMax.z; + + data.setClean(aCachedBBox); + } + else if (plug == aHairGroup) + { + // + // Shave supports (or at least did so at some point in time) different + // types of hair: eyebrows, beard, etc. These are known as 'hair + // groups'. This plugin just supports two hair groups: spline hair + // (group 4) and scalp hair (group 0). If we have any input curves + // then we make spline hair, otherwise it's scalp hair. + // + // The reason that we retrieve and then discard all of the surfaces + // and curves is because I believe it necessary for the proper + // propagation of dirty signals through the DG but I have yet to + // prove or disprove it. + // + MArrayDataHandle inputCurveArray(data.inputArrayValue(inputCurve)); + MArrayDataHandle inputMeshArray(data.inputArrayValue(inputMesh)); + MArrayDataHandle inputSurfArray(data.inputArrayValue(inputSurf)); + short hairGroup = 0; + + while (inputMeshArray.next()) + inputMeshArray.inputValue(); + + while (inputSurfArray.next()) + inputSurfArray.inputValue(); + + // If splineLock is on then the curves in inputCurveArray are used + // for controlling regular hair, not spline hair. + bool splineLock = data.inputValue(aSplineLockTrigger).asBool(); + + if (!splineLock && (inputCurveArray.elementCount() > 0)) + { + hairGroup = 4; + + // If you're wondering why we don't also run the loop below + // when splineLock is on, that's because the compute call for + // the splineLockTrigger plug will have already done that if + // splineLock is on. Not doing it again is a minor + // optimization. + while (inputCurveArray.next()) + inputCurveArray.inputValue(); + } + + data.outputValue(aHairGroup).set(hairGroup); + } + else if (plug == instancingStatusChanged) + { + MDataHandle hdl = data.outputValue(instancingStatusChanged); + + // + // If the shaveParamInstancingStatus attribute hadn't changed, then + // Maya would simply have returned the existing value without + // calling compute(). So the very fact that we're here means that + // it must have changed. + // + hdl.set(true); + } + else if (plug == instanceMeshChanged) + { + MDataHandle hdl = data.outputValue(instanceMeshChanged); + + // + // If the instanceMesh attribute hadn't changed, then Maya would + // simply have returned the existing value without calling + // compute(). So the very fact that we're here means that it must + // have changed. + // + hdl.set(true); + } + else if ((plug == outputMesh) || (plug == triggerAttr)) + { + // + // We often cannot properly compute the output mesh until the + // entire scene is in place: hair objects, skull object, + // shaveGlobals, etc. + // + // Once the scene has finished loading, an event handler will call + // our initializeNode() method, which will set the mNodeInitialized + // flag to true. At that point we can start normal processing. + // + // However, there is another possibility: this may be a new + // shaveHairShape created within an existing scene. Since there is no + // file load going on, there is no 'done loading' event to reset + // our flag. + // + // So, if we are creating a new node within an existing scene, then + // we'll just reset the flag ourselves. + // + MString cmdStr = data.inputValue(aEvalOption).asString(); + + if(cmdStr != "") + { + MStringArray cmdParams; + + cmdStr.split(' ', cmdParams); + + if (cmdParams[0] == "create") { + mNodeInitialized = true; + } + } + + // + // If we've already been fully initialized, then go ahead and + // compute the output mesh in the normal way. Otherwise, just + // output a null object. + // + if (mNodeInitialized) + { + status = computeOutputMesh(data, false); + } + else + { + MDataHandle hdl = data.outputValue(outputMesh, &status); + hdl.set(MObject::kNullObj); + } + + // + // Grab the input time, just to clear its dirty bit. + // + MDataHandle timeData = data.inputValue(timeAttr); + MTime timeIn = timeData.asTime(); + } + else if (plug == aSplineLockTrigger) + { + MArrayDataHandle inputCurveArray(data.inputArrayValue(inputCurve)); + + // Is spline locking on? + bool splineLock = data.inputValue(aSplineLock).asBool(); + + if (splineLock && (inputCurveArray.elementCount() > 0)) + { + MObject curve; + MObjectArray curves; + + do + { + curve = inputCurveArray.inputValue().asNurbsCurveTransformed(); + curves.append(curve); + } + while (inputCurveArray.next()); + + // Compute new curve data. + shaveUtil::sampleCurves(curves, SHAVE_VERTS_PER_GUIDE, mLockSplines); + } + else + free_geomWF(&mLockSplines); + + // Copy the splineLock value over to the trigger. + data.outputValue(plug).set(splineLock); + } + else + RETURN(MS::kInvalidParameter); + + data.setClean(plug); +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::compute -- done", NULL); +#endif + RETURN(status); +} + + +MStatus shaveHairShape::initializeNode() +{ + ENTER(); + + MStatus status; + MDataBlock datablock = forceCache(); + + if (mXplantRequired) { + scheduleXplant(); + } + + status = computeOutputMesh(datablock, true); + + mNodeInitialized = true; + + // The new mesh value has been placed on our output in such a way + // that it won't appear dirty to anyone connected to it. Let's + // remedy that. + // + dirtyOutputMesh(); + childChanged(); + + RETURN(status); +} + + +MStatus shaveHairShape::computeOutputMesh(MDataBlock& data, bool nodeJustLoaded) +{ + //MGlobal::displayInfo("computeOutputMesh"); + + ENTER(); + + MStatus status; + + // + // Set triggerAttr clean. + // + data.setClean(triggerAttr); + + // Create an empty mesh object. + MFnMeshData dataCreator; + MObject newOutputData = dataCreator.create (&status); + MChkErr(status, "ERROR creating outputData"); + + // Get handles to the input geom arrays. + MArrayDataHandle inputMeshArray(data.inputArrayValue(inputMesh)); + MArrayDataHandle inputSurfArray(data.inputArrayValue(inputSurf)); + MArrayDataHandle inputCurveArray(data.inputArrayValue(inputCurve)); + + bool splineLock = data.inputValue(aSplineLockTrigger).asBool(); + + bool haveGrowthGeom = (inputMeshArray.elementCount() > 0) + || (inputSurfArray.elementCount() > 0) + || (!splineLock && (inputCurveArray.elementCount() > 1)); + + bool visible = data.inputValue(MPxSurfaceShape::visibility).asBool(); + + // If this node is invisible or it doesn't have any growth geometry, + // then just output an empty mesh. The exception is if the node was + // just loaded from a scene file in which case we have to run through + // the motions the first time around to ensure that it is properly + // registered with the Shave engine. + // + if ((!visible && !nodeJustLoaded) || !haveGrowthGeom) + { + createEmptyMesh(newOutputData); + + MDataHandle hdl = data.outputValue(outputMesh); + hdl.set(newOutputData); + + RETURN(MS::kSuccess); + } + + shaveGlobals::getGlobals(); + + MDataHandle inputData; + bool doXplant = false; + bool replaceRest = false; + + // + // Before doing any Shave calls, we want to make sure that the + // SHAVEPARAMS reflect any changes the user may have made to the + // corresponding node attributes. So we must first copy the + // attribute values into the SHAVEPARAMS structure. + // + // However, there is one exception. If we are doing a hair create, + // then we *don't* want to use the attribute values but will + // instead go the other way, getting default param values from Shave and + // applying them to the attributes. + // + // So, that means that we must process the 'aEvalOption' + // attribute enough to know whether or not we are doing a 'create' + // command. + // + MString cmdFlag; + MStringArray cmdOptions; + bool doingCreate = false; + bool doingRecreate = false; + bool keepMaterials = true; + + cmdFlag = data.inputValue(aEvalOption, &status).asString(); + + if(cmdFlag != "") + { + cmdFlag.split(' ', cmdOptions); + cmdFlag = cmdOptions[0]; + cmdOptions.remove(0); + + if(cmdFlag == "create") + { + doingCreate = true; + } + } + + // + // Now, at long last, if we're not doing a create, then copy the + // attribute values into the SHAVEPARAMS structure. + // + short hairGroup = data.inputValue(aHairGroup).asShort(); + + if(!doingCreate) + { + updateParamsFromDatablock(data, hairGroup); + } + else + { + updateNodeName(); + } + + // + // Have any of the instance dependencies changed? + // + // Note that this code must execute even if 'checkInstancing' is false, + // because retrieving the attribute values will reset their dirty + // flags, which we want to have happen. + // + bool instanceMeshHasChanged = false; + bool instancingStatusHasChanged = false; + MDataHandle hdl; + + hdl = data.inputValue(instanceMeshChanged); + instanceMeshHasChanged = hdl.asBool(); + hdl.set(false); + + hdl = data.inputValue(instancingStatusChanged); + instancingStatusHasChanged = hdl.asBool(); + hdl.set(false); + + mIsInstanced = data.inputValue(shaveParamInstancingStatus).asBool(); + + // + // Should we check for changes in instancing? + // + // One case in which we shouldn't is if this is the first run of a node + // which was just loaded from a scene file. In that case all of its + // instancing data will already be in its blind data and the 'change' + // that we've detected is simply the various attributes being + // initialized at file load. + // + if (!nodeJustLoaded && !doingCreate) + { + // + // Is this node doing instancing? + // + if (mIsInstanced) + { + // + // Instancing is on. If it just changed to that, or if the + // instance mesh has changed, then we need to update the + // instance data. + // + if (instancingStatusHasChanged || instanceMeshHasChanged) + { + // + // Get the shader which is assigned to our display node. + // + MObject shader = getShader(); + + // + // Get the source mesh. We have to be careful here because + // someone may have set our instancingStatus attr without + // also setting a valid mesh, in which case calling + // asMeshTransformed() on the input handle will crash Maya. + // + // So let's take things one step at a time. + // + MObject srcMeshObj; + MDataHandle srcMeshHdl = data.inputValue(instanceMesh, &status); + bool meshValid = false; + + if (status) + { + srcMeshObj = srcMeshHdl.data(); + + if (!srcMeshObj.isNull() && srcMeshObj.hasFn(MFn::kMesh)) + { + srcMeshObj = srcMeshHdl.asMeshTransformed(); + MFnMesh mFn(srcMeshObj); + +#if 0 //werid , why bbox is empty + MBoundingBox bbox = mFn.boundingBox(&status); + fprintf(stdout,"pmin %f %f %f\n",bbox.min().x, bbox.min().y, bbox.min().z); + fprintf(stdout,"pmax %f %f %f\n",bbox.max().x, bbox.max().y, bbox.max().z); + fflush(stdout); + if(bbox.max().y - bbox.min().y > 0) +#endif + float ymin, ymax = 0.0f; + MFloatPointArray pointArray; + mFn.getPoints(pointArray); + + for(int k = 0; k < (int)pointArray.length(); k++) + { + if(k == 0) + { + ymin = ymax = pointArray[0].y; + } + else + { + ymin = ( ymin < pointArray[k].y ) ? ymin : pointArray[k].y; + ymax = ( ymax > pointArray[k].y ) ? ymax : pointArray[k].y; + } + } + //fprintf(stdout,"miny %f maxy %f\n", ymin, ymax);fflush(stdout); + if(fabs(ymax - ymin) > 0.000001f) + { + meshValid = true; + } + else + MGlobal::displayError("Shave: picked instance mesh is not valid, 'y' span is zero."); + } + else + MGlobal::displayError("Shave: picked instance object is not a mesh."); + + } + + if(meshValid) + { + // + // Let Shave know about the new instance object. + // + setInstanceObject(srcMeshObj, shader, &data); + } +#if 0 + // + // If we don't have a valid mesh, then create an empty one. + // + if (!meshValid) + { + MFnMeshData meshCreator; + + srcMeshObj = meshCreator.create(); + createEmptyMesh(srcMeshObj); + } + + // + // Let Shave know about the new instance object. + // + setInstanceObject(srcMeshObj, shader, &data); +#endif + } + } + else + { + // + // Instancing is off. If it just changed to that, then we + // need to clear the instance data. + // + if (instancingStatusHasChanged) clearInstanceObject(data); + } + } + + // + // Process the command, if any. + // + bool preserveParams = false; + + if(cmdFlag != "") + { + if(cmdFlag == "doTransplant") + { + doXplant = true; + mXplantRequired = false; + + if (cmdOptions.length() > 0) + { + if (cmdOptions[0] == "nomat") + { + keepMaterials = false; + } + else if (cmdOptions[0] == "preserveParams") + { + preserveParams = true; + } + } + } + else if(cmdFlag == "create") + { + cleanStatFiles(data); + } + else if (cmdFlag == "recreate") + { + doingRecreate = true; + } + else if(cmdFlag == "resetRest") + { + replaceRest = true; + } + + cmdFlag = ""; + cmdOptions.clear(); + + data.outputValue(aEvalOption).set(cmdFlag); + } + + MDataHandle outputHandle = data.outputValue (outputMesh, &status); + MChkErr(status, "ERROR getting polygon data handle\n"); + + // + // Get time - if we don't, it won't act as a trigger anymore. + // + MDataHandle timeData = data.inputValue(timeAttr, &status); + MChkErr(status, "Error getting time data handle\n"); + MTime tempTime = timeData.asTime(); + + // + // If we are in Live Mode, we need to artificially increment + // time so that we can see the effects of the shave Engine's + // frizz animation. + // + if (liveModeGlob) + { + mShaveFrame++; + } + else + { + mShaveFrame = (float)tempTime.value(); + } + + // + // Now that we have a time value, we need to update the time + // value for the engine. + // + // %%% Should we really be setting restMEM's time here? Shouldn't + // it remain at the time of the original rest state? + // + hairnode.restMEM.time = (int)mShaveFrame; + hairnode.statMEM.time = (int)mShaveFrame; + + // + // What is our current Display mode? + // + shaveDspyMode = data.inputValue(dspyMode, &status ).asShort(); + + // + // Prep for running dynamics. We do the actual math in MAYA_xform in + // createMesh() + // + inputData = data.inputValue(blockDynamics, &status); + + if(inputData.asBool()) + doDyn = kDynamicsOff; + else + { + inputData = data.inputValue(runDynamics, &status); + doDyn = (DynamicsMode)inputData.asLong(); + } + + // + // exportThis() will take our growth and collision geometry and build + // a WFTYPE (our 'memShaveObj' member variable) from it. If it detects + // that the topology of any of our geometry has changed since the + // WFTYPE was last built, then it will also export the geometry + // information to an OBJ file which will later be used in a call to + // SHAVExplant() so that Shave gets the new geometry. + // + // If this is the first time through for a node, exportThis() will + // necessarily detect a topology change since the old WFTYPE is empty. + // If the shaveHairShape was just created, then everything will work as + // intended. However, if the shaveHairShape is an existing node which was + // just read in from file, then we won't be doing a SHAVExplant() call + // because Shave already has the right geometry. That in turn means + // that we shouldn't let exportThis() create an OBJ file: while + // harmless, it is a waste of CPU cycles. + // + // So, if this is the first time through, and we are not in the process + // of creating (or recreating) this node, then pass exportThis() an + // empty filename, otherwise pass it a good one. + // + if (!beenHereBefore && !doingCreate && !doingRecreate && !doXplant) + { + exportData.objFileName = ""; + } + else + { + exportData.objFileName = shaveObjFile; + } + + int uSubdivs = (int)data.inputValue(surfTessU).asShort(); + int vSubdivs = (int)data.inputValue(surfTessV).asShort(); + + int subdDepth = (int)data.inputValue(subdTessDept).asShort(); + int subdSamples = (int)data.inputValue(subdTessSamp).asShort(); + + exportData.newtopo = doXplant; + exportData.theObj = &memShaveObj; + exportData.uvs = &growthCollisionUVs; + exportData.shaveHairShape = thisMObject(); + exportData.uSubdivs = uSubdivs; + exportData.vSubdivs = vSubdivs; + exportData.subdDepth = subdDepth; + exportData.subdSamples= subdSamples; + + shaveObjTranslator x; + x.exportThis(data, exportData); + + // + // it can be useful for debugging to export an obj + // built from the WFTYPE. + // + //save_geomWF(exportData.theObj ,"c:\\debug.obj"); + + // + // Store the values for the last tesselation params + // passed to the exporter. This will be used later to + // determine whether a retesselation is necesary or not. + // + exportData.lastUSubdiv = uSubdivs; + exportData.lastVSubdiv = vSubdivs; + + exportData.lastSubdDepth = subdDepth; + exportData.lastSubdSamples = subdSamples; + + if (beenHereBefore) doXplant = exportData.newtopo; + + + /*************** + * BLIND DATA * + ***************/ + + // + // Normally, blind data is generated by Shave and passed out to the + // node. The node then passes it back, unchanged, whenever Shave needs + // it. + // + // One exception is when a shaveHairShape is first loaded from file. In + // that case we have to take the blind data which was stored in the + // file and pass it on to Shave so that it can initialize its state. + // + // If blindInit is false, that means we haven't done that yet for this + // node, so we should do so now. + // + if ((blindInit == false) && !doingCreate && !doingRecreate) + { + inputData = data.inputValue(shaveBlindHair, &status); + + if (status) + { + if (dprint) cerr << "doing the blind init" << endl; + + blindShaveData * xx = (blindShaveData *) inputData.asPluginData(); + + if (xx != NULL) + { + if (dprint) cerr << "we have blind data" << endl; + + // + // As a final check, we only fill the hairnode with data if + // it doesn't already have some. + // + if (hairnode.restMEM.size == 0) + { + if (dprint) cerr << "allocate a shavenode" << endl; + + // need to do load/save for SHAVENODE + hairnode.restMEM.size = (long) xx->m.size; + hairnode.restMEM.pos = (long) xx->m.pos; + + if (hairnode.restMEM.size > 0) + { + alloc_MEMFILE(&(hairnode.restMEM)); + copy_MEMFILE(&(hairnode.restMEM), &(xx->m)); + } + } + + // + // The Shave ID stored in the hairnode may not be correct. + // For example, the file loaded in may be a reference file + // in which case its Shave IDs would start at 0, causing + // conflicts with the IDs of the shaveHairShapes already in the + // scene. So we must reset their IDs. + // + hairnode.restMEM.ID = getShaveID(); + + // 20070220: Joe told me in email "can you just coment + // it out for the moment - I'll try some scenes" +#if 0 + // + // I'm not completely sure why we need to do this. We had + // a bug such that dynamics wouldn't work right on older + // scenes: the hair would fly off. Joe couldn't fix it on + // his end for some reason, so he asked me to do it here. + // + replaceRest = true; +#endif + } + } + + // + // shave creates the 'stat' data dynamically, so we don't need to + // load that. + // + hairnode.statMEM.data = NULL; + hairnode.statMEM.size = 0; + hairnode.statMEM.pos = 0; + hairnode.statMEM.ID = getShaveID(); + } + + if (!beenHereBefore) + { + // + // MAYA REFRESH - we're making hairs from scratch. + // + if (doingCreate) + { + if (dprint) cerr << "creating the node 'from scratch'" << endl; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + SHAVEcreate_node((char*)shaveObjFileHFS.asChar(), &hairnode); +#else + SHAVEcreate_node((char*)shaveObjFile.asChar(), &hairnode); +#endif + SHAVEfetch_parms(&hairnode.shavep); + + // Update the attribute values to reflect the default + // parameter values which we got from Shave. + // + updateDatablockFromParams(data); + + // + // Set the displaySegmentLimit according to whether we have + // spline or normal hair. + // + if (hairGroup == 4) //does not make sense becuse attrib moved to globals + data.outputValue(aDisplaySegmentLimit).set(12); + else + data.outputValue(aDisplaySegmentLimit).set(6); + + // we want to dirty the kernal + // so it gets created on display + dirties.DIRTY_DISPLAY_COUNT = 1; + + } + else if (doingRecreate) + { +#if defined(OSMac_) && !defined(OSMac_MachO_) + SHAVEcreate_node((char*)shaveObjFileHFS.asChar(), &hairnode); +#else + SHAVEcreate_node((char*)shaveObjFile.asChar(), &hairnode); +#endif + updateParamsFromDatablock(data, hairGroup); + dirties.DIRTY_DISPLAY_COUNT = 1; + } + + // + // If we have new blind data from Shave, then store it out to the + // plugs. + // + // How do we know if we have new data? Well, if 'blindInit' is + // false, then we just got through setting Shave up to use + // whatever was in the plugs, so unless that has changed since + // then, there's no need to write it back out. + // + // The only chance for it to have changed since then is if + // 'doingCreate' or 'doingRecreate' is true. + // + // So if 'blindInit', 'doingCreate' and 'doingRecreate' are + // all false, we don't need to update the plugs. + // + // Why does it matter? Because if the shaveHairShape is from a + // referenced file, changing the blind data will cause a massive + // 'setAttr' to be registered with the referencing file, bloating + // its size. So we want to avoid that if the blind data really + // hasn't changed. + // + if (blindInit || doingCreate || doingRecreate) + { + updateBlindData(&data); + } + + beenHereBefore = true; + } + + blindInit = true; + + // + // If this is a freshly loaded node then give Shave a chance to update + // its internal data structures, in case they've changed since the + // scene was last saved. + // + if (nodeJustLoaded) + { + SHAVEupgrade_node(&hairnode); + } + + loadShaveEngine(mShaveFrame, doXplant, keepMaterials, preserveParams, data); + + createMesh(data, newOutputData); + + // + // if the replaceRest flag is set, we need to call replace_rest. + // this copies the + if (replaceRest) + { + SHAVEreplace_rest(&hairnode); + replaceRest = false; + } + + // + // Set the results to the output plug. + // + outputHandle.set(newOutputData); + + RETURN(MS::kSuccess); +} + + +/**************************************************************************** + * CREATE MESH -- This is the main body of code that does most of the calls + * to shavelib (the Shave library). + *****************************************************************************/ +void shaveHairShape::loadShaveEngine( + float frame, + bool doXplant, + bool keepMaterials, + bool preserveParams, + MDataBlock& block +) +{ + ENTER(); + + /* + * THREE POSSIBLE CASES: Refresh, Transplant, and Transform. + */ + if (doXplant) + { + /* + * TRANSPLANT - there's been a change in material assignments. + */ + if (dprint) cerr << "doing Xplant" << endl; + + MGlobal::executeCommand( + MString("shave_removeStatFiles(\"") + nodeName() + "\")" + ); + +#if defined(OSMac_) && !defined(OSMac_MachO_) + if (keepMaterials) + SHAVExplant((char*)shaveObjFileHFS.asChar(), &hairnode); + else + SHAVExplantNOMAT((char*)shaveObjFileHFS.asChar(), &hairnode); +#else + if (keepMaterials) + SHAVExplant((char*)shaveObjFile.asChar(), &hairnode); + else + SHAVExplantNOMAT((char*)shaveObjFile.asChar(), &hairnode); +#endif + updateBlindData(&block, !preserveParams); + } + + // Is splineLock on? + bool splineLock = block.inputValue(aSplineLockTrigger).asBool(); + + /* + * TRANSFORM - just changing frames, possibly computing/showing + * dynamics. + */ + + + /* + * doDyn: -1 To initialize the dynamics engine, calculate first frame. + * 0 Don't calculate/display dynamics + * 1 Calculate & display dynamics for this frame. + * 2 Don't calculate, but just display + */ + + // + // Load the hairnode into Shave's engine and set the dynamics state for + // this frame, if dynamics are active. + // + if (liveModeGlob) + { + if (shaveRender::renderState() == shaveRender::kRenderNone) + { + //MGlobal::displayInfo("SHAVExform"); + SHAVExform(&memShaveObj, &hairnode, 1, NULL); + if (splineLock) SHAVEsplinelock(&hairnode, &mLockSplines); + } + } + else if (doDyn == kDynamicsUse) + { + // + // Shave only understands integer frame numbers. So find the + // bounding frames. + // + int leftFrame = (int)frame; + int rightFrame = leftFrame; + float partialFrame = frame - (float)leftFrame; + + if (partialFrame > 0.001) rightFrame++; + + MString leftStatFile = shaveUtil::makeStatFileName( + nodeName(), (float)leftFrame + ); + + if (shaveUtil::fileExists(leftStatFile)) + { + // + // If the bounding frames are the same, then we're at an integer + // frame number, so we can just use that frame's statfile to set + // the dynamic state. + // + if (rightFrame == leftFrame) + { +#if defined(OSMac_) && !defined(OSMac_MachO_) + leftStatFile = shaveMacCarbon::makeMacFilename(leftStatFile); +#endif + } + { + // + // Now to handle the dynamics info. + // + // We're between frames. If the statfile for the other + // frame exists, then we can interpolate between them. + // + // Otherwise, we'll just have to pretend that there are no + // dynamics. + // + MString rightStatFile = shaveUtil::makeStatFileName( + nodeName(), (float)rightFrame + ); + + if (shaveUtil::fileExists(rightStatFile)) + { +#if defined(OSMac_) && !defined(OSMac_MachO_) + leftStatFile = shaveMacCarbon::makeMacFilename( + leftStatFile + ); + rightStatFile = shaveMacCarbon::makeMacFilename( + rightStatFile + ); +#endif + SHAVEset_state_between_and_glue( + &memShaveObj, + &hairnode, + partialFrame, + (char*)leftStatFile.asChar(), + (char*)rightStatFile.asChar() + ); + if (splineLock) SHAVEsplinelock(&hairnode, &mLockSplines); + } + else + { + //MGlobal::displayInfo("SHAVExform"); + SHAVExform(&memShaveObj, &hairnode, 0, NULL); + if (splineLock) SHAVEsplinelock(&hairnode, &mLockSplines); + } + } + } + else + { + // + // We don't have state information for this frame, so treat it + // as if it doesn't have any dynamics. + // + //MGlobal::displayInfo("SHAVExform"); + SHAVExform(&memShaveObj, &hairnode, 0, NULL); + if (splineLock) SHAVEsplinelock(&hairnode, &mLockSplines); + } + } + else if (doDyn == kDynamicsCreate) + { + MString statFile = shaveUtil::makeStatFileName(nodeName(), frame); + + if (statFile == "") + { + MGlobal::displayError( + "Cannot create stat file. Please check your stat file" + " directory to ensure that you have write access to it" + " and that the disk is not full." + ); + } + else + { + MGlobal::displayInfo(statFile); + + // Make sure that vertex-based texture info is up-to-date. + initVertTexInfoLookup(this, thisMObject(), &block); + + // Have Shave calculate and cache dynamics for this frame. +#if defined(OSMac_) && !defined(OSMac_MachO_) + statFile = shaveMacCarbon::makeMacFilename(statFile); +#endif + //MGlobal::displayInfo("SHAVExform"); + SHAVExform( + &memShaveObj, &hairnode, (int)doDyn, (char*)statFile.asChar() + ); + if (splineLock) SHAVEsplinelock(&hairnode, &mLockSplines); + } + } + else // doDyn == kDynamicsOff || doDyn == kDynamicsInit + { + if (dprint) cerr << "doing base xform - no dyn" << endl; + + //MGlobal::displayInfo("SHAVExform"); + SHAVExform(&memShaveObj, &hairnode, (int)doDyn, NULL); + if (splineLock) SHAVEsplinelock(&hairnode, &mLockSplines); + } + + LEAVE(); +} + +void shaveHairShape::doXform() +{ + SHAVExform(&memShaveObj, &hairnode, 0, NULL); +} + + +void shaveHairShape::createMesh(MDataBlock& datablock, MObject& outData) +{ + // Is this node visible? + // + if (datablock.inputValue(MPxSurfaceShape::visibility).asBool()) + { + // Are we rendering? + // + if (shaveRender::renderState() != shaveRender::kRenderNone) + { + // What are my render and shadow modes? + // + shaveRenderer* renderer = shaveRender::getRenderer(); + bool renderInstances; + shaveConstant::RenderMode renderMode; + + renderMode = renderer->getRenderMode(&renderInstances); + + if (mIsInstanced) + { + if (renderInstances && renderer->isGeomNode(this)) + makePolyMeshData(datablock, outData, false, false); + else + createEmptyMesh(outData); + } + else + { + shaveConstant::ShadowSource shadowSource; + shadowSource = renderer->getShadowSource(); + + switch (renderMode) + { + case shaveConstant::kBufferRender: + { + // Do we need geometry for the shadows? + // + if (shadowSource == shaveConstant::kMayaGeomShadows) + { + // Display geometry, but reduce it by the + // shadow/hair ratio. + // + makePolyMeshData(datablock, outData, false, true); + } + else + createEmptyMesh(outData); + } + break; + + case shaveConstant::kGeometryRender: + { + // Display geometry for every hair. + // + makePolyMeshData(datablock, outData, false, false); + } + break; + + default: + createEmptyMesh(outData); + } + } + } + else + { + // If we're displaying hair as geometry and have some hairs to + // display, then generate the corresponding output mesh. + // Otherwise just generate an empty output mesh. + // + unsigned numHairsToDisplay = getNumDisplayHairs(false, &datablock); +//#ifdef NEW_INSTNACE_DISPLAY + if(glisntGlob) + createEmptyMesh(outData); + else + { +//#else + if ((numHairsToDisplay > 0) && (shaveDspyMode == kHairDisplayGeom)) + { + // Display geometry, but reduce it by the display LOD. + // + makePolyMeshData(datablock, outData, true, false); + } + else + { + createEmptyMesh(outData); + } + } +//#endif + } + } + else + { + createEmptyMesh(outData); + } +} + + +void shaveHairShape::createEmptyMesh(MObject& outData) +{ + ENTER(); + + MPointArray noPoints; + MIntArray noInts; + MFnMesh meshFn; + + meshFn.create(0, 0, noPoints, noInts, noInts, outData); + + LEAVE(); +} + + +/**************************************************************************** + * MAKE MESH DATA -- This is where we call MAYAmake_a_hair and create + * the MFnMesh data, (MAYA's datastructure for Meshes) + *****************************************************************************/ + +#define SINGLE_HAIR_POLYS +MObject shaveHairShape::makePolyMeshData( + MDataBlock& datablock, + MObject parent, + bool useDisplayLOD, + bool forShadowsOnly +) +{ + ENTER(); + + MFloatPointArray points; + MFnMesh meshFS; + int numVertices; + int numFaces; + int x, i, vertexIndex; + MColorArray vertexColours; + MIntArray faceCounts; + MIntArray faceConnects; + MIntArray vertexIndexArray; + MObject newMesh; + + vertexIndex = 0; + + /* + * Make NUMBER_OF_HAIRS + */ + int totalNumHairs = 0; + int totalNumVertices = 0; + int totalNumFaces = 0; + MFloatArray Us; + MFloatArray Vs; + MIntArray UVids; + MIntArray UVCounts; + + short hairGroup = datablock.inputValue(aHairGroup).asShort(); + int numHairs = hairnode.shavep.haircount[hairGroup]; + + shaveGlobals::getGlobals(); + + if (useDisplayLOD) + { + numHairs = getNumDisplayHairs(false, &datablock); + } + else if (forShadowsOnly) + { + numHairs = hairnode.shavep.shadowHaircount[hairGroup]; + + // We seen some renderers crash when they get an empty mesh during + // shadow passes, so let's make sure we have at least one shadow + // hair. + // + if (numHairs < 1) numHairs = 1; + } + + + int hair; + int pass; + pass=0; + hair=0; +#ifdef SINGLE_HAIR_POLYS + for (pass = 0; pass < hairnode.shavep.passes[hairGroup]; pass++) +#endif + { +#ifdef SINGLE_HAIR_POLYS + for (hair = 0; hair < numHairs; hair++) +#endif + { +#ifdef SINGLE_HAIR_POLYS + SHAVEmake_a_hair( + pass, + hairGroup, + hair, + hairnode.shavep.segs[hairGroup], + &MAYAdata + ); + +#else + int *list; + CURVEINFO cinfo; + list=(int *) malloc (numHairs*hairnode.shavep.passes[hairGroup]*sizeof(int)); + for (hair=0;hair<numHairs*hairnode.shavep.passes[hairGroup];hair++) list[hair]=hair; + MTbunch_of_hairs(numHairs*hairnode.shavep.passes[hairGroup], list,hairGroup,0, &MAYAdata,&cinfo); + free(list); +#endif + + numVertices = MAYAdata.totalverts; + numFaces = MAYAdata.totalfaces; + + if (numVertices > 0) + { + /* + * Do vertices: + * 1) Make vertex array. + * 2) Make vertex color array + * 3) Make vertex index array (0,1,2,etc). Used to set vertex colors. + */ + for (x=0; x < numVertices; x++) + { + // Check it out: We get weird numbers + points.append (MAYAdata.v[x].x, MAYAdata.v[x].y, MAYAdata.v[x].z); + + vertexColours.append( + MAYAdata.color[x].x, + MAYAdata.color[x].y, + MAYAdata.color[x].z, + 1.0 + ); + + vertexIndexArray.append (vertexIndex); + vertexIndex++; + } + + /* + * Do facecounts + */ + for (i = 0; i < numFaces; i++) + { + faceCounts.append( + MAYAdata.face_end[i] - MAYAdata.face_start[i] + ); + } + + /* + * Do face connects index is relative to one hair + * so we multiply it to index into multiple hairs + */ + for (int f=0;f < MAYAdata.totalfaces;f++) + { + for (x = MAYAdata.face_start[f]; x < MAYAdata.face_end[f]; x++) + { + int index; + index=MAYAdata.facelist[x]; + faceConnects.append ( index + totalNumVertices ); + } + } + + totalNumHairs++; + totalNumVertices += numVertices; + totalNumFaces += numFaces; + } + } + } + + MStatus stat; + + newMesh = meshFS.create (totalNumVertices, totalNumFaces, + points, faceCounts, faceConnects, parent, &stat); + + meshFS.setVertexColors(vertexColours, vertexIndexArray); + + if (mIsInstanced) + { + // + // Get the source mesh for the instances. + // + MFnMesh sourceMeshFn( + datablock.inputValue(instanceMesh).asMesh() + ); + + // get the uv index mapping table + MIntArray indexTable; + MIntArray countTable; + sourceMeshFn.getAssignedUVs(countTable, indexTable); + + // get the uv arrays + MFloatArray refUs; + MFloatArray refVs; + sourceMeshFn.getUVs(refUs, refVs); + + // set the table for our new mesh. The new table is a duplicate of + // the originals. + meshFS.setUVs(refUs, refVs); + + if ((countTable.length() > 0) && (indexTable.length() > 0)) + { + // how many polys do we need to process? + int instMeshPolyCount = meshFS.numPolygons(); + int instRefPolyCount = sourceMeshFn.numPolygons(); + + if(instMeshPolyCount%instRefPolyCount) + cerr << "Sanity check: poly counts do not evenly map." << endl; + + // iterate through the polys, and do the mapping. Hopefully + // everyting is aligned... + int uvi, vi, pi, rpi = 0; + + // outer loop interates over every poly in our output mesh + for(pi = 0; pi < instMeshPolyCount; pi++) + { + // ref poly index is outMeshPolyIndex mod ref object poly count. + rpi = pi%instRefPolyCount; + + // inner loop iterates over current poly verts, vert count + // determined via uv assignment face count table (should always + // be 3) + for(vi = 0; vi < countTable[rpi]; vi++) + { + // get, then assign the index into our uv table. + sourceMeshFn.getPolygonUVid(rpi, vi, uvi); + meshFS.assignUV(pi, vi, uvi); + } + } + } + } + + if (!stat) { +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cerr << "Error creating hair mesh: " << stat.errorString().asChar() << endl; +#else + cerr << "Error creating hair mesh: " << stat.errorString() << endl; +#endif + } + + + RETURN(newMesh); +} + +bool GetDiffuseTexture(MObject shaderSG, MObject& map) +{ +#ifdef _DEBUG + MGlobal::displayInfo("GetDiffuseTexture"); +#endif + MStatus stat; + MFnDependencyNode dFn(shaderSG); + MPlug ssPlug = dFn.findPlug("surfaceShader",&stat); + if(!ssPlug.isNull() && stat == MStatus::kSuccess) + { + if(ssPlug.isConnected()) + { + MPlugArray sscons; + if(ssPlug.connectedTo(sscons,true,false,&stat) && + stat == MStatus::kSuccess && sscons.length() > 0) + { + if(!sscons[0].isNull()) + { + MObject shader = sscons[0].node(); + if(!shader.isNull()) + { + MFnDependencyNode dFn(shader); + MPlug colorPlug = dFn.findPlug("color",&stat); + if(colorPlug.isNull() || stat != MStatus::kSuccess) + { + //let try other plugs + colorPlug = dFn.findPlug("outColor",&stat); + + } + if(!colorPlug.isNull() && stat == MStatus::kSuccess) + { + if(colorPlug.isConnected()) + { + MPlugArray ccons; + if(colorPlug.connectedTo(ccons,true,false,&stat) && + stat == MStatus::kSuccess && ccons.length() > 0) + { + if(!ccons[0].isNull()) + { + MObject color_tex = ccons[0].node(); + if(!color_tex.isNull()) + { + map = color_tex; + return true; +#if 0 + if ( color_tex.hasFn( MFn::kFileTexture ) ) + { + map = color_tex; +#ifdef _DEBUG + MFnDependencyNode dFn(map); + MGlobal::displayInfo(dFn.name()); + MGlobal::displayInfo(MString("typeId " ) + dFn.typeId().id()); + +#endif + return true; + } +#endif + } + } + } + } + } + } + } + } + } + } + return false; +} + +bool GetTransparencyTexture(MObject shaderSG, MObject& map) +{ +#ifdef _DEBUG + MGlobal::displayInfo("GetDiffuseTexture"); +#endif + MStatus stat; + MFnDependencyNode dFn(shaderSG); + MPlug ssPlug = dFn.findPlug("surfaceShader",&stat); + if(!ssPlug.isNull() && stat == MStatus::kSuccess) + { + if(ssPlug.isConnected()) + { + MPlugArray sscons; + if(ssPlug.connectedTo(sscons,true,false,&stat) && + stat == MStatus::kSuccess && sscons.length() > 0) + { + if(!sscons[0].isNull()) + { + MObject shader = sscons[0].node(); + if(!shader.isNull()) + { + MFnDependencyNode dFn(shader); + MPlug colorPlug = dFn.findPlug("transparency",&stat); //Blin, Phong, Anizotropic + if(!colorPlug.isNull() && stat == MStatus::kSuccess) + { + if(colorPlug.isConnected()) + { + MPlugArray ccons; + if(colorPlug.connectedTo(ccons,true,false,&stat) && + stat == MStatus::kSuccess && ccons.length() > 0) + { + if(!ccons[0].isNull()) + { + MObject color_tex = ccons[0].node(); + if(!color_tex.isNull()) + { + map = color_tex; + return true; +#if 0 + if ( color_tex.hasFn( MFn::kFileTexture ) ) + { + +#ifdef _DEBUG + MFnDependencyNode dFn(map); + MGlobal::displayInfo(dFn.name()); + MGlobal::displayInfo(MString("typeId " ) + dFn.typeId().id()); + +#endif + return true; + } +#endif + } + } + } + } + } + } + } + } + } + } + return false; +} + +void shaveInstanceDisplay::Reset() +{ + points.clear(); + normals.clear(); + faceCounts.clear(); + faceStart.clear(); + faceEnd.clear(); + faceVerts.clear(); + uvs.clear(); +#if 0 + for(unsigned int i = 0;i < groups.size();i++) + if(groups[i]) + delete groups[i]; + + groups.clear(); +#endif +} + + + +void shaveInstanceDisplay::CreateMaps(MObject shader) +{ + hasXparency = false; + + MObject tex; + if(GetDiffuseTexture(shader,tex)) + { + //MImage img; + SampleMap(tex,img); + + if(diffuse != 0) + glDeleteTextures(1,&diffuse); + + glGenTextures(1, &diffuse); + + MObject tex2; + if(GetTransparencyTexture(shader,tex2)) + { + MImage img2; + SampleMap(tex2,img2); + + unsigned char* pix = img.pixels(); + unsigned char* pix2 = img2.pixels(); + + + int p = 0; + for (int i = 0; i < eTexH; i++) + { + for (int j = 0; j < eTexW; j++) + { + pix[p+3] = (unsigned char)(((int)pix2[p] + (int)pix2[p+1] + (int)pix2[p+2])/3); + //pix[p+3] = 100; + //pix[p+3] = 255; + p+=4; + } + } + hasXparency = true; + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, diffuse); + glTexImage2D(GL_TEXTURE_2D, + 0, //mip + GL_RGBA, //GL_RGB, //internal format + eTexW, + eTexH, + 0, //border + GL_RGBA, //format + GL_UNSIGNED_BYTE, //type + img.pixels()); //pixels + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +void shaveInstanceDisplay::SampleMap(MObject tex, MImage& img) +{ + MStatus stat; + MFnDependencyNode tFn(tex); + MString tname = tFn.name(); + + MFloatMatrix camMatrix; + + MFloatVectorArray col; + MFloatVectorArray tra; + MFloatArray u; + MFloatArray v; + + u.setLength(eTexW*eTexH); + v.setLength(eTexW*eTexH); + int k = 0; + float istep = 1.0f / (float)eTexH; + float jstep = 1.0f / (float)eTexW; + for (int i = 0;i < eTexH; i++) + { + float is = istep*(float)i; + for (int j = 0;j < eTexW; j++) + { + float js = jstep*(float)j; + u[k] = js; + v[k] = is; + k++; + } + } + col.setLength(eTexW*eTexH); + tra.setLength(eTexW*eTexH); + + stat = MRenderUtil::sampleShadingNetwork(tname+".outColor",u.length(),false,false,camMatrix, + NULL,&u,&v,NULL,NULL,NULL,NULL,NULL,col,tra); + + img.create(eTexW,eTexH); + unsigned char* pix = img.pixels(); + + k = 0; + int p = 0; + for (int i = 0; i < eTexH; i++) + { + for (int j = 0; j < eTexW; j++) + { + pix[p] = (unsigned char)(col[k].x*255.0f); + pix[p+1] = (unsigned char)(col[k].y*255.0f); + pix[p+2] = (unsigned char)(col[k].z*255.0f); + pix[p+3] = 255; + k++; + p+=4; + } + } +} +#if 0 +shaveInstanceDisplay::Group::Group( int start, int end, + const MFloatPointArray& points, + const MIntArray& faceCounts, + const MIntArray& faceStart, + const MIntArray& faceEnd, + const MIntArray& faceVerts) +{ + this->start = start; + this->end = end; + + int n = 0; + MFloatPoint c(0.0f,0.0f,0.0f); + for(int i = start; i <= end; i++) + { + int fc = faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = faceStart[i]; + int fe = faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int k = faceVerts[j]; + c += points[k]; + n++; + } + } + worldPos = c/n; +} +#endif + +/*const*/ shaveInstanceDisplay& shaveHairShape::getInstanceDisplay( ) +{ + if (!mNodeInitialized) + return mDisplayInstCache; + + if (!mDisplayInstCacheDirty) + return mDisplayInstCache; + + ENTER(); + + updateTexLookups(); + + makeCurrent(); + + mDisplayInstCache.Reset(); + + MObject shader = getShader(); + if(!shader.isNull()) + mDisplayInstCache.CreateMaps(shader); + + + + int numVertices; + int numFaces; + int x, i, vertexIndex; + MColorArray vertexColours; + vertexIndex = 0; + int totalNumHairs = 0; + int totalNumVertices = 0; + int totalNumFaces = 0; + MFloatArray Us; + MFloatArray Vs; + MIntArray UVids; + MIntArray UVCounts; + + + short hairGroup; + MPlug hairGroupPlug(thisMObject(),aHairGroup); + hairGroupPlug.getValue(hairGroup); + + int numHairs = getNumDisplayHairs(false); + + int hair; + int pass; + pass=0; + hair=0; + + int *list; + CURVEINFO cinfo; + list=(int *) malloc (numHairs*hairnode.shavep.passes[hairGroup]*sizeof(int)); + for (hair=0;hair<numHairs*hairnode.shavep.passes[hairGroup];hair++) list[hair]=hair; + + MTbunch_of_hairs(numHairs*hairnode.shavep.passes[hairGroup], list,hairGroup,0, &MAYAdata,&cinfo); + free(list); + + numVertices = MAYAdata.totalverts; + numFaces = MAYAdata.totalfaces; + + if (numVertices > 0) + { + /* + * Do vertices: + * 1) Make vertex array. + * 2) Make vertex color array + * 3) Make vertex index array (0,1,2,etc). Used to set vertex colors. + */ + for (x=0; x < numVertices; x++) + { + // Check it out: We get weird numbers + mDisplayInstCache.points.append (MAYAdata.v[x].x, MAYAdata.v[x].y, MAYAdata.v[x].z); + mDisplayInstCache.normals.append (MAYAdata.vn[x].x, MAYAdata.vn[x].y, MAYAdata.vn[x].z); + + //vertexColours.append( + // MAYAdata.color[x].x, + // MAYAdata.color[x].y, + // MAYAdata.color[x].z, + // 1.0 + //); + + //vertexIndexArray.append (vertexIndex); + //vertexIndex++; + } + + /* + * Do facecounts + */ + for (i = 0; i < numFaces; i++) + { + mDisplayInstCache.faceCounts.append( + MAYAdata.face_end[i] - MAYAdata.face_start[i] + ); + mDisplayInstCache.faceStart.append( + MAYAdata.face_start[i] + ); + mDisplayInstCache.faceEnd.append( + MAYAdata.face_end[i] + ); + } + + for (int f=0;f < MAYAdata.totalfverts;f++) + { + mDisplayInstCache.faceVerts.append(MAYAdata.facelist[f]); + } + + /* + * Do face connects index is relative to one hair + * so we multiply it to index into multiple hairs + */ +#if 0 + for (int f=0;f < MAYAdata.totalfaces;f++) + { + for (x = MAYAdata.face_start[f]; x < MAYAdata.face_end[f]; x++) + { + int index; + index=MAYAdata.facelist[x]; + faceConnects.append ( index + totalNumVertices ); + } + } + + totalNumHairs++; + totalNumVertices += numVertices; + totalNumFaces += numFaces; +#endif + } + + MObject srcMesh; + MPlug srcMeshPlug(thisMObject(),instanceMesh); + srcMeshPlug.getValue(srcMesh); + MFnMesh sourceMeshFn(srcMesh); + + // get the uv index mapping table + MIntArray indexTable; + MIntArray countTable; + sourceMeshFn.getAssignedUVs(countTable, indexTable); + + // get the uv arrays + MFloatArray refUs; + MFloatArray refVs; + sourceMeshFn.getUVs(refUs, refVs); + + if ((countTable.length() > 0) && (indexTable.length() > 0)) + { + // how many polys do we need to process? + int instRefPolyCount = sourceMeshFn.numPolygons(); + + int vi, pi, rpi = 0; + + // outer loop interates over every poly in our output mesh + for(pi = 0; pi < numFaces; pi++) + { + // ref poly index is outMeshPolyIndex mod ref object poly count. + rpi = pi%instRefPolyCount; + + // inner loop iterates over current poly verts, vert count + // determined via uv assignment face count table (should always + // be 3) + for(vi = 0; vi < countTable[rpi]; vi++) + { + // get, then assign the index into our uv table. + //sourceMeshFn.getPolygonUVid(rpi, vi, uvi); + float u, v; + sourceMeshFn.getPolygonUV(rpi,vi,u,v); + mDisplayInstCache.uvs.append(MFloatPoint(u,v,0.0f)); + } + } + } + unsigned int np = mDisplayInstCache.points.length(); + mDisplayInstCache.center = MFloatPoint(0.0f, 0.0f, 0.0f); + for(unsigned int i = 1; i < np; i++) + { + mDisplayInstCache.center += mDisplayInstCache.points[i]; + } + mDisplayInstCache.center.x /= (float)np; + mDisplayInstCache.center.y /= (float)np; + mDisplayInstCache.center.z /= (float)np; + float rSq = 0.0f; + for(unsigned int i = 1; i < np; i++) + { + MFloatPoint d = mDisplayInstCache.center - mDisplayInstCache.points[i]; + float dsq = d.x*d.x + d.y*d.y + d.z*d.z; + if(dsq > rSq) + rSq = dsq; + } + mDisplayInstCache.radius = sqrt(rSq); + + + + mDisplayInstCacheDirty = false; + RETURN(mDisplayInstCache); + +} + + + +void shaveHairShape::updateBlindData(MDataBlock* block, bool updateParams) +{ + ENTER(); + + MFnPluginData dataFn; + MObject wrapper; + blindShaveData* newData; + + if (block) + { + // TODO: + // + // MDataHandle provides the set(MPxData*) method, which + // would be easier to use, but it appears to only work + // if the attribute has already been initialized with + // data. Otherwise it fails. + // + // We may be able to get around that by first + // calling asPluginData(). If it returns NULL then the + // attribute has not yet been initialized and we will + // need to use MFnPluginData to create a wrapper object. + // If it succeeds then we can use set(MPxData*) to + // set the new data value without having to create a + // new wrapper object. + // + // The advantage of this approach is that it may allow + // us to bypass some of the redundant duplication of + // blind data that Maya is doing behind the scenes. + // + // For now, we always create a new wrapper since that is + // guaranteed to work in all situations. + // + wrapper = dataFn.create(blindShaveData::dataTypeId); + newData = (blindShaveData*)dataFn.data(); + } + else + { + newData = new blindShaveData; + } + + if (hairnode.restMEM.size > 0) + { + newData->m.pos = hairnode.restMEM.pos; + newData->setValue(hairnode.restMEM.data, hairnode.restMEM.size); + } + + if (block) + { + MDataHandle hdl = block->outputValue(shaveBlindHair); + hdl.set(wrapper); + + if (updateParams) + { + updateDatablockFromParams(*block); + } + } + else + { + MObject thisNode = thisMObject(); + MPlug blindPlug (thisNode, shaveBlindHair); + + blindPlug.setValue((MPxData*) newData); + + if (updateParams) + { + updatePlugsFromParams(); + } + } + + LEAVE(); +} + + +/* + * Delete all the stat files associated with this shaveHairShape. + * Called under the same condtions as MAYArefresh() and MAYAxform() + */ +void shaveHairShape::cleanStatFiles(MDataBlock& block) +{ + ENTER(); + + // + // MEL has functions for manipulating files under different platforms, + // so let's pass the bulk of this work to a script. + // + MGlobal::executeCommand( + MString("shave_removeStatFiles(\"") + nodeName() + "\")" + ); + + doDyn = kDynamicsOff; + + // Turn off dynamics. + // + block.outputValue(runDynamics).set((int)kDynamicsOff); + + LEAVE(); +} + + +void shaveHairShape::updateNodeName() +{ + ENTER(); + + strncpy( + hairnode.shavep.name, name().asChar(), sizeof(hairnode.shavep.name) + ); + + hairnode.shavep.name[sizeof(hairnode.shavep.name)-1] = '\0'; + + LEAVE(); +} + + +// Copy the shave engine's param values into their corresponding node +// attributes. +// +// This method sets the attr values using a datablock and therefore +// safe to be called during compute(). +// +void shaveHairShape::updateDatablockFromParams(MDataBlock& block) +{ + MDataHandle hdl; + + //printf("shave plugs updated\n");fflush(stdout); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::updateDatablockFromParams", NULL); +#endif + ENTER(); + + int index = getHairGroup(&block); + + block.outputValue(shaveParamHaircount).set((int)hairnode.shavep.haircount[index]); + block.outputValue(shaveParamPasses).set((short)hairnode.shavep.passes[index]); + + bool di = hairnode.shavep.dontinterpolate < 1 ? true : false; + block.outputValue(shaveParamNoInterp).set(di); + + block.outputValue(shaveParamTotalGuides).set((short)hairnode.shavep.total_guides); + + di = (hairnode.shavep.collide[index] > 0) ? true : false; + block.outputValue(shaveParamCollide).set(di); + + MPlug plug(thisMObject(), aSpecularTint); + block.outputValue(plug.child(0)).set(hairnode.shavep.spec_tint.x); + block.outputValue(plug.child(1)).set(hairnode.shavep.spec_tint.y); + block.outputValue(plug.child(2)).set(hairnode.shavep.spec_tint.z); + + plug.setAttribute(aSpecularTint2); + block.outputValue(plug.child(0)).set(hairnode.shavep.spec_tint2.x); + block.outputValue(plug.child(1)).set(hairnode.shavep.spec_tint2.y); + block.outputValue(plug.child(2)).set(hairnode.shavep.spec_tint2.z); + + + block.outputValue(aTipFade).set(hairnode.shavep.tipfade != 0); + block.outputValue(aSquirrel).set(hairnode.shavep.squirrel != 0); + block.outputValue(flyawayPerc).set((int)(hairnode.shavep.flyaway_percent*100.0f)); + block.outputValue(clumps).set(hairnode.shavep.clumps); + +#if 1 + // + // At the moment we only support collision method 1. However, due to + // an earlier bug, there may be some old shaveHairShapes out there with + // collision method 0. So let's force it back to 1. + // + // %%% When we add support other collision methods, this will have + // to change. + // + block.outputValue(shaveParamCollisionMethod).set((short)1); +#else + block.outputValue(shaveParamCollisionMethod).set((short)hairnode.shavep.collision_method); +#endif + + block.outputValue(shaveParamFrizzAnimDirX).set(hairnode.shavep.frizz_anim_dir[index].x); + block.outputValue(shaveParamFrizzAnimDirY).set(hairnode.shavep.frizz_anim_dir[index].y); + block.outputValue(shaveParamFrizzAnimDirZ).set(hairnode.shavep.frizz_anim_dir[index].z); + + block.outputValue(shaveParamSegs).set((short)hairnode.shavep.segs[index]); + block.outputValue(shaveParamGeomShadow).set(hairnode.shavep.geom_shadow); + block.outputValue(randomSeedOffset).set(hairnode.shavep.rand_seed_offset); + + block.outputValue(shaveParamFrizzRoot).set(hairnode.shavep.slider_val[0][index]); + block.outputValue(shaveParamFrizzTip).set(hairnode.shavep.slider_val[24][index]); + block.outputValue(shaveParamFrizzFreqX).set(hairnode.shavep.slider_val[1][index]); + block.outputValue(shaveParamFrizzFreqY).set(hairnode.shavep.slider_val[30][index]); + block.outputValue(shaveParamFrizzFreqZ).set(hairnode.shavep.slider_val[31][index]); + block.outputValue(shaveParamFrizzAnim).set(hairnode.shavep.slider_val[32][index]); + + block.outputValue(shaveParamKinkRoot).set(hairnode.shavep.slider_val[38][index]); + block.outputValue(shaveParamKinkTip).set(hairnode.shavep.slider_val[2][index]); + block.outputValue(shaveParamKinkFreqX).set(hairnode.shavep.slider_val[3][index]); + block.outputValue(shaveParamKinkFreqY).set(hairnode.shavep.slider_val[34][index]); + block.outputValue(shaveParamKinkFreqZ).set(hairnode.shavep.slider_val[35][index]); + + block.outputValue(shaveParamSplayRoot).set(hairnode.shavep.slider_val[26][index]); + block.outputValue(shaveParamSplayTip).set(hairnode.shavep.slider_val[27][index]); + + //vlad|05July2010 + block.outputValue(shaveParamMultAsp).set(hairnode.shavep.slider_val[44][index]); + + //vlad|09July2010 + block.outputValue(shaveParamOffset).set(hairnode.shavep.slider_val[45][index]); + + //vlad|09July2010 + block.outputValue(shaveParamAspect).set(hairnode.shavep.slider_val[46][index]); + + block.outputValue(shaveParamMultStrand).set((short)hairnode.shavep.slider_val[25][index]); + block.outputValue(shaveParamRandomizeMulti).set(hairnode.shavep.slider_val[42][index]); + block.outputValue(shaveParamThickRoot).set(hairnode.shavep.slider_val[20][index]); + + //guide thikness, to get the value from preset, nope it updates all the time + //block.outputValue(aDisplayGuideThick).set(hairnode.shavep.slider_val[20][index]); + + block.outputValue(shaveParamThickTip).set(hairnode.shavep.slider_val[37][index]); + block.outputValue(shaveParamSpecular).set(hairnode.shavep.slider_val[4][index]); + block.outputValue(shaveParamGloss).set(hairnode.shavep.slider_val[5][index]); + block.outputValue(shaveParamAmbDiff).set(hairnode.shavep.slider_val[6][index]); + block.outputValue(shaveParamSelfShadow).set(hairnode.shavep.slider_val[7][index]); + block.outputValue(shaveParamStiffness).set(hairnode.shavep.slider_val[8][index]); + block.outputValue(shaveParamRandScale).set(hairnode.shavep.slider_val[36][index]); + block.outputValue(shaveParamAnimSpeed).set(hairnode.shavep.slider_val[33][index]); + block.outputValue(shaveParamDisplacement).set(hairnode.shavep.slider_val[43][index]); + + block.outputValue(shaveParamRootRed).set((float)(hairnode.shavep.slider_val[17][index]/255.0f)); + block.outputValue(shaveParamRootGreen).set((float)(hairnode.shavep.slider_val[18][index]/255.0f)); + block.outputValue(shaveParamRootBlue).set((float)(hairnode.shavep.slider_val[19][index]/255.0f)); + + block.outputValue(shaveParamHairRed).set((float)(hairnode.shavep.slider_val[9][index]/255.0f)); + block.outputValue(shaveParamHairGreen).set((float)(hairnode.shavep.slider_val[10][index]/255.0f)); + block.outputValue(shaveParamHairBlue).set((float)(hairnode.shavep.slider_val[11][index]/255.0f)); + + block.outputValue(shaveParamMutantRed).set((float)(hairnode.shavep.slider_val[13][index]/255.0f)); + block.outputValue(shaveParamMutantGreen).set((float)(hairnode.shavep.slider_val[14][index]/255.0f)); + block.outputValue(shaveParamMutantBlue).set((float)(hairnode.shavep.slider_val[15][index]/255.0f)); + + block.outputValue(shaveParamHueVariation).set(hairnode.shavep.slider_val[12][index]); + block.outputValue(shaveParamValueVariation).set(hairnode.shavep.slider_val[39][index]); + block.outputValue(shaveParamMutantPercent).set(hairnode.shavep.slider_val[16][index]); + block.outputValue(shaveParamDampening).set(hairnode.shavep.slider_val[40][index]); + block.outputValue(shaveParamRootStiffness).set(hairnode.shavep.slider_val[21][index]); + block.outputValue(shaveParamScale).set(hairnode.shavep.slider_val[41][index]); + block.outputValue(flyawayStren).set(hairnode.shavep.slider_val[48][index]); + block.outputValue(messStren).set(hairnode.shavep.slider_val[47][index]); + + block.outputValue(clumpsStren).set(hairnode.shavep.slider_val[49][index]); + block.outputValue(clumpsRotStren).set(hairnode.shavep.slider_val[50][index]); + block.outputValue(clumpsColStren).set(hairnode.shavep.slider_val[51][index]); + block.outputValue(clumpsRotOffset).set(hairnode.shavep.slider_val[52][index]); + block.outputValue(clumpsRandomize).set(hairnode.shavep.slider_val[53][index]); + block.outputValue(clumpsFlatness).set(hairnode.shavep.slider_val[54][index]); + block.outputValue(clumpsScruffle).set(hairnode.shavep.slider_val[55][index]); +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::updateDatablockFromParams -- done", NULL); +#endif + + LEAVE(); +} + + +// Copy the shave engine's param values into their corresponding node +// attributes. +// +// This method sets the attr values using plugs and therefore should not be +// called during compute(). +// +void shaveHairShape::updatePlugsFromParams() +{ + //printf("shave plugs updated\n");fflush(stdout); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::updatePlugsFromParams", NULL); +#endif + ENTER(); + + int index = getHairGroup(); + + MPlug plug(thisMObject(), shaveParamHaircount); + plug.setValue((int)hairnode.shavep.haircount[index]); + + plug.setAttribute(shaveParamPasses); + plug.setValue((short)hairnode.shavep.passes[index]); + + plug.setAttribute(shaveParamNoInterp); + bool di = hairnode.shavep.dontinterpolate < 1 ? true : false; + plug.setValue(di); + + plug.setAttribute(shaveParamTotalGuides); + plug.setValue((short)hairnode.shavep.total_guides); + + plug.setAttribute(shaveParamCollide); + di = (hairnode.shavep.collide[index] > 0) ? true : false; + plug.setValue(di); + + plug.setAttribute(aSpecularTint); + plug.child(0).setValue(hairnode.shavep.spec_tint.x); + plug.child(1).setValue(hairnode.shavep.spec_tint.y); + plug.child(2).setValue(hairnode.shavep.spec_tint.z); + + plug.setAttribute(aSpecularTint2); + plug.child(0).setValue(hairnode.shavep.spec_tint2.x); + plug.child(1).setValue(hairnode.shavep.spec_tint2.y); + plug.child(2).setValue(hairnode.shavep.spec_tint2.z); + + + plug.setAttribute(aTipFade); + plug.setValue(hairnode.shavep.tipfade != 0); + + plug.setAttribute(aSquirrel); + plug.setValue(hairnode.shavep.squirrel != 0); + + plug.setAttribute(flyawayPerc); + plug.setValue((int)(hairnode.shavep.flyaway_percent*100.0f)); + + plug.setAttribute(clumps); + plug.setValue(hairnode.shavep.clumps); + + plug.setAttribute(shaveParamCollisionMethod); +#if 1 + // + // At the moment we only support collision method 1. However, due to + // an earlier bug, there may be some old shaveHairShapes out there with + // collision method 0. So let's force it back to 1. + // + // %%% When we add support other collision methods, this will have + // to change. + // + plug.setValue((short)1); +#else + plug.setValue((short)hairnode.shavep.collision_method); +#endif + + plug.setAttribute(shaveParamFrizzAnimDirX); + plug.setValue(hairnode.shavep.frizz_anim_dir[index].x); + + plug.setAttribute(shaveParamFrizzAnimDirY); + plug.setValue(hairnode.shavep.frizz_anim_dir[index].y); + + plug.setAttribute(shaveParamFrizzAnimDirZ); + plug.setValue(hairnode.shavep.frizz_anim_dir[index].z); + + plug.setAttribute(shaveParamSegs); + plug.setValue((short)hairnode.shavep.segs[index]); + + plug.setAttribute(shaveParamGeomShadow); + plug.setValue(hairnode.shavep.geom_shadow); + + plug.setAttribute(randomSeedOffset); + plug.setValue(hairnode.shavep.rand_seed_offset); + + plug.setAttribute(shaveParamFrizzRoot); + plug.setValue(hairnode.shavep.slider_val[0][index]); + + plug.setAttribute(shaveParamFrizzTip); + plug.setValue(hairnode.shavep.slider_val[24][index]); + + plug.setAttribute(shaveParamFrizzFreqX); + plug.setValue(hairnode.shavep.slider_val[1][index]); + + plug.setAttribute(shaveParamFrizzFreqY); + plug.setValue(hairnode.shavep.slider_val[30][index]); + + plug.setAttribute(shaveParamFrizzFreqZ); + plug.setValue(hairnode.shavep.slider_val[31][index]); + + plug.setAttribute(shaveParamFrizzAnim); + plug.setValue(hairnode.shavep.slider_val[32][index]); + + plug.setAttribute(shaveParamKinkRoot); + plug.setValue(hairnode.shavep.slider_val[38][index]); + + plug.setAttribute(shaveParamKinkTip); + plug.setValue(hairnode.shavep.slider_val[2][index]); + + plug.setAttribute(shaveParamKinkFreqX); + plug.setValue(hairnode.shavep.slider_val[3][index]); + + plug.setAttribute(shaveParamKinkFreqY); + plug.setValue(hairnode.shavep.slider_val[34][index]); + + plug.setAttribute(shaveParamKinkFreqZ); + plug.setValue(hairnode.shavep.slider_val[35][index]); + + plug.setAttribute(shaveParamSplayRoot); + plug.setValue(hairnode.shavep.slider_val[26][index]); + + plug.setAttribute(shaveParamSplayTip); + plug.setValue(hairnode.shavep.slider_val[27][index]); + + plug.setAttribute(shaveParamMultAsp); //vlad|05July2010 + plug.setValue(hairnode.shavep.slider_val[44][index]); + + plug.setAttribute(shaveParamOffset); //vlad|09July2010 + plug.setValue(hairnode.shavep.slider_val[45][index]); + + plug.setAttribute(shaveParamAspect); //vlad|09July2010 + plug.setValue(hairnode.shavep.slider_val[46][index]); + + plug.setAttribute(shaveParamMultStrand); + plug.setValue((short)hairnode.shavep.slider_val[25][index]); + + plug.setAttribute(shaveParamRandomizeMulti); + plug.setValue(hairnode.shavep.slider_val[42][index]); + + plug.setAttribute(shaveParamThickRoot); + plug.setValue(hairnode.shavep.slider_val[20][index]); + + //guide thikness, to get the value from preset, nope it updates all the time + //plug.setAttribute(aDisplayGuideThick); + //plug.setValue(hairnode.shavep.slider_val[20][index]); + + + plug.setAttribute(shaveParamThickTip); + plug.setValue(hairnode.shavep.slider_val[37][index]); + + plug.setAttribute(shaveParamSpecular); + plug.setValue(hairnode.shavep.slider_val[4][index]); + + plug.setAttribute(shaveParamGloss); + plug.setValue(hairnode.shavep.slider_val[5][index]); + + plug.setAttribute(shaveParamAmbDiff); + plug.setValue(hairnode.shavep.slider_val[6][index]); + + plug.setAttribute(shaveParamSelfShadow); + plug.setValue(hairnode.shavep.slider_val[7][index]); + + plug.setAttribute(shaveParamStiffness); + plug.setValue(hairnode.shavep.slider_val[8][index]); + + plug.setAttribute(shaveParamRandScale); + plug.setValue(hairnode.shavep.slider_val[36][index]); + + plug.setAttribute(shaveParamAnimSpeed); + plug.setValue(hairnode.shavep.slider_val[33][index]); + + plug.setAttribute(shaveParamDisplacement); + plug.setValue(hairnode.shavep.slider_val[43][index]); + + plug.setAttribute(shaveParamRootRed); + plug.setValue((float)(hairnode.shavep.slider_val[17][index]/255.0f)); + + plug.setAttribute(shaveParamRootGreen); + plug.setValue((float)(hairnode.shavep.slider_val[18][index]/255.0f)); + + plug.setAttribute(shaveParamRootBlue); + plug.setValue((float)(hairnode.shavep.slider_val[19][index]/255.0f)); + + plug.setAttribute(shaveParamHairRed); + plug.setValue((float)(hairnode.shavep.slider_val[9][index]/255.0f)); + + plug.setAttribute(shaveParamHairGreen); + plug.setValue((float)(hairnode.shavep.slider_val[10][index]/255.0f)); + + plug.setAttribute(shaveParamHairBlue); + plug.setValue((float)(hairnode.shavep.slider_val[11][index]/255.0f)); + + plug.setAttribute(shaveParamMutantRed); + plug.setValue((float)(hairnode.shavep.slider_val[13][index]/255.0f)); + + plug.setAttribute(shaveParamMutantGreen); + plug.setValue((float)(hairnode.shavep.slider_val[14][index]/255.0f)); + + plug.setAttribute(shaveParamMutantBlue); + plug.setValue((float)(hairnode.shavep.slider_val[15][index]/255.0f)); + + plug.setAttribute(shaveParamHueVariation); + plug.setValue(hairnode.shavep.slider_val[12][index]); + + plug.setAttribute(shaveParamValueVariation); + plug.setValue(hairnode.shavep.slider_val[39][index]); + + plug.setAttribute(shaveParamMutantPercent); + plug.setValue(hairnode.shavep.slider_val[16][index]); + + plug.setAttribute(shaveParamDampening); + plug.setValue(hairnode.shavep.slider_val[40][index]); + + plug.setAttribute(shaveParamRootStiffness); + plug.setValue(hairnode.shavep.slider_val[21][index]); + + plug.setAttribute(shaveParamScale); + plug.setValue(hairnode.shavep.slider_val[41][index]); + + plug.setAttribute(flyawayStren); + plug.setValue(hairnode.shavep.slider_val[48][index]); + + plug.setAttribute(messStren); + plug.setValue(hairnode.shavep.slider_val[47][index]); + + plug.setAttribute(clumpsStren); + plug.setValue(hairnode.shavep.slider_val[49][index]); + + plug.setAttribute(clumpsRotStren); + plug.setValue(hairnode.shavep.slider_val[50][index]); + + plug.setAttribute(clumpsColStren); + plug.setValue(hairnode.shavep.slider_val[51][index]); + + plug.setAttribute(clumpsRotOffset); + plug.setValue(hairnode.shavep.slider_val[52][index]); + + plug.setAttribute(clumpsRandomize); + plug.setValue(hairnode.shavep.slider_val[53][index]); + + plug.setAttribute(clumpsFlatness); + plug.setValue(hairnode.shavep.slider_val[54][index]); + + plug.setAttribute(clumpsScruffle); + plug.setValue(hairnode.shavep.slider_val[55][index]); +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::updatePlugsFromParams -- done", NULL); +#endif + + LEAVE(); +} + + +void shaveHairShape::updateParams() +{ + ENTER(); + + updateParamsFromPlugs(getHairGroup()); + + LEAVE(); +} + + +void shaveHairShape::updateParamsFromDatablock(MDataBlock& data, int index) +{ + //printf("shave params updated\n");fflush(stdout); + + ENTER(); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::updateParamsFromDatablock", NULL); +#endif + updateNodeName(); + + hairnode.shavep.slider_val[0][5] = gravityGlob; + + hairnode.shavep.haircount[index] = + data.inputValue(shaveParamHaircount).asInt(); + + setShadowHaircount(index); + + bool di = data.inputValue (shaveParamNoInterp).asBool(); + hairnode.shavep.dontinterpolate = di ? 0 : 1; + hairnode.shavep.passes[index] = (int)data.inputValue(shaveParamPasses).asShort(); + hairnode.shavep.total_guides = (int)data.inputValue(shaveParamTotalGuides).asShort(); + di = data.inputValue (shaveParamCollide).asBool(); + hairnode.shavep.collide[index] = di ? 1 : 0; + hairnode.shavep.collision_method = (int)data.inputValue(shaveParamCollisionMethod).asShort(); + + float3& color = data.inputValue(aSpecularTint).asFloat3(); + hairnode.shavep.spec_tint.x = color[0]; + hairnode.shavep.spec_tint.y = color[1]; + hairnode.shavep.spec_tint.z = color[2]; + + float3& color2 = data.inputValue(aSpecularTint2).asFloat3(); + hairnode.shavep.spec_tint2.x = color2[0]; + hairnode.shavep.spec_tint2.y = color2[1]; + hairnode.shavep.spec_tint2.z = color2[2]; + + hairnode.shavep.tipfade = (data.inputValue(aTipFade).asBool() ? 1 : 0); + + hairnode.shavep.squirrel = (data.inputValue(aSquirrel).asBool() ? 1 : 0); + + hairnode.shavep.flyaway_percent = (float)data.inputValue(flyawayPerc).asInt()/100.0f; + hairnode.shavep.clumps = data.inputValue(clumps).asInt(); + + hairnode.shavep.rand_seed_offset = data.inputValue(randomSeedOffset).asInt(); + + // + // At the moment we only support collision method 1. However, due + // to an earlier bug, there may be some old shaveHairShapes out there + // with collision method 0. So let's force it back to 1. + // + // %%% When we add support for other collision methods, this will have + // to change. + // + hairnode.shavep.collision_method = 1; + + hairnode.shavep.frizz_anim_dir[index].x = data.inputValue(shaveParamFrizzAnimDirX).asFloat(); + hairnode.shavep.frizz_anim_dir[index].y = data.inputValue(shaveParamFrizzAnimDirY).asFloat(); + hairnode.shavep.frizz_anim_dir[index].z = data.inputValue(shaveParamFrizzAnimDirZ).asFloat(); + hairnode.shavep.segs[index] = (int)data.inputValue(shaveParamSegs).asShort(); + hairnode.shavep.geom_shadow = data.inputValue(shaveParamGeomShadow).asFloat(); + hairnode.shavep.slider_val[0][index] = data.inputValue(shaveParamFrizzRoot).asFloat(); + hairnode.shavep.slider_val[24][index] = data.inputValue(shaveParamFrizzTip).asFloat(); + hairnode.shavep.slider_val[1][index] = data.inputValue(shaveParamFrizzFreqX).asFloat(); + hairnode.shavep.slider_val[30][index] = data.inputValue(shaveParamFrizzFreqY).asFloat(); + hairnode.shavep.slider_val[31][index] = data.inputValue(shaveParamFrizzFreqZ).asFloat(); + hairnode.shavep.slider_val[32][index] = data.inputValue(shaveParamFrizzAnim).asFloat(); + hairnode.shavep.slider_val[38][index] = data.inputValue(shaveParamKinkRoot).asFloat(); + hairnode.shavep.slider_val[2][index] = data.inputValue(shaveParamKinkTip).asFloat(); + hairnode.shavep.slider_val[3][index] = data.inputValue(shaveParamKinkFreqX).asFloat(); + hairnode.shavep.slider_val[34][index] = data.inputValue(shaveParamKinkFreqY).asFloat(); + hairnode.shavep.slider_val[35][index] = data.inputValue(shaveParamKinkFreqZ).asFloat(); + hairnode.shavep.slider_val[26][index] = data.inputValue(shaveParamSplayRoot).asFloat(); + hairnode.shavep.slider_val[27][index] = data.inputValue(shaveParamSplayTip).asFloat(); + hairnode.shavep.slider_val[44][index] = data.inputValue(shaveParamMultAsp).asFloat(); //vlad|05July2010 + hairnode.shavep.slider_val[45][index] = data.inputValue(shaveParamOffset).asFloat(); //vlad|09July2010 + hairnode.shavep.slider_val[46][index] = data.inputValue(shaveParamAspect).asFloat(); //vlad|09July2010 + hairnode.shavep.slider_val[25][index] = (float)data.inputValue(shaveParamMultStrand).asShort(); + hairnode.shavep.slider_val[42][index] = data.inputValue(shaveParamRandomizeMulti).asFloat(); + hairnode.shavep.slider_val[20][index] = data.inputValue(shaveParamThickRoot).asFloat(); + hairnode.shavep.slider_val[37][index] = data.inputValue(shaveParamThickTip).asFloat(); + hairnode.shavep.slider_val[4][index] = data.inputValue(shaveParamSpecular).asFloat(); + hairnode.shavep.slider_val[5][index] = data.inputValue(shaveParamGloss).asFloat(); + hairnode.shavep.slider_val[6][index] = data.inputValue(shaveParamAmbDiff).asFloat(); + hairnode.shavep.slider_val[7][index] = data.inputValue(shaveParamSelfShadow).asFloat(); + hairnode.shavep.slider_val[8][index] = data.inputValue(shaveParamStiffness).asFloat(); + hairnode.shavep.slider_val[36][index] = data.inputValue(shaveParamRandScale).asFloat(); + hairnode.shavep.slider_val[33][index] = data.inputValue(shaveParamAnimSpeed).asFloat(); + hairnode.shavep.slider_val[43][index] = data.inputValue(shaveParamDisplacement).asFloat(); + hairnode.shavep.slider_val[17][index] = data.inputValue(shaveParamRootRed).asFloat()*255.0f; + hairnode.shavep.slider_val[18][index] = data.inputValue(shaveParamRootGreen).asFloat()*255.0f; + hairnode.shavep.slider_val[19][index] = data.inputValue(shaveParamRootBlue).asFloat()*255.0f; + hairnode.shavep.slider_val[9][index] = data.inputValue(shaveParamHairRed).asFloat()*255.0f; + hairnode.shavep.slider_val[10][index] = data.inputValue(shaveParamHairGreen).asFloat()*255.0f; + hairnode.shavep.slider_val[11][index] = data.inputValue(shaveParamHairBlue).asFloat()*255.0f; + hairnode.shavep.slider_val[13][index] = data.inputValue(shaveParamMutantRed).asFloat()*255.0f; + hairnode.shavep.slider_val[14][index] = data.inputValue(shaveParamMutantGreen).asFloat()*255.0f; + hairnode.shavep.slider_val[15][index] = data.inputValue(shaveParamMutantBlue).asFloat()*255.0f; + hairnode.shavep.slider_val[12][index] = data.inputValue(shaveParamHueVariation).asFloat(); + hairnode.shavep.slider_val[39][index] = data.inputValue(shaveParamValueVariation).asFloat(); + hairnode.shavep.slider_val[16][index] = data.inputValue(shaveParamMutantPercent).asFloat(); + hairnode.shavep.slider_val[40][index] = data.inputValue(shaveParamDampening).asFloat(); + hairnode.shavep.slider_val[21][index] = data.inputValue(shaveParamRootStiffness).asFloat(); + hairnode.shavep.slider_val[41][index] = data.inputValue(shaveParamScale).asFloat(); + + hairnode.shavep.slider_val[48][index] = data.inputValue(flyawayStren).asFloat(); + hairnode.shavep.slider_val[47][index] = data.inputValue(messStren).asFloat(); + + hairnode.shavep.slider_val[49][index] = data.inputValue(clumpsStren).asFloat(); + hairnode.shavep.slider_val[50][index] = data.inputValue(clumpsRotStren).asFloat(); + hairnode.shavep.slider_val[51][index] = data.inputValue(clumpsColStren).asFloat(); + hairnode.shavep.slider_val[52][index] = data.inputValue(clumpsRotOffset).asFloat(); + hairnode.shavep.slider_val[53][index] = data.inputValue(clumpsRandomize).asFloat(); + hairnode.shavep.slider_val[54][index] = data.inputValue(clumpsFlatness).asFloat(); + hairnode.shavep.slider_val[55][index] = data.inputValue(clumpsScruffle).asFloat(); +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::updateParamsFromDatablock - done", NULL); +#endif + LEAVE(); +} + + +void shaveHairShape::updateParamsFromPlugs(int index) +{ + //printf("shave params updated\n");fflush(stdout); + + ENTER(); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::updateParamsFromPlugs", NULL); +#endif + updateNodeName(); + + MFnDependencyNode nodeFn(thisMObject()); + + hairnode.shavep.slider_val[0][5] = gravityGlob; + + hairnode.shavep.haircount[index] = + nodeFn.findPlug(shaveParamHaircount, true).asInt(); + + setShadowHaircount(index); + + bool di = nodeFn.findPlug(shaveParamNoInterp, true).asBool(); + hairnode.shavep.dontinterpolate = di ? 0 : 1; + hairnode.shavep.passes[index] = (int)nodeFn.findPlug(shaveParamPasses, true).asShort(); + hairnode.shavep.total_guides = (int)nodeFn.findPlug(shaveParamTotalGuides, true).asShort(); + di = nodeFn.findPlug(shaveParamCollide, true).asBool(); + hairnode.shavep.collide[index] = di ? 1 : 0; + hairnode.shavep.collision_method = (int)nodeFn.findPlug(shaveParamCollisionMethod, true).asShort(); + + hairnode.shavep.spec_tint.x = nodeFn.findPlug(aSpecularTint, true).child(0).asFloat(); + hairnode.shavep.spec_tint.y = nodeFn.findPlug(aSpecularTint, true).child(1).asFloat(); + hairnode.shavep.spec_tint.z = nodeFn.findPlug(aSpecularTint, true).child(2).asFloat(); + + hairnode.shavep.spec_tint2.x = nodeFn.findPlug(aSpecularTint2, true).child(0).asFloat(); + hairnode.shavep.spec_tint2.y = nodeFn.findPlug(aSpecularTint2, true).child(1).asFloat(); + hairnode.shavep.spec_tint2.z = nodeFn.findPlug(aSpecularTint2, true).child(2).asFloat(); + + hairnode.shavep.tipfade = (nodeFn.findPlug(aTipFade, true).asBool() ? 1 : 0); + + hairnode.shavep.squirrel = (nodeFn.findPlug(aSquirrel, true).asBool() ? 1 : 0); + + hairnode.shavep.flyaway_percent = (float)nodeFn.findPlug(flyawayPerc, true).asInt()/100.0f; + hairnode.shavep.clumps = nodeFn.findPlug(clumps, true).asInt(); + + hairnode.shavep.rand_seed_offset = nodeFn.findPlug(randomSeedOffset, true).asInt(); + + // + // At the moment we only support collision method 1. However, due + // to an earlier bug, there may be some old shaveHairShapes out there + // with collision method 0. So let's force it back to 1. + // + // %%% When we add support for other collision methods, this will have + // to change. + // + hairnode.shavep.collision_method = 1; + + hairnode.shavep.frizz_anim_dir[index].x = nodeFn.findPlug(shaveParamFrizzAnimDirX, true).asFloat(); + hairnode.shavep.frizz_anim_dir[index].y = nodeFn.findPlug(shaveParamFrizzAnimDirY, true).asFloat(); + hairnode.shavep.frizz_anim_dir[index].z = nodeFn.findPlug(shaveParamFrizzAnimDirZ, true).asFloat(); + hairnode.shavep.segs[index] = (int)nodeFn.findPlug(shaveParamSegs, true).asShort(); + hairnode.shavep.geom_shadow = nodeFn.findPlug(shaveParamGeomShadow, true).asFloat(); + hairnode.shavep.slider_val[0][index] = nodeFn.findPlug(shaveParamFrizzRoot, true).asFloat(); + hairnode.shavep.slider_val[24][index] = nodeFn.findPlug(shaveParamFrizzTip, true).asFloat(); + hairnode.shavep.slider_val[1][index] = nodeFn.findPlug(shaveParamFrizzFreqX, true).asFloat(); + hairnode.shavep.slider_val[30][index] = nodeFn.findPlug(shaveParamFrizzFreqY, true).asFloat(); + hairnode.shavep.slider_val[31][index] = nodeFn.findPlug(shaveParamFrizzFreqZ, true).asFloat(); + hairnode.shavep.slider_val[32][index] = nodeFn.findPlug(shaveParamFrizzAnim, true).asFloat(); + hairnode.shavep.slider_val[38][index] = nodeFn.findPlug(shaveParamKinkRoot, true).asFloat(); + hairnode.shavep.slider_val[2][index] = nodeFn.findPlug(shaveParamKinkTip, true).asFloat(); + hairnode.shavep.slider_val[3][index] = nodeFn.findPlug(shaveParamKinkFreqX, true).asFloat(); + hairnode.shavep.slider_val[34][index] = nodeFn.findPlug(shaveParamKinkFreqY, true).asFloat(); + hairnode.shavep.slider_val[35][index] = nodeFn.findPlug(shaveParamKinkFreqZ, true).asFloat(); + hairnode.shavep.slider_val[26][index] = nodeFn.findPlug(shaveParamSplayRoot, true).asFloat(); + hairnode.shavep.slider_val[27][index] = nodeFn.findPlug(shaveParamSplayTip, true).asFloat(); + hairnode.shavep.slider_val[44][index] = nodeFn.findPlug(shaveParamMultAsp, true).asFloat(); + hairnode.shavep.slider_val[45][index] = nodeFn.findPlug(shaveParamOffset, true).asFloat(); + hairnode.shavep.slider_val[46][index] = nodeFn.findPlug(shaveParamAspect, true).asFloat(); + hairnode.shavep.slider_val[25][index] = (float)nodeFn.findPlug(shaveParamMultStrand, true).asShort(); + hairnode.shavep.slider_val[42][index] = nodeFn.findPlug(shaveParamRandomizeMulti, true).asFloat(); + hairnode.shavep.slider_val[20][index] = nodeFn.findPlug(shaveParamThickRoot, true).asFloat(); + hairnode.shavep.slider_val[37][index] = nodeFn.findPlug(shaveParamThickTip, true).asFloat(); + hairnode.shavep.slider_val[4][index] = nodeFn.findPlug(shaveParamSpecular, true).asFloat(); + hairnode.shavep.slider_val[5][index] = nodeFn.findPlug(shaveParamGloss, true).asFloat(); + hairnode.shavep.slider_val[6][index] = nodeFn.findPlug(shaveParamAmbDiff, true).asFloat(); + hairnode.shavep.slider_val[7][index] = nodeFn.findPlug(shaveParamSelfShadow, true).asFloat(); + hairnode.shavep.slider_val[8][index] = nodeFn.findPlug(shaveParamStiffness, true).asFloat(); + hairnode.shavep.slider_val[36][index] = nodeFn.findPlug(shaveParamRandScale, true).asFloat(); + hairnode.shavep.slider_val[33][index] = nodeFn.findPlug(shaveParamAnimSpeed, true).asFloat(); + hairnode.shavep.slider_val[43][index] = nodeFn.findPlug(shaveParamDisplacement, true).asFloat(); + hairnode.shavep.slider_val[17][index] = nodeFn.findPlug(shaveParamRootRed, true).asFloat()*255.0f; + hairnode.shavep.slider_val[18][index] = nodeFn.findPlug(shaveParamRootGreen, true).asFloat()*255.0f; + hairnode.shavep.slider_val[19][index] = nodeFn.findPlug(shaveParamRootBlue, true).asFloat()*255.0f; + hairnode.shavep.slider_val[9][index] = nodeFn.findPlug(shaveParamHairRed, true).asFloat()*255.0f; + hairnode.shavep.slider_val[10][index] = nodeFn.findPlug(shaveParamHairGreen, true).asFloat()*255.0f; + hairnode.shavep.slider_val[11][index] = nodeFn.findPlug(shaveParamHairBlue, true).asFloat()*255.0f; + hairnode.shavep.slider_val[13][index] = nodeFn.findPlug(shaveParamMutantRed, true).asFloat()*255.0f; + hairnode.shavep.slider_val[14][index] = nodeFn.findPlug(shaveParamMutantGreen, true).asFloat()*255.0f; + hairnode.shavep.slider_val[15][index] = nodeFn.findPlug(shaveParamMutantBlue, true).asFloat()*255.0f; + hairnode.shavep.slider_val[12][index] = nodeFn.findPlug(shaveParamHueVariation, true).asFloat(); + hairnode.shavep.slider_val[39][index] = nodeFn.findPlug(shaveParamValueVariation, true).asFloat(); + hairnode.shavep.slider_val[16][index] = nodeFn.findPlug(shaveParamMutantPercent, true).asFloat(); + hairnode.shavep.slider_val[40][index] = nodeFn.findPlug(shaveParamDampening, true).asFloat(); + hairnode.shavep.slider_val[21][index] = nodeFn.findPlug(shaveParamRootStiffness, true).asFloat(); + hairnode.shavep.slider_val[41][index] = nodeFn.findPlug(shaveParamScale, true).asFloat(); + + hairnode.shavep.slider_val[48][index] = nodeFn.findPlug(flyawayStren, true).asFloat(); + hairnode.shavep.slider_val[47][index] = nodeFn.findPlug(messStren, true).asFloat(); + + hairnode.shavep.slider_val[49][index] = nodeFn.findPlug(clumpsStren, true).asFloat(); + hairnode.shavep.slider_val[50][index] = nodeFn.findPlug(clumpsRotStren, true).asFloat(); + hairnode.shavep.slider_val[51][index] = nodeFn.findPlug(clumpsColStren, true).asFloat(); + hairnode.shavep.slider_val[52][index] = nodeFn.findPlug(clumpsRotOffset, true).asFloat(); + hairnode.shavep.slider_val[53][index] = nodeFn.findPlug(clumpsRandomize, true).asFloat(); + hairnode.shavep.slider_val[54][index] = nodeFn.findPlug(clumpsFlatness, true).asFloat(); + hairnode.shavep.slider_val[55][index] = nodeFn.findPlug(clumpsScruffle, true).asFloat(); +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::updateParamsFromPlugs - done", NULL); +#endif + LEAVE(); +} + + +void shaveHairShape::attrDebug(int index) +{ +for(index = 0; index < 5; index++) +{ + printf("shave MTL index is: %d\n", index); +/* + printf("%d\n", hairnode.shavep.haircount[index]); + printf("%d\n", hairnode.shavep.dontinterpolate); +*/ + printf("PASSES: %d\n", hairnode.shavep.passes[index]); +/* + printf("%d\n", hairnode.shavep.total_guides); + printf("%d\n", hairnode.shavep.instancing_status); + printf("%d\n", hairnode.shavep.collide[index]); + printf("%d\n", hairnode.shavep.collision_method); + printf("%f\n", hairnode.shavep.frizz_anim_dir[index].x); + printf("%f\n", hairnode.shavep.frizz_anim_dir[index].y); + printf("%f\n", hairnode.shavep.frizz_anim_dir[index].z); + printf("%d\n", hairnode.shavep.segs[index]); + printf("%f\n", hairnode.shavep.slider_val[0][index]); + printf("%f\n", hairnode.shavep.slider_val[24][index]); + printf("%f\n", hairnode.shavep.slider_val[1][index]); + printf("%f\n", hairnode.shavep.slider_val[30][index]); + printf("%f\n", hairnode.shavep.slider_val[31][index]); + printf("%f\n", hairnode.shavep.slider_val[32][index]); + printf("%f\n", hairnode.shavep.slider_val[38][index]); + printf("%f\n", hairnode.shavep.slider_val[2][index]); + printf("%f\n", hairnode.shavep.slider_val[3][index]); + printf("%f\n", hairnode.shavep.slider_val[34][index]); + printf("%f\n", hairnode.shavep.slider_val[35][index]); + printf("%f\n", hairnode.shavep.slider_val[26][index]); + printf("%f\n", hairnode.shavep.slider_val[27][index]); + printf("%f\n", hairnode.shavep.slider_val[25][index]); + printf("%f\n", hairnode.shavep.slider_val[20][index]); + printf("%f\n", hairnode.shavep.slider_val[37][index]); + printf("%f\n", hairnode.shavep.slider_val[4][index]); + printf("%f\n", hairnode.shavep.slider_val[5][index]); + printf("%f\n", hairnode.shavep.slider_val[6][index]); + printf("%f\n", hairnode.shavep.slider_val[7][index]); + printf("%f\n", hairnode.shavep.slider_val[8][index]); + printf("%f\n", hairnode.shavep.slider_val[36][index]); + printf("%f\n", hairnode.shavep.slider_val[33][index]); + printf("%f\n", hairnode.shavep.slider_val[40][index]); + printf("%f\n", hairnode.shavep.slider_val[17][index]); + printf("%f\n", hairnode.shavep.slider_val[18][index]); + printf("%f\n", hairnode.shavep.slider_val[19][index]); + printf("%f\n", hairnode.shavep.slider_val[9][index]); + printf("%f\n", hairnode.shavep.slider_val[10][index]); + printf("%f\n", hairnode.shavep.slider_val[11][index]); + printf("%f\n", hairnode.shavep.slider_val[13][index]); + printf("%f\n", hairnode.shavep.slider_val[14][index]); + printf("%f\n", hairnode.shavep.slider_val[15][index]); + printf("%f\n", hairnode.shavep.slider_val[12][index]); + printf("%f\n", hairnode.shavep.slider_val[39][index]); + printf("%f\n", hairnode.shavep.slider_val[16][index]); +*/ + } +} + + +MString shaveHairShape::nodeName() +{ + ENTER(); + + MStringArray split; + MString nodename; + name().split(':', split); + if(split.length() == 1) + nodename = split[0]; + else + { + nodename = split[0]; + for(int i = 1; i < (int)split.length(); i++) + { + nodename += "_"; + nodename += split[i]; + } + } + RETURN(nodename); +} + + +MObject shaveHairShape::getDisplayShape() +{ + ENTER(); + + // + // The display node's shape should be connected to the + // 'displayNode' attribute. + // + MPlug plug(thisMObject(), displayNodeAttr); + MPlugArray connections; + + plug.connectedTo(connections, true, false); + + if (connections.length() == 0) + { + RETURN(MObject::kNullObj); + } + + RETURN(connections[0].node()); +} + + +MObject shaveHairShape::getDisplayTransform() +{ + ENTER(); + + MFnDagNode dagNodeFn(getDisplayShape()); + + // + // Return the first parent transform (there should only be one). + // + RETURN(dagNodeFn.parent(0)); +} + + +MObject shaveHairShape::getShader() +{ + ENTER(); + + MObject displayNode = getDisplayShape(); + + if (!displayNode.isNull()) + { + MFnDependencyNode nodeFn(displayNode); + MPlug groupPlug = nodeFn.findPlug("instObjGroups"); + + if (groupPlug.numConnectedElements() > 0) + { + MPlugArray groupConnections; + groupPlug + .connectionByPhysicalIndex(0) + .connectedTo(groupConnections, false, true); + + // + // If we're connected to a shadingEngine node, then we're done. + // + if (groupConnections.length() > 0) + { +#if 0 + MObject shader = groupConnections[0].node(); + + ////////// debug ///////// + //MFnDependencyNode dFn(shader); + //MGlobal::displayError(MString("? shader ") + dFn.name()); + ////////////////////////// + if (shader.hasFn(MFn::kShadingEngine)) + { + RETURN(shader); + } +#else + for(unsigned int i = 0; i < groupConnections.length(); i++) + { + MObject shader = groupConnections[i].node(); + + if (shader.hasFn(MFn::kShadingEngine)) + { +#ifdef _DEBUG + MFnDependencyNode dFn(shader); + MGlobal::displayInfo(MString("shader ") + dFn.name()); +#endif + RETURN(shader); + } + } +#endif + } + } + } + + RETURN(MObject::kNullObj); +} + +unsigned int shaveHairShape::numLayers() +{ + ENTER(); + int n=0; + MObject displayNode = getDisplayShape(); + + + if (!displayNode.isNull()) + { + MFnDependencyNode nodeFn(displayNode); + MPlug groupPlug = nodeFn.findPlug("instObjGroups"); + + if (groupPlug.numConnectedElements() > 0) + { + MPlugArray groupConnections; + groupPlug + .connectionByPhysicalIndex(0) + .connectedTo(groupConnections, false, true); + + for(unsigned int i = 0; i < groupConnections.length(); i++) + { + MObject c = groupConnections[i].node(); + if(c.hasFn( MFn::kRenderLayer)) + n++; + } + } + } + + RETURN(n); +} + +MObject shaveHairShape::getLayer(unsigned int idx) +{ + ENTER(); + + MObject displayNode = getDisplayShape(); + + if (!displayNode.isNull()) + { + MFnDependencyNode nodeFn(displayNode); + MPlug groupPlug = nodeFn.findPlug("instObjGroups"); + int k = 0; + if (groupPlug.numConnectedElements() > 0) + { + MPlugArray groupConnections; + groupPlug + .connectionByPhysicalIndex(0) + .connectedTo(groupConnections, false, true); + + for(unsigned int i = 0; i < groupConnections.length(); i++) + { + MObject c = groupConnections[i].node(); + if(c.hasFn( MFn::kRenderLayer) && idx == k) + RETURN(c); + + k++; + } + } + } + + RETURN(MObject::kNullObj); +} + +// This is a compute-safe method which retrieves geom data from the +// passed-in datablock, not from MPlugs. +MStatus shaveHairShape::getGeomFromArrayAttr( + MDataBlock& block, + MObject arrayAttr, + MObject componentGroupIDAttr, + MObjectArray& geoms, + MObjectArray& components +) +{ + ENTER(); + + // + // Step through each connected element in the array. + // + // Maya Bug: I used to just get numConnectedElements() then step + // through them using connectionByPhysicalIndex(). But + // the latter continues to report connections after + // they've been broken, which puts it out of synch with + // numConnectedElements(). So I now run through every + // element and check for connections. + // + MArrayDataHandle arrayHdl = block.inputArrayValue(arrayAttr); + MPlug arrayPlug(thisMObject(), arrayAttr); + MArrayDataHandle idArrayHdl = block.inputArrayValue(componentGroupIDAttr); + unsigned int numElements = arrayPlug.evaluateNumElements(); + unsigned int i; + + + for (i = 0; i < numElements; i++) + { + MPlug plug = arrayPlug.elementByPhysicalIndex(i); + MPlugArray srcPlugs; + + plug.connectedTo(srcPlugs, true, false); + + if (srcPlugs.length() > 0) + { + arrayHdl.jumpToElement(plug.logicalIndex()); + + MObject geomData = arrayHdl.inputValue().data(); + geoms.append(geomData); + + // + // If the caller passed us a groupID attribute, then we need to + // grab the components as well. + // + MObject component(MObject::kNullObj); + + if (!componentGroupIDAttr.isNull() + && geomData.hasFn(MFn::kGeometryData)) + { + // + // Let's see if we have a group ID for this node. + // + MPlug idArray(thisMObject(), componentGroupIDAttr); + MPlug idPlug = idArray.elementByLogicalIndex( + plug.logicalIndex() + ); + + if (idPlug.isConnected()) + { + idArrayHdl.jumpToElement(plug.logicalIndex()); + + int groupID = idArrayHdl.inputValue().asInt(); + MFnGeometryData geomDataFn(geomData); + + if (geomDataFn.hasObjectGroup(groupID)) + { + // + // Grab this group's component. + // + component = geomDataFn.objectGroupComponent(groupID); + } + } + } + + components.append(component); + } + } + + RETURN(MS::kSuccess); +} + + +MStatus shaveHairShape::getSelectionListFromArrayAttr( + MSelectionList& list, MObject arrayAttr, MObject componentGroupIDAttr +) +{ + ENTER(); + + // + // Step through each connected element in the array. + // + // Maya Bug: I used to just get numConnectedElements() then step + // through them using connectionByPhysicalIndex(). But + // the latter continues to report connections after + // they've been broken, which puts it out of synch with + // numConnectedElements(). So I now run through every + // element and check for connections. + // + MPlug destArrayPlug(thisMObject(), arrayAttr); + unsigned int numDestElements = destArrayPlug.evaluateNumElements(); + unsigned int i; + + + for (i = 0; i < numDestElements; i++) + { + MPlug destPlug = destArrayPlug.elementByPhysicalIndex(i); + MPlugArray srcPlugs; + + destPlug.connectedTo(srcPlugs, true, false); + + if (srcPlugs.length() > 0) + { + // + // If the source node is not a DAG node, then just add it to + // the list. + // + if (!srcPlugs[0].node().hasFn(MFn::kDagNode)) + list.add(srcPlugs[0].node()); + else + { + // + // If the source plug is an array element then we will + // assume that it is an instanced attribute and use its + // index as our instance number. Otherwise we'll just take + // the first instance for the node. + // + MDagPathArray paths; + MDagPath::getAllPathsTo(srcPlugs[0].node(), paths); + MDagPath instancePath = paths[0]; + + if (srcPlugs[0].isElement()) + { + unsigned instance = srcPlugs[0].logicalIndex(); + + if (instance < paths.length()) + instancePath = paths[instance]; + } + + // + // If the caller passed us a groupID attribute, then we need to + // grab the components as well. + // + MObject component(MObject::kNullObj); + + if (!componentGroupIDAttr.isNull()) + { + MObject geomData; + destPlug.getValue(geomData); + + if (geomData.hasFn(MFn::kGeometryData)) + { + // + // Let's see if we have a group ID for this node. + // + MPlug idArray(thisMObject(), componentGroupIDAttr); + MPlug idPlug = idArray.elementByLogicalIndex( + destPlug.logicalIndex() + ); + + if (idPlug.isConnected()) + { + int groupID; + + idPlug.getValue(groupID); + + MFnGeometryData geomDataFn(geomData); + + if (geomDataFn.hasObjectGroup(groupID)) + { + // + // Grab this group's component. + // + component = geomDataFn.objectGroupComponent(groupID); + } + } + } + } + + // + // Add the node, and component if one was found, to the list. + // + list.add(instancePath, component); + } + } + } + + RETURN(MS::kSuccess); +} + + +MStatus shaveHairShape::getCollisionGeom( + MDataBlock& block, + MObjectArray& geoms, + MObjectArray& components +) +{ + ENTER(); + + MStatus status; + + geoms.clear(); + components.clear(); + + // + // Having learned lessons from the growth list, the collision list is + // contained in a single array attribute, which makes coding a bit + // simpler. + // + status = getGeomFromArrayAttr( + block, + collisionObjectsAttr, + collisionObjectsGroupIDAttr, + geoms, + components + ); + + RETURN(status); +} + + +MStatus shaveHairShape::getGrowthGeom( + MDataBlock& block, + MObjectArray& geoms, + MObjectArray& components +) +{ + ENTER(); + + MStatus status; + + geoms.clear(); + components.clear(); + + if (getHairGroup(&block) == 4) + { + // + // We've got spline-based hair, so the only growth surfaces we care + // about are curves. + // + status = getGeomFromArrayAttr( + block, inputCurve, MObject::kNullObj, geoms, components + ); + } + else + { + // + // We've got surface-based hair. First, add the NURBS and subd + // surfaces. + // + status = getGeomFromArrayAttr( + block, inputSurf, MObject::kNullObj, geoms, components + ); + + // + // Now add the meshes. With meshes it's possible to grow from just + // a subset of its faces, so we also pass down the array attribute + // containing the component group IDs for the meshes. + // + if (status) + { + status = getGeomFromArrayAttr( + block, + inputMesh, + growthObjectsGroupIDAttr, + geoms, + components + ); + } + } + + RETURN(status); +} + + +MStatus shaveHairShape::getCollisionList(MSelectionList& list) +{ + ENTER(); + + MStatus status; + + list.clear(); + + // + // Having learned lessons from the growth list, the collision list is + // contained in a single array attribute, which makes coding a bit + // simpler. + // + status = getSelectionListFromArrayAttr( + list, collisionObjectsAttr, collisionObjectsGroupIDAttr + ); + + RETURN(status); +} + + +MStatus shaveHairShape::getGrowthList(MSelectionList& list) +{ + ENTER(); + + MStatus status; + + list.clear(); + + if (getHairGroup() == 4) + { + // + // We've got spline-based hair, so the only growth surfaces we care + // about are curves. + // + status = getSelectionListFromArrayAttr(list, inputCurve); + } + else + { + // + // We've got surface-based hair. First, add the NURBS and subd + // surfaces. + // + status = getSelectionListFromArrayAttr(list, inputSurf); + + // + // Now add the meshes. With meshes it's possible to grow from just + // a subset of its faces, so we also pass down the array attribute + // containing the component group IDs for the meshes. + // + if (status) + { + status = getSelectionListFromArrayAttr( + list, inputMesh, growthObjectsGroupIDAttr + ); + } + } + + RETURN(status); +} + + +MObject shaveHairShape::createGeomSet(const MObject& setAttr) +{ + MStatus st; + + // Create the set. + MDGModifier dgmod; + MObject set = dgmod.createNode("objectSet", &st); + MString setType = (setAttr == aGrowthSet ? "growth" : "collision"); + + // Rename it to something useful. + MString setName = name() + "_" + setType; + if (st) st = dgmod.renameNode(set, setName); + + // Connect its 'message' attr to the attr which was passed to us. + if (st) + { + st = dgmod.connect( + set, + MPxObjectSet::message, + thisMObject(), + setAttr + ); + } + + // Commit the changes. + if (st) st = dgmod.doIt(); + + if (!st || set.isNull()) + { + MGlobal::displayError( + MString("shaveHairShape: cannot create ") + setType + " set" + ); + set = MObject::kNullObj; + } + + return set; +} + + +MStatus shaveHairShape::setGeomList( + const MObject& setAttr, const MSelectionList& list +) +{ + MStatus st; + MObject set = getGeomSet(setAttr); + + // If there's no set then create one. + if (set.isNull()) + { + set = createGeomSet(setAttr); + if (set.isNull()) return MS::kFailure; + } + + MFnSet setFn(set, &st); + setFn.clear(); + + // Maya displays a warning if the added list is empty. Let's avoid that. + // + if (list.length() > 0) { + st = setFn.addMembers(list); + } + + // Maya Bug: If we don't retrieve the new member list, the + // dagSetMembers connections won't be there when we go + // looking for them later. + MSelectionList dummy; + setFn.getMembers(dummy, false); + + // Update our geometry connections to reflect the new list. + if (st) st = connectGeometry(setAttr, set); + + if (mNodeInitialized) + { + // If the set of surfaces to which we are attached is the same as + // before, but the faces within those surfaces has changed, our + // topology change detection code won't be able to detect that because + // the WFTYPE has no indication of which faces within each surface are + // growth faces: Shave keeps track of that info internally. So we must + // force the node to do an xplant the next time it is evaluated. + scheduleXplant(); + } else { + // Can't do an xplant yet. Let the node know that one will be + // required once it's ready. + // + mXplantRequired = true; + } + + return st; +} + + +// The DAG networks surrounding the object sets used to hold growth and +// collision geometry sometimes get broken for reasons we've not yet +// been able to identify. +// +// When you add an entire mesh to a set, Maya makes the following connection: +// +// (1a) mesh.instObjGroups[I] -> set.dagSetMembers[N] +// +// where 'I' is the instance number (usually 0) and N is the next available +// index. +// +// If you only add a portion of a mesh (i.e. a subset of its components) +// to a set, Maya will instead make these connection +// +// (1b) mesh.instObjGroups[I].objectGroups[M] -> set.dagSetMembers[N] +// (2) groupIdNode.message -> set.groupNodes[N] +// (3) groupIdNode.groupId -> mesh.instObjGroups[I].objectGroups[M].objectGroupId +// +// Where 'groupIdNode' is a groupId node that Maya creates automatically. +// +// Note that Maya also creates a groupParts node and makes connections to +// it, but that's not of interest here. +// +// In either case, Shave will add the following connections: +// +// (4) mesh.worldMesh[I] -> hairNode.inputMesh[K] +// (5) set.message -> hairNode.growthSet +// +// If only a portion of the mesh has been added, then Shave will make one +// further connection: +// +// (6) mesh.instObjGroups[I].objectGroups[N].objectGroupId -> hairNode.growthObjectsGroupID[K] +// +// You can replace 'growth' with 'collision' when dealing with the collision +// set. +// +// +// This method attempts to fix the following problems: +// +// o Connection (1b) is present which means that only a subset of the +// geometry's components are included in the set, but connection (6) +// is missing. +// +// o Connection (5) is missing but there are some connection (6)s and +// they all belong to the same set. +// +MStatus shaveHairShape::repairGeomList( + const MObject& setAttr, const MObject& groupIdAttr +) +{ + MStatus st; + bool needRepair = false; + MObject set = getGeomSet(setAttr); + + if (set.isNull()) + { + bool tooManySets = false; + + // There's no set connected to the attr. We need to repair that. + // + needRepair = true; + + // If there are any partial meshes in the set then there should be + // one or more connection (6)s which we can follow back to the set. + // + MPlug groupIdPlug(thisMObject(), groupIdAttr); + unsigned int numElements = groupIdPlug.evaluateNumElements(); + + for (unsigned int i = 0; (i < numElements) && !tooManySets; ++i) + { + // Find the source plug for this connection (6). + // + MPlug element = groupIdPlug.elementByPhysicalIndex(i); + MPlugArray conns; + + element.connectedTo(conns, true, false); + + if (conns.length() == 0) continue; + + // We should now be at the objectGroupId plug on some + // DAG node. Follow its connection (3) to the groupId node. + // + conns[0].connectedTo(conns, true, false); + + if (conns.length() == 0) continue; + + MObject groupIdNode = conns[0].node(); + + if (!groupIdNode.hasFn(MFn::kGroupId)) continue; + + // Follow the groupId node's message plug to all of its + // destinations. One of them should be a connection (2) + // to a set node. + // + MPlug msgPlug(groupIdNode, MPxNode::message); + + msgPlug.connectedTo(conns, false, true); + + unsigned int numDestinations = conns.length(); + + if (numDestinations == 0) continue; + + MObject thisGroupsSet; + + for (unsigned int j = 0; (j < numDestinations); ++j) + { + MObject destNode = conns[0].node(); + + if (destNode.hasFn(MFn::kSet)) + { + // If this is the first set that we've found for this + // groupId, save it. + // + if (thisGroupsSet.isNull()) + { + thisGroupsSet = destNode; + } + else if (destNode != thisGroupsSet) + { + // This groupId is connected to multiple sets. + // I don't *think* that's legal, but even if it is + // there's no way for us to know which one is the + // right one, so we must skip this group. + // + thisGroupsSet = MObject::kNullObj; + break; + } + } + } + + if (!thisGroupsSet.isNull()) + { + // If this is the first set we've found then save it. + // + if (set.isNull()) + { + set = thisGroupsSet; + } + else if (thisGroupsSet != set) + { + // There is no common set that all the partial meshes + // agree on. We have to give up. + // + set = MObject::kNullObj; + tooManySets = true; + } + } + } + + // If we couldn't find an appropriate set then there's nothing more + // we can do. + // + if (set.isNull()) + { + st = MS::kFailure; + } + else + { + // Connect the set's message attr to the setAttr which was passed + // to us. i.e. make connection (5) + // + MDGModifier dgmod; + + st = dgmod.connect( + set, MPxObjectSet::message, thisMObject(), setAttr + ); + + if (st) + { + st = dgmod.doIt(); + } + } + } + else + { + // Check that for each connection (1b) to the set's + // dagSetMembers plug, there is a corresponding connection (6) + // to the groupIdAttr which was passed in to us. + // + MPlug dsmPlug(set, MPxObjectSet::dagSetMembers); + + unsigned int numElements = dsmPlug.evaluateNumElements(); + + for (unsigned int i = 0; (i < numElements) && !needRepair; ++i) + { + MPlug element = dsmPlug.elementByPhysicalIndex(i); + MPlugArray conns; + + // Follow connection (1b) back to its source. + // + element.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + MPlug plug = conns[0]; + + // If the source plug is some DAG node's objectGroups + // attr, then step down to its objectGroupId child and see + // if that has a connection (6) to our node's groupIdAttr. + // + if (plug == MPxTransform::objectGroups) + { + bool foundConnection = false; + + plug = plug.child(MPxTransform::objectGroupId); + plug.connectedTo(conns, false, true); + + for (unsigned i = 0; (i < conns.length()) && !foundConnection; ++i) + { + foundConnection = (conns[i].node() == thisMObject()) + &&(conns[i].attribute() == groupIdAttr); + } + + if (!foundConnection) + { + needRepair = true; + } + } + } + } + } + + // If we need repair, and nothing has gone wrong along the way, then redo + // all the geometry connections. + // + if (needRepair && st) + { + st = connectGeometry(setAttr, set); + } + + return st; +} + + +// Connect the geometry in a growth or collision set to the corresponding +// attributes on the shaveHairShape. +MStatus shaveHairShape::connectGeometry(MObject setAttr, MObject set) +{ + MStatus st; + + // Get rid of all of our existing connections. + MDGModifier* dgmod = new MDGModifier; + MObject groupIDAttr; + + if (setAttr == aCollisionSet) + { + disconnectIncoming(collisionObjectsAttr, dgmod); + groupIDAttr = collisionObjectsGroupIDAttr; + } + else if (setAttr == aGrowthSet) + { + bool splineLock; + MPlug splineLockTrigger(thisMObject(), aSplineLockTrigger); + + splineLockTrigger.getValue(splineLock); + + if (!splineLock) disconnectIncoming(inputCurve, dgmod); + disconnectIncoming(inputMesh, dgmod); + disconnectIncoming(inputSurf, dgmod); + groupIDAttr = growthObjectsGroupIDAttr; + } + else + { + delete dgmod; + return MS::kInvalidParameter; + } + + disconnectIncoming(groupIDAttr, dgmod); + + st = dgmod->doIt(); + + delete dgmod; + dgmod = NULL; + + if (st && !set.isNull()) + { + MFnSet setFn(set, &st); + + // Step through all of the dagSetMembers connections and make + // corresponding connections to this node. + MPlugArray conns; + MPlug dsmArray(set, MPxObjectSet::dagSetMembers); + MPlug ourGroupArray(thisMObject(), groupIDAttr); + MPlug setGroupArray(set, MPxObjectSet::groupNodes); + MPlug element; + unsigned int i; + unsigned int nextIndex = 0; + unsigned int numElements = dsmArray.evaluateNumElements(); + + dgmod = new MDGModifier; + + for (i = 0; i < numElements; ++i) + { + element = dsmArray.elementByPhysicalIndex(i); + element.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + MPlug plug = conns[0]; + + // Which attribute carries the shape's world geometry, + // and to which attribute do we need to connect it? + MObject shape = plug.node(); + MObject targetAttr; + MObject worldAttr; + + if (!getWorldGeomAttrs(shape, worldAttr, targetAttr)) + continue; + + // For collision surfaces the target is always the + // 'collisionObjects' attr. + if (setAttr == aCollisionSet) targetAttr = collisionObjectsAttr; + + // The connection will be to either an element of the + // growth node's 'objectGroups' array, or to an element of + // its 'instObjGroups' array. If it's to an 'objectGroups' + // element then the connection represents a subset of the + // object's faces and we need to get a connection to its + // corresponding groupId. + if (plug == MPxTransform::objectGroups) + { + MPlug groupIdPlug = plug.child(MPxTransform::objectGroupId); + + dgmod->connect( + groupIdPlug, + ourGroupArray.elementByLogicalIndex(nextIndex) + ); + + // Step up to the 'instObjGroups' element since that's + // what the rest of the code is expecting. + plug = plug.array().parent(); + } + + // We should now have an 'instObjGroups' element. Its + // logical index will be the instance number for the + // shape. + unsigned int instance = plug.logicalIndex(); + + // Get the world shape plug for the appropriate instance. + MPlug worldPlug(shape, worldAttr); + worldPlug = worldPlug.elementByLogicalIndex(instance); + + // Get the target plug on our hair node. + MPlug targetPlug(thisMObject(), targetAttr); + targetPlug = targetPlug.elementByLogicalIndex(nextIndex); + + // Connect them. + dgmod->connect(worldPlug, targetPlug); + + nextIndex++; + } + } + + st = dgmod->doIt(); + } + + return st; +} + + +MObject shaveHairShape::getGeomSet(const MObject& attr) +{ + MObject set; + MPlug plug(thisMObject(), attr); + MPlugArray conns; + + plug.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + if (conns[0].node().hasFn(MFn::kSet)) + set = conns[0].node(); + else + { + // This is not a valid connection so get rid of it. + MDGModifier dgmod; + dgmod.disconnect(conns[0], plug); + dgmod.doIt(); + } + } + + return set; +} + + +MString shaveHairShape::getUVSet(MObject growthObject, MObject texture) +{ + ENTER(); + + MString uvSetName(""); + MPlug assignments(thisMObject(), uvSetAssignmentsAttr); + unsigned int numAssignments = assignments.evaluateNumElements(); + MPlugArray connections; + unsigned int i; + + for (i = 0; i < numAssignments; i++) + { + // + // Find the connection feeding into this assignment's uvSetName + // plug. + // + assignments[i].child(uvSetNameAttr).connectedTo( + connections, true, false + ); + + // + // Does it come from the growth object? + // + if ((connections.length() > 0) + && (connections[0].node() == growthObject)) + { + // + // Step through each of the textures for which this growth + // object has an explicit UV set assignment. + // + MPlug textures = assignments[i].child(uvSetTexturesAttr); + unsigned int numTextures = textures.evaluateNumElements(); + unsigned int t; + + for (t = 0; t < numTextures; t++) + { + // + // Find the texture node which is supplying this + // connection. + // + textures[t].connectedTo(connections, true, false); + + // + // Does it match the texture we're looking for? + // + if ((connections.length() > 0) + && (connections[0].node() == texture)) + { + // + // Return the UV set name for this assignment. + // + assignments[i].child(uvSetNameAttr).getValue(uvSetName); + + RETURN(uvSetName); + } + } + } + } + + // + // No assignment was found for this combination of growth object and + // texture, so return the empty string. + // + RETURN(uvSetName); +} + + +void shaveHairShape::disconnectIncoming(const MObject& attr, MDGModifier* dgmod) +{ + MPlug plug(thisMObject(), attr); + + disconnectPlugTree(plug, true, dgmod); +} + + +void shaveHairShape::disconnectOutgoing(const MObject& attr, MDGModifier* dgmod) +{ + MPlug plug(thisMObject(), attr); + + disconnectPlugTree(plug, false, dgmod); +} + + +void shaveHairShape::disconnectAll(const MObject& attr, MDGModifier* dgmod) +{ + MPlug plug(thisMObject(), attr); + + disconnectPlugTree(plug, true, dgmod); + disconnectPlugTree(plug, false, dgmod); +} + + +void shaveHairShape::disconnectPlugTree( + MPlug& plug, bool incoming, MDGModifier* dgmod +) +{ + bool doCommit = false; + + if (dgmod == NULL) + { + dgmod = new MDGModifier; + doCommit = true; + } + + if (plug.isLocked()) plug.setLocked(false); + + MPlugArray conns; + plug.connectedTo(conns, incoming, !incoming); + + unsigned int i; + for (i = 0; i < conns.length(); ++i) + { + if (conns[i].isLocked()) conns[i].setLocked(false); + + if (incoming) + dgmod->disconnect(conns[i], plug); + else + dgmod->disconnect(plug, conns[i]); + } + + if (plug.isArray()) + { + unsigned int numElements = plug.evaluateNumElements(); + MPlug element; + + for (i = 0; i < numElements; ++i) + { + // For some reason, if I pass plug.elementByPhysicalIndex(i) + // directly to the disconnectPlugTree call, the compiler + // chooses the 'const MObject&' version rather than the + // 'MPlug&' version. So let's be more explicit about this. + element = plug.elementByPhysicalIndex(i); + disconnectPlugTree(element, incoming, dgmod); + } + } + else if (plug.isCompound()) + { + MPlug child; + + for (i = 0; i < plug.numChildren(); ++i) + { + // For some reason, if I pass plug.child(i) + // directly to the disconnectPlugTree call, the compiler + // chooses the 'const MObject&' version rather than the + // 'MPlug&' version. So let's be more explicit about this. + child = plug.child(i); + disconnectPlugTree(child, incoming, dgmod); + } + } + + if (doCommit) + { + dgmod->doIt(); + delete dgmod; + } +} + + +void shaveHairShape::deleteMe(MDGModifier& dgMod) +{ + ENTER(); + + MFnDependencyNode nodeFn(thisMObject()); + + if (nodeFn.isLocked()) nodeFn.setLocked(false); + + // + // If the node is from a referenced file, we can't delete + // it, so just make it invisible (and lock it, if possible). + // + if (nodeFn.isFromReferencedFile()) + { + // Set the visibility to false. + // + MPlug plug(thisMObject(), MPxSurfaceShape::visibility); + + if (plug.isLocked()) plug.setLocked(false); + + plug.setValue(false); + plug.setLocked(true); + + // Break any connections from the output mesh. + // + disconnectOutgoing(outputMesh, &dgMod); + + // Break all incoming connections to growth and collision objects. + // + disconnectIncoming(inputMesh, &dgMod); + disconnectIncoming(inputCurve, &dgMod); + disconnectIncoming(inputSurf, &dgMod); + disconnectIncoming(collisionObjectsAttr, &dgMod); + + // + // Destroy anything else of value. + // + SHAVEclear_scene(); + + clearBlindDataAttr(shaveBlindHair); + + SHAVEfree_node(&hairnode); + SHAVEinit_node(&hairnode, getShaveID()); + nodeFn.setLocked(true); + } + else + { + dgMod.deleteNode(thisMObject()); + } + + LEAVE(); +} + + +void shaveHairShape::clearBlindDataAttr(MObject& attr) +{ + ENTER(); + + setBlindDataAttr(attr, NULL, 0); + + LEAVE(); +} + + +void shaveHairShape::setBlindDataAttr(MObject& attr, const void* data, long dataLen) +{ + ENTER(); + + blindShaveData* blindData = new blindShaveData; + + blindData->setValue(data, dataLen); + + MPlug plug(thisMObject(), attr); + bool plugWasLocked = plug.isLocked(); + + if (plugWasLocked) plug.setLocked(false); + + plug.setValue((MPxData*)blindData); + + if (plugWasLocked) plug.setLocked(true); + + LEAVE(); +} + + +SHAVENODE* shaveHairShape::getHairNode() +{ + ENTER(); + + // + // Grab the trigger value to ensure that the hairnode is up to date. + // + MPlug plug(thisMObject(), triggerAttr); + float value; + plug.getValue(value); + + RETURN(&hairnode); +} + + +void shaveHairShape::setShadowHaircount(int group) +{ + ENTER(); + + int n; + + n = (int)(shaveShadowHairRatioGlob * (float)hairnode.shavep.haircount[group] + + 0.5f); + + // + // If the shadow/hair ratio is non-zero, then make sure that we always + // get at least one shadow hair. + // + if ((n < 1) && (shaveShadowHairRatioGlob > 0.0001f)) n = 1; + + hairnode.shavep.shadowHaircount[group] = n; + + LEAVE(); +} + + +// +// Create a mesh for the hairs, for use in a separate mesh node (i.e. does +// not affect the shaveHairShape's display node). +// +MObject shaveHairShape::createExternalMesh(MObject parent, MStatus* st) +{ + ENTER(); + + if (st) *st = MS::kSuccess; + + MDataBlock block = forceCache(); + + RETURN(makePolyMeshData(block, parent, false, false)); +} + + +// +// Recomb the hair using a set of curves passed in as a WFTYPE. +// +void shaveHairShape::recomb(WFTYPE& curves) +{ + SHAVEspline_recomb(&hairnode, &curves); + + // The contents of 'hairnode' will likely have changed, so we must + // update the blind data which relies on it. + // + updateBlindData(); + + // The changes to 'hairnode' probably mean that the output mesh should + // be recomputed. Let's force that. + // + dirtyOutputMesh(); +} + + +#if 0 +static MFloatPoint interpolate( + MFloatPoint p1, MFloatPoint p2, MFloatPoint p3, MFloatPoint p4, float u +) +{ + float ret; + float u3, u2; + + if (u >= 1.0f) RETURN(p3); + if (u <= 0.0f) RETURN(p2); + + u3 = u * u * u; + u2 = u * u; + + return ((-u3 + (2.0f * u2) - u) * p1 + + (3.0f * u3 - 5.0f * u2 + 2.0) * p2 + + (-3.0f * u3 + (4.0f * u2) + u) * p3 + + (u3 + -u2) * p4) / 2.0f; +} +#endif + + +// +// Take the edits to spline hair guides and apply them to corresponding +// curves in Maya. +// +void shaveHairShape::updateSplines() +{ + ENTER(); + + MStatus st; + + // + // If this isn't spline hair then there aren't any splines to update. + // + if (getHairGroup() != 4) LEAVE(); + + // + // Create a fitBspline node feeding into a rebuildCurve node. + // + MDGModifier dgMod; + +#if 0 + MObject rebuildNode = dgMod.createNode("rebuildCurve", &st); +#else + MFnDependencyNode rebuildNodeFn; + MObject rebuildNode = rebuildNodeFn.create( + "rebuildCurve", + "shave_rebuildCurveTemp", + &st + ); +#endif + MChkErrVoid(st, "cannot create 'rebuildCurve' node."); + + MObject fitNode = dgMod.createNode("fitBspline", &st); + MChkErrVoid(st, "cannot create 'fitBspline' node."); + + MFnDependencyNode fitNodeFn(fitNode); +#if 0 + MFnDependencyNode rebuildNodeFn(rebuildNode); +#endif + MPlug curveOut = fitNodeFn.findPlug("outputCurve"); + MPlug curveIn = rebuildNodeFn.findPlug("inputCurve"); + + st = dgMod.connect(curveOut, curveIn); + MChkErrVoid(st, "cannot connect 'fitBspline' node to 'rebuildCurve' node."); + + st = dgMod.doIt(); + MChkErrVoid(st, "cannot commit updateSplines network."); + + curveIn = fitNodeFn.findPlug("inputCurve"); + curveOut = rebuildNodeFn.findPlug("outputCurve"); + + MSelectionList curves; + + getGrowthList(curves); + + int curveNum = 0; + MDagPath curvePath; + SOFTGUIDE guide; + int i; + MItSelectionList iter(curves); + + for (iter.reset(); !iter.isDone(); iter.next()) + { + iter.getDagPath(curvePath); + curvePath.extendToShape(); + + if (curvePath.hasFn(MFn::kNurbsCurve)) + { +#if 0 + if (SHAVEfetch_guideNOISE(curveNum++, &guide) == -1) +#else + if (SHAVEfetch_guide(curveNum++, &guide) == -1) +#endif + { + MGlobal::displayWarning( + "Shave: spline hair has too few guides." + ); + break; + } + + // + // Set up the rebuildCurve node to produce the same type of + // curve as we already have. + // + MFnNurbsCurve curveFn(curvePath); + MPlug plug; + + plug = rebuildNodeFn.findPlug("spans"); + plug.setValue(curveFn.numSpans()); + + plug = rebuildNodeFn.findPlug("degree"); + plug.setValue(curveFn.degree()); + + // + // Get the guide's vertices. + // + MPointArray guidePts(SHAVE_VERTS_PER_GUIDE); + MDoubleArray knots(SHAVE_VERTS_PER_GUIDE); + double paramPerKnot = 1.0 / (double)(SHAVE_VERTS_PER_GUIDE-1); + + for (i = 0; i < SHAVE_VERTS_PER_GUIDE; i++) + { + guidePts[i] = MPoint( + guide.guide[i].x, + guide.guide[i].y, + guide.guide[i].z + ); + knots[i] = paramPerKnot * (double)i; + } + + // + // Make sure that the curve ends exactly on param value 1. + // + knots[i-1] = 1.0; + + // + // Create a degree-1 curve. + // + MFnNurbsCurveData curveDataFn; + MObject guideCurveData = curveDataFn.create(); + MFnNurbsCurve guideCurveFn; + MObject guideCurve; + + guideCurve = guideCurveFn.create( + guidePts, + knots, + 1, + MFnNurbsCurve::kOpen, + false, + true, + guideCurveData, + &st + ); + + if (!st) + { + MRptErr(st, "cannot create degree-1 curve for guide."); + break; + } + + // + // Place the curve on the fitBspline node's input. + // + curveIn.setValue(guideCurveData); + + // + // Get the output curve from the rebuildCurve node. + // + curveOut.getValue(guideCurveData); + + // + // Get the positions of the resulting curve's cvs. + // + MPointArray cvs; + + guideCurveFn.setObject(guideCurveData); + st = guideCurveFn.getCVs(cvs); + + if (!st) + { + MRptErr(st, "cannot get cvs for rebuilt guide."); + break; + } + + // + // Move the original curve's cvs to the same positions. + // + st = curveFn.setCVs(cvs, MSpace::kWorld); + + if (!st) + { + MRptErr(st, "cannot set new cvs positions on guide spline."); + break; + } + + curveFn.updateCurve(); + } + } + + dgMod.undoIt(); + + MDGModifier dgMod2; + dgMod2.deleteNode(rebuildNode); + dgMod2.doIt(); + + LEAVE(); +} + +int shaveHairShape::getDisplayMode() const +{ + short hairDisplayMode; + MPlug plug(thisMObject(),shaveHairShape::dspyMode); + plug.getValue(hairDisplayMode); + + return hairDisplayMode; +} + +bool shaveHairShape::getDisplayGuides() const +{ + shaveGlobals::getGlobals(); + return displayGuidesGlob; +// bool displayGuides; +// MPlug plug(thisMObject(),shaveHairShape::aDisplayGuides); +// plug.getValue(displayGuides); + +// return displayGuides; +} + +bool shaveHairShape::getDoHairXparency() const +{ + shaveGlobals::getGlobals(); + return doHairXparencyGlob; + //bool b; + //MPlug plug(thisMObject(), displayHiarDoXparency); + //plug.getValue(b); + //return b; +} + + +float shaveHairShape::getHairXparency() const +{ + float f; + MPlug plug(thisMObject(), displayHiarXparency); + plug.getValue(f); + return f; +} + +bool shaveHairShape::getDoFallback() const +{ + return doFallbackGlob; +} + +bool shaveHairShape::getDoTipfade() const +{ + bool b; + MPlug plug(thisMObject(), aTipFade); + plug.getValue(b); + return b; +} + +float shaveHairShape::getAmbDiff() const +{ + float v; + MPlug plug(thisMObject(), shaveParamAmbDiff); + plug.getValue(v); + return v; +} + +MColor shaveHairShape::getSpecularTint() const +{ + MColor v; + // create a plug to the attribute on this node + MPlug plug(thisMObject(), aSpecularTint); + + // use the plug to get the compound attribute + MObject object; + plug.getValue(object); + + // attach a numeric attribute function set to the color + MFnNumericData fn(object); + + // get the data from the color and wang in val + fn.getData(v.r, v.g, v.b); + + return v; +} + +MColor shaveHairShape::getSpecularTint2() const +{ + MColor v; + // create a plug to the attribute on this node + MPlug plug(thisMObject(), aSpecularTint2); + + // use the plug to get the compound attribute + MObject object; + plug.getValue(object); + + // attach a numeric attribute function set to the color + MFnNumericData fn(object); + + // get the data from the color and wang in val + fn.getData(v.r, v.g, v.b); + + return v; +} + + +float shaveHairShape::getSpecular() const +{ + float v; + MPlug plug(thisMObject(), shaveParamSpecular); + plug.getValue(v); + return v; +} + +float shaveHairShape::getGloss() const +{ + float v; + MPlug plug(thisMObject(), shaveParamGloss); + plug.getValue(v); + return v; +} + +unsigned shaveHairShape::getNumPasses() const +{ + int passes; + MPlug plugp(thisMObject(), shaveParamPasses); + plugp.getValue(passes); + + return passes; +} +// +// Apply the display parameters to 'totalCount' and return the number of +// them which should be displayed. +// +unsigned shaveHairShape::getDisplayCount( + unsigned totalCount, bool forcefallback, MDataBlock* block + ) const +{ + ENTER(); + + int passes; + + if (block != NULL) + { + passes = block->inputValue(shaveParamPasses).asInt(); + } + else + { + MPlug plug(thisMObject(), shaveParamPasses); + plug.getValue(passes); + } + + shaveGlobals::getGlobals(); + + unsigned displayCount = (unsigned)(displayRatioGlob * (float)totalCount + 0.5); + if (displayCount > (unsigned)totalCount) + { + displayCount = (unsigned)totalCount; + } + + unsigned int c = passes*displayCount; + + +#ifdef GLOBAL_FALLBACK + bool fallback = doFallbackGlob; + if(forcefallback || liveModeGlob || (fallback && ( dirties.BRUSH_MOUSE_DOWN || dirties.GLOBAL_MOUSE_DOWN || (dirties.BRUSH_JUST_MOVED && IsToolActive())))) +#else + if(forcefallback || doFallbackGlob) +#endif + { + float fallback_ratio = fallbackRatioGlob; + + if (fallback_ratio < 0.0f) + fallback_ratio = 0.0f; + else if (fallback_ratio > 1.0f) + fallback_ratio = 1.0f; + + unsigned displayCount = (unsigned)(fallback_ratio * (float)totalCount + 0.5); + if (displayCount > (unsigned)totalCount) + { + displayCount = (unsigned)totalCount; + } + + c = passes*displayCount; + + //c *= fallback_ratio; + + //c /= 10; + } + RETURN(c); +} + + +// +// Return the number of hairs which should be displayed. +// +unsigned shaveHairShape::getNumDisplayHairs( + bool forcefallback, MDataBlock* block + ) const +{ + ENTER(); + + RETURN(getDisplayCount( + (unsigned)hairnode.shavep.haircount[getHairGroup(block)], + forcefallback, + block + )); +} + + +// +// Return the number of segments to use when displaying hair. +// +int shaveHairShape::getNumDisplaySegments() const +{ + ENTER(); + + int hairGroup = getHairGroup(); + + //int numSegs; + //MPlug plug(thisMObject(), aDisplaySegmentLimit); + //plug.getValue(numSegs); + + shaveGlobals::getGlobals(); + float p = 0.01f*displaySegmentLimitGlob; + int numSegs = (int)(p*(float)hairnode.shavep.segs[hairGroup]); + + //if (hairnode.shavep.segs[hairGroup] < numSegs) + // numSegs = hairnode.shavep.segs[hairGroup]; + + if (numSegs < 1) numSegs = 1; + + RETURN(numSegs); +} + + +void shaveHairShape::computeBoundingBox(MBoundingBox& bbox, MDataBlock& block) +{ + ENTER(); + + bbox.clear(); + + if (mNodeInitialized) + { + WFTYPE hairGeom; + + hairGeom.totalverts = 0; + init_geomWF(&hairGeom); + + // + // Calculate the bounding box for the hair. + // + CURVEINFO curveInfo; + unsigned hair; + int hairGroup = getHairGroup(&block); + unsigned numHairsToDraw = getNumDisplayHairs(&block); + int v; + + for (hair = 0; hair < numHairsToDraw; hair++) + { + SHAVEmake_a_curve( + 0, + hairGroup, + (int)hair, + hairnode.shavep.segs[hairGroup], + &hairGeom, + &curveInfo + ); + + if (!curveInfo.killme) + { + for (v = 0; v < hairGeom.totalverts; v++) + { + bbox.expand( + MPoint(hairGeom.v[v].x,hairGeom.v[v].y,hairGeom.v[v].z) + ); + } + } + } + + free_geomWF(&hairGeom); + } + + LEAVE(); +} + + +// +// Unless Joe can give me a cheaper way of getting the bounding box, we'd +// best not supply the standard 'boundingBox' method because it's too slow +// and gets called too frequently. +// +// I've provided a renamed version so that the bounding box draw can use it +// but Maya won't. +// +MBoundingBox shaveHairShape::boundingBoxTemp() const +{ + ENTER(); + + MPlug plug(thisMObject(), aCachedBBoxMin); + MFnNumericData dataFn; + MObject dataObj; + MPoint bboxMin; + MPoint bboxMax; + + plug.getValue(dataObj); + dataFn.setObject(dataObj); + dataFn.getData(bboxMin.x, bboxMin.y, bboxMin.z); + + plug.setAttribute(aCachedBBoxMax); + plug.getValue(dataObj); + dataFn.setObject(dataObj); + dataFn.getData(bboxMax.x, bboxMax.y, bboxMax.z); + + MBoundingBox bbox(bboxMin, bboxMax); + + RETURN(bbox); +} + + +const shaveHairShape::GuidesSnapshot& shaveHairShape::getGuides() +{ + ENTER(); + + if (mNodeInitialized) + { + // If we're not the current hair shape, then clear all of our hidden + // flags. + // + MDagPath currentHair = shaveUtil::getCurrentHairShape(); + unsigned i; + + if (!currentHair.isValid() || (thisMObject() != currentHair.node())) + { + for (i = 0; i < mGuideCache.guides.size(); i++) + mGuideCache.guides[i].hidden = false; + } + + // If the frame has changed, save the previous cache. + // + if (mGuideCache.frame != mShaveFrame) + { + mPrevGuides = mGuideCache; + } + + // If the cache is dirty, update it. + // + mGuideCache.frame = mShaveFrame; + + if (mGuideCacheDirty) + { + Guide g; + SOFTGUIDE sg; + int v; + + makeCurrent(false); + + g.hidden = false; + g.select = 0; + g.verts.setLength(SHAVE_VERTS_PER_GUIDE); + + for (i = 0; SHAVEfetch_guide((int)i, &sg) != -1; i++) + { + for (v = 0; v < SHAVE_VERTS_PER_GUIDE; v++) + { + // + // Reuse any existing cache elements, preserving their + // selection info. + // + if (i < mGuideCache.guides.size()) + { + Guide& existing = mGuideCache.guides[i]; + + existing.verts[v].x = sg.guide[v].x; + existing.verts[v].y = sg.guide[v].y; + existing.verts[v].z = sg.guide[v].z; + existing.hidden = (sg.hidden != 0); + } + else + { + g.verts[v].x = sg.guide[v].x; + g.verts[v].y = sg.guide[v].y; + g.verts[v].z = sg.guide[v].z; + g.hidden = (sg.hidden != 0); + } + } + + // + // If we've run out of existing cache elements, create a new + // one for this guide. + // + if (i >= mGuideCache.guides.size()) + { + mGuideCache.guides.push_back(g); + mGuideSelectionsDirty = true; + } + } + + // + // Remove any old guides which lie beyond the new end of the + // cache. + // + mGuideCache.guides.erase( + mGuideCache.guides.begin() + i, mGuideCache.guides.end() + ); + + mGuideCacheDirty = false; + } + + if (mGuideSelectionsDirty) + { + clearComponentSelections(); + + // + // If we are the current hair shape then apply Maya's + // selections to our cache. + // + if (currentHair.isValid() && (thisMObject() == currentHair.node())) + { + // + // Set a bit for each selected vert on each guide. + // + MObject comp; + int elem; + int guideIdx; + unsigned i; + MSelectionList list; + MDagPath path; + int vertIdx; + + MGlobal::getActiveSelectionList(list); + + for (i = 0; i < list.length(); i++) + { + list.getDagPath(i, path, comp); + + if (path.isValid() + && !comp.isNull() + && (path.node() == thisMObject())) + { + if (comp.hasFn(MFn::kSingleIndexedComponent)) + { + MFnSingleIndexedComponent compFn(comp); + + for (elem = 0; elem < compFn.elementCount(); elem++) + { + guideIdx = compFn.element(elem); + mGuideCache.guides[guideIdx].select = 0x7fff; + } + } + else if (comp.hasFn(MFn::kDoubleIndexedComponent)) + { + MFnDoubleIndexedComponent compFn(comp); + + for (elem = 0; elem < compFn.elementCount(); elem++) + { + compFn.getElement(elem, guideIdx, vertIdx); + + // + // The '| 1' at the end of the statement + // below is because vert 0 is used as a + // flag to indicate if *any* verts in the + // guide are selected. + // + mGuideCache.guides[guideIdx].select |= + (1 << vertIdx) | 1; + } + } + + mHaveSelections = true; + } + } + } + + mGuideSelectionsDirty = false; + } + } + + RETURN(mGuideCache); +} + + +#if 0 +const std::vector<shaveHairShape::DisplayHair>& shaveHairShape::getDisplayHairs() +{ + // + // If the cache is dirty, destroy it. + // + if (mDisplayHairCacheDirty) + mDisplayHairCache.clear(); + + // + // How many hairs should we be displaying? + // + unsigned numToDisplay = getNumDisplayHairs(); + + mDisplayHairCache.reserve(numToDisplay); + + // + // If cache doesn't already have at least numToDisplay hairs in it, + // then add more. + // + if (mDisplayHairCache.size() < numToDisplay) + { + makeCurrent(); + + CURVEINFO curveInfo; + int hairGroup = getHairGroup(); + int numSegs = hairnode.shavep.segs[hairGroup]; + unsigned h; + WFTYPE geom; + DisplayHair emptyHair; + DisplayStrand strand; + + geom.totalverts = 0; + init_geomWF(&geom); + + for (h = mDisplayHairCache.size(); h < numToDisplay; h++) + { + SHAVEmake_a_curve(0, hairGroup, (int)h, numSegs, &geom, &curveInfo); + + if (!curveInfo.killme && (geom.totalverts > 0)) + { + // + // Create a new element in the vector and get a reference to it. + // Doing it this way involves less copying of memory than if we + // create the entire hair first, then push it on. + // + mDisplayHairCache.push_back(emptyHair); + + DisplayHair& displayHair = + mDisplayHairCache[mDisplayHairCache.size()-1]; + + // + // Each 'face' is a strand. + // + int f; + + for (f = 0; f < geom.totalfaces; f++) + { + strand.verts.clear(); + strand.colors.clear(); + + int fv; + + for (fv = geom.face_start[f]; fv < geom.face_end[f]; fv++) + { + int vertInd = geom.facelist[fv]; + + strand.verts.append(MVector( + geom.v[vertInd].x, geom.v[vertInd].y, geom.v[vertInd].z + )); + + strand.colors.append(MColor( + geom.color[vertInd].x, + geom.color[vertInd].y, + geom.color[vertInd].z + )); + + displayHair.push_back(strand); + } + } + } + } + + free_geomWF(&geom); + } + + mDisplayHairCacheDirty = false; + + RETURN(mDisplayHairCache); +} +#else + +typedef struct +{ +#ifdef REUSABLE_THREADS + int th; +//#ifdef _WIN32 +// HANDLE th; +//#else +// pthread_t th; +//#endif +#endif + int hairGroup; + int numSegs; + int startHair; + int endHair; + std::vector<shaveHairShape::DisplayHair>* cache; +} ThreadData; + + +#ifdef REUSABLE_THREADS +#ifdef _WIN32 + static DWORD APIENTRY cacheOneHairTh (void* data) +#else + static void* APIENTRY cacheOneHairTh (void* data) +#endif + { + ThreadData* threadInfo = (ThreadData*)data; + shaveHairShape::cacheOneHair((unsigned int)threadInfo->th, data); +#ifdef _WIN32 + return 0; +#endif + } +#endif +//////////////////////// +//static int num_segs_dump = 0; +//////////////////////// +void shaveHairShape::cacheOneHair(unsigned threadID, void* data) +{ + ENTER(); + + CURVEINFO curveInfo; + WFTYPE geom; + int i; + ThreadData* threadInfo = (ThreadData*)data; + + geom.totalverts = 0; + init_geomWF(&geom); + + for (i = threadInfo->startHair; i <= threadInfo->endHair; i++) + { + SHAVEmake_a_curveMT( + 0, + threadInfo->hairGroup, + i, + threadInfo->numSegs, + &geom, + &curveInfo, + (int)threadID + ); + + ////////////////////////// + //if(num_segs_dump != threadInfo->numSegs) + // printf("threadInfo->numSegs %i\n",threadInfo->numSegs); + ///////////////////////// + + if (!curveInfo.killme && (geom.totalverts > 0)) + { + DisplayHair& displayHair = (*(threadInfo->cache))[i]; + DisplayStrand strand; + + // + // Each 'face' is a strand. + // + int f=0; + for (f = 0; f < geom.totalfaces; f++) + { + strand.verts.clear(); + strand.colors.clear(); + + int fv; + strand.tiprad = curveInfo.tiprad; + strand.rootrad = curveInfo.baserad; + /////////////////////////// + //if(num_segs_dump != threadInfo->numSegs) + //{ + // printf("geom.face_start[f] %i\n",geom.face_start[f]); + // printf("geom.face_end[f] %i\n",geom.face_end[f]); + // printf("face_end - geom.face_start %i\n",geom.face_end[f]-geom.face_start[f]); + //} + ////////////////////////// + int k = 0; + for (fv = geom.face_start[f]; fv < geom.face_end[f]; fv++) + { +#if 0 + VERT color; + float alpha; + float u; + int hh; + + u=(float)fv/(float)(geom.face_end[f]-geom.face_start[f]-1); + alpha=1.0-u; + alpha*=1.0/passes; + color.x=m->return_hair[hh].rootcolor.x*u+m->return_hair[hh].tipcolor.x*(1.0-u); + color.y=m->return_hair[hh].rootcolor.y*u+m->return_hair[hh].tipcolor.y*(1.0-u); + color.z=m->return_hair[hh].rootcolor.z*u+m->return_hair[hh].tipcolor.z*(1.0-u); +#endif + if(k > geom.face_end[f]-geom.face_start[f]) + break; + k++; + + int vertInd = fv; + + /////////////////////////// + //if(num_segs_dump != threadInfo->numSegs) + //{ + // printf("geom.facelist[fv] %i\n",vertInd); + // printf("geom.v %f %f %f\n",geom.v[vertInd].x, geom.v[vertInd].y, geom.v[vertInd].z); + //} + ////////////////////////// + + strand.verts.append(MVector( + geom.v[vertInd].x, + geom.v[vertInd].y, + geom.v[vertInd].z + + )); + + strand.colors.append(MColor( + geom.color[vertInd].x, + geom.color[vertInd].y, + geom.color[vertInd].z, + geom.alpha[vertInd] + )); + //for Joe: alpha is 1.0 for all the verts + //printf("alpha %f\n", geom.alpha[vertInd]); + } + + displayHair.push_back(strand); + } + + ///////////////////// + //num_segs_dump = threadInfo->numSegs; + //fflush(stdout); + //////////////////// + } + } + + free_geomWF(&geom); + + LEAVE(); +} + + +void shaveHairShape::clearDisplayHairs() +{ + mDisplayHairCache.displayHair.clear(); + mDisplayHairCacheDirty = true; +} + +void shaveHairShape::invalidateDisplayHairs() +{ + mDisplayHairCacheDirty = true; +} + +//const std::vector<shaveHairShape::DisplayHair>& shaveHairShape::getDisplayHairs() +const shaveHairShape::DisplayHairCache& shaveHairShape::getDisplayHairs(M3dView& view, bool cleanup) +{ + ENTER(); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::getDisplayHairs(2)", NULL); +#endif + if (SHAVEis_it_loaded() && mNodeInitialized) + { + //goes + //maybe that is not a good place for grabbing textures? + //::compute is bad one as well + if(cleanup) + { + //doFallbackGlob = false; + //test it + //printf("before tex update - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + updateTexLookups(); // this should be just for display counts + ///////////////// + //qApp->flush(); + + //printf("after tex update - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + //doFallbackGlob = true; + + + + ////////dbg////// + //int numToDisplay = getNumDisplayHairs(false); + //printf("count %i\n",numToDisplay);fflush(stdout); + } + +#if 0 + unsigned int numToDisplay = getNumDisplayHairs(false); + // + // If the cache is dirty, destroy it. + // + if ((mDisplayHairCacheDirty && cleanup) || mDisplayHairCache.displayHair.capacity() != numToDisplay) + { + + //mDisplayHairCache.displayHair.reserve(numToDisplay); + mDisplayHairCache.displayHair.resize(numToDisplay); + mDisplayHairCache.displayHair.shrink_to_fit(); + mDisplayHairCache.displayHair.clear(); + } + //////////////test/////////////// + //{ + // DisplayHair emptyHair; + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(emptyHair); + // RETURN(mDisplayHairCache); + //} + ///////////////////////////// + // + // How many hairs should we be displaying? + // + unsigned int curCacheSize = mDisplayHairCache.displayHair.size(); +#else + // + // How many hairs should we be displaying? + // + unsigned numToDisplay = getNumDisplayHairs(false); //setting it to zero bumps fps to 200 + unsigned bunchsize = getNumDisplayHairs(true); + if(bunchsize == 0) bunchsize = numToDisplay; + // + // If the cache is dirty, destroy it. + // + if (cleanup && (mDisplayHairCacheDirty || numToDisplay < mDisplayHairCache.displayHair.size())) + mDisplayHairCache.displayHair.clear(); + + + unsigned int curCacheSize = (unsigned int)mDisplayHairCache.displayHair.size(); + + + mDisplayHairCache.displayHair.reserve(numToDisplay); + + ////////////// test - fake cache //////////// 200fps + //if(mDisplayHairCache.displayHair.size() == 0) + //{ + // mDisplayHairCache.displayHair.clear(); + // DisplayStrand astrand; + // for(int k = 0; k<6; k++) + // { + // astrand.verts.append(MVector(k*5,k,k)); + // astrand.colors.append(MColor(1.0,0.0,0.0)); + // } + // astrand.rootrad = 1.0f; + // astrand.tiprad = 1.0f; + + // DisplayHair ahair; + // ahair.push_back(astrand); + + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(ahair); + // + // mDisplayHairCache.radius = 1.0f; + // RETURN(mDisplayHairCache); + //} + ///////////// end test //////// + +#endif + + // + // If cache doesn't already have at least numToDisplay hairs in it, + // then add more. + // + //unsigned int upto = curCacheSize + DisplayHairCache::eBucnchSize < numToDisplay ? curCacheSize + DisplayHairCache::eBucnchSize : numToDisplay; + unsigned int upto = curCacheSize + bunchsize < numToDisplay ? curCacheSize + bunchsize : numToDisplay; + //unsigned int upto = numToDisplay; + //printf("%i %i %i\n", upto,upto2,curCacheSize);fflush(stdout); + unsigned int numh = upto - curCacheSize; + //printf("curCaheSize %i upto %i \n",curCacheSize, upto); +#if 0 + if (curCacheSize < /*numToDisplay*/ upto ) +#else + if (curCacheSize < numToDisplay) +#endif + { + /////////////////////////////////// + //MFnDependencyNode dFn(thisMObject()); + //printf(" update %s\n",dFn.name().asChar()); + ////////////////test/////////////// + //{ + // DisplayHair emptyHair; + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(emptyHair); + // RETURN(mDisplayHairCache); + //} + /////////////////////////////// + + if(cleanup) //only the first iteration + makeCurrent(false); + + int hairGroup = getHairGroup(); + int numSegs = getNumDisplaySegments(); + unsigned h; + unsigned i; + DisplayHair emptyHair; +#if 0 + unsigned numNew = numToDisplay - curCacheSize; +#else + unsigned numNew = upto - curCacheSize; +#endif + + ////////////// test - fake cache //////////// fast 200 fps + //{ + // mDisplayHairCache.displayHair.clear(); + // DisplayStrand astrand; + // for(int k = 0; k<6; k++) + // { + // astrand.verts.append(MVector(k*5,k,k)); + // astrand.colors.append(MColor(1.0,0.0,0.0)); + // } + // astrand.rootrad = 1.0f; + // astrand.tiprad = 1.0f; + + // DisplayHair ahair; + // ahair.push_back(astrand); + + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(ahair); + // + // mDisplayHairCache.radius = 1.0f; + // RETURN(mDisplayHairCache); + //} + ///////////// end test //////// + + //moved down to the thread stuff + //numThreads = shaveUtil::getThreadSettings(numNew, &hairsPerThread); + //ThreadData* threadInfo = new ThreadData[numThreads?numThreads:1]; + //void* threadGroup = SHAVEstart_thread_group(numThreads); + + + ////////////// test - fake cache //////////// slow 17 fps + //{ + // mDisplayHairCache.displayHair.clear(); + // DisplayStrand astrand; + // for(int k = 0; k<6; k++) + // { + // astrand.verts.append(MVector(k*5,k,k)); + // astrand.colors.append(MColor(1.0,0.0,0.0)); + // } + // astrand.rootrad = 1.0f; + // astrand.tiprad = 1.0f; + + // DisplayHair ahair; + // ahair.push_back(astrand); + + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(ahair); + // + // mDisplayHairCache.radius = 1.0f; + // RETURN(mDisplayHairCache); + //} + ///////////// end test //////// + + + + SHAVEPARMS pp; +// pp= &hairnode.shavep; + SHAVEfetch_parms(&pp); +// SHAVEset_parms(pp); +#if 0 + if (hairGroup==0) pp.haircount[0]=numToDisplay; + if (hairGroup==4) pp.haircount[4]=numToDisplay; +#else + if (hairGroup==0) pp.haircount[0]=upto; + if (hairGroup==4) pp.haircount[4]=upto; +#endif + if (hairGroup==0) pp.segs[0]=numSegs; + if (hairGroup==4) pp.segs[4]=numSegs; + + ////////////////test/////////////// + //{ + // DisplayHair emptyHair; + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(emptyHair); + // RETURN(mDisplayHairCache); + //} + /////////////////////////////// + + ////////////// test - fake cache //////////// slow 17 fps + //{ + // mDisplayHairCache.displayHair.clear(); + // DisplayStrand astrand; + // for(int k = 0; k<6; k++) + // { + // astrand.verts.append(MVector(k*5,k,k)); + // astrand.colors.append(MColor(1.0,0.0,0.0)); + // } + // astrand.rootrad = 1.0f; + // astrand.tiprad = 1.0f; + + // DisplayHair ahair; + // ahair.push_back(astrand); + + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(ahair); + // + // mDisplayHairCache.radius = 1.0f; + // RETURN(mDisplayHairCache); + //} + ///////////// end test //////// + + + if (SHAVEis_it_loaded()) + { + if ( (dirties.DIRTY_DISPLAY_COUNT) || (dirties.DIRTY_DISPLAYSEGS)||(dirties.DIRTY_HAIRCOUNT)||(dirties.DIRTY_GUIDE_SEGS)||(dirties.DIRTY_PASSES)||(dirties.DIRTY_TEXTURE_JOE)||(dirties.DIRTY_CLUMPS)||(dirties.DIRTY_MULT)) + { + if (numToDisplay>0) + { + ////////////////////////////////// + MDagPath camPath; + if(MStatus::kSuccess == view.getCamera(camPath)) + { + Matrix camvm; + MVector mayaUpVector, mayaDirectionVector; + double rotationVector[3]; + VERT shaveUpVector, shaveDirection, shavePosition; + MVector unitDirectionVector(0., 0., 1.); + MVector unitUpVector(0., 1., 0.); + + MTransformationMatrix::RotationOrder order = MTransformationMatrix::kXYZ; + + MTransformationMatrix camWorldMatrix = camPath.inclusiveMatrix(); + MFnCamera fnCamera(camPath); + + // Get MAYA camera translation and rotation info + MVector translation = camWorldMatrix.translation(MSpace::kWorld); + camWorldMatrix.getRotation(rotationVector, order); + + // Set SHAVE camera position in World space + shavePosition.x = (float) translation.x; + shavePosition.y = (float) translation.y; + shavePosition.z = (float) translation.z; + + // UP VECTOR + mayaUpVector = unitUpVector.rotateBy(rotationVector, order); + shaveUpVector.x = (float) mayaUpVector.x; + shaveUpVector.y = (float) mayaUpVector.y; + shaveUpVector.z = (float) mayaUpVector.z; + + // DIRECTION VECTOR + mayaDirectionVector = unitDirectionVector.rotateBy(rotationVector, order); + shaveDirection.x = (float) -mayaDirectionVector.x; + shaveDirection.y = (float) -mayaDirectionVector.y; + shaveDirection.z = (float) -mayaDirectionVector.z; + + int port_width = view.portWidth(); + int port_height = view.portHeight(); + + // FIELD OF VIEW + float pixelAspect = 1.0f; + double hfovagain, vfovagain; + shaveRender::portFieldOfView( + port_width, + port_height, + pixelAspect, + hfovagain, + vfovagain, + fnCamera + ); + + const float rad2degrees = (180.0f / (float)M_PI); + vfovagain *= rad2degrees; + SHAVEmake_view_matrix( + camvm, shaveUpVector, shaveDirection, shavePosition, (float)vfovagain + ); + + SHAVEset_cameraOPEN( + camvm, + shavePosition, + port_width, + port_height, + pixelAspect, + (float)vfovagain, + 0.01f + ); + } + ///////////////////////////////// + ////////////////test/////////////// + //{ + // DisplayHair emptyHair; + // for (int h = 0; h < numToDisplay; h++) + // mDisplayHairCache.displayHair.push_back(emptyHair); + // RETURN(mDisplayHairCache); + //} + /////////////////////////////// + + int *hairlist=NULL; + hairlist = (int*)malloc(sizeof(int)*numToDisplay); + for(unsigned int k=0; k<numToDisplay; k++) + hairlist[k] = k; + int hairlist_size; + hairlist_size=numToDisplay; + + //if(upto == numToDisplay) //do it only on the last iteration + { + dirties.DIRTY_DISPLAY_COUNT=0; + dirties.DIRTY_DISPLAYSEGS=0; + dirties.DIRTY_DISPLAY_COUNT=0; + dirties.DIRTY_DISPLAYSEGS=0; + dirties.DIRTY_HAIRCOUNT=0; + dirties.DIRTY_GUIDE_SEGS=0; + dirties.DIRTY_PASSES=0; + dirties.DIRTY_TEXTURE_JOE=0; + dirties.DIRTY_CLUMPS=0; + dirties.DIRTY_MULT=0; + } + } + } + } + + // + // Populate the cache with empty hairs. + // +#if 0 + for (h = curCacheSize; h < numToDisplay; h++) + mDisplayHairCache.displayHair.push_back(emptyHair); +#else + for (h = curCacheSize; h < upto; h++) + mDisplayHairCache.displayHair.push_back(emptyHair); +#endif + + if(upto < 160) + { + // + // single thread + // + ThreadData threadInfo; + threadInfo.cache = &mDisplayHairCache.displayHair; + threadInfo.hairGroup = hairGroup; + threadInfo.numSegs = numSegs; + threadInfo.startHair = 0; + threadInfo.endHair = upto - 1; + + cacheOneHair(0,&threadInfo); + } + else + { + // + // Launch the threads. + // +#ifdef REUSABLE_THREADS + unsigned int ncpus = LThreadGroup::GetNumCPUs(); + unsigned int hairsPerThread = numNew/ncpus; + ThreadData* threadInfo = new ThreadData[ncpus]; + + h = curCacheSize; + for (i = 0; i < ncpus; i++) + { + threadInfo[i].th = i; //lthGrp()->Get(i)->GetHandle(); + threadInfo[i].cache = &mDisplayHairCache.displayHair; + threadInfo[i].hairGroup = hairGroup; + threadInfo[i].numSegs = numSegs; + threadInfo[i].startHair = h; + threadInfo[i].endHair = h + hairsPerThread - 1; +#if 0 + if (threadInfo[i].endHair >= (int)numToDisplay) + threadInfo[i].endHair = (int)numToDisplay - 1; +#else + if (threadInfo[i].endHair >= (int)upto) + threadInfo[i].endHair = (int)upto - 1; +#endif + h += hairsPerThread; + } + + for(unsigned int i = 0; i < ncpus; i++) + { + ThreadData* thd = &threadInfo[i]; + _lthGrp()->Start(i,/*(THPROC*)*/cacheOneHairTh,thd); + } + _lthGrp()->WaitToFinish(); + + delete [] threadInfo; +#else + unsigned int hairsPerThread = 0; + unsigned int numThreads = shaveUtil::getThreadSettings(numNew, &hairsPerThread); + ThreadData* threadInfo = new ThreadData[numThreads?numThreads:1]; + void* threadGroup = SHAVEstart_thread_group(numThreads); + +#if 0 + for (h = curCacheSize, i = 0; h < numToDisplay; h += hairsPerThread, i++) +#else + for (h = curCacheSize, i = 0; h < upto; h += hairsPerThread, i++) +#endif + { + threadInfo[i].cache = &mDisplayHairCache.displayHair; + threadInfo[i].hairGroup = hairGroup; + threadInfo[i].numSegs = numSegs; + threadInfo[i].startHair = h; + threadInfo[i].endHair = h + hairsPerThread - 1; +#if 0 + if (threadInfo[i].endHair >= (int)numToDisplay) + threadInfo[i].endHair = (int)numToDisplay - 1; +#else + if (threadInfo[i].endHair >= (int)upto) + threadInfo[i].endHair = (int)upto - 1; +#endif + SHAVEstart_thread(threadGroup, cacheOneHair, &threadInfo[i]); + } + + SHAVEend_thread_group(threadGroup); + + delete [] threadInfo; +#endif + } + + //maybe we need to do this only on the last iteration? + if(upto == numToDisplay) + { + //compute extent by tips + unsigned int s = (unsigned int)mDisplayHairCache.displayHair.size(); + MFloatPoint c(0.0f, 0.0f, 0.0f); + float lSq = 0.0f; + for (i = 0; i < s; i++) + { + const DisplayHair& hair = mDisplayHairCache.displayHair[i]; + if(hair.size() == 0) continue; + const DisplayStrand& strand = hair[0]; + int xx = strand.verts.length()-1; + c.x += strand.verts[xx].x; + c.y += strand.verts[xx].y; + c.z += strand.verts[xx].z; + } + c.x /= (float)s; + c.y /= (float)s; + c.z /= (float)s; + for (i = 0; i < s; i++) + { + const DisplayHair& hair = mDisplayHairCache.displayHair[i]; + if(hair.size() == 0) continue; + const DisplayStrand& strand = hair[0]; + int xx = strand.verts.length()-1; + MFloatPoint v(strand.verts[xx].x,strand.verts[xx].y,strand.verts[xx].z); + MFloatPoint d = v-c; + float lsq = d.x*d.x + d.y*d.y + d.z*d.z; + if(lsq > lSq) lSq = lsq; + } + mDisplayHairCache.center = c; + mDisplayHairCache.radius = sqrt(lSq); + } + } + + if(upto == numToDisplay || numToDisplay == 0) + mDisplayHairCacheDirty = false; + } + +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::getDisplayHairs(2) - done ", NULL); +#endif + RETURN(mDisplayHairCache); +} + +const shaveHairShape::DisplayHairCache& shaveHairShape::getDisplayHairs(M3dView& view) +{ + ENTER(); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::getDisplayHairs(1)", NULL); +#endif + + if (mNodeInitialized && SHAVEis_it_loaded()) + { + updateTexLookups(); // this should be just for display counts + + // If the cache is dirty, destroy it. + // + if (mDisplayHairCacheDirty) + mDisplayHairCache.displayHair.clear(); + + // + // How many hairs should we be displaying? + // + unsigned int curCacheSize = (unsigned int)mDisplayHairCache.displayHair.size(); + unsigned int numToDisplay = (unsigned int)getNumDisplayHairs(false); + + mDisplayHairCache.displayHair.reserve(numToDisplay); + + // + // If cache doesn't already have at least numToDisplay hairs in it, + // then add more. + // + if (curCacheSize < numToDisplay) + { + makeCurrent(); + + int hairGroup = getHairGroup(); + int numSegs = getNumDisplaySegments(); + unsigned h; + unsigned i; + DisplayHair emptyHair; + unsigned numNew = numToDisplay - curCacheSize; + unsigned hairsPerThread; + unsigned numThreads; + + numThreads = shaveUtil::getThreadSettings(numNew, &hairsPerThread); + + ThreadData* threadInfo = new ThreadData[numThreads?numThreads:1]; + void* threadGroup = SHAVEstart_thread_group(numThreads); + SHAVEPARMS pp; +// pp= &hairnode.shavep; + SHAVEfetch_parms(&pp); +// SHAVEset_parms(pp); + if (hairGroup==0) pp.haircount[0]=numToDisplay; + if (hairGroup==4) pp.haircount[4]=numToDisplay; + if (hairGroup==0) pp.segs[0]=numSegs; + if (hairGroup==4) pp.segs[4]=numSegs; + + if ((numToDisplay>0) + && ( (dirties.DIRTY_DISPLAY_COUNT) || (dirties.DIRTY_DISPLAYSEGS) + || (dirties.DIRTY_HAIRCOUNT) || (dirties.DIRTY_GUIDE_SEGS) + || (dirties.DIRTY_PASSES) || (dirties.DIRTY_TEXTURE_JOE) + || (dirties.DIRTY_CLUMPS) || (dirties.DIRTY_MULT) + ) + ) + { + ////////////////////////////////// + MDagPath camPath; + if(MStatus::kSuccess == view.getCamera(camPath)) + { + Matrix camvm; + MVector mayaUpVector, mayaDirectionVector; + double rotationVector[3]; + VERT shaveUpVector, shaveDirection, shavePosition; + MVector unitDirectionVector(0., 0., 1.); + MVector unitUpVector(0., 1., 0.); + + MTransformationMatrix::RotationOrder order = MTransformationMatrix::kXYZ; + + MTransformationMatrix camWorldMatrix = camPath.inclusiveMatrix(); + MFnCamera fnCamera(camPath); + + // Get MAYA camera translation and rotation info + MVector translation = camWorldMatrix.translation(MSpace::kWorld); + camWorldMatrix.getRotation(rotationVector, order); + + // Set SHAVE camera position in World space + shavePosition.x = (float) translation.x; + shavePosition.y = (float) translation.y; + shavePosition.z = (float) translation.z; + + // UP VECTOR + mayaUpVector = unitUpVector.rotateBy(rotationVector, order); + shaveUpVector.x = (float) mayaUpVector.x; + shaveUpVector.y = (float) mayaUpVector.y; + shaveUpVector.z = (float) mayaUpVector.z; + + // DIRECTION VECTOR + mayaDirectionVector = unitDirectionVector.rotateBy(rotationVector, order); + shaveDirection.x = (float) -mayaDirectionVector.x; + shaveDirection.y = (float) -mayaDirectionVector.y; + shaveDirection.z = (float) -mayaDirectionVector.z; + + int port_width = view.portWidth(); + int port_height = view.portHeight(); + + // FIELD OF VIEW + float pixelAspect = 1.0f; + double hfovagain, vfovagain; + shaveRender::portFieldOfView( + port_width, + port_height, + pixelAspect, + hfovagain, + vfovagain, + fnCamera + ); + + const float rad2degrees = (180.0f / (float)M_PI); + vfovagain *= rad2degrees; + SHAVEmake_view_matrix( + camvm, shaveUpVector, shaveDirection, shavePosition, (float)vfovagain + ); + + SHAVEset_cameraOPEN( + camvm, + shavePosition, + port_width, + port_height, + pixelAspect, + (float)vfovagain, + 0.01f + ); + } + ///////////////////////////////// + int *hairlist=NULL; + hairlist = (int*)malloc(sizeof(int)*numToDisplay); + for(unsigned int k=0; k<numToDisplay; k++) + hairlist[k] = k; + int hairlist_size; + hairlist_size=numToDisplay; + + dirties.DIRTY_DISPLAY_COUNT=0; + dirties.DIRTY_DISPLAYSEGS=0; + dirties.DIRTY_DISPLAY_COUNT=0; + dirties.DIRTY_DISPLAYSEGS=0; + dirties.DIRTY_HAIRCOUNT=0; + dirties.DIRTY_GUIDE_SEGS=0; + dirties.DIRTY_PASSES=0; + dirties.DIRTY_TEXTURE_JOE=0; + dirties.DIRTY_CLUMPS=0; + dirties.DIRTY_MULT=0; + } + + // + // Populate the cache with empty hairs. + // + for (h = curCacheSize; h < numToDisplay; h++) + mDisplayHairCache.displayHair.push_back(emptyHair); + + // + // Launch the threads. + // + for (h = curCacheSize, i = 0; h < numToDisplay; h += hairsPerThread, i++) + { + threadInfo[i].cache = &mDisplayHairCache.displayHair; + threadInfo[i].hairGroup = hairGroup; + threadInfo[i].numSegs = numSegs; + threadInfo[i].startHair = h; + threadInfo[i].endHair = h + hairsPerThread - 1; + + if (threadInfo[i].endHair >= (int)numToDisplay) + threadInfo[i].endHair = (int)numToDisplay - 1; + SHAVEstart_thread(threadGroup, cacheOneHair, &threadInfo[i]); + } + + SHAVEend_thread_group(threadGroup); + + delete [] threadInfo; + + //compute extent by tips + unsigned int s = (unsigned int)mDisplayHairCache.displayHair.size(); + MFloatPoint c(0.0f, 0.0f, 0.0f); + float lSq = 0.0f; + for (i = 0; i < s; i++) + { + const DisplayHair& hair = mDisplayHairCache.displayHair[i]; + if(hair.size() == 0) continue; + const DisplayStrand& strand = hair[0]; + int xx = strand.verts.length()-1; + c.x += strand.verts[xx].x; + c.y += strand.verts[xx].y; + c.z += strand.verts[xx].z; + } + c.x /= (float)s; + c.y /= (float)s; + c.z /= (float)s; + for (i = 0; i < s; i++) + { + const DisplayHair& hair = mDisplayHairCache.displayHair[i]; + if(hair.size() == 0) continue; + const DisplayStrand& strand = hair[0]; + int xx = strand.verts.length()-1; + MFloatPoint v(strand.verts[xx].x,strand.verts[xx].y,strand.verts[xx].z); + MFloatPoint d = v-c; + float lsq = d.x*d.x + d.y*d.y + d.z*d.z; + if(lsq > lSq) lSq = lsq; + } + mDisplayHairCache.center = c; + mDisplayHairCache.radius = sqrt(lSq); + } + + mDisplayHairCacheDirty = false; + } +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::getDisplayHairs(1) - done", NULL); +#endif + RETURN(mDisplayHairCache); +} + + + +#if 0 +bool shaveHairShape::getDisplayHairs(unsigned int numToDisplay, shaveHairShape::DisplayHairCache& cache) +{ + ENTER(); + + if (mNodeInitialized) + { + cache.displayHair.clear(); + cache.displayHair.reserve(numToDisplay); + + { + makeCurrent(); + + int hairGroup = getHairGroup(); + int numSegs = getNumDisplaySegments(); + unsigned h; + unsigned i; + DisplayHair emptyHair; + //unsigned numNew = numToDisplay/* - curCacheSize*/; + unsigned hairsPerThread; + unsigned numThreads; + + numThreads = shaveUtil::getThreadSettings(numToDisplay, &hairsPerThread); + + ThreadData* threadInfo = new ThreadData[numThreads?numThreads:1]; + void* threadGroup = SHAVEstart_thread_group(numThreads); + + // + // Populate the cache with empty hairs. + // + for (h = 0; h < numToDisplay; h++) + cache.displayHair.push_back(emptyHair); + + // + // Launch the threads. + // + for (h = 0, i = 0; h < numToDisplay; h += hairsPerThread, i++) + { + threadInfo[i].cache = &cache.displayHair; + threadInfo[i].hairGroup = hairGroup; + threadInfo[i].numSegs = numSegs; + threadInfo[i].startHair = h; + threadInfo[i].endHair = h + hairsPerThread - 1; + + if (threadInfo[i].endHair >= (int)numToDisplay) + threadInfo[i].endHair = (int)numToDisplay - 1; + + SHAVEstart_thread(threadGroup, cacheOneHair, &threadInfo[i]); + } + + SHAVEend_thread_group(threadGroup); + + delete [] threadInfo; + + //compute extent by tips + unsigned int s = (unsigned int)cache.displayHair.size(); + MFloatPoint c(0.0f, 0.0f, 0.0f); + float lSq = 0.0f; + for (i = 0; i < s; i++) + { + const DisplayHair& hair = cache.displayHair[i]; + if(hair.size() == 0) continue; + const DisplayStrand& strand = hair[0]; + int xx = strand.verts.length()-1; + c.x += strand.verts[xx].x; + c.y += strand.verts[xx].y; + c.z += strand.verts[xx].z; + } + c.x /= (float)s; + c.y /= (float)s; + c.z /= (float)s; + for (i = 0; i < s; i++) + { + const DisplayHair& hair = cache.displayHair[i]; + if(hair.size() == 0) continue; + const DisplayStrand& strand = hair[0]; + int xx = strand.verts.length()-1; + MFloatPoint v(strand.verts[xx].x,strand.verts[xx].y,strand.verts[xx].z); + MFloatPoint d = v-c; + float lsq = d.x*d.x + d.y*d.y + d.z*d.z; + if(lsq > lSq) lSq = lsq; + } + cache.center = c; + cache.radius = sqrt(lSq); + } + + } + + RETURN(true); +} +#endif + +#endif + + +unsigned shaveHairShape::getGuideCount() +{ + ENTER(); + + if (mNodeInitialized) + { + if (mGuideCountDirty) + { + SOFTGUIDE guide; + int guideIndex = 0; + + makeCurrent(); + + while (SHAVEfetch_guide(guideIndex, &guide) != -1) + guideIndex++; + + mGuideCount = (int)guideIndex; + mGuideCountDirty = false; + } + } + + RETURN(mGuideCount); +} +//////////////////// procedrual api functions declared in shaveProceduralAPI.h ///////// +#ifdef USE_PROCEDURALS +void SHAVESDKadd_procedural(PROCEDURAL *params, PFPROCEDURAL callback, SHAVENODE *current_node){} +HAIRTYPE* SHAVEupdate_procedural_values(PROCEDURAL *params, SHAVENODE *current_node){return NULL;} +void SHAVESDKdelete_procedural(SHAVENODE *current_node){} +#endif + +/////////////////////////////////////////////////////////////////////////////////////// + + + + +MStatus shaveHairShape::setDependentsDirty( + const MPlug& dirty, MPlugArray& affected +) +{ + //////////////////////// + //printf("plug diritied %s\n",dirty.name().asChar()); fflush(stdout); + //MGlobal::displayInfo(MString("plug diritied ")+dirty.name()); + //////////////////////// + + ENTER(); + ////////////////// +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairShape::setDependentsDirty", NULL); +#endif + if(dirty == shaveParamHaircount) + { + dirties.DIRTY_HAIRCOUNT = 1; + dirties.DIRTY_TEXTURE = 1; //need to update cache as well + } + else if(dirty == shaveParamPasses) + { + dirties.DIRTY_PASSES = 1; + dirties.DIRTY_TEXTURE = 1; //need to update cache as well + } + //todo: need to dirty it in shave globals + //else if(dirty == displayHairMaxAttr || + // dirty == displayHairRatioAttr) + //{ + // dirties.DIRTY_DISPLAY_COUNT = 1; + // dirties.DIRTY_TEXTURE = 1; //need to update cache as well + //} + //else if(dirty == aDisplaySegmentLimit) + // dirties.DIRTY_DISPLAYSEGS = 1; //dirty it globals + else if(dirty == timeAttr) + dirties.DIRTY_TIME = 1; + else if(dirty == shaveParamMultStrand) + dirties.DIRTY_CLUMPS = 1; + else if(dirty == clumps) + dirties.DIRTY_MULT = 1; + + //its constant SHAVE_VERTS_PER_GUIDE-1 + //else if(dirty == aDisplaySegmentLimit) + // dirties.DIRTY_GUIDE_SEGS = 1; + + //////////////////// + + +#ifdef PER_NODE_TEXLOOKUP + bool texChanged = false; + if(dirty.isConnected()) + { + if(dirty == hairColorTexture || + dirty == rootHairColorTexture || + dirty == mutantHairColorTexture) + { + texChanged = true; + } + MPlug texPlug(thisMObject(),shaveHairShape::shaveTextureAttr); + int nel = texPlug.numElements(); + for(int k = 0; k < nel; k++) + { + MPlug checkPlug = texPlug.elementByLogicalIndex(k); + if(checkPlug == dirty) + { + texChanged = true; + break; + } + } + } + //not sure that it's a good place + if(texChanged /* && !IsBuildingLookups()*/) + { + //////////////////////// + //printf("textures changed %s\n",dirty.name().asChar()); fflush(stdout); + //////////////////////// + + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + //mDirtyTexture = true; + dirties.DIRTY_TEXTURE = 1; + dirties.DIRTY_TEXTURE_JOE = 1; + + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + + //MObjectArray shaveHairShapes; + //shaveUtil::getShaveNodes(shaveHairShapes); + //if (shaveHairShapes.length() > 0) + //{ + // shaveGlobals::Globals globals; + // shaveGlobals::getGlobals(globals); + + //texture_is_updating = true; + //initTexInfoLookup2(shaveHairShapes, "", globals.verbose,true ,thisMObject()); + //texture_is_updating = false; + //} + + } +#endif + + //////////////////////////////////////////// + + if ((dirty == growthObjectsGroupIDAttr) + || (dirty == inputCurve) + || (dirty == inputMesh) + || (dirty == inputSurf)) + { + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + mGuideCacheDirty = true; + mGuideCountDirty = true; + ///////////////// + doFallbackGlob = true; + //////////////// + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + } + else if + ((dirty == shaveParamHaircount) + || (dirty == aDisplaySegmentLimit) //it's in globals now + || (dirty == aDisplayGuideThick) //it's in globals now + || (dirty == aDisplayGuides) //it's in globals now + || (dirty == collisionObjectsAttr) + || (dirty == collisionObjectsGroupIDAttr) + || (dirty == instanceMesh) + || (dirty == runDynamics) + || (dirty == shaveBlindHair) + || (dirty == shaveParamAmbDiff) + || (dirty == shaveParamAnimSpeed) + || (dirty == shaveParamDampening) + || (dirty == shaveParamDisplacement) + || (dirty == shaveParamFrizzAnim) + || (dirty == shaveParamFrizzAnimDir) + || (dirty == shaveParamFrizzAnimDirX) + || (dirty == shaveParamFrizzAnimDirY) + || (dirty == shaveParamFrizzAnimDirZ) + || (dirty == shaveParamFrizzFreqX) + || (dirty == shaveParamFrizzFreqY) + || (dirty == shaveParamFrizzFreqZ) + || (dirty == shaveParamFrizzRoot) + || (dirty == shaveParamFrizzTip) + || (dirty == shaveParamGloss) + || (dirty == shaveParamHairColor) + || (dirty == shaveParamHueVariation) + || (dirty == shaveParamInstancingStatus) + || (dirty == shaveParamKinkFreqX) + || (dirty == shaveParamKinkFreqY) + || (dirty == shaveParamKinkFreqZ) + || (dirty == shaveParamKinkRoot) + || (dirty == shaveParamKinkTip) + || (dirty == shaveParamMultStrand) + || (dirty == shaveParamMutantColor) + || (dirty == shaveParamMutantPercent) + || (dirty == shaveParamNoInterp) + || (dirty == shaveParamPasses) + || (dirty == shaveParamRandScale) + || (dirty == shaveParamRandomizeMulti) + || (dirty == shaveParamRootColor) + || (dirty == shaveParamRootStiffness) + || (dirty == shaveParamScale) + || (dirty == shaveParamSegs) + || (dirty == shaveParamSelfShadow) + || (dirty == shaveParamSpecular) + || (dirty == shaveParamSplayRoot) + || (dirty == shaveParamSplayTip) + || (dirty == shaveParamMultAsp) //vlad|05July2010 + || (dirty == shaveParamOffset) //vlad|09July2010 + || (dirty == shaveParamAspect) //vlad|09July2010 + || (dirty == shaveParamStiffness) + || (dirty == shaveParamThickRoot) + || (dirty == shaveParamThickTip) + || (dirty == shaveParamValueVariation) + || (dirty == surfTessU) + || (dirty == surfTessV) + || (dirty == subdTessDept) + || (dirty == subdTessSamp) + || (dirty == textureCacheUpdatedAttr) + || (dirty == timeAttr) + || (dirty == aSquirrel) + || (dirty == flyawayPerc) + || (dirty == flyawayStren) + || (dirty == messStren) + || (dirty == clumps) + || (dirty == clumpsStren) + || (dirty == clumpsColStren) + || (dirty == clumpsRotStren) + || (dirty == clumpsRotOffset) + || (dirty == clumpsRandomize) + || (dirty == clumpsFlatness) + || (dirty == clumpsScruffle) + || (dirty == randomSeedOffset) + || (dirty == triggerAttr) + ) + { + //printf("display cache dirtied\n");fflush(stdout); + + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + mGuideCacheDirty = true; + ///////////////// + if(dirty != triggerAttr && + dirty != textureCacheUpdatedAttr) + { + doFallbackGlob = true; + } + //////////////// + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + } + //todo: move it to shave globals +// else if( dirty == shaveParamHaircount || +// dirty == displayHairMaxAttr || +// dirty == displayHairRatioAttr) +// +// { +// mDisplayInstCacheDirty = true; +// MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); +// } +#ifdef USE_PROCEDURALS + else if (dirty == procedural) + { + if(dirty.isConnected()) + { + MStatus stat; + MPlugArray cons; + if(dirty.connectedTo(cons,true,false,&stat)) + { + if(stat == MStatus::kSuccess && + cons.length() > 0) + { + MObject procnode = cons[0].node(); + + MFnDependencyNode dFn(procnode); + shaveProcedural* procedural = (shaveProcedural*)dFn.userNode(); + + PROCEDURAL* params = procedural->GetShaveParams(); + PFPROCEDURAL aproc = procedural->GetShaveProc(); + + HAIRTYPE* sample = SHAVEupdate_procedural_values(params,&hairnode); + } + } + } + } +#endif +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairShape::setDependentsDirty - done", NULL); +#endif + RETURN(MS::kSuccess); +} + +//static bool texture_is_updating = false; + +MStatus shaveHairShape::connectionMade ( const MPlug& plug, const MPlug& otherPlug, bool asSrc ) +{ +#ifdef USE_PROCEDURALS + if(plug == procedural) + { + MObject procnode = otherPlug.node(); + MFnDependencyNode dFn(procnode); + shaveProcedural* procedural = (shaveProcedural*)dFn.userNode(); + + PROCEDURAL* params = procedural->GetShaveParams(); + PFPROCEDURAL aproc = procedural->GetShaveProc(); + + // fires 'unsignd int' loses precicion on OSX , type cast will fix it + SHAVESDKadd_procedural(params,aproc,&hairnode); + } +#endif + +#ifdef PER_NODE_TEXLOOKUP + bool texConnected = false; + if(plug == hairColorTexture || + plug == rootHairColorTexture || + plug == mutantHairColorTexture) + { + texConnected = true; + } + MPlug texPlug(thisMObject(),shaveHairShape::shaveTextureAttr); + int nel = texPlug.numElements(); + for(int k = 0; k < nel; k++) + { + MPlug checkPlug = texPlug.elementByLogicalIndex(k); + if(checkPlug == plug) + { + texConnected = true; + break; + } + } + //not sure that it's a good place + if(texConnected /*&& !IsBuildingLookups()*/) + { + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + + //mDirtyTexture = true; + dirties.DIRTY_TEXTURE = 1; + dirties.DIRTY_TEXTURE_JOE = 1; + + //MObjectArray shaveHairShapes; + //shaveUtil::getShaveNodes(shaveHairShapes); + //if (shaveHairShapes.length() > 0) + //{ + // shaveGlobals::Globals globals; + // shaveGlobals::getGlobals(globals); + // initTexInfoLookup2(shaveHairShapes, "", globals.verbose,true ,thisMObject()); + //} + } +#endif + + return MPxSurfaceShape::connectionMade(plug,otherPlug,asSrc); +} + +MStatus shaveHairShape::connectionBroken (const MPlug & plug, const MPlug &otherPlug, bool asSrc ) +{ + +#ifdef USE_PROCEDURALS + if(plug == procedural) + { + SHAVESDKdelete_procedural(&hairnode); + } +#endif + +#ifdef PER_NODE_TEXLOOKUP + bool texConnected = false; + if(plug == hairColorTexture || + plug == rootHairColorTexture || + plug == mutantHairColorTexture) + { + texConnected = true; + } + MPlug texPlug(thisMObject(),shaveHairShape::shaveTextureAttr); + int nel = texPlug.numElements(); + for(int k = 0; k < nel; k++) + { + MPlug checkPlug = texPlug.elementByLogicalIndex(k); + if(checkPlug == plug) + { + texConnected = true; + break; + } + } + //not sure that it's a good place + if(texConnected /* && !IsBuildingLookups()*/) + { + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + + //mDirtyTexture = true; + dirties.DIRTY_TEXTURE = 1; + dirties.DIRTY_TEXTURE_JOE = 1; + } +#endif + return MPxSurfaceShape::connectionBroken(plug,otherPlug,asSrc); +} + +#ifdef PER_NODE_TEXLOOKUP +void shaveHairShape::updateTexLookups() +{ + if(!this->mNodeInitialized) + return; + + if(/*mDirtyTexture*/ dirties.DIRTY_TEXTURE == 1 && !IsBuildingLookups()) + { + + MObjectArray shaveHairShapes; + shaveUtil::getShaveNodes(shaveHairShapes); + if (shaveHairShapes.length() > 0) + { + //////////////////////// + //printf("update textures\n"); fflush(stdout); + //////////////////////// + shaveGlobals::Globals globals; + shaveGlobals::getGlobals(globals); + + //printf("00 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + //something there is causing stacking in fallback + //there is a lot of stuff happening + initTexInfoLookup2(shaveHairShapes, "", globals.verbose,true ,thisMObject()); + + //nope, does not help and creates exra flickering + //if(doFallbackGlob) + //need to make sure that there are some textures + //{ + // //printf("fullRedrawOnIdle fired\n");fflush(stdout); + + // qint64 t = GetQTimer().elapsed(); + // SetLastMoveTime(t); + + // shaveStyleCmd::redrawOnIdlDone = false; + // MGlobal::executeCommandOnIdle("shaveStyle -fullRedrawOnIdle"); + //} + //nope, makes entire view flickering + //M3dView::active3dView().refresh(true,true); + } + + //mDirtyTexture = false; + dirties.DIRTY_TEXTURE = 0; + + } +} +#endif + +int shaveHairShape::getHairGroup(MDataBlock* block) const +{ + ENTER(); + + short hairGroup; + + if (block) + { + hairGroup = block->inputValue(aHairGroup).asShort(); + } + else + { + MPlug plug(thisMObject(), aHairGroup); + plug.getValue(hairGroup); + } + + RETURN((int)hairGroup); +} + + +#if MAYA_API_VERSION >= 20180000 +bool shaveHairShape::getInternalValue(const MPlug &plug, MDataHandle &hdl) +#else +bool shaveHairShape::getInternalValueInContext( + const MPlug& plug, MDataHandle& hdl, MDGContext& ctx +) +#endif +{ + ENTER(); + + if (plug == aNodeVersion) + { + // + // If the version is zero then it has not yet been explicitly set, + // so we must determine an appropriate value. + // + if (mNodeVersion == 0) + { + // + // If we're in the middle of file loading, we might still be + // awaiting the set command so we should continue to return 0. + // Otherwise we assume that this is a newly-created node and + // return the latest version. + // + // I used to do this in the postConstructor() but that was + // causing random crashes within Maya, so we do it here now, + // instead. + // + if (!shaveUtil::isLoadingFile()) mNodeVersion = kNodeVersion; + } + + hdl.set(mNodeVersion); + RETURN(true); + } + + if (!plug.isArray() + && ((plug == mControlPoints) || (plug.parent() == mControlPoints) + || (plug == aGuide) || (plug.parent() == aGuide))) + { + bool isGuide = ((plug == aGuide) || (plug.parent() == aGuide)); + bool isParent = ((plug == mControlPoints) || (plug == aGuide)); + int plugIndex; + + if (isParent) + plugIndex = plug.logicalIndex(); + else + plugIndex = plug.parent().logicalIndex(); + + SOFTGUIDE guide; + int guideIndex; + int vertIndex; + + if (isGuide) + { + guideIndex = plugIndex; + + // + // For the position of a guide, we use its middle vertex. + // + vertIndex = SHAVE_VERTS_PER_GUIDE / 2; + } + else + { + guideIndex = plugIndex / SHAVE_VERTS_PER_GUIDE; + vertIndex = plugIndex % SHAVE_VERTS_PER_GUIDE; + } + + // + // Make sure that this node is loaded into the Shave engine. + // + makeCurrent(); + + if (SHAVEfetch_guide(guideIndex, &guide) == -1) + { + if (isParent) + hdl.set(0.0, 0.0, 0.0); + else + hdl.set(0.0); + } + else + { + if (isParent) + { + hdl.set( + (double)guide.guide[vertIndex].x, + (double)guide.guide[vertIndex].y, + (double)guide.guide[vertIndex].z + ); + } + else if (plug == plug.parent().child(0)) + hdl.set((double)guide.guide[vertIndex].x); + else if (plug == plug.parent().child(1)) + hdl.set((double)guide.guide[vertIndex].y); + else + hdl.set((double)guide.guide[vertIndex].z); + } + + RETURN(true); + } + + if (plug == mHasHistoryOnCreate) + { + hdl.set(false); + RETURN(true); + } + +#if MAYA_API_VERSION >= 20180000 + RETURN(MPxSurfaceShape::getInternalValue(plug, hdl)); +#else + RETURN(MPxSurfaceShape::getInternalValueInContext(plug, hdl, ctx)); +#endif +} + + +#if MAYA_API_VERSION >= 20180000 +bool shaveHairShape::setInternalValue(const MPlug &plug, const MDataHandle &hdl) +#else +bool shaveHairShape::setInternalValueInContext( + const MPlug& plug, const MDataHandle& hdl, MDGContext& ctx +) +#endif +{ + ENTER(); + + if (plug == aNodeVersion) + { + mNodeVersion = hdl.asInt(); + RETURN(true); + } + +#if MAYA_API_VERSION >= 20180000 + RETURN(MPxSurfaceShape::setInternalValue(plug, hdl)); +#else + RETURN(MPxSurfaceShape::setInternalValueInContext(plug, hdl, ctx)); +#endif +} + + +void shaveHairShape::componentToPlugs( + MObject& component, MSelectionList& list +) const +{ + ENTER(); + + if (component.hasFn(MFn::kDoubleIndexedComponent)) + { + MFnDoubleIndexedComponent compFn(component); + int guide; + int i; + int numComponents = compFn.elementCount(); + MPlug plug(thisMObject(), mControlPoints); + int vert; + + for (i = 0; i < numComponents; i++) + { + compFn.getElement(i, guide, vert); + + plug.selectAncestorLogicalIndex( + guide * SHAVE_VERTS_PER_GUIDE + vert, mControlPoints + ); + + list.add(plug); + } + } + else if (component.hasFn(MFn::kSingleIndexedComponent)) + { + MFnSingleIndexedComponent compFn(component); + int i; + int numComponents = compFn.elementCount(); + MPlug plug(thisMObject(), aGuide); + + for (i = 0; i < numComponents; i++) + { + plug.selectAncestorLogicalIndex(compFn.element(i), aGuide); + list.add(plug); + } + } + + LEAVE(); +} + + +MObject shaveHairShape::createFullVertexGroup() const +{ + ENTER(); + + shaveHairShape* nonConstThis = const_cast<shaveHairShape*>(this); + unsigned numGuides = nonConstThis->getGuideCount(); + MFnDoubleIndexedComponent compFn; + MObject comp = compFn.create(kShaveGuideVertComponent); + + compFn.setCompleteData((int)numGuides, SHAVE_VERTS_PER_GUIDE); + + RETURN(comp); +} + + +MPxGeometryIterator* shaveHairShape::geometryIteratorSetup( + MObjectArray& components1, + MObject& components2, + bool forReadOnly +) +{ + ENTER(); + + shaveHairGeomIt* iter = 0; + + if (components2.isNull()) + iter = new shaveHairGeomIt(this, components1); + else + iter = new shaveHairGeomIt(this, components2); + + RETURN(iter); +} + + +bool shaveHairShape::match( + const MSelectionMask& mask, const MObjectArray& componentList +) const +{ + ENTER(); + bool result = false; + + if (componentList.length() == 0) + { + MSelectionMask shapeMask = getShapeSelectionMask(); + result = mask.intersects(shapeMask); + } + else + { + for (unsigned int i = 0; i < componentList.length(); ++i) + { + if (((componentList[i].apiType() == MFn::kMeshEdgeComponent) && mask.intersects(MSelectionMask::kSelectMeshEdges)) + || ((componentList[i].apiType() == MFn::kSurfaceCVComponent) && mask.intersects(MSelectionMask::kSelectCVs))) + { + result = true; + break; + } + } + } + + RETURN(result); +} + + +MPxSurfaceShape::MatchResult shaveHairShape::matchComponent( + const MSelectionList& item, + const MAttributeSpecArray& specs, + MSelectionList& list +) +{ + ENTER(); + + if (specs.length() == 1) + { + MAttributeSpec spec = specs[0]; + MString attrName = spec.name(); + + // + // We like to use 'guide' or 'gd' for guide components, but because + // we're forced to use an existing component type (mesh edge) we + // also have to accept Maya's 'e' name for the component. + // + if ((attrName == "guide") || (attrName == "gd") || (attrName == "e")) + { + if (spec.dimensions() != 1) RETURN(kMatchInvalidAttributeDim); + + MAttributeIndex attrIndex = spec[0]; + int lower = 0; + int upper = 0; + unsigned numGuides = getGuideCount(); + + if (attrIndex.hasLowerBound()) attrIndex.getLower(lower); + if (attrIndex.hasUpperBound()) attrIndex.getUpper(upper); + + if ((lower > upper) || (lower < 0) || (upper >= (int)numGuides)) + RETURN(kMatchInvalidAttributeRange); + + MFnSingleIndexedComponent compFn; + MObject comp; + + comp = compFn.create(kShaveGuideComponent); + + int guide; + + for (guide = lower; guide <= upper; guide++) + compFn.addElement(guide); + + MDagPath path; + + item.getDagPath(0, path); + list.add(path, comp); + + RETURN(kMatchOk); + } + + // + // We like to use 'vertex' or 'vtx' for vertex components, but + // because we're forced to use an existing component type (surface + // cv) we also have to accept Maya's 'cv' name for the component. + // + if ((attrName == "vertex") || (attrName == "vtx") || (attrName == "cv")) + { + if (spec.dimensions() != 2) RETURN(kMatchInvalidAttributeDim); + + MAttributeIndex attrIndex = spec[0]; + int lowerGuide = 0; + int upperGuide = 0; + unsigned numGuides = getGuideCount(); + + if (attrIndex.hasLowerBound()) attrIndex.getLower(lowerGuide); + if (attrIndex.hasUpperBound()) attrIndex.getUpper(upperGuide); + + if ((lowerGuide > upperGuide) + || (lowerGuide < 0) + || (upperGuide >= (int)numGuides)) + { + RETURN(kMatchInvalidAttributeRange); + } + + attrIndex = spec[1]; + + int lowerVert = 0; + int upperVert = 0; + + if (attrIndex.hasLowerBound()) attrIndex.getLower(lowerVert); + if (attrIndex.hasUpperBound()) attrIndex.getUpper(upperVert); + + if ((lowerVert > upperVert) + || (lowerVert < 0) + || (upperVert >= SHAVE_VERTS_PER_GUIDE)) + { + RETURN(kMatchInvalidAttributeRange); + } + + MFnDoubleIndexedComponent compFn; + MObject comp; + + comp = compFn.create(kShaveGuideVertComponent); + + int guide; + int vert; + + for (guide = lowerGuide; guide <= upperGuide; guide++) + { + for (vert = lowerVert; vert <= upperVert; vert++) + compFn.addElement(guide, vert); + } + + MDagPath path; + + item.getDagPath(0, path); + list.add(path, comp); + + RETURN(kMatchOk); + } + } + + RETURN(MPxSurfaceShape::matchComponent(item, specs, list)); +} + + +void shaveHairShape::transformUsing( + const MMatrix& mat, const MObjectArray& components +) +{ + ENTER(); + + transformUsing(mat, components, kNoPointCaching, 0); + + LEAVE(); +} + + +// +// The C++ standard does not allow locally declared types to be used as +// template arguments. To get around this we have to to declare the types +// globally. By using an anonymous namespace we can prevent the types from +// being exported and polluting the external namespace. +// +namespace +{ + struct VertRef + { + int vertIdx; + int cacheIdx; + }; + + typedef std::vector<struct VertRef> VertRefs; +} + + +void shaveHairShape::transformUsing( + const MMatrix& mat, + const MObjectArray& components, + MPxSurfaceShape::MVertexCachingMode cachingMode, + MPointArray* pointCache +) +{ + ENTER(); + + if (pointCache == 0) cachingMode = kNoPointCaching; + + // + // Our points are in worldSpace while the transformation matrix is + // expecting local space. So modify the matrix to take us into local + // space at the start, then back out to worldSpace when done. + // + MDagPath path; + + MDagPath::getAPathTo(thisMObject(), path); + + MMatrix m = path.inclusiveMatrixInverse()*mat*path.inclusiveMatrix(); + + // + // Make sure that this hair shape is loaded into the shave engine. + // + makeCurrent(); + + if (components.length() == 0) + { + // + // No components were specified, so we must apply the + // transformation to every vert. + // + unsigned cacheIdx = 0; + unsigned cacheLen = 0; + SOFTGUIDE guide; + int guideIdx; + int i; + + if (cachingMode == kSavePoints) + { + cacheLen = getGuideCount() * SHAVE_VERTS_PER_GUIDE; + pointCache->setLength(cacheLen); + } + else if (pointCache) + cacheLen = pointCache->length(); + + for (guideIdx = 0; + SHAVEfetch_guide(guideIdx, &guide) != -1; + guideIdx++) + { + for (i = 0; i < SHAVE_VERTS_PER_GUIDE; i++) + { + if (cachingMode == kRestorePoints) + { + if (cacheIdx >= cacheLen) break; + + MPoint& p = (*pointCache)[cacheIdx]; + + guide.guide[i].x = (float)p.x; + guide.guide[i].y = (float)p.y; + guide.guide[i].z = (float)p.z; + } + else + { + MPoint p( + guide.guide[i].x, + guide.guide[i].y, + guide.guide[i].z + ); + + if (cachingMode == kSavePoints) + (*pointCache)[cacheIdx] = p; + + p *= m; + + guide.guide[i].x = (float)p.x; + guide.guide[i].y = (float)p.y; + guide.guide[i].z = (float)p.z; + } + + guide.select[i] = 1; + cacheIdx++; + } + + // + // We may have exited early from the inner loop because the + // pointCache was exhausted, in which case we should exit the + // outer loop, too. + // + if (i < SHAVE_VERTS_PER_GUIDE) break; + + SOFTput_guide(guideIdx, &guide); + } + } + else + { + // + // Sort the components by guides. + // + std::map<int, VertRefs> guideVerts; + int guideIdx; + unsigned i; + int j; + int k; + struct VertRef vr; + + vr.cacheIdx = 0; + + for (i = 0; i < components.length(); i++) + { + // + // Vertices + // + if (components[i].hasFn(MFn::kDoubleIndexedComponent)) + { + MFnDoubleIndexedComponent compFn(components[i]); + + for (j = 0; j < compFn.elementCount(); j++) + { + compFn.getElement(j, guideIdx, vr.vertIdx); + + guideVerts[guideIdx].push_back(vr); + vr.cacheIdx++; + } + } + // + // Entire guides. + // + else if (components[i].hasFn(MFn::kSingleIndexedComponent)) + { + MFnSingleIndexedComponent compFn(components[i]); + + for (j = 0; j < compFn.elementCount(); j++) + { + guideIdx = compFn.element(j); + + for (k = 0; k < SHAVE_VERTS_PER_GUIDE; k++) + { + vr.vertIdx = k; + guideVerts[guideIdx].push_back(vr); + vr.cacheIdx++; + } + } + } + } + + unsigned cacheLen = 0; + + if (cachingMode == kSavePoints) + { + cacheLen = vr.cacheIdx; + pointCache->setLength(cacheLen); + } + else if (pointCache) + cacheLen = pointCache->length(); + + SOFTGUIDE guide; + std::map<int, VertRefs>::iterator iter; + + for (iter = guideVerts.begin(); iter != guideVerts.end(); iter++) + { + VertRefs& verts = (*iter).second; + + guideIdx = (*iter).first; + + if (SHAVEfetch_guide(guideIdx, &guide) != -1) + { + guide.select[0] = 1; + + for (j = 1; j < SHAVE_VERTS_PER_GUIDE; j++) + guide.select[j] = 0; + + for (i = 0; i < verts.size(); i++) + { + int cacheIdx = verts[i].cacheIdx; + int vertIdx = verts[i].vertIdx; + + if (cachingMode == kRestorePoints) + { + if (cacheIdx < (int)cacheLen) + { + MPoint& p = (*pointCache)[cacheIdx]; + + guide.guide[vertIdx].x = (float)p.x; + guide.guide[vertIdx].y = (float)p.y; + guide.guide[vertIdx].z = (float)p.z; + } + } + else + { + MPoint p( + guide.guide[vertIdx].x, + guide.guide[vertIdx].y, + guide.guide[vertIdx].z + ); + + if (cachingMode == kSavePoints) + (*pointCache)[cacheIdx] = p; + + p *= m; + + guide.guide[vertIdx].x = (float)p.x; + guide.guide[vertIdx].y = (float)p.y; + guide.guide[vertIdx].z = (float)p.z; + } + + guide.select[vertIdx] = 1; + } + + SOFTput_guide(guideIdx, &guide); + } + } + } + + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + mGuideCacheDirty = true; + ///////////////// + doFallbackGlob = true; + //////////////// + applyEdits(true); + childChanged(); + + LEAVE(); +} + + +void shaveHairShape::clearComponentSelections() +{ + ENTER(); + + // + // Clear the selection bits. + // + Guides::iterator iter; + + for (iter = mGuideCache.guides.begin(); iter != mGuideCache.guides.end(); iter++) + { + (*iter).select = 0; + } + + mHaveSelections = false; + mGuideSelectionsDirty = false; + + LEAVE(); +} + + +void shaveHairShape::clearHiddenGuides() +{ + ENTER(); + + Guides::iterator iter; + + for (iter = mGuideCache.guides.begin(); iter != mGuideCache.guides.end(); iter++) + { + (*iter).hidden = false; + } + + LEAVE(); +} + + +void shaveHairShape::clearSelections() +{ + ENTER(); + + clearComponentSelections(); + clearHiddenGuides(); + + LEAVE(); +} + + +void shaveHairShape::updateSelections(const MSelectionList& selections) +{ + ENTER(); + + MObject comp; + bool haveNewSelections = false; + unsigned i; + MDagPath path; + + // + // Find the first selected component from this node, if there is one. + // + for (i = 0; i < selections.length(); i++) + { + selections.getDagPath(i, path, comp); + + if (path.isValid() && !comp.isNull() && (path.node() == thisMObject())) + { + haveNewSelections = true; + break; + } + } + + // + // If we have any new selections to set or old ones to clear then mark + // the selections as being dirty. + // + if (haveNewSelections || mHaveSelections) + { + mGuideSelectionsDirty = true; + } + + LEAVE(); +} + + +void shaveHairShape::makeCurrent(bool trigger) +{ + + + ENTER(); + + //printf("makeCurrent(%s)\n",trigger?"true":"false");fflush(stdout); + + // + // Make sure that the shaveNode is up-to-date by pulling its trigger. + // + if(trigger) + { + MPlug plug(thisMObject(), triggerAttr); + float value; + plug.getValue(value); + } + // + // If the node is not currently loaded into the shave engine, then load + // it in now. + // + if (!isCurrent()) + SHAVEflush_state(&hairnode); + + /////////////// +// MFnDependencyNode dFn(thisMObject()); +// MGlobal::displayInfo(MString("SHAVEflush_state ")+ dFn.name() + MString(" ") + this->getShaveID()); + ////////////////// + + + LEAVE(); +} + + +void shaveHairShape::applyEdits(bool resetRestPose) +{ + ENTER(); + + if (getHairGroup() == 4) + updateSplines(); + else + { + // + // The edits will be in the engine's current state. So let's fetch + // that into our local store. + // + SHAVEfetch_node(&hairnode); + + if (resetRestPose) SHAVEreplace_rest(&hairnode); + + updateBlindData(); + } + + // + // Presumably, if something was edited, then we have changes that need + // to be redrawn. + // + guidesChanged(); + + LEAVE(); +} + + +void shaveHairShape::guidesChanged() +{ + ENTER(); + + // + // If one or more guides have been modified then our display hair cache + // is no longer valid. + // + mDisplayHairCacheDirty = true; + mDisplayInstCacheDirty = true; + mGuideCacheDirty = true; + + ////////////////// + doFallbackGlob = true; + ////////////////// + + childChanged(); + + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + + LEAVE(); +} + + +void shaveHairShape::applySelections() +{ + ENTER(); + + makeCurrent(); + + const GuidesSnapshot& cachedGuides = getGuides(); + + bool componentMode = (MGlobal::selectionMode() + == MGlobal::kSelectComponentMode); + SOFTGUIDE guide; + int g; + int v; + + for (g = 0; SHAVEfetch_guide(g, &guide) != -1; g++) + { + // + // If we're not in component mode, then we treat every vert as + // being selected. + // + for (v = 1; v < SHAVE_VERTS_PER_GUIDE; v++) + { + if (!componentMode || (cachedGuides.guides[g].select & (1 << v))) + guide.select[v] = 1; + else + guide.select[v] = 0; + } + + // + // If any verts on the guide were selected then select the root + // vert as well. + // + if (!componentMode || cachedGuides.guides[g].select) + guide.select[0] = 1; + else + guide.select[0] = 0; + + SOFTput_guideSELECTONLY(g, &guide); + } + + LEAVE(); +} + +float shaveHairShape::getGuideThinkness() const +{ + shaveGlobals::getGlobals(); + return guideThickGlob; + //float n = 0.0f; + //MPlug gth(thisMObject(),aDisplayGuideThick); + //gth.getValue(n); + + //return n; +} + +float shaveHairShape::getGuideExt() const +{ + float extent = 0.0f; + MPlug plug(thisMObject(),aDisplayGuideExt); + plug.getValue(extent); + + return extent; +} + +void shaveHairShape::setGuideExt(float e) +{ + MPlug gth(thisMObject(),aDisplayGuideExt); + gth.setValue(e); +} + +void shaveHairShape::setGuideDisplayOverride(bool enabled) +{ + ENTER(); + + mGuideDisplayOverride = enabled; + + // + // If the override is off and the regular guide display flag is also + // off, then clear the guide cache to free up that memory. + // + if (!mGuideDisplayOverride) + { + //MPlug plug(thisMObject(), aDisplayGuides); + //plug.getValue(enabled); + enabled = displayGuidesGlob; + + if (!enabled) + { + mGuideCache.guides.clear(); + mGuideCacheDirty = true; + } + } + + LEAVE(); +} + + +void shaveHairShape::hideSelectedGuides() +{ + ENTER(); + + // + // Mark all selected guides in the cache as hidden. + // + unsigned i; + + for (i = 0; i < mGuideCache.guides.size(); i++) + { + if (mGuideCache.guides[i].select) mGuideCache.guides[i].hidden = true; + } + + // + // Tell Shave to hide the selected guides. + // + SHAVEselect_hide(); + + // + // Select all remaining unhidden guides. + // + int g; + SOFTGUIDE guide; + + for (g = 0; SHAVEfetch_guide(g, &guide) != -1; g++) + { + if (!guide.hidden) + { + for (i = 0; i < SHAVE_VERTS_PER_GUIDE; i++) + guide.select[i] = 1; + + SOFTput_guideSELECTONLY(g, &guide); + } + } + + // + // Update Maya's selection list. That will eventually result in us + // being asked to update our cached selections, so there's no point in + // updating them right now. + // + updateMayaComponentSelections(); + + LEAVE(); +} + + +void shaveHairShape::unhideGuides() +{ + ENTER(); + + clearHiddenGuides(); + SHAVEselect_unhide(); + + // + // Let Maya know that a redisplay is required. + // + childChanged(); + + LEAVE(); +} + + +// +// Set Maya's component selections to reflect the current state of guide +// and vert selections inside the shave node. +// +// We completely replace the current selection list here, rather than +// trying to preserve other selections. +// +// Changing the selection list will fire the selectionChanged callback +// which will in turn do component conversions and update the hair shape's +// cache, if necessary. +// +void shaveHairShape::updateMayaComponentSelections() +{ + ENTER(); + + if (MGlobal::selectionMode() == MGlobal::kSelectComponentMode) + { + makeCurrent(); + + MString selMode; + MGlobal::executeCommand("optionVar -q shaveBrushSelectMode", selMode); + + bool isGuides = (selMode == "guide"); + MFnSingleIndexedComponent guideCompFn; + MFnDoubleIndexedComponent vertCompFn; + MObject comp; + + if (isGuides) + { + comp = guideCompFn.create(shaveHairShape::kShaveGuideComponent); + } + else + { + comp = vertCompFn.create(shaveHairShape::kShaveGuideVertComponent); + } + + SOFTGUIDE g; + int i; + int j; + + for (i = 0; SHAVEfetch_guide(i, &g) != -1; i++) + { + if (isGuides) + { + if (g.select[0]) guideCompFn.addElement(i); + } + else + { + bool isSelected = false; + + for (j = 1; j < SHAVE_VERTS_PER_GUIDE; j++) + { + if (g.select[j]) + { + vertCompFn.addElement(i, j); + isSelected = true; + } + } + + if (isSelected) vertCompFn.addElement(i, 0); + } + } + + MSelectionList list; + MDagPath path; + + MDagPath::getAPathTo(thisMObject(), path); + list.add(path, comp); + + MGlobal::setActiveSelectionList(list); + } + + LEAVE(); +} + + +// +// Add a UV set to the set of those that Shave knows about for this node. +// +int shaveHairShape::addShaveUVSets() +{ + ENTER(); + + SHAVENODE* hairNode = getHairNode(); + + // 'growthCollisionUVs' contains UV values in its 'v' array, which is + // what SHAVEadd_uvset() is expecting. Note that these are + // per-face-vert UVs while those in memShaveObj.uv are per-vert. + int setIdx = SHAVEadd_uvset(hairNode, &growthCollisionUVs); + + RETURN(setIdx); +} + + +void shaveHairShape::removeShaveUVSets() +{ + ENTER(); + + SHAVEclear_uvsets(getHairNode()); + + LEAVE(); +} + + +MObjectArray shaveHairShape::activeComponents() const +{ + ENTER(); + + MObjectArray comps = MPxSurfaceShape::activeComponents(); + cout << "activeComponents returns " << comps.length() << " elements" << endl; + RETURN(comps); +} + + +bool shaveHairShape::hasActiveComponents() const +{ + ENTER(); + + bool result = MPxSurfaceShape::hasActiveComponents(); + cout << "hasActiveComponents returns " << result << endl; + RETURN(result); +} + + +int shaveHairShape::initNumShaveNodes() +{ + ENTER(); + + MObjectArray nodes; + shaveUtil::getShaveNodes(nodes); + + numShaveNodes = (int)nodes.length(); + + RETURN(numShaveNodes); +} + + +MStatus shaveHairShape::legalConnection( + const MPlug& ourPlug, const MPlug& theirPlug, bool weAreSource, bool& isLegal +) +{ + if (((ourPlug == aCollisionSet) || (ourPlug == aGrowthSet)) && !weAreSource) + { + // We only allow incoming connections to 'collisionSet' and + // 'growthSet' from objectSet nodes. + isLegal = theirPlug.node().hasFn(MFn::kSet); + return MS::kSuccess; + } + + return MPxSurfaceShape::legalConnection(ourPlug, theirPlug, weAreSource, isLegal); +} + + +void shaveHairShape::beforeDuplication(void* clientData) +{ + if (clientData) static_cast<shaveHairShape*>(clientData)->beforeDuplication(); +} + + +void shaveHairShape::beforeDuplication() +{ + // When Maya duplicates a node it insists on putting the duplicate + // into all of the same sets as the original. We don't want the + // duplicate to appear in the original's growth and collision sets so + // we save their contents at the start of the duplication and restore + // them afterward. + saveGeomSet(aCollisionSet, mSavedCollisionList); + saveGeomSet(aGrowthSet, mSavedGrowthList); +} + + +void shaveHairShape::afterDuplication(void* clientData) +{ + if (clientData) static_cast<shaveHairShape*>(clientData)->afterDuplication(); +} + + +void shaveHairShape::afterDuplication() +{ + restoreGeomSet(aCollisionSet, mSavedCollisionList); + restoreGeomSet(aGrowthSet, mSavedGrowthList); +} + + +void shaveHairShape::saveGeomSet(const MObject& attr, MSelectionList& saveList) +{ + MObject set = getGeomSet(attr); + + saveList.clear(); + + if (!set.isNull()) + { + MFnSet setFn(set); + setFn.getMembers(saveList, false); + } +} + + +void shaveHairShape::restoreGeomSet(const MObject& attr, MSelectionList& saveList) +{ + MSelectionList newMembers; + MObject set; + + set = getGeomSet(attr); + if (!set.isNull()) + { + MFnSet setFn(set); + setFn.getMembers(newMembers, false); + + // Remove the original members from the current list of members. + newMembers.merge(saveList, MSelectionList::kRemoveFromList); + + // If there are any members left in the list they were added by + // the duplication and must be removed from the set. + if (!newMembers.isEmpty()) setFn.removeMembers(newMembers); + } +} + + +bool shaveHairShape::getWorldGeomAttrs( + const MObject& shape, MObject& worldGeomAttr, MObject& growthTargetAttr +) +{ + static MObject worldCurveAttr = MObject::kNullObj; + static MObject worldMeshAttr = MObject::kNullObj; + static MObject worldSubdivAttr = MObject::kNullObj; + static MObject worldSurfaceAttr = MObject::kNullObj; + + bool result = true; + + MFnDependencyNode shapeFn(shape); + + if (shape.hasFn(MFn::kMesh)) + { + if (worldMeshAttr.isNull()) + worldMeshAttr = shapeFn.attribute("worldMesh"); + + worldGeomAttr = worldMeshAttr; + growthTargetAttr = inputMesh; + } + else if (shape.hasFn(MFn::kNurbsCurve)) + { + if (worldCurveAttr.isNull()) + worldCurveAttr = shapeFn.attribute("worldSpace"); + + worldGeomAttr = worldCurveAttr; + growthTargetAttr = inputCurve; + } + else if (shape.hasFn(MFn::kNurbsSurface)) + { + if (worldSurfaceAttr.isNull()) + worldSurfaceAttr = shapeFn.attribute("worldSpace"); + + worldGeomAttr = worldSurfaceAttr; + growthTargetAttr = inputSurf; + } + else if (shape.hasFn(MFn::kSubdiv)) + { + if (worldSubdivAttr.isNull()) + worldSubdivAttr = shapeFn.attribute("worldSubdiv"); + + worldGeomAttr = worldSubdivAttr; + growthTargetAttr = inputSurf; + } + else + { + result = false; + } + + return result; +} + + +MStatus shaveHairShape::setSplineLocks(const MDagPathArray& curves) +{ + // Clear any existing splineLocks. + clearSplineLocks(); + + // We can't use splineLocks on spline hair. + if (getHairGroup() == 4) return MS::kInvalidParameter; + + // If 'curves' actually contains some curves, attach them to our + // 'inputCurve' attr. + if (curves.length() > 0) + { + MDGModifier dgmod; + MPlug inputCurveArray(thisMObject(), inputCurve); + unsigned int i; + + for (i = 0; i < curves.length(); ++i) + { + // Find the plug containing the curve's worldSpace geometry. + MFnNurbsCurve curveFn(curves[i]); + MPlug worldGeom = curveFn.findPlug("worldSpace"); + + // Get the correct instance. + worldGeom = worldGeom.elementByLogicalIndex( + curves[i].instanceNumber() + ); + + // Make the connection. + dgmod.connect(worldGeom, inputCurveArray.elementByLogicalIndex(i)); + } + + dgmod.doIt(); + + // Set the flag to indicate that we're using splineLocks. + MPlug splineLockPlug(thisMObject(), aSplineLock); + splineLockPlug.setValue(true); + } + + return MS::kSuccess; +} + + +void shaveHairShape::clearSplineLocks() +{ + MPlug splineLockPlug(thisMObject(), aSplineLock); + bool splineLockOn; + SHAVENODE* hairnode = getHairNode(); + + splineLockPlug.getValue(splineLockOn); + + if (splineLockOn) + { + disconnectIncoming(inputCurve, NULL); + splineLockPlug.setValue(false); + free_geomWF(&mLockSplines); + SHAVEsplinelock_clear(hairnode); + } +} + + +void shaveHairShape::dirtyOutputMesh() +{ + // The output mesh depends upon the trigger attr. + // + MPlug trigger(thisMObject(), triggerAttr); + trigger.setValue(1.0f); +} + + + +void shaveHairShape::scheduleXplant() +{ + MPlug plug(thisMObject(), aEvalOption); + + // If there's any other eval option already set up (e.g. "create"), + // then we shouldn't be doing an xplant as well. Only do it if + // evalOption is empty. + // + if (plug.asString() == "") { + MString cmd("doTransplant"); + plug.setValue(cmd); + } +} + + +MSelectionMask shaveHairShape::getComponentSelectionMask() const +{ + MSelectionMask mask(MSelectionMask::kSelectCVs); + mask.addMask(MSelectionMask::kSelectMeshEdges); + + return mask; +} + + +MSelectionMask shaveHairShape::getShapeSelectionMask() const +{ + return MSelectionMask(kShapeSelectionMaskName); +} diff --git a/mayaPlug/shaveHairShape.h b/mayaPlug/shaveHairShape.h new file mode 100644 index 0000000..c010ee3 --- /dev/null +++ b/mayaPlug/shaveHairShape.h @@ -0,0 +1,1202 @@ +#ifndef shaveHairShape_h +#define shaveHairShape_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <vector> +#include <assert.h> + +#include <maya/MColorArray.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFn.h> +#include <maya/MFnMesh.h> +#include <maya/MImage.h> +#include <maya/MMessage.h> +#include <maya/MPxSurfaceShape.h> +#include <maya/MSelectionList.h> +#include <maya/MVectorArray.h> +#include <maya/MViewport2Renderer.h> + +#include "shaveSDK.h" +#include "shaveObjExporter.h" +#include "ShavePerVertTexInfo.h" +#include "shaveTextureStore.h" + +#ifdef OSMac_MachO_ +# include <OpenGL/gl.h> +# include <OpenGL/glu.h> +#else +# include <GL/gl.h> +# include <GL/glu.h> +#endif + +#define kStringLength 512 + +#if MAYA_API_VERSION < 20180000 +class M3dView; +class MDataHandle; +class MDGContext; +class MDGModifier; +class MObjectArray; +class MPlug; +class MPlugArray; +class MPxGeometryIterator; +#endif + + +////////////////////// threads //////////////////////////////// + +#define REUSABLE_THREADS +#ifdef REUSABLE_THREADS + +#ifndef APIENTRY +#ifdef _WIN32 +#define APIENTRY WINAPI +#else +#define APIENTRY +#endif +#endif + +#ifdef _WIN32 +typedef DWORD/* unsigned int*/ (APIENTRY THPROC)(void* param); +#else +typedef void* (APIENTRY THPROC)( void* param); +#endif + +class LEvent { +public: +#ifdef _WIN32 + LEvent( bool signalled = false, LPSECURITY_ATTRIBUTES sa = NULL); +#else + LEvent( bool signalled = false); +#endif + + virtual ~LEvent(); + + void Set(); + void Reset(); + void Wait(); + +#ifdef _WIN32 + HANDLE handle; +#else + pthread_mutex_t mutex; + pthread_cond_t cond; + bool triggered; +#endif + +}; + +class LThread { +public: + + LThread( + THPROC* pThreadFunc, // address of thread callback + void* pThreadData = NULL, // address of callback's parameter, + unsigned int exeFlags = 0, // creation flag + unsigned int sSize = 0, // stack size + bool inheritable = false // inheritable + ); + virtual ~LThread(); + + enum ThState + { + eWaiting, + eAvilable, + eRunning, + eExit + }; + virtual void Create(); + virtual void Execute(THPROC* pThreadFunc = NULL, void* pThreadData = NULL); + virtual void WaitToFinish(); + +#ifdef _WIN32 + DWORD GetThreadId() const {return thId(); } + HANDLE GetHandle() const {return th();} +#else + int GetThreadId() const {return thId(); } + pthread_t* GetHandle() {return &_th();} +#endif + int GetState() const {return state(); } + void* GetThreadArgument() const {return thArg(); } + + LEvent* GetFinish() const {return efinish();} + LEvent* GetStart() const {return estart();} + + +protected: + + static void getErrorDetail(int&,std::string&); + + //extended thread proc, will be used if mutex and doneEvent not NULL + static THPROC thFnEx; + + void execute(); + + //const member access +#ifdef _WIN32 + inline HANDLE th() const {return m_th;} + inline unsigned int thId() const {return m_thId;} + inline DWORD exitCode() const {return m_exitCode;} + inline LPSECURITY_ATTRIBUTES sa() const {return m_sa;} +#else + inline const pthread_t& th() const {return m_th;} + inline int thId() const {return m_thId;} + inline int exitCode() const {return m_exitCode;} +#endif + inline LEvent* estart() const {return m_estart;} + inline LEvent* efinish()const {return m_efinish;} + inline void* thArg() const {return m_thArg;} + inline THPROC* thFn() const {return m_thFn;} + inline unsigned int stackSize()const {return m_stackSize;} + inline unsigned int exeFlags() const {return m_exeFlags;} + inline ThState state() const {return m_state;} + + + //member access +#ifdef _WIN32 + inline HANDLE& _th() {return m_th;} + inline DWORD& _thId() {return m_thId;} + inline DWORD& _exitCode(){return m_exitCode;} + inline LPSECURITY_ATTRIBUTES& _sa() {return m_sa;} +#else + inline pthread_t& _th() {return m_th;} + inline int& _thId() {return m_thId;} + inline int& _exitCode(){return m_exitCode;} +#endif + inline LEvent*& _estart() {return m_estart;} + inline LEvent*& _efinish() {return m_efinish;} + + inline void*& _thArg() {return m_thArg;} + inline THPROC*& _thFn() {return m_thFn;} + + inline unsigned int& _stackSize(){return m_stackSize;} + inline unsigned int& _exeFlags() {return m_exeFlags;} + + inline ThState& _state() {return m_state;} + + +private: +#ifdef _WIN32 + HANDLE m_th; + DWORD m_thId; + DWORD m_exitCode; + LPSECURITY_ATTRIBUTES m_sa; +#else + pthread_t m_th; + int m_thId; + int m_exitCode; +#endif + LEvent* m_estart; + LEvent* m_efinish; + + void* m_thArg; + THPROC* m_thFn; + + unsigned int m_stackSize; + unsigned int m_exeFlags; + + ThState m_state; +}; + + +class LThreadGroup { +public: + LThreadGroup(THPROC* thFn = NULL); + virtual ~LThreadGroup(); + + LThread* Get(unsigned int i) const {return th(i);} + + virtual bool Start(unsigned int i, THPROC* pThreadFunc, void* thData); + virtual void WaitToFinish(int ms = -1); + + static unsigned int GetNumCPUs(); + +protected: + + //const memeber access + inline const std::vector<LThread*>& ths() const {return m_ths;} + inline LThread* th(unsigned int i) const {return m_ths[i];} + + //member access + inline std::vector<LThread*>& _ths() {return m_ths;} + inline LThread*& _th(unsigned int i) {return m_ths[i];} + +private: + + std::vector<LThread*> m_ths; +}; +#endif + +/////////////////////////////////////////////////////////////// + + +class shaveDirties { +public: + shaveDirties() + { + last_haircout = 0; + this_haircount = 0; + last_passes = 0; + this_passes = 0; + last_display_count = 0; + this_display_count = 0; + last_display_segs = 0; + this_display_segs = 0; + last_guide_segs = 0; + this_guide_segs = 0; + // these are DIRTY FLAGS you need to set before each update + DIRTY_HAIRCOUNT = 1; + DIRTY_PASSES = 1; + DIRTY_DISPLAY_COUNT = 1; + DIRTY_DISPLAYSEGS = 1; + DIRTY_GUIDE_SEGS = 1; + DIRTY_TEXTURE = 1; + DIRTY_TEXTURE_JOE = 1; + DIRTY_TIME = 1; + DIRTY_CLUMPS = 1; + DIRTY_MULT = 1; + + BRUSH_MOUSE_DOWN = 0; +// BRUSH_JUST_MOVED = 0; + GLOBAL_MOUSE_DOWN = 0; + } + ~shaveDirties(){} + + int last_haircout; + int this_haircount; + int last_passes; + int this_passes; + int last_display_count; + int this_display_count; + int last_display_segs; + int this_display_segs; + int last_guide_segs; // these are new just declare them 15 + int this_guide_segs; + // these are DIRTY FLAGS you need to set before each update + int DIRTY_HAIRCOUNT; + int DIRTY_PASSES; + int DIRTY_DISPLAY_COUNT; + int DIRTY_DISPLAYSEGS; + int DIRTY_GUIDE_SEGS; + int DIRTY_TEXTURE; + int DIRTY_TEXTURE_JOE; + int DIRTY_TIME; + int DIRTY_CLUMPS; + int DIRTY_MULT; + + int BRUSH_JUST_MOVED; + int BRUSH_MOUSE_DOWN; //only for styling + int GLOBAL_MOUSE_DOWN; //does not depend where in MayaWindow +}; + + + +class shaveInstanceDisplay { +public: + + MFloatPoint center; + float radius; + + MImage img; //result + + MFloatPointArray points; + MFloatPointArray normals; + MFloatPointArray uvs; + MIntArray faceCounts; + MIntArray faceStart; + MIntArray faceEnd; + MIntArray faceVerts; + + GLuint diffuse; + //GLuint xparenc; + + bool hasXparency; + + enum { + eTexW = 320, + eTexH = 320 + }; + + shaveInstanceDisplay() { diffuse = 0; hasXparency = false; /*xparenc = 0;*/ } + virtual ~shaveInstanceDisplay() {}; + + void Reset(); + void CreateMaps(MObject shader); + void SampleMap(MObject map, MImage& img); +}; + + +class shaveHairShape : public MPxSurfaceShape +{ +public: + static MString drawDbClassification; + static MString drawRegistrantId; + + typedef enum + { + kDynamicsInit = -1, + kDynamicsOff = 0, + kDynamicsCreate = 1, + kDynamicsUse = 2 + } DynamicsMode; + + typedef enum + { + kHairDisplayNone = 0, + kHairDisplayHair = 1, + kHairDisplayGeom = 2 + } HairDisplayMode; + + typedef struct + { + MVectorArray verts; + MColorArray colors; + float tiprad; + float rootrad; + + } DisplayStrand; + + typedef std::vector<DisplayStrand> DisplayHair; + + struct DisplayHairCache{ + enum + { + eBucnchSize = 3000 //5000 //how many hairs to fetch per iteration + }; + std::vector<DisplayHair> displayHair; + MFloatPoint center; + float radius; + }; + + typedef struct + { + MFloatVectorArray verts; + short select; + bool hidden; + } Guide; + + typedef std::vector<Guide> Guides; + + typedef struct + { + float frame; + Guides guides; + } GuidesSnapshot; + + + //******************************************************************* + // + // Methods + // + //******************************************************************* +public: + // + // Standard methods. + // + shaveHairShape(); + virtual ~shaveHairShape(); + + virtual bool acceptsGeometryIterator(bool writeable = true) + { return !writeable; } + + virtual bool acceptsGeometryIterator( + MObject&, + bool writeable=true, + bool forReadOnly = false + ) + { return !writeable && forReadOnly; } + + virtual MObjectArray activeComponents() const; + + virtual void componentToPlugs( + MObject& component, + MSelectionList& selectionList + ) const; + + virtual MStatus compute(const MPlug& plug, MDataBlock& data); + virtual MObject createFullVertexGroup() const; + static void* creator(); + + virtual MPxGeometryIterator* + geometryIteratorSetup( + MObjectArray& components1, + MObject& components2, + bool forReadOnly = false + ); + +#if MAYA_API_VERSION >= 20180000 + virtual bool getInternalValue( + const MPlug &plug, + MDataHandle &hdl + ); +#else + virtual bool getInternalValueInContext( + const MPlug& plug, + MDataHandle& hdl, + MDGContext& context + ); +#endif + + virtual MSelectionMask getComponentSelectionMask() const; + virtual MSelectionMask getShapeSelectionMask() const; + virtual bool hasActiveComponents() const; + static MStatus initialize(); + virtual bool isBounded() const { return false; } + + virtual MStatus legalConnection( + const MPlug& ourPlug, + const MPlug& theirPlug, + bool weAreSource, + bool& isLegal + ); + + virtual bool match( + const MSelectionMask& mask, + const MObjectArray& componentList + ) const; + + virtual MatchResult matchComponent( + const MSelectionList& item, + const MAttributeSpecArray& spec, + MSelectionList& list + ); + + virtual void postConstructor(); + +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + +#if MAYA_API_VERSION >= 20180000 + virtual bool setInternalValue( + const MPlug &plug, + const MDataHandle &hdl + ); +#else + virtual bool setInternalValueInContext( + const MPlug& plug, + const MDataHandle& hdl, + MDGContext& context + ); +#endif + + virtual MStatus setDependentsDirty( + const MPlug& dirty, + MPlugArray& affected + ); + + virtual MStatus connectionMade ( + const MPlug& plug, + const MPlug& otherPlug, + bool asSrc ); + + virtual MStatus connectionBroken ( + const MPlug & plug, + const MPlug &otherPlug, + bool asSrc ); + + virtual MStatus shouldSave(const MPlug& plug, bool& saveIt); + + virtual void transformUsing( + const MMatrix& mat, + const MObjectArray& components + ); + + virtual void transformUsing( + const MMatrix& mat, + const MObjectArray& components, + MVertexCachingMode cachingMode, + MPointArray* pointCache + ); + + // + // Non-standard class methods. + // + static void doAffects(MObject targetAttr); + static int getMaxShaveID(); + static int getNumShaveNodes(); + static int initNumShaveNodes(); + static void nodeAdded(); + static void nodeRemoved(); + + // + // Non-standard instance methods. + // + int addShaveUVSets(); + void applyEdits(bool resetRestPose); + void applySelections(); + void attrDebug(int); + virtual MBoundingBox boundingBoxTemp() const; + void clearBlindDataAttr(MObject& attr); + void clearComponentSelections(); + void clearHiddenGuides(); + void clearSelections(); + void clearSplineLocks(); + MObject createExternalMesh(MObject parent, MStatus* st); + void deleteMe(MDGModifier& dgMod); + void enableHairDisplay(bool enable) { mHairDisplayEnabled = enable; } + + MStatus getCollisionGeom( + MDataBlock& block, + MObjectArray& geoms, + MObjectArray& components + ); + + MStatus getCollisionList(MSelectionList& list); + + unsigned getNumPasses() const; + + //const std::vector<DisplayHair>& getDisplayHairs(); + void invalidateDisplayHairs(); + void clearDisplayHairs(); + const DisplayHairCache& getDisplayHairs(M3dView& view); + const DisplayHairCache& getDisplayHairs(M3dView& view, bool cleanup); //iterative + const DisplayHairCache& getDisplayHairs() const {return mDisplayHairCache;}// no compute +#if 0 + bool getDisplayHairs(unsigned int hairCount, DisplayHairCache& cache); +#endif + + MObject getDisplayShape(); + MObject getDisplayTransform(); + + MStatus getGeomFromArrayAttr( + MDataBlock& block, + MObject arrayAttr, + MObject componentGroupIDAttr, + MObjectArray& geoms, + MObjectArray& components + ); + + MStatus getGrowthGeom( + MDataBlock& block, + MObjectArray& geoms, + MObjectArray& components + ); + + /*const*/ shaveInstanceDisplay& getInstanceDisplay(); + + MStatus getGrowthList(MSelectionList& list); + unsigned getGuideCount(); + + bool getGuideDisplayOverride() const + { return mGuideDisplayOverride; } + + const GuidesSnapshot& getGuides(); + const GuidesSnapshot& getDirtyGuides() { return mGuideCache; } + const GuidesSnapshot& getPrevGuides() { return mPrevGuides; } + int getHairGroup(MDataBlock* block=NULL) const; + SHAVENODE* getHairNode(); + + int getDisplayMode() const; + bool getDisplayGuides() const; + + bool getDoTipfade() const; + float getAmbDiff() const; + // + // Draw opacity attributes + // + bool getDoHairXparency() const; + float getHairXparency() const; + MColor getSpecularTint() const; + MColor getSpecularTint2() const; + float getSpecular() const; + float getGloss() const; + + void dirtyDisplay() {mDisplayHairCacheDirty=true;mDisplayInstCacheDirty = true;} + void dirtyGuides() {mGuideCacheDirty = true;} + + bool getDoFallback() const; + + bool getInstancingStatus(); + + // + // Return the number of hairs which should be displayed. + // + unsigned getNumDisplayHairs( + bool forcefallback, MDataBlock* block=NULL + ) const; + + // + // Return the number of segments to use when displaying hair. + // + int getNumDisplaySegments() const; + + float getGuideThinkness() const; + float getGuideExt() const; + void setGuideExt(float e); + + MStatus getSelectionListFromArrayAttr( + MSelectionList& list, + MObject arrayAttr, + MObject componentGroupIDAttr = MObject::kNullObj + ); + + MObject getShader(); + unsigned int numLayers(); + MObject getLayer(unsigned int idx); + + int getShaveID() const; + int getStackIndex() const; + MString getUVSet(MObject growthObject, MObject texture); + + // + // Let the node know that it's guides have been modified by an external + // entity (e.g. the brush). + // + void guidesChanged(); + + void hideSelectedGuides(); + MStatus initializeNode(); + void initTextureLookup(); + bool isCurrent() const; + bool isHairDisplayEnabled() const { return mHairDisplayEnabled; } + bool isInstanced() const; + void makeCurrent(bool trigger=true); + MString nodeName(); + bool nodeIsInitialized() const; + void recomb(WFTYPE& curves); + void removeShaveUVSets(); + + MStatus repairGeomList( + const MObject& setAttr, const MObject& groupIdAttr + ); + + void setBlindDataAttr( + MObject& attr, const void* data, long dataLen + ); + + MStatus setCollisionList(const MSelectionList& list); + MStatus setGrowthList(const MSelectionList& list); + MStatus setSplineLocks(const MDagPathArray& curves); + void setStackIndex(int index); + void unhideGuides(); + void updateNodeName(); + void updateParams(); + void updateDatablockFromParams(MDataBlock& block); + void updatePlugsFromParams(); + void updateSelections(const MSelectionList& selections); + void updateMayaComponentSelections(); + + void doXform(); + +#ifdef PER_NODE_TEXLOOKUP + void updateTexLookups(); +#endif + + void setBrushActive(bool b) + { + mBrushIsActive=b; + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + } + + bool getBrushActive() { return mBrushIsActive;} + + void setBrushDown(bool b) + { + assert(false); //not hide but reduce display hair count. + mBrushIsDown=b; + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + } + + bool getBrushDown() { return mBrushIsDown;} + + static void cacheOneHair(unsigned threadID, void* data); + +protected: + // Callback called at the end of a 'duplicate' command. + void afterDuplication(); + static void afterDuplication(void* clientData); + + // Callback called at the start of a 'duplicate' command. + void beforeDuplication(); + static void beforeDuplication(void* clientData); + + + + // Delete all of the stat files associate with this node. + void cleanStatFiles(MDataBlock& block); + + MStatus clearInstanceObject(MDataBlock& block); + void computeBoundingBox(MBoundingBox& bbox, MDataBlock& block); + + MStatus computeOutputMesh( + MDataBlock& datablock, bool nodeJustLoaded + ); + + // Connect the geometry in 'set' to the appropriate inputs on the + // shaveHairShape. 'setAttr' contains the shaveHairShape attribute to + // which the set is connected. + MStatus connectGeometry(MObject setAttr, MObject set); + + void createEmptyMesh(MObject& meshData); + + // Create a geometry set of the appropriate type (growth or collision) + // as determined by 'setAttr'. + MObject createGeomSet(const MObject& setAttr); + + void createMesh(MDataBlock& datablock, MObject& outData); + void dirtyOutputMesh(); + + // Recursively disconnect all incoming connections to 'attr' and its + // children/elements. + void disconnectIncoming( + const MObject& attr, MDGModifier* dgmod = NULL + ); + + // Recursively disconnect all outgoing connections to 'attr' and its + // children/elements. + void disconnectOutgoing( + const MObject& attr, MDGModifier* dgmod = NULL + ); + + // Recursively disconnect all connections to 'attr' and its + // children/elements. + void disconnectAll( + const MObject& attr, MDGModifier* dgmod = NULL + ); + + // Recursively disconnect all incoming (if 'incoming' is true) or + // outgoing (if 'incoming' is false) connections to 'plug' and its + // children/elements. + void disconnectPlugTree( + MPlug& plug, bool incoming, MDGModifier* dgmod = NULL + ); + + // Apply the display parameters to 'totalCount' and return the number + // of them which should be displayed. + // + unsigned getDisplayCount( + unsigned totalCount, + bool forcefallback, + MDataBlock* block=NULL + ) const; + + // Retrieves the geometry set connected to 'setAttr'. Returns + // MObject::kNullObj if there is no set connected. + MObject getGeomSet(const MObject& setAttr); + + // Returns the attribute on 'shape' which contains its world geometry + // array. 'growthTargetAttr' returns the input attribute on the + // shaveHairShape node which receives the corresponding growth geometry. + // There's no input attribute returned for collision geometry because + // it is always 'collisionObjects'. + static bool getWorldGeomAttrs( + const MObject& shape, + MObject& worldGeomAttr, + MObject& growthTargetAttr + ); + + bool isPermanentlyDisabled() const; + + void loadShaveEngine( + float frame, + bool doXplant, + bool keepMaterials, + bool preserveParams, + MDataBlock& block + ); + + + + MObject makePolyMeshData( + MDataBlock& datablock, + MObject parent, + bool useDisplayLOD, + bool forShadowsOnly + ); + + + + + void restoreGeomSet(const MObject& attr, MSelectionList& list); + void saveGeomSet(const MObject& attr, MSelectionList& list); + + // Forces the node to do an xplant the next time it is evaluated. + // + void scheduleXplant(); + + // Replaces any existing growth or collision surfaces (as determined + // by 'attr') with those specified in 'list', creating the external + // objectSet if necessary. + MStatus setGeomList(const MObject& attr, const MSelectionList& list); + + void setGuideDisplayOverride(bool enable); + + MStatus setInstanceObject( + MObject instanceMesh, + MObject shader, + MDataBlock* block=NULL + ); + + void setPermanentlyDisabled(bool disabled); + void setShadowHaircount(int group); + void updateBlindData(MDataBlock* block=NULL, bool updateParams=true); + void updateParamsFromDatablock(MDataBlock&, int); + void updateParamsFromPlugs(int); + void updateSplines(); + + + + + //******************************************************************* + // + // Member Variables + // + //******************************************************************* + + // + // Global member variables. + // +public: + static MTypeId id; + static const int kNodeVersion; + static int mNextShaveID; + static const MString nodeTypeName; + static const MFn::Type kShaveGuideComponent = MFn::kMeshEdgeComponent; + static const MFn::Type kShaveGuideVertComponent = MFn::kSurfaceCVComponent; + static const char* kShapeSelectionMaskName; + static const char* kGuideSelectionMaskName; + static const char* kStrandSelectionMaskName; + static const char* kVertSelectionMaskName; + +protected: + static int numShaveNodes; + + + // + // Local member variables. + // +public: + DynamicsMode doDyn; // Local echo of runDynamics attr + ShaveExportData exportData; + MString growthType; + SHAVENODE hairnode; // the data we pass to/from Shave + bool isRibDumper; + WFTYPE MAYAdata; + WFTYPE memShaveObj; // In-memory OBJ used w. MAYAxform + + // 'memShaveObj' above contains the growth and collision geom. It also + // contains per-vert UV info, but that turns out to not work well for + // shared UVs. 'growthCollisionUVs' below is a workaround. It contains + // per-face-vert UVs for the growth and collision geom. The only + // reason that we include the collision geom UVs is that we were in + // lockdown and that meant the minimal changes to existing code. Also, + // it might feasibly prove useful at some future point. For that same + // reason, we continue to set the per-vert UVs in 'memShaveObj', but + // hopefully no one is looking at them. + WFTYPE growthCollisionUVs; // Per-face-vert UV info. + + short shaveDspyMode; // 0: guides, 1: hairs + MString shaveObjFile; // temp file, used for + // interprocess communication +#if defined(OSMac_) && !defined(OSMac_MachO_) + MString shaveObjFileHFS;// HFS version of shaveObjFile, + // for CFM version of Maya on OSX. +#endif + // Cached texture values for per-vertex parameters. + ShavePerVertTexInfo vertTexInfo; + + shaveDirties dirties; + +protected: +#ifdef REUSABLE_THREADS + inline LThreadGroup* lthGrp() const {return m_lthGrp;} + inline LThreadGroup*& _lthGrp() {return m_lthGrp;} +#endif + + float availableMem; + bool beenHereBefore; // Call MAYArefresh or xplant/xform? + bool blindInit; // have we loaded blind data? + bool doneSelections; + MObject geomMesh; + MFnMesh instanceRefMesh; + MCallbackId mAfterDuplicateCB; + MCallbackId mBeforeDuplicateCB; + bool mBrushIsActive; + bool mBrushIsDown; + //std::vector<DisplayHair> mDisplayHairCache; + DisplayHairCache mDisplayHairCache; + shaveInstanceDisplay mDisplayInstCache; + bool mDisplayHairCacheDirty; + bool mDisplayInstCacheDirty; +// bool mDirtyDisplaycount; +// bool mDirtyTexture; +// bool mDirtyHaircount; + bool mFirstTime; // For debugging use only + GuidesSnapshot mGuideCache; + GuidesSnapshot mPrevGuides; + bool mGuideCacheDirty; + unsigned mGuideCount; + bool mGuideCountDirty; + bool mGuideDisplayOverride; + bool mGuideSelectionsDirty; + bool mHairDisplayEnabled; + bool mHaveSelections; + bool mIsInstanced; + WFTYPE mLockSplines; + bool mNodeInitialized; + int mNodeVersion; + MSelectionList mSavedCollisionList; + MSelectionList mSavedGrowthList; + float mShaveFrame; + int mShaveID; +// vlad|06Apr2010 -- replaced by 'stackIndex' attribute +// int mStackIndex; + std::vector<short> mVertSelections; + bool mXplantRequired; + + + //******************************************************************* + // + // Attributes + // + //******************************************************************* +public: +// vlad|06Apr2010 -- attribute replacement of 'int mStackIndex' +// so stack index can be retrieved from another module by +// attribute name + static MObject stackIndex; + + static MObject aCachedBBox; + static MObject aCachedBBoxMin; + static MObject aCachedBBoxMax; + + static MObject aCollisionSet; + static MObject aDisplayGuides; //goes to globals + static MObject aDisplaySegmentLimit; //goes to globals + static MObject aDisplayGuideThick; //goes to globals + static MObject aDisplayGuideExt; + static MObject aEvalOption; + static MObject aGrowthSet; + + static MObject aGuide; + static MObject aGuideX; + static MObject aGuideY; + static MObject aGuideZ; + + static MObject aHairGroup; + static MObject aNodeVersion; + static MObject aShaveVersion; + static MObject aSpecularTint; + static MObject aSpecularTint2; + static MObject aSplineLock; + static MObject aSplineLockTrigger; + static MObject aTipFade; + static MObject aSquirrel; + static MObject blockDynamics; + static MObject collisionObjectsAttr; + static MObject collisionObjectsGroupIDAttr; + static MObject displayHiarDoXparency; //goes to shave globals + static MObject displayHiarDoSsao; //goes to shave globals + static MObject displayHiarXparency; + static MObject displayNodeAttr; + static MObject dspyMode; + static MObject growthObjectsGroupIDAttr; + + static MObject hairColorTexture; + static MObject hairColorTextureR; + static MObject hairColorTextureG; + static MObject hairColorTextureB; + + static MObject inputCurve; + static MObject inputMesh; + static MObject inputSurf; + static MObject instanceMesh; + static MObject instanceMeshChanged; + static MObject instancingStatusChanged; + static MObject isActive; + + static MObject mutantHairColorTexture; + static MObject mutantHairColorTextureR; + static MObject mutantHairColorTextureG; + static MObject mutantHairColorTextureB; + + static MObject neverBeenInstanced; + static MObject outputMesh; + static MObject renderStateAttr; + static MObject ribDumper; + static MObject ribInsert; + static MObject runDynamics; + + static MObject rootHairColorTexture; + static MObject rootHairColorTextureR; + static MObject rootHairColorTextureG; + static MObject rootHairColorTextureB; + + static MObject shaveBlindHair; + static MObject shaveBlindState; + static MObject shaveInfo; + static MObject shaveParamAmbDiff; // slider 6 + static MObject shaveParamAnimSpeed; // slider 33 + static MObject shaveParamCollide; + static MObject shaveParamCollisionMethod; + static MObject shaveParamDampening; // slider 40 + static MObject shaveParamDensity; // slider 28 + static MObject shaveParamDisplacement; // slider 43 + + static MObject shaveParamFrizzAnimDir; + static MObject shaveParamFrizzAnimDirX; + static MObject shaveParamFrizzAnimDirY; + static MObject shaveParamFrizzAnimDirZ; + + static MObject shaveParamFrizzAnim; // slider 32 + static MObject shaveParamFrizzFreqX; // slider 1 + static MObject shaveParamFrizzFreqY; // slider 30 + static MObject shaveParamFrizzFreqZ; // slider 31 + static MObject shaveParamFrizzRoot; // slider 0 + static MObject shaveParamFrizzTip; // slider 24 + static MObject shaveParamGeomShadow; + static MObject shaveParamGloss; // slider 5 + static MObject shaveParamGravity; + + static MObject shaveParamHairColor; + static MObject shaveParamHairRed; // slider 9 + static MObject shaveParamHairGreen; // slider 10 + static MObject shaveParamHairBlue; // slider 11 + + static MObject shaveParamHaircount; + static MObject shaveParamHueVariation; // slider 12 + static MObject shaveParamInstancingStatus; + static MObject shaveParamKinkFreqX; // slider 3 + static MObject shaveParamKinkFreqY; // slider 34 + static MObject shaveParamKinkFreqZ; // slider 35 + static MObject shaveParamKinkRoot; // slider 38 + static MObject shaveParamKinkTip; // slider 2 + static MObject shaveParamMultStrand; // slider 25 + + static MObject shaveParamMutantColor; + static MObject shaveParamMutantRed; // slider 13 + static MObject shaveParamMutantGreen; // slider 14 + static MObject shaveParamMutantBlue; // slider 15 + + static MObject shaveParamMutantPercent; // slider 16 + + static MObject shaveParamNoInterp; + static MObject shaveParamPainted; + static MObject shaveParamPasses; + static MObject shaveParamRandScale; // slider 36 + static MObject shaveParamRandomizeMulti; // slider 42 + + static MObject shaveParamRootColor; + static MObject shaveParamRootRed; // slider 17 + static MObject shaveParamRootGreen; // slider 18 + static MObject shaveParamRootBlue; // slider 19 + + static MObject shaveParamRootStiffness; // slider 21 + static MObject shaveParamScale; // slider 41 + static MObject shaveParamSegs; + static MObject shaveParamSelfShadow; // slider 7 + static MObject shaveParamSpecular; // slider 4 + static MObject shaveParamSplayRoot; // slider 26 + static MObject shaveParamSplayTip; // slider 27 + static MObject shaveParamMultAsp; // slider 44 - vlad|05July2010 + static MObject shaveParamOffset; // slider 45 - vlad|09July2010 + static MObject shaveParamAspect; // slider 46 - vlad|09July2010 + static MObject shaveParamStiffness; // slider 8 + static MObject shaveParamThickRoot; // slider 20 + static MObject shaveParamThickTip; // slider 37 + static MObject shaveParamTotalGuides; + static MObject shaveParamValueVariation; // slider 39 + static MObject shaveTexMutantColor; + static MObject shaveTextureAttr; + static MObject surfTessU; + static MObject surfTessV; + static MObject subdTessDept; + static MObject subdTessSamp; + static MObject textureCacheUpdatedAttr; + static MObject timeAttr; + static MObject triggerAttr; + static MObject overrideGeomShaderAttr; + + static MObject uvSetAssignmentsAttr; + static MObject uvSetNameAttr; + static MObject uvSetTexturesAttr; + + static MObject flyawayPerc; + static MObject flyawayStren; + static MObject messStren; + + static MObject clumps; + static MObject clumpsStren; + static MObject clumpsColStren; + static MObject clumpsRotStren; + static MObject clumpsRotOffset; + static MObject clumpsRandomize; + static MObject clumpsFlatness; + static MObject clumpsScruffle; + + static MObject procedural; + + static MObject randomSeedOffset; + + static MObject versionLockAttr; + + // Deprecated Attributes + static MObject displayHairMaxAttr; + static MObject displayHairRatioAttr; // moved to shave globals + static MObject shaveParamsDisplaceStren; + +private: +#ifdef REUSABLE_THREADS + LThreadGroup* m_lthGrp; +#endif +}; + + +inline int shaveHairShape::getMaxShaveID() +{ return mNextShaveID - 1; } + +inline int shaveHairShape::getShaveID() const +{ return mShaveID; } + +//vlad|06Apr2010 -- replaced by 'stackIndex' attribute +//inline int shaveHairShape::getStackIndex() const +//{ return mStackIndex; } + +inline bool shaveHairShape::isCurrent() const +{ return ((int)SHAVEquery_shave_ID() == getShaveID()); } + +inline bool shaveHairShape::isInstanced() const +{ return mIsInstanced; } + +inline bool shaveHairShape::nodeIsInitialized() const +{ return mNodeInitialized; } + +inline MStatus shaveHairShape::setCollisionList(const MSelectionList& list) +{ return setGeomList(aCollisionSet, list); } + +inline MStatus shaveHairShape::setGrowthList(const MSelectionList& list) +{ return setGeomList(aGrowthSet, list); } + +//vlad|06Apr2010 -- replaced by 'stackIndex' attribute +//inline void shaveHairShape::setStackIndex(int index) +//{ mStackIndex = index; } + + +// +// plugin.cpp has callbacks which keep track of shaveHairShape additions & +// deletions. The actual count is maintained by the following methods. +// +// Now you might wonder why this class doesn't keep track of the count +// itself, using its constructor and destructor. Unfortunately that won't +// work because if a node is deleted in Maya, it's constructor doesn't get +// called until the undo queue is flushed. Similarly, if a deletion is +// undone, it will not result in a new node being created, so the +// constructor won't be called. +// +inline void shaveHairShape::nodeAdded() +{ numShaveNodes++; } + + +inline void shaveHairShape::nodeRemoved() +{ numShaveNodes--; } + + +inline int shaveHairShape::getNumShaveNodes() +{ return numShaveNodes; } + +#endif + diff --git a/mayaPlug/shaveHairShapeAttrs.cpp b/mayaPlug/shaveHairShapeAttrs.cpp new file mode 100644 index 0000000..994fd0e --- /dev/null +++ b/mayaPlug/shaveHairShapeAttrs.cpp @@ -0,0 +1,1764 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +// +// This is actually part of the shaveHairShape class implementation. +// However, since attribute initialization involves a large amount of +// fairly repetitious code, we've pulled it out into a separate source file +// to help make shaveHairShape.cpp a bit more managable. +// +#include <maya/MFnStringData.h> +#include <maya/MFnCompoundAttribute.h> +#include <maya/MFnData.h> +#include <maya/MFnEnumAttribute.h> +#include <maya/MFnGenericAttribute.h> +#include <maya/MFnMessageAttribute.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnNumericData.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MFnUnitAttribute.h> + +#include "shaveBlindData.h" +#include "shaveError.h" +#include "shaveHairShape.h" +#include "shaveRender.h" + +// +// Attributes +// +// NOTE: The following Shave texture channels do not have their +// own separate attributes, just elements within the shaveTextureAttr +// array: +// +// 22 - wiggle amplitude +// 23 - wiggle speed +// 28 - hair density +// 29 - cut length +// +// The 'wiggle' parameters are not supported in Maya as frizz anim +// is a better way of dealing with it. +// +// The 'hair density' and 'cut length' parameters are supported, +// but only when mapped. Since there is no single-valued version +// of the parameters, they don't need separate attributes. +// +// It makes no sense to have an unmapped version of 'hair density' +// because an overall change in hair density is much more +// effectively achieved by altering the total haircount. +// +// It could be argued that an unmapped version of 'cut length' +// would be useful, and that may be introduced at some future +// time. +// +// Texture channel 44 is currently unused. +// + +//vlad|06Apr2010 -- attribute replacement of 'int mStackIndex' +MObject shaveHairShape::stackIndex; + +MObject shaveHairShape::aCachedBBox, + shaveHairShape::aCachedBBoxMin, + shaveHairShape::aCachedBBoxMax; + +MObject shaveHairShape::aCollisionSet; +MObject shaveHairShape::aDisplayGuides; //goes to globals +MObject shaveHairShape::aDisplaySegmentLimit; +MObject shaveHairShape::aDisplayGuideThick; +MObject shaveHairShape::aDisplayGuideExt; +MObject shaveHairShape::aEvalOption; +MObject shaveHairShape::aGrowthSet; + +MObject shaveHairShape::aGuide; +MObject shaveHairShape::aGuideX; +MObject shaveHairShape::aGuideY; +MObject shaveHairShape::aGuideZ; + +MObject shaveHairShape::aHairGroup; +MObject shaveHairShape::aNodeVersion; +MObject shaveHairShape::aShaveVersion; +MObject shaveHairShape::aSpecularTint; +MObject shaveHairShape::aSpecularTint2; +MObject shaveHairShape::aSplineLock; +MObject shaveHairShape::aSplineLockTrigger; +MObject shaveHairShape::aTipFade; +MObject shaveHairShape::aSquirrel; +MObject shaveHairShape::blockDynamics; +MObject shaveHairShape::collisionObjectsAttr; +MObject shaveHairShape::collisionObjectsGroupIDAttr; +MObject shaveHairShape::displayHiarDoXparency; +MObject shaveHairShape::displayHiarDoSsao; +MObject shaveHairShape::displayHiarXparency; +MObject shaveHairShape::displayNodeAttr; +MObject shaveHairShape::dspyMode; +MObject shaveHairShape::growthObjectsGroupIDAttr; + +MObject shaveHairShape::hairColorTexture, + shaveHairShape::hairColorTextureR, + shaveHairShape::hairColorTextureG, + shaveHairShape::hairColorTextureB; + +MObject shaveHairShape::inputCurve; +MObject shaveHairShape::inputMesh; +MObject shaveHairShape::inputSurf; +MObject shaveHairShape::instanceMesh; +MObject shaveHairShape::instanceMeshChanged; +MObject shaveHairShape::instancingStatusChanged; + +MObject shaveHairShape::mutantHairColorTexture, + shaveHairShape::mutantHairColorTextureR, + shaveHairShape::mutantHairColorTextureG, + shaveHairShape::mutantHairColorTextureB; + +MObject shaveHairShape::neverBeenInstanced; +MObject shaveHairShape::outputMesh; +MObject shaveHairShape::renderStateAttr; +MObject shaveHairShape::ribInsert; + +MObject shaveHairShape::rootHairColorTexture, + shaveHairShape::rootHairColorTextureR, + shaveHairShape::rootHairColorTextureG, + shaveHairShape::rootHairColorTextureB; + +MObject shaveHairShape::runDynamics; +MObject shaveHairShape::shaveBlindHair; +MObject shaveHairShape::shaveBlindState; +MObject shaveHairShape::shaveParamAmbDiff; // param 6 +MObject shaveHairShape::shaveParamAnimSpeed; // param 33 +MObject shaveHairShape::shaveParamCollide; +MObject shaveHairShape::shaveParamCollisionMethod; +MObject shaveHairShape::shaveParamDampening; // param 40 +MObject shaveHairShape::shaveParamDisplacement; // param 43 +MObject shaveHairShape::shaveParamFrizzAnim; // param 32 + +MObject shaveHairShape::shaveParamFrizzAnimDir, + shaveHairShape::shaveParamFrizzAnimDirX, + shaveHairShape::shaveParamFrizzAnimDirY, + shaveHairShape::shaveParamFrizzAnimDirZ; + +MObject shaveHairShape::shaveParamFrizzFreqX; // param 1 +MObject shaveHairShape::shaveParamFrizzFreqY; // param 30 +MObject shaveHairShape::shaveParamFrizzFreqZ; // param 31 +MObject shaveHairShape::shaveParamFrizzRoot; // param 0 +MObject shaveHairShape::shaveParamFrizzTip; // param 24 +MObject shaveHairShape::shaveParamGeomShadow; +MObject shaveHairShape::shaveParamGloss; // param 5 +MObject shaveHairShape::shaveParamGravity; + +MObject shaveHairShape::shaveParamHairColor, + shaveHairShape::shaveParamHairRed, // param 9 + shaveHairShape::shaveParamHairGreen, // param 10 + shaveHairShape::shaveParamHairBlue; // param 11 + +MObject shaveHairShape::shaveParamHaircount; +MObject shaveHairShape::shaveParamHueVariation; // param 12 +MObject shaveHairShape::shaveParamInstancingStatus; +MObject shaveHairShape::shaveParamKinkFreqX; // param 3 +MObject shaveHairShape::shaveParamKinkFreqY; // param 34 +MObject shaveHairShape::shaveParamKinkFreqZ; // param 35 +MObject shaveHairShape::shaveParamKinkRoot; // param 38 +MObject shaveHairShape::shaveParamKinkTip; // param 2 +MObject shaveHairShape::shaveParamMultStrand; // param 25 + +MObject shaveHairShape::shaveParamsDisplaceStren; + +MObject shaveHairShape::shaveParamMutantColor, + shaveHairShape::shaveParamMutantRed, // param 13 + shaveHairShape::shaveParamMutantGreen, // param 14 + shaveHairShape::shaveParamMutantBlue; // param 15 + +MObject shaveHairShape::shaveParamMutantPercent; // param 16 +MObject shaveHairShape::shaveParamNoInterp; +MObject shaveHairShape::shaveParamPainted; +MObject shaveHairShape::shaveParamPasses; +MObject shaveHairShape::shaveParamRandomizeMulti; // param 42 +MObject shaveHairShape::shaveParamRandScale; // param 36 + +MObject shaveHairShape::shaveParamRootColor, + shaveHairShape::shaveParamRootRed, // param 17 + shaveHairShape::shaveParamRootGreen, // param 18 + shaveHairShape::shaveParamRootBlue; // param 19 + +MObject shaveHairShape::shaveParamRootStiffness; // param 21 +MObject shaveHairShape::shaveParamScale; // param 41 +MObject shaveHairShape::shaveParamSegs; +MObject shaveHairShape::shaveParamSelfShadow; // param 7 +MObject shaveHairShape::shaveParamSpecular; // param 4 +MObject shaveHairShape::shaveParamSplayRoot; // param 26 +MObject shaveHairShape::shaveParamSplayTip; // param 27 +MObject shaveHairShape::shaveParamMultAsp; // param 44 - vlad|05July2010 +MObject shaveHairShape::shaveParamOffset; // param 45 - vlad|09July2010 +MObject shaveHairShape::shaveParamAspect; // param 46 - vlad|09July2010 +MObject shaveHairShape::shaveParamStiffness; // param 8 +MObject shaveHairShape::shaveParamThickRoot; // param 20 +MObject shaveHairShape::shaveParamThickTip; // param 37 +MObject shaveHairShape::shaveParamTotalGuides; +MObject shaveHairShape::shaveParamValueVariation; // param 39 +MObject shaveHairShape::shaveTexMutantColor; +MObject shaveHairShape::shaveTextureAttr; +MObject shaveHairShape::surfTessU; +MObject shaveHairShape::surfTessV; +MObject shaveHairShape::subdTessDept; +MObject shaveHairShape::subdTessSamp; +MObject shaveHairShape::textureCacheUpdatedAttr; +MObject shaveHairShape::timeAttr; +MObject shaveHairShape::triggerAttr; +MObject shaveHairShape::overrideGeomShaderAttr; +MObject shaveHairShape::uvSetAssignmentsAttr; +MObject shaveHairShape::uvSetNameAttr; +MObject shaveHairShape::uvSetTexturesAttr; +MObject shaveHairShape::versionLockAttr; + +MObject shaveHairShape::flyawayPerc; +MObject shaveHairShape::flyawayStren; +MObject shaveHairShape::messStren; + +MObject shaveHairShape::clumps; +MObject shaveHairShape::clumpsStren; +MObject shaveHairShape::clumpsColStren; +MObject shaveHairShape::clumpsRotStren; +MObject shaveHairShape::clumpsRotOffset; +MObject shaveHairShape::clumpsRandomize; +MObject shaveHairShape::clumpsFlatness; +MObject shaveHairShape::clumpsScruffle; + +MObject shaveHairShape::procedural; + +MObject shaveHairShape::randomSeedOffset; + + +// Deprecated Attributes +MObject shaveHairShape::displayHairMaxAttr; +MObject shaveHairShape::displayHairRatioAttr; +MObject shaveHairShape::isActive; + + +MStatus shaveHairShape::initialize() +{ + MStatus st; + + MFnCompoundAttribute cAttr; + MFnEnumAttribute eAttr; + MFnGenericAttribute gAttr; + MFnMessageAttribute mAttr; + MFnNumericAttribute nAttr; + MFnTypedAttribute tAttr; + MFnUnitAttribute uAttr; + + + //*************************************************************** + // + // HELPER ATTRIBUTES + // + //*************************************************************** + +// vlad|06Apr2010 -- attribute replacement of 'int mStackIndex' +// so stack index can be retrieved from another module by +// attribute name + stackIndex = nAttr.create( + "stackIndex", + "sti", + MFnNumericData::kInt, + 0, + &st); + MChkErr(st, "can't create 'stackIndex' attribute."); + nAttr.setStorable(false); + nAttr.setKeyable (false); + nAttr.setHidden (true); + + st = addAttribute(stackIndex); + MChkErr(st, "can't add 'stackIndex' attribute."); + + //MGlobal::displayInfo("new stackIndex attribute added."); + + + //*************************************************************** + // + // REGULAR ATTRIBUTES + // + //*************************************************************** + + blockDynamics = nAttr.create( + "disableDynamics", + "disabled", + MFnNumericData::kBoolean, + false, + &st + ); + MChkErr(st, "can't create 'disableDynamics' attribute."); + nAttr.setStorable(true); + st = addAttribute(blockDynamics); + MChkErr(st, "can't add 'disableDynamics' attribute."); + + collisionObjectsAttr = gAttr.create("collisionObjects", "co", &st); + MChkErr(st, "can't create 'collisionObjects' attribute."); + gAttr.setArray (true); + gAttr.setHidden(true); + gAttr.setStorable(false); + gAttr.setDisconnectBehavior(MFnAttribute::kDelete); + gAttr.addAccept(MFnData::kMesh); + gAttr.addAccept(MFnData::kNurbsCurve); + gAttr.addAccept(MFnData::kNurbsSurface); + gAttr.addAccept(MFnData::kSubdSurface); + st = addAttribute(collisionObjectsAttr); + MChkErr(st, "can't add 'collisionObjects' attribute."); + + collisionObjectsGroupIDAttr = nAttr.create( + "collisionObjectsGroupID", + "cmgi", + MFnNumericData::kLong, + 0, + &st + ); + MChkErr(st, "can't create 'collisionObjectsGroupID' attribute."); + nAttr.setArray(true); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setDisconnectBehavior(MFnAttribute::kDelete); + st = addAttribute(collisionObjectsGroupIDAttr); + MChkErr(st, "can't add 'collisionObjectsGroupID' attribute."); + + displayNodeAttr = mAttr.create("displayNode", "dn"); + mAttr.setStorable(false); + mAttr.setHidden(true); + st = addAttribute(displayNodeAttr); + MChkErr(st, "can't add 'displayNode' attribute."); + + dspyMode = eAttr.create("displayAs", "dspyas", 1); + eAttr.addField("none", kHairDisplayNone); + eAttr.addField("hairs", kHairDisplayHair); + eAttr.addField("geometry", kHairDisplayGeom); + eAttr.setStorable(true); + eAttr.setHidden(true); + st = addAttribute(dspyMode); + MChkErr(st, "can't add 'displayAs' attribute."); + + aDisplayGuides = nAttr.create( + "displayGuides", + "dg", + MFnNumericData::kBoolean, + 0, + &st + ); + MChkErr(st, "can't create 'displayGuides' attribute."); + nAttr.setDefault(false); + st = addAttribute(aDisplayGuides); + MChkErr(st, "can't add 'displayGuides' attribute."); + + //goes to globals + aDisplaySegmentLimit = nAttr.create( + "displaySegmentLimit", + "dsl", + MFnNumericData::kLong, + 6, + &st + ); + MChkErr(st, "can't create 'displaySegmentLimit' attribute."); + nAttr.setHidden(true); // So it doesn't get saved as a preset. + nAttr.setMin(1); + nAttr.setSoftMax(20); + st = addAttribute(aDisplaySegmentLimit); + MChkErr(st, "can't add 'displaySegmentLimit' attribute."); + + //goes to globals + aDisplayGuideThick = nAttr.create( + "displayGuideThick", + "dgt", + MFnNumericData::kFloat, + 0, + &st + ); + MChkErr(st, "can't create 'displayGuideThick' attribute."); + nAttr.setHidden(true); // So it doesn't get saved as a preset. + nAttr.setMin(0.0f); + nAttr.setSoftMax(1.0f); + nAttr.setDefault(/*0.7f*/ 0.0f); + st = addAttribute(aDisplayGuideThick); + MChkErr(st, "can't add 'displayGuideThick' attribute."); + + aDisplayGuideExt = nAttr.create( + "displayGuideExt", + "dge", + MFnNumericData::kFloat, + 0, + &st + ); + MChkErr(st, "can't create 'displayGuideExt' attribute."); + nAttr.setHidden(true); + st = addAttribute(aDisplayGuideExt); + MChkErr(st, "can't add 'displayGuideExt' attribute."); + + growthObjectsGroupIDAttr = + nAttr.create("growthObjectsGroupID", "gmgi", MFnNumericData::kLong); + nAttr.setArray(true); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setDisconnectBehavior(MFnAttribute::kDelete); + st = addAttribute(growthObjectsGroupIDAttr); + MChkErr(st, "can't add 'growthObjectsGroupID' attribute."); + + hairColorTextureR = + nAttr.create("hairColorTextureR", "hctr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + st = addAttribute(hairColorTextureR); + MChkErr(st, "can't add 'hairColorTextureR' attribute."); + + hairColorTextureG = + nAttr.create("hairColorTextureG", "hctg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + st = addAttribute(hairColorTextureG); + MChkErr(st, "can't add 'hairColorTextureG' attribute."); + + hairColorTextureB = + nAttr.create("hairColorTextureB", "hctb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + st = addAttribute(hairColorTextureB); + MChkErr(st, "can't add 'hairColorTextureB' attribute."); + + hairColorTexture = nAttr.create( + "hairColorTexture", + "htc", + hairColorTextureR, + hairColorTextureG, + hairColorTextureB + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(false); + nAttr.setKeyable(true); + st = addAttribute(hairColorTexture); + MChkErr(st, "can't add 'hairColorTexture' attribute."); + + inputCurve = tAttr.create("inputCurve", "incrv", MFnData::kNurbsCurve); + tAttr.setArray (true); + tAttr.setHidden(true); + tAttr.setStorable(false); + tAttr.setDisconnectBehavior(MFnAttribute::kDelete); + st = addAttribute(inputCurve); + MChkErr(st, "can't add 'inputCurve' attribute."); + + inputMesh = tAttr.create ("inputMesh", "input", MFnData::kMesh); + tAttr.setArray (true); + tAttr.setHidden(true); + tAttr.setStorable(false); + tAttr.setDisconnectBehavior(MFnAttribute::kDelete); + st = addAttribute(inputMesh); + MChkErr(st, "can't add 'inputMesh' attribute."); + + inputSurf = gAttr.create("inputSurface", "insrf"); + gAttr.setArray (true); + gAttr.setHidden(true); + gAttr.setStorable(false); + gAttr.addAccept(MFnData::kNurbsSurface); + gAttr.addAccept(MFnData::kSubdSurface); + gAttr.setDisconnectBehavior(MFnAttribute::kDelete); + st = addAttribute(inputSurf); + MChkErr(st, "can't add 'inputSurface' attribute."); + + instanceMesh = tAttr.create("instanceMesh", "imsh", MFnData::kMesh); + tAttr.setHidden(true); + tAttr.setStorable(true); + st = addAttribute(instanceMesh); + MChkErr(st, "can't add 'instanceMesh' attribute."); + + mutantHairColorTextureR = nAttr.create( + "mutantHairColorTextureR", + "mhctr", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + st = addAttribute(mutantHairColorTextureR); + MChkErr(st, "can't add 'mutantHairColorTextureR' attribute."); + + mutantHairColorTextureG = nAttr.create( + "mutantHairColorTextureG", + "mhctg", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + st = addAttribute(mutantHairColorTextureG); + MChkErr(st, "can't add 'mutantHairColorTextureG' attribute."); + + mutantHairColorTextureB = nAttr.create( + "mutantHairColorTextureB", + "mhctb", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + st = addAttribute(mutantHairColorTextureB); + MChkErr(st, "can't add 'mutantHairColorTextureB' attribute."); + + mutantHairColorTexture = nAttr.create( + "mutantHairColorTexture", + "mhtc", + mutantHairColorTextureR, + mutantHairColorTextureG, + mutantHairColorTextureB + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(false); + nAttr.setKeyable(true); + st = addAttribute(mutantHairColorTexture); + MChkErr(st, "can't add 'mutantHairColorTexture' attribute."); + + neverBeenInstanced = nAttr.create( + "neverBeenInstanced", + "nbi", + MFnNumericData::kBoolean, + true + ); + nAttr.setConnectable(false); + nAttr.setHidden(true); + nAttr.setStorable(true); + st = addAttribute(neverBeenInstanced); + MChkErr(st, "can't add 'neverBeenInstanced' attribute."); + + aNodeVersion = nAttr.create("nodeVersion", "nv", MFnNumericData::kLong); + nAttr.setDefault(0); + nAttr.setHidden(true); + nAttr.setInternal(true); + nAttr.setWritable(true); + nAttr.setStorable(true); + st = addAttribute(aNodeVersion); + MChkErr(st, "can't add 'nodeVersion' attribute."); + + aShaveVersion = tAttr.create( + "shaveVersion", + "sv", + MFnStringData::kString, + MObject::kNullObj, + &st + ); + MChkErr(st, "can't create 'shaveVersion' attribute."); + tAttr.setStorable(true); + st = addAttribute(aShaveVersion); + MChkErr(st, "can't add 'shaveVersion' attribute."); + + outputMesh = tAttr.create("outputMesh", "out", MFnData::kMesh); + tAttr.setStorable(false); + tAttr.setHidden(true); + tAttr.setWritable(false); + st = addAttribute(outputMesh); + MChkErr(st, "can't add 'outputMesh' attribute."); + + overrideGeomShaderAttr = nAttr.create( + "overrideGeomShader", + "ogsh", + MFnNumericData::kBoolean, + true + ); + nAttr.setStorable(true); + st = addAttribute(overrideGeomShaderAttr); + MChkErr(st, "can't add 'overrideGeomShader' attribute."); + + renderStateAttr = + eAttr.create("renderState", "rs", shaveRender::kRenderNone); + eAttr.addField("None", shaveRender::kRenderNone); + eAttr.addField("Rendering", shaveRender::kRendering); + eAttr.addField("Rendering Frame", shaveRender::kRenderingFrame); + eAttr.setStorable(false); + eAttr.setHidden(true); + st = addAttribute(renderStateAttr); + MChkErr(st, "can't add 'renderState' attribute."); + + ribInsert = tAttr.create("ribStuff", "rbs", MFnStringData::kString); + tAttr.setStorable(true); + st = addAttribute(ribInsert); + MChkErr(st, "can't add 'ribStuff' attribute."); + + rootHairColorTextureR = nAttr.create( + "rootHairColorTextureR", + "rhctr", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + st = addAttribute(rootHairColorTextureR); + MChkErr(st, "can't add 'rootHairColorTextureR' attribute."); + + rootHairColorTextureG = nAttr.create( + "rootHairColorTextureG", + "rhctg", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + st = addAttribute(rootHairColorTextureG); + MChkErr(st, "can't add 'rootHairColorTextureG' attribute."); + + rootHairColorTextureB = nAttr.create( + "rootHairColorTextureB", + "rhctb", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + st = addAttribute(rootHairColorTextureB); + MChkErr(st, "can't add 'rootHairColorTextureB' attribute."); + + rootHairColorTexture = nAttr.create( + "rootHairColorTexture", + "rhtc", + rootHairColorTextureR, + rootHairColorTextureG, + rootHairColorTextureB + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(false); + nAttr.setKeyable(true); + st = addAttribute(rootHairColorTexture); + MChkErr(st, "can't add 'rootHairColorTexture' attribute."); + + runDynamics = nAttr.create("runDynamics", "rd", MFnNumericData::kLong, 0); + nAttr.setStorable(true); + nAttr.setHidden(true); + st = addAttribute(runDynamics); + MChkErr(st, "can't add 'runDynamics' attribute."); + + shaveBlindHair = tAttr.create( + "blindShaveData", + "BSD", + blindShaveData::dataTypeId, + MObject::kNullObj, + &st + ); + MChkErr(st, "can't create 'blindShaveData' attribute."); + tAttr.setHidden(true); + tAttr.setStorable(true); + st = addAttribute(shaveBlindHair); + MChkErr(st, "can't add 'blindShaveData' attribute."); + + aEvalOption = tAttr.create("evalOption", "evo", MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + st = addAttribute(aEvalOption); + MChkErr(st, "can't add 'evalOption' attribute."); + + + //*************************************************************** + // + // ATTRIBUTES FOR SHAVE ENGINE PARAMETERS + // + // These parameters represent values which get mapped directly to the + // SHAVEPARMS structure passed to the Shave engine. + // + //*************************************************************** + + shaveParamAmbDiff = + nAttr.create("amb/diff", "spambdif", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + nAttr.setKeyable(true); + st = addAttribute(shaveParamAmbDiff); + MChkErr(st, "can't add 'amb/diff' attribute."); + + shaveParamCollide = nAttr.create( + "enableCollision", + "spec", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(true); + st = addAttribute(shaveParamCollide); + MChkErr(st, "can't add 'enableCollision' attribute."); + + shaveParamCollisionMethod = + nAttr.create("collisionMethod", "spcm", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setHidden(true); + st = addAttribute(shaveParamCollisionMethod); + MChkErr(st, "can't add 'collisionMethod' attribute."); + + shaveParamDampening = + nAttr.create("dampening", "damp", MFnNumericData::kFloat, 0.0); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + st = addAttribute(shaveParamDampening); + MChkErr(st, "can't add 'dampening' attribute."); + + shaveParamFrizzAnim = + nAttr.create("frizzAnim", "spfa", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzAnim); + MChkErr(st, "can't add 'frizzAnim' attribute."); + + shaveParamFrizzAnimDirX = + nAttr.create("animDirX", "spadx", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzAnimDirX); + MChkErr(st, "can't add 'animDirX' attribute."); + + shaveParamFrizzAnimDirY = + nAttr.create("animDirY", "spady", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzAnimDirY); + MChkErr(st, "can't add 'animDirY' attribute."); + + shaveParamFrizzAnimDirZ = + nAttr.create("animDirZ", "spadz", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzAnimDirZ); + MChkErr(st, "can't add 'animDirZ' attribute."); + + shaveParamFrizzAnimDir = nAttr.create( + "frizzAnimDir", + "spad", + shaveParamFrizzAnimDirX, + shaveParamFrizzAnimDirY, + shaveParamFrizzAnimDirZ + ); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzAnimDir); + MChkErr(st, "can't add 'frizzAnimDir' attribute."); + + shaveParamAnimSpeed = + nAttr.create("animSpeed", "spas", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamAnimSpeed); + MChkErr(st, "can't add 'animSpeed' attribute."); + + shaveParamDisplacement = + nAttr.create("displacement", "disp", MFnNumericData::kFloat, 0.0f); + nAttr.setStorable(true); + nAttr.setKeyable(true); + nAttr.setSoftMin(-10.0); + nAttr.setSoftMax(10.0); + st = addAttribute(shaveParamDisplacement); + MChkErr(st, "can't add 'displacement' attribute."); + + shaveParamFrizzFreqX = + nAttr.create("frizzXFrequency", "spffx", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzFreqX); + MChkErr(st, "can't add 'frizzXFrequency' attribute."); + + shaveParamFrizzFreqY = + nAttr.create("frizzYFrequency", "spffy", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzFreqY); + MChkErr(st, "can't add 'frizzYFrequency' attribute."); + + shaveParamFrizzFreqZ = + nAttr.create("frizzZFrequency", "spffz", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzFreqZ); + MChkErr(st, "can't add 'frizzZFrequency' attribute."); + + shaveParamFrizzRoot = + nAttr.create("rootFrizz", "sprf", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzRoot); + MChkErr(st, "can't add 'rootFrizz' attribute."); + + shaveParamFrizzTip = + nAttr.create("tipFrizz", "sptf", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamFrizzTip); + MChkErr(st, "can't add 'tipFrizz' attribute."); + + shaveParamGeomShadow = + nAttr.create("geomShadow", "spgs", MFnNumericData::kFloat, 1.0); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + nAttr.setStorable(true); + st = addAttribute(shaveParamGeomShadow); + MChkErr(st, "can't add 'geomShadow' attribute."); + + shaveParamGloss = + nAttr.create("gloss", "spgloss", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(0.099999); + nAttr.setKeyable(true); + st = addAttribute(shaveParamGloss); + MChkErr(st, "can't add 'gloss' attribute."); + + shaveParamHairRed = + nAttr.create("hairRed", "sphr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamHairRed); + MChkErr(st, "can't add 'hairRed' attribute."); + + shaveParamHairGreen = + nAttr.create("hairGreen", "sphg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamHairGreen); + MChkErr(st, "can't add 'hairGreen' attribute."); + + shaveParamHairBlue = + nAttr.create("hairBlue", "sphb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamHairBlue); + MChkErr(st, "can't add 'hairBlue' attribute."); + + shaveParamHairColor = nAttr.create( + "hairColor", + "sphcol", + shaveParamHairRed, + shaveParamHairGreen, + shaveParamHairBlue + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamHairColor); + MChkErr(st, "can't add 'hairColor' attribute."); + + shaveParamHaircount = + nAttr.create("hairCount", "sphc", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(30000); + st = addAttribute(shaveParamHaircount); + MChkErr(st, "can't add 'hairCount' attribute."); + + shaveParamHueVariation = + nAttr.create("hueVariation", "sphv", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(100); + nAttr.setKeyable(true); + st = addAttribute(shaveParamHueVariation); + MChkErr(st, "can't add 'hueVariation' attribute."); + + shaveParamInstancingStatus = nAttr.create( + "instancingStatus", + "spis", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(true); + st = addAttribute(shaveParamInstancingStatus); + MChkErr(st, "can't add 'instancingStatus' attribute."); + + shaveParamKinkFreqX = + nAttr.create("kinkXFrequency", "spkfx", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamKinkFreqX); + MChkErr(st, "can't add 'kinkXFrequency' attribute."); + + shaveParamKinkFreqY = + nAttr.create("kinkYFrequency", "spkfy", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamKinkFreqY); + MChkErr(st, "can't add 'kinkYFrequency' attribute."); + + shaveParamKinkFreqZ = + nAttr.create("kinkZFrequency", "spkfz", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamKinkFreqZ); + MChkErr(st, "can't add 'kinkZFrequency' attribute."); + + shaveParamKinkRoot = + nAttr.create("rootKink", "spkr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamKinkRoot); + MChkErr(st, "can't add 'rootKink' attribute."); + + shaveParamKinkTip = + nAttr.create("tipKink", "spkt", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamKinkTip); + MChkErr(st, "can't add 'tipKink' attribute."); + + shaveParamMultStrand = + nAttr.create("multiStrandCount", "spms", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(10); + st = addAttribute(shaveParamMultStrand); + MChkErr(st, "can't add 'multiStrandCount' attribute."); + + shaveParamMutantRed = + nAttr.create("mutantHairRed", "spmhr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + st = addAttribute(shaveParamMutantRed); + MChkErr(st, "can't add 'mutantHairRed' attribute."); + + shaveParamMutantGreen = + nAttr.create("mutantHairGreen", "spmhg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + st = addAttribute(shaveParamMutantGreen); + MChkErr(st, "can't add 'mutantHairGreen' attribute."); + + shaveParamMutantBlue = + nAttr.create("mutantHairBlue", "spmhb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + st = addAttribute(shaveParamMutantBlue); + MChkErr(st, "can't add 'mutantHairBlue' attribute."); + + shaveParamMutantColor = nAttr.create( + "mutantHairColor", + "spmhc", + shaveParamMutantRed, + shaveParamMutantGreen, + shaveParamMutantBlue + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamMutantColor); + MChkErr(st, "can't add 'mutantHairColor' attribute."); + + shaveParamMutantPercent = + nAttr.create("percentMutantHairs", "spmp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(100); + nAttr.setKeyable(true); + st = addAttribute(shaveParamMutantPercent); + MChkErr(st, "can't add 'percentMutantHairs' attribute."); + + shaveParamNoInterp = nAttr.create( + "interpolateGuides", + "dointerp", + MFnNumericData::kBoolean, + true + ); + nAttr.setStorable(true); + st = addAttribute(shaveParamNoInterp); + MChkErr(st, "can't add 'interpolateGuides' attribute."); + + shaveParamPasses = + nAttr.create("hairPasses", "sphp", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(20); + st = addAttribute(shaveParamPasses); + MChkErr(st, "can't add 'hairPasses' attribute."); + + shaveParamRandomizeMulti = + nAttr.create("randomizeMulti", "sprm", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + st = addAttribute(shaveParamRandomizeMulti); + MChkErr(st, "can't add 'randomizeMulti' attribute."); + + shaveParamRandScale = + nAttr.create("randScale", "sprs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + nAttr.setKeyable(true); + st = addAttribute(shaveParamRandScale); + MChkErr(st, "can't add 'randScale' attribute."); + + shaveParamRootRed = + nAttr.create("rootRed", "sprr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamRootRed); + MChkErr(st, "can't add 'rootRed' attribute."); + + shaveParamRootGreen = + nAttr.create("rootGreen", "sprg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamRootGreen); + MChkErr(st, "can't add 'rootGreen' attribute."); + + shaveParamRootBlue = + nAttr.create("rootBlue", "sprb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamRootBlue); + MChkErr(st, "can't add 'rootBlue' attribute."); + + shaveParamRootColor = nAttr.create( + "rootColor", + "sprtc", + shaveParamRootRed, + shaveParamRootGreen, + shaveParamRootBlue + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + st = addAttribute(shaveParamRootColor); + MChkErr(st, "can't add 'rootColor' attribute."); + + shaveParamRootStiffness = + nAttr.create("rootStiffness", "rstf", MFnNumericData::kFloat, 1.0); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + st = addAttribute(shaveParamRootStiffness); + MChkErr(st, "can't add 'rootStiffness' attribute."); + + shaveParamScale = nAttr.create("scale", "sc", MFnNumericData::kFloat, 1.0); + nAttr.setMin(0.01); + nAttr.setSoftMax(1.0); + st = addAttribute(shaveParamScale); + MChkErr(st, "can't add 'scale' attribute."); + + shaveParamSegs = + nAttr.create("hairSegments", "spsegs", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(50); + st = addAttribute(shaveParamSegs); + MChkErr(st, "can't add 'hairSegments' attribute."); + + shaveParamSelfShadow = + nAttr.create("selfShadow", "spss", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + st = addAttribute(shaveParamSelfShadow); + MChkErr(st, "can't add 'selfShadow' attribute."); + + shaveParamSpecular = + nAttr.create("specular", "spspec", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(1); + nAttr.setKeyable(true); + st = addAttribute(shaveParamSpecular); + MChkErr(st, "can't add 'specular' attribute."); + + shaveParamSplayRoot = + nAttr.create("rootSplay", "sprsp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamSplayRoot); + MChkErr(st, "can't add 'rootSplay' attribute."); + + shaveParamSplayTip = + nAttr.create("tipSplay", "sptsp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamSplayTip); + MChkErr(st, "can't add 'tipSplay' attribute."); + + //vlad|05July2010 + shaveParamMultAsp = + nAttr.create("multAsp", "mlasp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamMultAsp); + MChkErr(st, "can't add 'multAsp' attribute."); + + //vlad|09July2010 + shaveParamOffset = + nAttr.create("Offset", "offs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamOffset); + MChkErr(st, "can't add 'Offset' attribute."); + + //vlad|09July2010 + shaveParamAspect = + nAttr.create("Aspect", "aspc", MFnNumericData::kFloat, 1.0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + st = addAttribute(shaveParamAspect); + MChkErr(st, "can't add 'Aspect' attribute."); + + shaveParamStiffness = + nAttr.create("stiffness", "spstiff", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + nAttr.setKeyable(true); + st = addAttribute(shaveParamStiffness); + MChkErr(st, "can't add 'stiffness' attribute."); + + shaveParamThickRoot = + nAttr.create("rootThickness", "sptr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(2); + nAttr.setKeyable(true); + st = addAttribute(shaveParamThickRoot); + MChkErr(st, "can't add 'rootThickness' attribute."); + + shaveParamThickTip = + nAttr.create("tipThickness", "sptt", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(2); + nAttr.setKeyable(true); + st = addAttribute(shaveParamThickTip); + MChkErr(st, "can't add 'tipThickness' attribute."); + + shaveParamTotalGuides = + nAttr.create("totalGuides", "sptg", MFnNumericData::kShort, 0); + nAttr.setHidden(true); + st = addAttribute(shaveParamTotalGuides); + MChkErr(st, "can't add 'totalGuides' attribute."); + + shaveParamValueVariation = + nAttr.create("valueVariation", "spvv", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(100); + nAttr.setKeyable(true); + st = addAttribute(shaveParamValueVariation); + MChkErr(st, "can't add 'valueVariation' attribute."); + + + //*************************************************************** + // + // MORE REGULAR ATTRIBUTES + // + //*************************************************************** + + shaveTextureAttr = nAttr.create("shaveTex", "shtx", MFnNumericData::kFloat); + nAttr.setArray(true); + nAttr.setIndexMatters(true); + nAttr.setStorable(true); + st = addAttribute(shaveTextureAttr); + MChkErr(st, "can't add 'shaveTex' attribute."); + + surfTessU = + nAttr.create("uSubdivisions", "tusd", MFnNumericData::kShort, 2); + nAttr.setStorable(true); + st = addAttribute(surfTessU); + MChkErr(st, "can't add 'uSubdivisions' attribute."); + + surfTessV = + nAttr.create("vSubdivisions", "tvsd", MFnNumericData::kShort, 2); + nAttr.setStorable(true); + st = addAttribute(surfTessV); + MChkErr(st, "can't add 'vSubdivisions' attribute."); + + subdTessDept = nAttr.create("Depth", "sde", MFnNumericData::kShort, 1); + nAttr.setStorable(true); + st = addAttribute(subdTessDept); + MChkErr(st, "can't add 'subdTessDept' attribute."); + + subdTessSamp = nAttr.create("sampleCount", "ssc", MFnNumericData::kShort, 1); + nAttr.setStorable(true); + st = addAttribute(subdTessSamp); + MChkErr(st, "can't add 'subdTessSamp' attribute."); + + + timeAttr = uAttr.create("time", "tm", MFnUnitAttribute::kTime, 0.0); + uAttr.setHidden(true); + st = addAttribute(timeAttr); + MChkErr(st, "can't add 'time' attribute."); + + triggerAttr = nAttr.create("trigger", "trg", MFnNumericData::kFloat, 0); + nAttr.setHidden(true); + nAttr.setWritable(false); + nAttr.setStorable(false); + st = addAttribute(triggerAttr); + MChkErr(st, "can't add 'trigger' attribute."); + + uvSetNameAttr = tAttr.create("hairUVSetName", "husn", MFnStringData::kString); + tAttr.setHidden(true); + tAttr.setDisconnectBehavior(MFnAttribute::kReset); + + // + // The short name for this attribute used to be "uvst". Although that + // does not appear to conflict with anything, it was causing crashes in + // Maya 6.0. So I added an 'x' to the end and the problems went away. + // + uvSetTexturesAttr = mAttr.create("hairUVSetTextures", "husx"); + mAttr.setStorable(false); + mAttr.setHidden(true); + mAttr.setArray(true); + mAttr.setIndexMatters(false); + mAttr.setDisconnectBehavior(MFnAttribute::kDelete); + + uvSetAssignmentsAttr = cAttr.create("hairUVSetAssignments", "husa"); + cAttr.setHidden(true); + cAttr.setArray(true); + cAttr.setIndexMatters(false); + cAttr.setDisconnectBehavior(MFnAttribute::kDelete); + cAttr.addChild(uvSetNameAttr); + cAttr.addChild(uvSetTexturesAttr); + st = addAttribute(uvSetAssignmentsAttr); + MChkErr(st, "can't add 'hairUVSetAssignments' attribute."); + + versionLockAttr = nAttr.create("versionLock", "vl", MFnNumericData::kLong); + nAttr.setHidden(true); + nAttr.setStorable(true); + st = addAttribute(versionLockAttr); + MChkErr(st, "can't add 'versionLock' attribute."); + + //goes to globals + displayHiarDoXparency = nAttr.create( + "displayHairTransprency", + "dht", + MFnNumericData::kBoolean, + true + ); + + + st = addAttribute(displayHiarDoXparency); + + displayHiarXparency = nAttr.create( + "displayHairTransprencyVal", + "dhv", + MFnNumericData::kFloat, + 1.0 + ); + + nAttr.setHidden(true); // So it doesn't get saved as a preset. + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setDefault(1.0f); + + st = addAttribute(displayHiarXparency); + + //goes to globals + displayHiarDoSsao = nAttr.create( + "displayHairSsao", + "dho", + MFnNumericData::kBoolean, + false + ); + st = addAttribute(displayHiarDoSsao); + + textureCacheUpdatedAttr = nAttr.create( + "textureCacheUpdated", + "tau", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(false); + st = addAttribute(textureCacheUpdatedAttr); + MChkErr(st, "can't add 'textureCacheUpdated' attribute."); + + // + // We cannot use createPoint() here because that will create a float + // point while Maya's manips will blindly assume double, leading to mem + // overwrites and crashes. So we have to set up the three doubleLinear + // children ourselves. + // + aGuideX = uAttr.create( + "guideX", "gdx", MFnUnitAttribute::kDistance, 0.0, &st + ); + MChkErr(st, "can't create 'guideX' attribute."); + uAttr.setHidden(true); + uAttr.setInternal(true); + + aGuideY = uAttr.create( + "guideY", "gdy", MFnUnitAttribute::kDistance, 0.0, &st + ); + MChkErr(st, "can't create 'guideY' attribute."); + uAttr.setHidden(true); + uAttr.setInternal(true); + + aGuideZ = uAttr.create( + "guideZ", "gdz", MFnUnitAttribute::kDistance, 0.0, &st + ); + MChkErr(st, "can't create 'guideZ' attribute."); + uAttr.setHidden(true); + uAttr.setInternal(true); + + aGuide = nAttr.create("guide", "gd", aGuideX, aGuideY, aGuideZ, &st); + MChkErr(st, "can't create 'guide' attribute."); + nAttr.setHidden(true); + nAttr.setArray(true); + nAttr.setInternal(true); + st = addAttribute(aGuide); + MChkErr(st, "can't add 'guide' attribute."); + + aHairGroup = nAttr.create( + "hairGroup", + "hg", + MFnNumericData::kShort, + 0, + &st + ); + MChkErr(st, "can't create 'hairGroup' attribute."); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setWritable(false); + st = addAttribute(aHairGroup); + MChkErr(st, "can't add 'hairGroup' attribute."); + + aSpecularTint = nAttr.createColor("specularTint", "sptn", &st); + MChkErr(st, "can't create 'specularTint' attribute."); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + st = addAttribute(aSpecularTint); + MChkErr(st, "can't add 'specularTint' attribute."); + + aSpecularTint2 = nAttr.createColor("specularTint2", "spt2", &st); + MChkErr(st, "can't create 'specularTint2' attribute."); + nAttr.setDefault(0.0f, 0.0f, 0.0f); + st = addAttribute(aSpecularTint2); + MChkErr(st, "can't add 'specularTint2' attribute."); + + + aTipFade = nAttr.create("tipFade", "tf", MFnNumericData::kBoolean, 0, &st); + MChkErr(st, "can't create 'tipFade' attribute."); + nAttr.setDefault(true); + nAttr.setStorable(true); + st = addAttribute(aTipFade); + MChkErr(st, "can't add 'tipFade' attribute."); + + aSquirrel = nAttr.create("squirrel", "sq", MFnNumericData::kBoolean, 0, &st); + MChkErr(st, "can't create 'Binary Fade' attribute."); + nAttr.setDefault(true); + nAttr.setStorable(true); + st = addAttribute(aSquirrel); + MChkErr(st, "can't add 'Binary Fade' attribute."); + + flyawayPerc = + nAttr.create("flyawayPerc", "fwpc", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(100); + st = addAttribute(flyawayPerc); + MChkErr(st, "can't add 'flyawayPerc' attribute."); + + + flyawayStren = + nAttr.create("flyawayStren", "fwst", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(flyawayStren); + MChkErr(st, "can't add 'flyawayStren' attribute."); + + messStren = + nAttr.create("messStren", "mest", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(messStren); + MChkErr(st, "can't add 'messStren' attribute."); + + + clumps = + nAttr.create("clumps", "clp", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(100); + st = addAttribute(clumps); + MChkErr(st, "can't add 'clumps' attribute."); + + clumpsStren = + nAttr.create("clumpsStrength", "cpst", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsStren); + MChkErr(st, "can't add 'clumpsStren' attribute."); + + clumpsScruffle = + nAttr.create("clumpsScruffle", "clsf", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(400.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsScruffle); + MChkErr(st, "can't add 'clumpsScruffle' attribute."); + + + clumpsColStren = + nAttr.create("clumpsColStren", "clcs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsColStren); + MChkErr(st, "can't add 'clumpsColStren' attribute."); + + clumpsRotStren = + nAttr.create("clumpsRotStren", "clrs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setMax(40.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsRotStren); + MChkErr(st, "can't add 'clumpsRotStren' attribute."); + + clumpsRotOffset = + nAttr.create("clumpsRotOffset", "clro", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsRotOffset); + MChkErr(st, "can't add 'clumpsRotOffset' attribute."); + + + clumpsRandomize = + nAttr.create("clumpsRandomize", "clrd", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsRandomize); + MChkErr(st, "can't add 'clumpsRandomize' attribute."); + + clumpsFlatness = + nAttr.create("clumpsFlatness", "clfl", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + st = addAttribute(clumpsFlatness); + MChkErr(st, "can't add 'clumpsFlatness' attribute."); + + + procedural = + nAttr.create("procedural", "proc", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + st = addAttribute(procedural); + MChkErr(st, "can't add 'procedural' attribute."); + + randomSeedOffset = + nAttr.create("randomSeedOffset", "rdso", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setKeyable(false); + st = addAttribute(randomSeedOffset); + MChkErr(st, "can't add 'randomSeedOffset' attribute."); + + // + // Bounding box cache (separate from our inherited 'nodeBoundingBox' + // attr). + // + aCachedBBoxMin = nAttr.create( + "cachedBBoxMin", + "cbn", + MFnNumericData::k3Double, + 0.0, + &st + ); + MChkErr(st, "cannot create cachedBBoxMin attribute."); + + aCachedBBoxMax = nAttr.create( + "cachedBBoxMax", + "cbx", + MFnNumericData::k3Double, + 0.0, + &st + ); + MChkErr(st, "cannot create cachedBBoxMax attribute."); + + aCachedBBox = cAttr.create("cachedBBox", "cbb", &st); + MChkErr(st, "cannot create cachedBBox attribute."); + + st = cAttr.addChild(aCachedBBoxMin); + MChkErr(st, "cannot add cachedBBoxMin to cachedBBox attribute."); + + st = cAttr.addChild(aCachedBBoxMax); + MChkErr(st, "cannot add cachedBBoxMax to cachedBBox attribute."); + + st = addAttribute(aCachedBBox); + MChkErr(st, "cannot add cachedBBox attribute."); + + + aCollisionSet = mAttr.create("collisionSet", "cs", &st); + MChkErr(st, "cannot create collisionSet attribute."); + mAttr.setHidden(true); + st = addAttribute(aCollisionSet); + MChkErr(st, "cannot add collisionSet attribute."); + + + aGrowthSet = mAttr.create("growthSet", "gs", &st); + MChkErr(st, "cannot create growthSet attribute."); + mAttr.setHidden(true); + st = addAttribute(aGrowthSet); + MChkErr(st, "cannot add growthSet attribute."); + + aSplineLock = nAttr.create( + "splineLock", "slk", MFnNumericData::kBoolean, 0, &st + ); + MChkErr(st, "cannot create splineLock attribute."); + nAttr.setHidden(true); + st = addAttribute(aSplineLock); + MChkErr(st, "cannot add splineLock attribute."); + + aSplineLockTrigger = nAttr.create( + "splineLockTrigger", "slkt", MFnNumericData::kBoolean, 0, &st + ); + MChkErr(st, "cannot create splineLockTrigger attribute."); + nAttr.setHidden(true); + nAttr.setConnectable(false); + nAttr.setStorable(false); + nAttr.setWritable(false); + st = addAttribute(aSplineLockTrigger); + MChkErr(st, "cannot add splineLockTrigger attribute."); + + doAffects(aCachedBBox); + doAffects(outputMesh); + doAffects(triggerAttr); + doAffects(MPxSurfaceShape::visibility); + + attributeAffects(triggerAttr, outputMesh); + + attributeAffects(inputCurve, aHairGroup); + attributeAffects(inputMesh, aHairGroup); + attributeAffects(inputSurf, aHairGroup); + + attributeAffects(inputCurve, aSplineLockTrigger); + attributeAffects(aSplineLock, aSplineLockTrigger); + + + //*************************************************************** + // + // INTERMEDIATE ATTRIBUTES FOR TESTING DIRTY INPUTS + // + // When compute() is called for an output plug, it is sometimes useful + // to know which input plug(s) are dirty. As there's no direct way of + // determining this, we use intermediate attrs which are affected by + // the input attr we're interested in and nothing else. If, when we + // query them, they are recomputed, then we know that the input attr + // has changed. + // + //*************************************************************** + + // + // Whenever the instancing attrs change, we need to update the blind + // data, but we don't want to have to do that every time the outputMesh + // is dirty, so let's create some intermediate attributes to tell us + // when the instancing attrs change. + // + //vlad|10Mar2010 - I do not see anything about it in Docs, probably Autodesk did + //not update it yet. But they added some attribute named "isc" to MPxNode + //so we can not add attribute with name already used. + // + //Solution: rename shave's attribute to "isc0" and "instancingStatusChanged0" + //it is safe because: + //1) attribute is not saved ( not storable ) so will not affect older scenes + //2) we do not access *this* attribute by name using MFnDependencyNode::findPlug + instancingStatusChanged = nAttr.create( + "instancingStatusChanged0", + "isc0", //vlad|10Mar2010 - let's try another name + MFnNumericData::kBoolean, + /*false*/0 //there is 'int' param + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setWritable(false); + + st = addAttribute(instancingStatusChanged); + MChkErr(st, "can't add 'instancingStatusChanged' attribute."); + attributeAffects(shaveParamInstancingStatus, instancingStatusChanged); + + instanceMeshChanged = nAttr.create( + "instanceMeshChanged", + "imc", + MFnNumericData::kBoolean + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setWritable(false); + + st = addAttribute(instanceMeshChanged); + MChkErr(st, "can't add 'instanceMeshChanged' attribute."); + attributeAffects(instanceMesh, instanceMeshChanged); + + + //*************************************************************** + // + // DEPRECATED ATTRIBUTES + // + // These attributes are no longer used. They are only retained + // so that old scenes can still be loaded. + // + //*************************************************************** + + isActive = nAttr.create( "active", "act", MFnNumericData::kBoolean, true); + nAttr.setHidden(true); + // We want to keep this as storable for now so that we can revert + // scenes which might get screwed up. + nAttr.setStorable(true); + st = addAttribute(isActive); + + shaveParamsDisplaceStren = + nAttr.create("displaceStrength", "dist", MFnNumericData::kFloat, 0); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(shaveParamsDisplaceStren); + + displayHairMaxAttr = nAttr.create( + "displayHairMax", "dhm", MFnNumericData::kLong + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(displayHairMaxAttr); + + displayHairRatioAttr = nAttr.create( + "displayHairRatio", "dhr", MFnNumericData::kFloat + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(displayHairRatioAttr); + + return MS::kSuccess; +} + + +// +// Do the 'attributeAffects' call for an output attr which is affected by +// all the major input attrs. +// +// We also use this for chained dependencies where some other attr is +// affected by one which is affected by all the major input attrs. We have +// to do it this way because Maya's DG does not directly support chaining +// within a single node. +// +void shaveHairShape::doAffects(MObject targetAttr) +{ + attributeAffects( aDisplayGuides, targetAttr ); + attributeAffects( aSplineLock, targetAttr ); + attributeAffects( collisionObjectsAttr, targetAttr ); + attributeAffects( collisionObjectsGroupIDAttr, targetAttr ); + attributeAffects( displayHiarDoXparency, targetAttr ); + attributeAffects( displayHiarDoSsao, targetAttr ); + attributeAffects( shaveParamsDisplaceStren, targetAttr ); + attributeAffects( displayHiarXparency, targetAttr ); +// attributeAffects( displayHairMaxAttr, targetAttr ); +// attributeAffects( displayHairRatioAttr, targetAttr ); + attributeAffects( dspyMode, targetAttr ); + attributeAffects( growthObjectsGroupIDAttr, targetAttr ); + attributeAffects( inputCurve, targetAttr ); + attributeAffects( inputMesh, targetAttr ); + attributeAffects( inputSurf, targetAttr ); + attributeAffects( instanceMesh, targetAttr ); + attributeAffects( renderStateAttr, targetAttr ); + attributeAffects( runDynamics, targetAttr ); + attributeAffects( shaveBlindHair, targetAttr ); + attributeAffects( aEvalOption, targetAttr ); + attributeAffects( shaveParamAmbDiff, targetAttr ); + attributeAffects( shaveParamAnimSpeed, targetAttr ); + attributeAffects( shaveParamDampening, targetAttr ); + attributeAffects( shaveParamDisplacement, targetAttr ); + attributeAffects( shaveParamFrizzAnim, targetAttr ); + attributeAffects( shaveParamFrizzAnimDir, targetAttr ); + attributeAffects( shaveParamFrizzAnimDirX, targetAttr ); + attributeAffects( shaveParamFrizzAnimDirY, targetAttr ); + attributeAffects( shaveParamFrizzAnimDirZ, targetAttr ); + attributeAffects( shaveParamFrizzFreqX, targetAttr ); + attributeAffects( shaveParamFrizzFreqY, targetAttr ); + attributeAffects( shaveParamFrizzFreqZ, targetAttr ); + attributeAffects( shaveParamFrizzRoot, targetAttr ); + attributeAffects( shaveParamFrizzTip, targetAttr ); + attributeAffects( shaveParamGeomShadow, targetAttr ); + attributeAffects( shaveParamGloss, targetAttr ); + attributeAffects( shaveParamHaircount, targetAttr ); + attributeAffects( shaveParamHairColor, targetAttr ); + attributeAffects( shaveParamHueVariation, targetAttr ); + attributeAffects( shaveParamInstancingStatus, targetAttr ); + attributeAffects( shaveParamKinkFreqX, targetAttr ); + attributeAffects( shaveParamKinkFreqY, targetAttr ); + attributeAffects( shaveParamKinkFreqZ, targetAttr ); + attributeAffects( shaveParamKinkRoot, targetAttr ); + attributeAffects( shaveParamKinkTip, targetAttr ); + attributeAffects( shaveParamMultStrand, targetAttr ); + attributeAffects( shaveParamMutantColor, targetAttr ); + attributeAffects( shaveParamMutantPercent, targetAttr ); + attributeAffects( shaveParamNoInterp, targetAttr ); + attributeAffects( shaveParamPasses, targetAttr ); + attributeAffects( shaveParamRandScale, targetAttr ); + attributeAffects( shaveParamRandomizeMulti, targetAttr ); + attributeAffects( shaveParamRootColor, targetAttr ); + attributeAffects( shaveParamRootStiffness, targetAttr ); + attributeAffects( shaveParamScale, targetAttr ); + attributeAffects( shaveParamSegs, targetAttr ); + attributeAffects( shaveParamSelfShadow, targetAttr ); + attributeAffects( shaveParamSpecular, targetAttr ); + attributeAffects( shaveParamSplayRoot, targetAttr ); + attributeAffects( shaveParamSplayTip, targetAttr ); + attributeAffects( shaveParamMultAsp, targetAttr ); //vlad|05July2010 + attributeAffects( shaveParamOffset, targetAttr ); //vlad|09July2010 + attributeAffects( shaveParamAspect, targetAttr ); //vlad|09July2010 + attributeAffects( shaveParamStiffness, targetAttr ); + attributeAffects( shaveParamThickRoot, targetAttr ); + attributeAffects( shaveParamThickTip, targetAttr ); + attributeAffects( shaveParamValueVariation, targetAttr ); + attributeAffects( surfTessU, targetAttr ); + attributeAffects( surfTessV, targetAttr ); + attributeAffects( subdTessDept, targetAttr ); + attributeAffects( subdTessSamp, targetAttr ); + attributeAffects( textureCacheUpdatedAttr, targetAttr ); + attributeAffects( timeAttr, targetAttr ); + attributeAffects( aSquirrel, targetAttr ); //to update param -- vlad|02July2010 + + attributeAffects( flyawayPerc, targetAttr ); + attributeAffects( flyawayStren, targetAttr ); + attributeAffects( messStren, targetAttr ); + + attributeAffects( clumps, targetAttr ); + attributeAffects( clumpsStren, targetAttr ); + attributeAffects( clumpsColStren, targetAttr ); + attributeAffects( clumpsRotStren, targetAttr ); + attributeAffects( clumpsRotOffset, targetAttr ); + attributeAffects( clumpsRandomize, targetAttr ); + attributeAffects( clumpsFlatness, targetAttr ); + attributeAffects( clumpsScruffle, targetAttr ); + + attributeAffects( randomSeedOffset, targetAttr ); +} + +//vlad|06Apr2010 -- 'stackIndex' value access +int shaveHairShape::getStackIndex() const +{ + int index; + MPlug stackIndexPlug(thisMObject(),stackIndex); + stackIndexPlug.getValue(index); + return index; +} +//vlad|06Apr2010 -- 'stackIndex' value access +void shaveHairShape::setStackIndex(int index) +{ + MPlug stackIndexPlug(thisMObject(),stackIndex); + stackIndexPlug.setValue(index); +} + + diff --git a/mayaPlug/shaveHairUI.cpp b/mayaPlug/shaveHairUI.cpp new file mode 100644 index 0000000..adb3f15 --- /dev/null +++ b/mayaPlug/shaveHairUI.cpp @@ -0,0 +1,5777 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#include <QtCore/QEvent> +#include <QtGui/QMouseEvent> +#include <QtGui/QTabletEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include <vector> +#include <sstream> +#include <assert.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> + +#include <maya/M3dView.h> +#include <maya/MBoundingBox.h> +#include <maya/MDrawInfo.h> +#include <maya/MDrawRequest.h> +#include <maya/MDrawRequestQueue.h> +#include <maya/MFnDoubleIndexedComponent.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MGlobal.h> +#include <maya/MMatrix.h> +#include <maya/MPlug.h> +#include <maya/MPoint.h> +#include <maya/MPointArray.h> +#include <maya/MSelectInfo.h> +#include <maya/MSelectionList.h> +#include <maya/MSelectionMask.h> +#include <maya/MFnCamera.h> +#include <maya/MAnimControl.h> +#include <maya/MDagPath.h> +#include <maya/MFloatPointArray.h> +#include <maya/MIOStream.h> + +#include "shaveCheckObjectVisibility.h" +#include "shaveCursorCtx.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveHairUI.h" +#include "shaveMaya.h" +#include "shaveRender.h" +#include "shaveSDKTYPES.h" +#include "shaveStyleCmd.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" + +#ifdef OSMac_ +#include <mach-o/dyld.h> +#include <stdlib.h> +#include <string.h> +#include <OpenGL/gl.h> +#include <OpenGL/glu.h> +#endif + +// +// Some magic numbers which the API *should* give you, but doesn't. +// +static const int LEAD_COLOR = 18; +static const int ACTIVE_SURFACE_COLOR = 15; +static const int DORMANT_SURFACE_COLOR = 4; +static const int ACTIVE_TEMPLATE_COLOR = 19; +static const int ACTIVE_AFFECTED_COLOR = 8; +static const int HILITE_COLOR = 17; +static const int ACTIVE_VERTEX_COLOR = 16; +static const int DORMANT_VERTEX_COLOR = 8; +static const int ACTIVE_ISOPARM_COLOR = 16; + +static const int DORMANT_GUIDE_COMPONENT_COLOR = 22; +static const int ACTIVE_GUIDE_COMPONENT_COLOR = LEAD_COLOR; +static const int LEAD_HAIR_COLOR = 22; +static const int ACTIVE_HAIR_COLOR = 2; + +static const float POINT_SIZE = 3.0f; + +#ifdef NEW_INSTNACE_DISPLAY +#ifndef GL_ACTIVE_TEXTURE +PFNGLATCIVETEXTUREPROC glActiveTexture = NULL; +#endif +#endif + +///// shaders +#if defined(_WIN32) || defined(LINUX) +typedef GLuint (APIENTRY * PFNGLCREATESHADERPROC) (GLenum shaderType); +PFNGLCREATESHADERPROC glCreateShader = NULL; + +typedef void (APIENTRY * PFNGLCOMPILESHADERPROC) (GLuint shader); +PFNGLCOMPILESHADERPROC glCompileShader = NULL; + +typedef void (APIENTRY * PFNGATTACHSHADERPROC) (GLuint program, GLuint shader); +PFNGATTACHSHADERPROC glAttachShader = NULL; + +typedef void (APIENTRY * PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +PFNGLDETACHSHADERPROC glDetachShader = NULL; + +typedef void (APIENTRY * PFNGLDELETESHADERPROC) (GLuint shader); +PFNGLDELETESHADERPROC glDeleteShader = NULL; + +typedef void (APIENTRY * PFNGLGETSHADDERIVPROC) (GLuint shader, GLenum pname, GLint *params); +PFNGLGETSHADDERIVPROC glGetShaderiv = NULL; + +typedef void (APIENTRY * PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog); +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = NULL; + +#if GL_GLEXT_VERSION > 71 +typedef void (APIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +PFNGLSHADERSOURCEPROC glShaderSource = NULL; +#else +typedef void (APIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const char **string, const GLint *length); +PFNGLSHADERSOURCEPROC glShaderSource = NULL; +#endif + + +/////// programs +typedef GLuint (APIENTRY * PFNGLCREATEPROGRAMPROC) (void); +PFNGLCREATEPROGRAMPROC glCreateProgram = NULL; + +typedef void (APIENTRY * PFNGLDELETEPROGRAMPROC) (GLuint program); +PFNGLDELETEPROGRAMPROC glDeleteProgram = NULL; + +typedef void (APIENTRY * PFNGLLINKPROGRAMPROC) (GLuint program); +PFNGLLINKPROGRAMPROC glLinkProgram = NULL; + +typedef void (APIENTRY * PFNGLUSEPROGRAMPROC) (GLuint program); +PFNGLUSEPROGRAMPROC glUseProgram = NULL; + +typedef void (APIENTRY * PFNVALIDATEPROGRAMPROC) (GLuint program); +PFNVALIDATEPROGRAMPROC glValidateProgram = NULL; + +typedef void (APIENTRY * PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +PFNGLGETPROGRAMIVPROC glGetProgramiv = NULL; + +typedef void (APIENTRY * PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei maxLength, GLsizei *length, char *infoLog); +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = NULL; + +typedef GLint (APIENTRY * PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const char *name); +PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL; + +/////// uniforms +typedef void (APIENTRY * PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +PFNGLUNIFORM3FPROC glUniform3f = NULL; + +typedef void (APIENTRY * PFNGLUNIFORM1FPROC) (GLint location, GLfloat f); +PFNGLUNIFORM1FPROC glUniform1f = NULL; + +typedef void (APIENTRY * PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +PFNGLUNIFORM1IPROC glUniform1i = NULL; + +//////per vertex attributes +typedef GLint (APIENTRY * PFNGLGETATTRIBLOCATION)(GLuint program, char *name); +PFNGLGETATTRIBLOCATION glGetAttribLocation = NULL; + +typedef void (APIENTRY * PFNGLVERTEXATTRIB1FV)(GLuint index, GLfloat v); +PFNGLVERTEXATTRIB1FV glVertexAttrib1f = NULL; + +typedef void (APIENTRY * PFNGLVERTEXATTRIB3FV)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2); +PFNGLVERTEXATTRIB3FV glVertexAttrib3f = NULL; + +typedef void (APIENTRY * PFNGLVERTEXATTRIBPOINTER)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer); +PFNGLVERTEXATTRIBPOINTER glVertexAttribPointer = NULL; + +typedef void (APIENTRY * PFNGLENABLEVERTEXATTRIBARRAY)(GLuint index); +PFNGLENABLEVERTEXATTRIBARRAY glEnableVertexAttribArray = NULL; + +/////////FBO +typedef void (APIENTRY * PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = NULL; + +typedef void (APIENTRY * PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = NULL; + +typedef void (APIENTRY * PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = NULL; + +typedef GLenum (APIENTRY * PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = NULL; + +typedef void (APIENTRY * PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); + PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT = NULL; + +typedef void (APIENTRY * PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = NULL; + +typedef void (APIENTRY * PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = NULL; + +typedef void (APIENTRY * PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT = NULL; + +typedef void (APIENTRY * PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT = NULL; + +typedef void (APIENTRY * PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT = NULL; + +typedef void (APIENTRY * PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT = NULL; + +typedef void (APIENTRY * PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT = NULL; + +typedef GLboolean (APIENTRY * PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT = NULL; + +typedef void (APIENTRY * PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = NULL; + +typedef void (APIENTRY * PFNGLBLITFRAMEBUFFEREXT)( GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLint,GLbitfield,GLenum); +PFNGLBLITFRAMEBUFFEREXT glBlitFramebufferEXT = NULL; + +////MISC +typedef void (APIENTRY * PFNGLGENERATEMIPMAPEXTPROC) (GLenum texture); +PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = NULL; +#endif + + +////////////////////////////////////////////////////////////// Profiling //////////////////////////////////////////////////////// +#ifdef DO_PROFILE +namespace Profile { + +//// out + +class LStrFile { +public: + const static char* NUM_PATTERNS_TAG; + const static char* PATTERN_TAG; + const static char* NO_STR_VAL; + + enum _e_consts + { + eMaxStringLen = 300 + }; + + enum _e_errcodes + { + eOK = 1, + eIOError = 0, //GetLastError() should be used for details + eTagError = -1, + eDataError = -2 + }; + + LStrFile(const char *filename,const char *mode) +{ + _stream() = NULL; + Open(filename, mode); +} + ~LStrFile() +{ + Close(); +} + + bool IsNULL() const {return stream() == NULL;} + + int Close() +{ + int result=0; + if(stream()) + result = fclose(_stream()); + + _stream() = NULL; + return result; +} + bool Open(const char *filename,const char *mode) +{ + Close(); + _stream() = fopen(filename,mode); + //errno_t err = fopen_s(&_stream(),filename,mode); //vc8.0 stuff + return (stream() != NULL); +} + + int Write(const char* buf) +{ + if(stream()) + { + int res = fprintf(_stream(),"%s",buf); + //int res = fprintf_s(_stream(),"[%s] %i [/%s] \n",NUM_PATTERNS_TAG, n, NUM_PATTERNS_TAG); //vc8.0 stuff + if(res > 0) + fflush(_stream()); + + return (res > 0) ? eOK : eIOError; + } + return eOK; +} + +protected: + inline const FILE* stream() const {return m_stream;} + inline FILE*& _stream() {return m_stream;} + +private: + FILE* m_stream; + +}; + +class LDiagFile { +public: + LStrFile* diagDump; + LDiagFile() + { + diagDump = NULL; + } + ~LDiagFile() + { + if(diagDump) + delete diagDump; + } +}; +static LDiagFile diag; + + + +LStrFile* OpenDiagFile() +{ + MString mdir; + MGlobal::executeCommand("internalVar -userTmpDir",mdir); + if(mdir != "") + { + MString log = mdir + "shave_profile.log"; + + + diag.diagDump = new LStrFile(log.asChar(),"wt"); + + if(diag.diagDump->IsNULL()) + { + printf("Shave: can't write log to %s\n",log.asChar()); + fflush(stdout); + + MGlobal::displayError(MString("Shave: can't write log to ") + log); + + return NULL; + } + MGlobal::displayInfo(MString("LipService: writing log to ") + log); + return diag.diagDump; + } + else + { + printf("cant write log, internalVar -userBitmapsDir returned empty path\n"); + fflush(stdout); + return NULL; + } +} + +void CloseDiagFile() +{ + if(diag.diagDump) + { + delete diag.diagDump; + diag.diagDump = NULL; + } +} + +LStrFile* GetDiagFile() +{ + return diag.diagDump; +} + +//// frofile functions + +int numZeros(long N) //nuber of traling zeros for nano +{ + int d = 0; + while(N > 0) { + d++; + N/=10; + } + int z = 9 - d; + return z; +} + +static bool just_started = false; +#ifdef WIN32 +//static unsigned int last = 0; +//static unsigned int start = 0; +LARGE_INTEGER last; +LARGE_INTEGER start; +#elif defined(LINUX) +timespec last; +timespec start; + +timespec diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + return temp; +} +#endif + + + +void ProfileStart(LStrFile* f) +{ + if(f==NULL) + f = OpenDiagFile(); + + just_started = true; + + char tbuf[100]; + time_t t = time(0); + struct tm* tnow = localtime(&t); + sprintf(tbuf,"%i:%i:%i", tnow->tm_hour, tnow->tm_min, tnow->tm_sec); + +#ifdef WIN32 +#if 0 + QueryPerformanceCounter(&last); + QueryPerformanceCounter(&start); +#endif + if(f) + { + char buf[100]; sprintf(buf,"[%s] initalized \n",tbuf); + f->Write(buf); + } + else + { + fprintf(stdout,"[%s] initalized\n",tbuf); + fflush(stdout); + } +#elif defined(LINUX) +#if 0 + int res1 = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); + int res2 = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &last); + + if(f) + { + char buf[100]; + if(res1 != -1 && res2 != -1) + sprintf(buf,"[%s] initalized \n",tbuf); + else + sprintf(buf,"Profiling: start failed!\n"); + + f->Write(buf); + } + else + { + if(res1 != -1 && res2 != -1) + fprintf(stdout,"[%s] initalized\n",tbuf); + else + fprintf(stdout,"Profiling: failed!\n"); + + fflush(stdout); + } +#else + if(f) + { + char buf[100]; sprintf(buf,"[%s] initalized\n",tbuf); + f->Write(buf); + } + else + { + fprintf(stdout,"[%s] initalized\n",tbuf); + fflush(stdout); + } +#endif + +#else + +#endif + +} + +void ProfileDump(char* msg, LStrFile* f) +{ + if(f==NULL) + f=GetDiagFile(); + + char tbuf[100]; + time_t t = time(0); + struct tm* now = localtime(&t); + sprintf(tbuf,"%i:%i:%i", now->tm_hour, now->tm_min, now->tm_sec); + + if(just_started) + { +#ifdef WIN32 + QueryPerformanceCounter(&last); + QueryPerformanceCounter(&start); + + if(f) + { + char buf[100]; sprintf(buf,"[%s] started | %s\n",tbuf,msg); + f->Write(buf); + } + else + { + fprintf(stdout,"[%s] started | %s \n",tbuf,msg); + fflush(stdout); + } +#elif defined(LINUX) + int res1 = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); + int res2 = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &last); + + if(f) + { + char buf[100]; + if(res1 != -1 && res2 != -1) + sprintf(buf,"[%s] started | %s\n",tbuf,msg); + else + sprintf(buf,"Profiling: start failed! | %s\n",msg); + + f->Write(buf); + } + else + { + if(res1 != -1 && res2 != -1) + fprintf(stdout,"[%s] started | %s\n",tbuf,msg); + else + fprintf(stdout,"Profiling: start failed! | %s\n",msg); + + fflush(stdout); + } +#else + +#endif + just_started = false; + } + else + { +#ifdef WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + + long long from_start = now.QuadPart - start.QuadPart; + long long from_last = now.QuadPart - last.QuadPart; + + last = now; + float t1 = (float)(from_last*1000.0/freq.QuadPart); + float t2 = (float)(from_start*1000.0/freq.QuadPart); + if(f) + { + char buf[200];sprintf(buf,"[%s] %f (total %f) | %s\n",tbuf,t1,t2,msg); + f->Write(buf); + } + else + { + fprintf(stdout,"[%s] %f (total %f) | %s\n",tbuf,t1,t2,msg); + fflush(stdout); + } + ///////////// + //int s, n; + //char buf0[20] = ""; int nz = numZeros(n);for(int k = 0; k < nz; k++) strcat(buf0,"0"); + //char buf[200];sprintf(buf,"[%s] %u.%u (total %u.%u) | %s\n",tbuf,s,buf0,n); + //////////// +#elif defined(LINUX) + timespec now; + int res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now); + if(res != -1) + { + timespec tdiff = diff(last,now); + timespec tdiff2 = diff(start,now); + last=now; + + char buf1[20] = ""; int nz1 = numZeros(tdiff.tv_nsec);for(int k = 0; k < nz1; k++) strcat(buf1,"0"); + char buf2[20] = ""; int nz2 = numZeros(tdiff2.tv_nsec);for(int k = 0; k < nz2; k++) strcat(buf2,"0"); + char buf[200];sprintf(buf,"[%s] %u.%s%u (total %u.%s%u) | %s\n",tbuf, + (unsigned int)tdiff.tv_sec, buf1, (unsigned int)tdiff.tv_nsec, + (unsigned int)tdiff2.tv_sec, buf2, (unsigned int)tdiff2.tv_nsec, + msg); + + if(f) + { + + f->Write(buf); + } + else + { + fprintf(stdout,"%s\n",buf); + fflush(stdout); + } + } +#endif + } + +} + +void ProfileEnd(LStrFile* f) +{ + if(f==NULL) + f=GetDiagFile(); + + char tbuf[100]; + time_t t = time(0); + struct tm* fnow = localtime(&t); + sprintf(tbuf,"%i:%i:%i", fnow->tm_hour, fnow->tm_min, fnow->tm_sec); + +#ifdef WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + + long long from_start = now.QuadPart - start.QuadPart; + + if(f) + { + char buf[200];sprintf(buf,"[%s] finished: %f\n",tbuf,(float)(from_start*1000.0/freq.QuadPart)); + f->Write(buf); + } + else + { + fprintf(stdout,"[%s] finished: %f \n",tbuf,(float)(from_start*1000.0/freq.QuadPart)); + fflush(stdout); + } +#elif defined(LINUX) + timespec now; + int res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now); + if(res != -1) + { + timespec tdiff = diff(start,now); + now = last; + char buf1[20] = ""; int nz1 = numZeros(tdiff.tv_nsec);for(int k = 0; k < nz1; k++) strcat(buf1,"0"); + char buf[200];sprintf(buf,"[%s] finished: %u.%s%u \n",tbuf,(unsigned int)tdiff.tv_sec, buf1, (unsigned int)tdiff.tv_nsec); + if(f) + { + + f->Write(buf); + } + else + { + fprintf(stdout,"%s\n",tbuf,(unsigned int)tdiff.tv_sec, (unsigned int)tdiff.tv_nsec); + fflush(stdout); + } + } +#else +#endif +} + +} //end of namespace LServiceSDK +#endif +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////// GL EXT INITIALIZATION //////////////////////////// + +static bool glFuncionsInited = false; + +bool GLFunctionsInited() +{ + return glFuncionsInited; +} + +bool InitGLFunctionPointers() +{ +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + + bool ret=true; + MGlobal::displayInfo("InitGLFunctionPointers"); + + //// these are delcalred in OSX's gl.h so no need to get ptrs +#ifdef WIN32 + //glNewBufferRegionEXT = (PFNGLNEWBUFFERREGIONEXTPROC) getGlFncPtr("glNewBufferRegionEXT"); +// if(!glNewBufferRegionEXT) +// { +// MGlobal::displayInfo(MString("glNewBufferRegionEXTT not found (error")+ GetLastError() + ")"); +// return false; +// } + +//glDeleteBufferRegionEXT = (PFNGLDELETEBUFFERREGIONEXTPROC) getGlFncPtr("glDeleteBufferRegionEXT"); +// if(!glDeleteBufferRegionEXT) +// { +// MGlobal::displayInfo(MString("glDeleteBufferRegionEXT not found (error")+ GetLastError() + ")"); +// ret= false; +// } + +//glReadBufferRegionEXT = (PFNGLREADBUFFERREGIONEXTPROC) getGlFncPtr("glReadBufferRegionEXT"); +// if(!glReadBufferRegionEXT) +// { +// MGlobal::displayInfo(MString("glReadBufferRegionEXT not found (error")+ GetLastError() + ")"); +// ret= false; +// } + +//glDrawBufferRegionEXT = (PFNGLDRAWBUFFERREGIONEXTPROC) getGlFncPtr("glDrawBufferRegionEXT"); +// if(!glDrawBufferRegionEXT) +// { +// MGlobal::displayInfo(MString("glDrawBufferRegionEXT not found (error")+ GetLastError() + ")"); +// ret= false; +// } + +//glBufferRegionEnabledEXT = (PFNGLBUFFERREGIONENABLEDEXTPROC) getGlFncPtr("glBufferRegionEnabledEXT"); +// if(!glBufferRegionEnabledEXT) +// { +// MGlobal::displayInfo(MString("glBufferRegionEnabledEXT not found (error")+ GetLastError() + ")"); +// ret= false; +// } + glActiveTexture = (PFNGLATCIVETEXTUREPROC) getGlFncPtr("glActiveTexture"); + if(!glActiveTexture) + { + MGlobal::displayInfo(MString("glActiveTexture not found (error")+ GetLastError() + ")"); + ret= false; + } + assert(_CrtCheckMemory()); +#if 0 + glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) getGlFncPtr("glMultiTexCoord2f"); + if(!glMultiTexCoord2f) + { + MGlobal::displayInfo(MString("glMultiTexCoord2f not found (error")+ GetLastError() + ")"); + ret= false; + } +#endif + glAttachShader = (PFNGATTACHSHADERPROC) getGlFncPtr("glAttachShader"); + if(!glAttachShader) + { + MGlobal::displayInfo(MString("glAttachShader not found (error")+ GetLastError() + ")"); + ret= false; + } + // ret= false; + + glCompileShader = (PFNGLCOMPILESHADERPROC) getGlFncPtr("glCompileShader"); + if(!glCompileShader) + { + MGlobal::displayInfo(MString("glCompileShader not found (error")+ GetLastError() + ")"); + ret= false; + } + + glDetachShader = (PFNGLDETACHSHADERPROC) getGlFncPtr("glDetachShader"); + if(!glDetachShader) + { + MGlobal::displayInfo(MString("glDetachShader not found (error")+ GetLastError() + ")"); + ret= false; + } + + glDeleteShader = (PFNGLDELETESHADERPROC) getGlFncPtr("glDeleteShader"); + if(!glDeleteShader) + { + MGlobal::displayInfo(MString("glDeleteShader not found (error")+ GetLastError() + ")"); + ret= false; + } + + glCreateShader = (PFNGLCREATESHADERPROC) getGlFncPtr("glCreateShader"); + if(!glCreateShader) + { + MGlobal::displayInfo(MString("glCreateShader not found (error")+ GetLastError() + ")"); + ret= false; + } + + glShaderSource = (PFNGLSHADERSOURCEPROC) getGlFncPtr("glShaderSource"); + if(!glShaderSource) + { + MGlobal::displayInfo(MString("glShaderSource not found (error")+ GetLastError() + ")"); + ret= false; + } + + glGetShaderiv = (PFNGLGETSHADDERIVPROC) getGlFncPtr("glGetShaderiv"); + if(!glGetShaderiv) + { + MGlobal::displayInfo(MString("glGetShaderiv not found (error")+ GetLastError() + ")"); + ret= false; + } + + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) getGlFncPtr("glGetShaderInfoLog"); + if(!glGetShaderInfoLog) + { + MGlobal::displayInfo(MString("glGetShaderInfoLog not found (error")+ GetLastError() + ")"); + ret= false; + } + + glDeleteProgram = (PFNGLDELETEPROGRAMPROC) getGlFncPtr("glDeleteProgram"); + if(!glDeleteProgram) + { + MGlobal::displayInfo(MString("glDeleteProgram not found (error")+ GetLastError() + ")"); + ret= false; + } + + glCreateProgram = (PFNGLCREATEPROGRAMPROC) getGlFncPtr("glCreateProgram"); + if(!glCreateProgram) + { + MGlobal::displayInfo(MString("glCreateProgram not found (error")+ GetLastError() + ")"); + ret= false; + } + + glLinkProgram = (PFNGLLINKPROGRAMPROC) getGlFncPtr("glLinkProgram"); + if(!glLinkProgram) + { + MGlobal::displayInfo(MString("glLinkProgram not found (error")+ GetLastError() + ")"); + ret= false; + } + + glUseProgram = (PFNGLUSEPROGRAMPROC) getGlFncPtr("glUseProgram"); + if(!glUseProgram) + { + MGlobal::displayInfo(MString("glUseProgram not found (error")+ GetLastError() + ")"); + ret= false; + } + + glGetProgramiv = (PFNGLGETPROGRAMIVPROC) getGlFncPtr("glGetProgramiv"); + if(!glGetProgramiv) + { + MGlobal::displayInfo(MString("glGetProgramiv not found (error")+ GetLastError() + ")"); + ret= false; + } + + glValidateProgram = (PFNVALIDATEPROGRAMPROC) getGlFncPtr("glValidateProgram"); + if(!glValidateProgram) + { + MGlobal::displayInfo(MString("glValidateProgram not found (error")+ GetLastError() + ")"); + ret= false; + } + + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) getGlFncPtr("glGetProgramInfoLog"); + if(!glGetProgramInfoLog) + { + MGlobal::displayInfo(MString("glGetProgramInfoLog not found (error")+ GetLastError() + ")"); + ret= false; + } + + glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) getGlFncPtr("glGetUniformLocation"); + if(!glGetUniformLocation) + { + MGlobal::displayInfo(MString("glGetUniformLocation not found (error")+ GetLastError() + ")"); + ret= false; + } + + glUniform3f = (PFNGLUNIFORM3FPROC) getGlFncPtr("glUniform3f"); + if(!glUniform3f) + { + MGlobal::displayInfo(MString("glUniform3f not found (error")+ GetLastError() + ")"); + ret= false; + } + + glUniform1f = (PFNGLUNIFORM1FPROC) getGlFncPtr("glUniform1f"); + if(!glUniform1f) + { + MGlobal::displayInfo(MString("glUniform1f not found (error")+ GetLastError() + ")"); + ret= false; + } + + glUniform1i = (PFNGLUNIFORM1IPROC) getGlFncPtr("glUniform1i"); + if(!glUniform1i) + { + MGlobal::displayInfo(MString("glUniform1i not found (error")+ GetLastError() + ")"); + ret= false; + } + + glVertexAttrib1f = (PFNGLVERTEXATTRIB1FV) getGlFncPtr("glVertexAttrib1f"); + if(!glVertexAttrib1f) + { + MGlobal::displayInfo(MString("glVertexAttrib1f not found (error")+ GetLastError() + ")"); + ret= false; + } + + glVertexAttrib3f = (PFNGLVERTEXATTRIB3FV) getGlFncPtr("glVertexAttrib3f"); + if(!glVertexAttrib3f) + { + MGlobal::displayInfo(MString("glVertexAttrib3f not found (error")+ GetLastError() + ")"); + ret= false; + } + + glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTER) getGlFncPtr("glVertexAttribPointer"); + if(!glVertexAttribPointer) + { + MGlobal::displayInfo(MString("glVertexAttribPointer not found (error")+ GetLastError() + ")"); + ret= false; + } + + glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAY) getGlFncPtr("glEnableVertexAttribArray"); + if(!glEnableVertexAttribArray) + { + MGlobal::displayInfo(MString("glEnableVertexAttribArray not found (error")+ GetLastError() + ")"); + ret= false; + } + +// glWindowPos2i = (PFNGLWINDOWPOS2I) getGlFncPtr("glWindowPos2i"); +// if(!glWindowPos2i) +// { +// MGlobal::displayInfo(MString("glWindowPos2i not found (error")+ GetLastError() + ")"); +// ret= false; +// } + +// glWindowPos3f = (PFNGLWINDOWPOS3F) getGlFncPtr("glWindowPos3f"); +// if(!glWindowPos3f) +// { +// MGlobal::displayInfo(MString("glWindowPos3f not found (error")+ GetLastError() + ")"); +// ret= false; +// } + + glGetAttribLocation = (PFNGLGETATTRIBLOCATION) getGlFncPtr("glGetAttribLocation"); + if(!glGetAttribLocation) + { + MGlobal::displayInfo(MString("glGetAttribLocation not found (error")+ GetLastError() + ")"); + ret= false; + } + + + //PBO +// glBindBufferARB = (PFNGLBINDBUFFERARBPROC) getGlFncPtr("glBindBufferARB"); +// glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) getGlFncPtr("glDeleteBuffersARB"); +// glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) getGlFncPtr("glGenBuffersARB"); +// glBufferDataARB = (PFNGLBUFFERDATAARBPROC) getGlFncPtr("glBufferDataARB"); + +// if(!glBindBufferARB) +// { +// MGlobal::displayInfo(MString("glBindBufferARB not found (error")+ GetLastError() + ")"); +// ret= false; +// } + + //FBOs + glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)wglGetProcAddress("glGenFramebuffersEXT"); + if(!glGenFramebuffersEXT) + { + MGlobal::displayInfo(MString("glGenFramebuffersEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)wglGetProcAddress("glDeleteFramebuffersEXT"); + if(!glDeleteFramebuffersEXT) + { + MGlobal::displayInfo(MString("glDeleteFramebuffersEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)wglGetProcAddress("glBindFramebufferEXT"); + if(!glBindFramebufferEXT) + { + MGlobal::displayInfo(MString("glBindFramebufferEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)wglGetProcAddress("glCheckFramebufferStatusEXT"); + if(!glCheckFramebufferStatusEXT) + { + MGlobal::displayInfo(MString("glCheckFramebufferStatusEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)wglGetProcAddress("glGetFramebufferAttachmentParameterivEXT"); + if(!glGetFramebufferAttachmentParameterivEXT) + { + MGlobal::displayInfo(MString("glGetFramebufferAttachmentParameterivEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)wglGetProcAddress("glFramebufferTexture2DEXT"); + if(!glFramebufferTexture2DEXT) + { + MGlobal::displayInfo(MString("glFramebufferTexture2DEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)wglGetProcAddress("glFramebufferRenderbufferEXT"); + if(!glFramebufferRenderbufferEXT) + { + MGlobal::displayInfo(MString("glFramebufferRenderbufferEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)wglGetProcAddress("glGenRenderbuffersEXT"); + if(!glGenRenderbuffersEXT) + { + MGlobal::displayInfo(MString("glGenRenderbuffersEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)wglGetProcAddress("glDeleteRenderbuffersEXT"); + if(!glDeleteRenderbuffersEXT) + { + MGlobal::displayInfo(MString("glDeleteRenderbuffersEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)wglGetProcAddress("glBindRenderbufferEXT"); + if(!glBindRenderbufferEXT) + { + MGlobal::displayInfo(MString("glBindRenderbufferEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)wglGetProcAddress("glRenderbufferStorageEXT"); + if(!glRenderbufferStorageEXT) + { + MGlobal::displayInfo(MString("glRenderbufferStorageEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)wglGetProcAddress("glGetRenderbufferParameterivEXT"); + if(!glGetRenderbufferParameterivEXT) + { + MGlobal::displayInfo(MString("glGetRenderbufferParameterivEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC)wglGetProcAddress("glIsRenderbufferEXT"); + if(!glIsRenderbufferEXT) + { + MGlobal::displayInfo(MString("glIsRenderbufferEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)getGlFncPtr("glRenderbufferStorageMultisampleEXT"); + if(!glRenderbufferStorageMultisampleEXT) + { + MGlobal::displayInfo(MString("glRenderbufferStorageMultisampleEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXT)getGlFncPtr("glBlitFramebufferEXT"); + if(!glBlitFramebufferEXT) + { + MGlobal::displayInfo(MString("glBlitFramebufferEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) getGlFncPtr("glGenerateMipmapEXT"); + if(!glGenerateMipmapEXT) + { + MGlobal::displayInfo(MString("glGenerateMipmapEXT not found (error")+ GetLastError() + ")"); + ret= false; + } + +#elif defined LINUX + + //glBindBufferARB = (PFNGLBINDBUFFERARBPROC) getGlFncPtr("glBindBufferARB"); + //if(!glBindBufferARB) + //{ + // fprintf (stdout,"glAttachShader and glBindBufferARB not found\n");fflush(stdout); + // ret= false; + //} + //glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) getGlFncPtr("glDeleteBuffersARB"); + //if(!glDeleteBuffersARB) + //{ + // fprintf (stdout,"glAttachShader and glDeleteBuffersARB not found\n");fflush(stdout); + // ret= false; + //} + //glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) getGlFncPtr("glGenBuffersARB"); + //if(!glGenBuffersARB) + //{ + // fprintf (stdout,"glAttachShader and glGenBuffersARB not found\n");fflush(stdout); + // ret= false; + //} + //glBufferDataARB = (PFNGLBUFFERDATAARBPROC) getGlFncPtr("glBufferDataARB"); + //if(!glBufferDataARB) + //{ + // fprintf (stdout,"glAttachShader and glBufferDataARB not found\n");fflush(stdout); + // ret= false; + //} + + glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTER) getGlFncPtr("glVertexAttribPointer"); + if(!glVertexAttribPointer) + { + fprintf (stdout,"glAttachShader and glVertexAttribPointer not found\n");fflush(stdout); + ret= false; + } + + glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAY) getGlFncPtr("glEnableVertexAttribArray"); + if(!glEnableVertexAttribArray) + { + fprintf (stdout,"glAttachShader and glEnableVertexAttribArray not found\n");fflush(stdout); + ret= false; + } + + +#ifndef GL_ACTIVE_TEXTURE + glActiveTexture = (PFNGLATCIVETEXTUREPROC) getGlFncPtr("glActiveTexture"); + if(!glActiveTexture) + { + fprintf (stdout,"glActiveTexture not found \n");fflush(stdout); + ret= false; + } +#endif + //glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) getGlFncPtr("glMultiTexCoord2f"); + //if(!glMultiTexCoord2f) + //{ + // fprintf (stdout,"glMultiTexCoord2f not found \n");fflush(stdout); + // ret= false; + //} + glAttachShader = (PFNGATTACHSHADERPROC) getGlFncPtr("glAttachShader"); + if(!glAttachShader) + { + glAttachShader = (PFNGATTACHSHADERPROC) getGlFncPtr("glAttachObjectARB"); + if(!glAttachShader) + { + fprintf (stdout,"glAttachShader and glAttachObjectARB not found\n");fflush(stdout); + ret= false; + } + else + fprintf (stdout,"glAttachObjectARB - OK\n"); + } + else + fprintf (stdout,"glAttachShader - OK\n"); + + glCompileShader = (PFNGLCOMPILESHADERPROC) getGlFncPtr("glCompileShader"); + if(!glCompileShader) + { + fprintf (stdout,"glCompileShader not found \n");fflush(stdout); + ret= false; + } + + glDetachShader = (PFNGLDETACHSHADERPROC) getGlFncPtr("glDetachShader"); + if(!glDetachShader) + { + fprintf (stdout,"glDetachShader not found \n");fflush(stdout); + ret= false; + } + + glDeleteShader = (PFNGLDELETESHADERPROC) getGlFncPtr("glDeleteShader"); + if(!glDeleteShader) + { + fprintf (stdout,"glDeleteShader not found \n");fflush(stdout); + ret= false; + } + + glCreateShader = (PFNGLCREATESHADERPROC) getGlFncPtr("glCreateShader"); + if(!glCreateShader) + { + fprintf (stdout,"glCreateShader not found \n");fflush(stdout); + ret= false; + } + + glShaderSource = (PFNGLSHADERSOURCEPROC) getGlFncPtr("glShaderSource"); + if(!glShaderSource) + { + fprintf (stdout,"glShaderSource not found\n");fflush(stdout); + ret= false; + } + + glGetShaderiv = (PFNGLGETSHADDERIVPROC) getGlFncPtr("glGetShaderiv"); + if(!glGetShaderiv) + { + fprintf (stdout,"glGetShaderiv not found\n");fflush(stdout); + ret= false; + } + + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) getGlFncPtr("glGetShaderInfoLog"); + if(!glGetShaderInfoLog) + { + fprintf (stdout,"glGetShaderInfoLog not found \n");fflush(stdout); + ret= false; + } + + glDeleteProgram = (PFNGLDELETEPROGRAMPROC) getGlFncPtr("glDeleteProgram"); + if(!glDeleteProgram) + { + fprintf (stdout,"glDeleteProgram not found\n");fflush(stdout); + ret= false; + } + + glCreateProgram = (PFNGLCREATEPROGRAMPROC) getGlFncPtr("glCreateProgram"); + if(!glCreateProgram) + { + fprintf (stdout,"glCreateProgram not found \n");fflush(stdout); + ret= false; + } + + glLinkProgram = (PFNGLLINKPROGRAMPROC) getGlFncPtr("glLinkProgram"); + if(!glLinkProgram) + { + fprintf (stdout,"glLinkProgram not found \n");fflush(stdout); + ret= false; + } + + glUseProgram = (PFNGLUSEPROGRAMPROC) getGlFncPtr("glUseProgram"); + if(!glUseProgram) + { + fprintf (stdout,"glUseProgram not found\n");fflush(stdout); + ret= false; + } + + glGetProgramiv = (PFNGLGETPROGRAMIVPROC) getGlFncPtr("glGetProgramiv"); + if(!glGetProgramiv) + { + fprintf (stdout,"glGetProgramiv not found\n");fflush(stdout); + ret= false; + } + + glValidateProgram = (PFNVALIDATEPROGRAMPROC) getGlFncPtr("glValidateProgram"); + if(!glValidateProgram) + { + fprintf (stdout,"glValidateProgram not found \n");fflush(stdout); + ret= false; + } + + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) getGlFncPtr("glGetProgramInfoLog"); + if(!glGetProgramInfoLog) + { + fprintf (stdout,"glGetProgramInfoLog not found\n");fflush(stdout); + ret= false; + } + + glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) getGlFncPtr("glGetUniformLocation"); + if(!glGetUniformLocation) + { + fprintf (stdout,"glGetUniformLocation not found\n");fflush(stdout); + ret= false; + } + + glUniform3f = (PFNGLUNIFORM3FPROC) getGlFncPtr("glUniform3f"); + if(!glUniform3f) + { + fprintf (stdout,"glUniform3f not found\n");fflush(stdout); + ret= false; + } + + glUniform1f = (PFNGLUNIFORM1FPROC) getGlFncPtr("glUniform1f"); + if(!glUniform1f) + { + fprintf (stdout,"glUniform1f not found \n");fflush(stdout); + ret= false; + } + + glUniform1i = (PFNGLUNIFORM1IPROC) getGlFncPtr("glUniform1i"); + if(!glUniform1i) + { + fprintf (stdout,"glUniform1i not found \n");fflush(stdout); + ret= false; + } + + glGetAttribLocation = (PFNGLGETATTRIBLOCATION) getGlFncPtr("glGetAttribLocation"); + if(!glGetAttribLocation) + { + fprintf (stdout,"glGetAttribLocation not found\n");fflush(stdout); + ret= false; + } + + glVertexAttrib1f = (PFNGLVERTEXATTRIB1FV) getGlFncPtr("glVertexAttrib1f"); + if(!glVertexAttrib1f) + { + fprintf (stdout,"glVertexAttrib1f not found\n");fflush(stdout); + ret= false; + } + + glVertexAttrib3f = (PFNGLVERTEXATTRIB3FV) getGlFncPtr("glVertexAttrib3f"); + if(!glVertexAttrib3f) + { + fprintf (stdout,"glVertexAttrib3f not found\n");fflush(stdout); + ret= false; + } + //glWindowPos2i = (PFNGLWINDOWPOS2I) getGlFncPtr("glWindowPos2i"); + //if(!glWindowPos2i) + //{ + // fprintf (stdout,"glWindowPos2i not found\n");fflush(stdout); + // ret= false; + //} + //FBOs + glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)getGlFncPtr("glGenFramebuffersEXT"); + if(!glGenFramebuffersEXT) + { + fprintf (stdout,"glGenFramebuffersEXT not found.\n");fflush(stdout); + ret= false; + } + glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)getGlFncPtr("glDeleteFramebuffersEXT"); + if(!glDeleteFramebuffersEXT) + { + fprintf (stdout,"glDeleteFramebuffersEXT not found.\n");fflush(stdout); + ret= false; + } + glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)getGlFncPtr("glBindFramebufferEXT"); + if(!glBindFramebufferEXT) + { + fprintf (stdout,"glBindFramebufferEXT not found.\n");fflush(stdout); + ret= false; + } + glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)getGlFncPtr("glCheckFramebufferStatusEXT"); + if(!glCheckFramebufferStatusEXT) + { + fprintf (stdout,"glCheckFramebufferStatusEXT not found.\n");fflush(stdout); + ret= false; + } + glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)getGlFncPtr("glGetFramebufferAttachmentParameterivEXT"); + if(!glGetFramebufferAttachmentParameterivEXT) + { + fprintf (stdout,"glGetFramebufferAttachmentParameterivEXT not found.\n");fflush(stdout); + ret= false; + } + glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)getGlFncPtr("glFramebufferTexture2DEXT"); + if(!glFramebufferTexture2DEXT) + { + fprintf (stdout,"glFramebufferTexture2DEXT not found.\n");fflush(stdout); + ret= false; + } + glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)getGlFncPtr("glFramebufferRenderbufferEXT"); + if(!glFramebufferRenderbufferEXT) + { + fprintf (stdout,"glFramebufferRenderbufferEXT not found.\n");fflush(stdout); + ret= false; + } + glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)getGlFncPtr("glGenRenderbuffersEXT"); + if(!glGenRenderbuffersEXT) + { + fprintf (stdout,"glGenRenderbuffersEXT not found.\n");fflush(stdout); + ret= false; + } + glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)getGlFncPtr("glDeleteRenderbuffersEXT"); + if(!glDeleteRenderbuffersEXT) + { + fprintf (stdout,"glDeleteRenderbuffersEXT not found.\n");fflush(stdout); + ret= false; + } + glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)getGlFncPtr("glBindRenderbufferEXT"); + if(!glBindRenderbufferEXT) + { + fprintf (stdout,"glBindRenderbufferEXT not found.\n");fflush(stdout); + ret= false; + } + glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)getGlFncPtr("glRenderbufferStorageEXT"); + if(!glRenderbufferStorageEXT) + { + fprintf (stdout,"glRenderbufferStorageEXT not found.\n");fflush(stdout); + ret= false; + } + glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)getGlFncPtr("glGetRenderbufferParameterivEXT"); + if(!glGetRenderbufferParameterivEXT) + { + fprintf (stdout,"glGetRenderbufferParameterivEXT not found.\n");fflush(stdout); + ret= false; + } + glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC)getGlFncPtr("glIsRenderbufferEXT"); + if(!glIsRenderbufferEXT) + { + fprintf (stdout,"glIsRenderbufferEXT not found.\n");fflush(stdout); + ret= false; + } + +#endif + + //////////// paltform dependent stufff ////////////// +#ifdef WIN32 + //wglShareLists = (PFWGLSHARELISTS) wglGetProcAddress("wglShareLists"); + //if(!wglShareLists) + //{ + // fprintf (stdout,"wglShareLists not found (error %i)\n",GetLastError());fflush(stdout); + // // ret= false; + //} +#ifdef USE_PBUFFER + wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC) getGlFncPtr("wglCreatePbufferARB"); + if(!wglCreatePbufferARB) + { + fprintf (stdout,"wglCreatePbufferARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) getGlFncPtr("wglChoosePixelFormatARB"); + if(!wglChoosePixelFormatARB) + { + fprintf (stdout,"wglChoosePixelFormatARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC) getGlFncPtr("wglGetPbufferDCARB"); + if(!wglGetPbufferDCARB) + { + fprintf (stdout,"wglGetPbufferDCARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC) getGlFncPtr("wglReleasePbufferDCARB"); + if(!wglReleasePbufferDCARB) + { + fprintf (stdout,"wglReleasePbufferDCARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERPROC) getGlFncPtr("wglDestroyPbufferARB"); + if(!wglDestroyPbufferARB) + { + fprintf (stdout,"wglDestroyPbufferARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglQueryPbufferARB = (PFNWGLQUERYPBUFFERPROC) getGlFncPtr("wglQueryPbufferARB"); + if(!wglQueryPbufferARB) + { + fprintf (stdout,"wglQueryPbufferARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRBVARBPROC) getGlFncPtr("wglGetPixelFormatAttribivARB"); + if(!wglGetPixelFormatAttribivARB) + { + fprintf (stdout,"wglGetPixelFormatAttribivARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC) getGlFncPtr("wglBindTexImageARB"); + if(!wglBindTexImageARB) + { + fprintf (stdout,"wglBindTexImageARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } + + wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARB) getGlFncPtr("wglReleaseTexImageARB"); + if(!wglReleaseTexImageARB) + { + fprintf (stdout,"wglReleaseTexImageARB not found (error %i)\n",GetLastError());fflush(stdout); + ret= false; + } +#endif +#endif //end of WIN32 + if(ret) + glFuncionsInited = true; + +#ifdef _WIN32 + assert(_CrtCheckMemory()); +#endif + return ret; +} + +void FreeGLFunctionPointers() +{ + glFuncionsInited = false; +} + + +////////////////////////////// GLSL /////////////////////////////////////////// + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslGenericProgram - generic glsl shader program | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +const float cDefLightXDir = 0.0f; +const float cDefLightYDir = -0.5f; +const float cDefLightZDir = -1.0f; + +glslGenericProgram::glslGenericProgram() +{ + //wglGetProcAddress("bla bla"); + + _vShader() = 0; + _fShader() = 0; + _program() = 0; + _numLights() = 0; + + //some default light dir + AddLight(); + MVector dir(cDefLightXDir, cDefLightYDir, cDefLightZDir); + //MVector pos(cDefLightXPos, cDefLightYPos, cDefLightZPos); + dir.normalize(); + SetLightDir(0,dir); + + MVector pos = -20.0f*dir; + SetLightPos(0,pos); + + s_vertexErrDumped = false; + s_fragErrDumped = false; + s_varErrDumped = false; + s_linkErrDumped= false; + +} + + +glslGenericProgram::~glslGenericProgram() +{ + if(vShader() && program()) + { + glDetachShader(program(),vShader()); + glDeleteShader(vShader()); + } + if(fShader() && program()) + { + glDetachShader(program(),fShader()); + glDeleteShader(fShader()); + } + if(program()) + { + glUseProgram(0); + glDeleteProgram(program()); + } + glFlush(); +} + +bool glslGenericProgram::LoadShaders(char* vShaderFile, char* fShaderFile) +{ + + _vShader() = glCreateShader(GL_VERTEX_SHADER); + if(!loadShader( vShader(), vShaderFile)) + { + MGlobal::displayError(MString("Shave GLSL: unable to load vertex shader file ") + MString(vShaderFile)); + return false; + } + + _fShader() = glCreateShader(GL_FRAGMENT_SHADER); + if(!loadShader( fShader(), fShaderFile)) + { + MGlobal::displayError(MString("Shave GLSL: unable to load fragment shader file ") + MString(fShaderFile)); + return false; + } + if(!compileShader(vShader(),eVertShader)) + { + MGlobal::displayError("Shave GLSL: can not compile vertex shader\n"); + return false; + } + if(!compileShader(fShader(),eFragShader)) + { + MGlobal::displayError("Shave GLSL: can not compile fragment shader\n"); + return false; + } + _program() = glCreateProgram(); + + glAttachShader(program(),vShader()); + glAttachShader(program(),fShader()); + + if(!linkProgram(program())) + { + MGlobal::displayError("Shave GLSL: can not link program\n"); + return false; + } + + return true; +} + +bool glslGenericProgram::LoadShaders() +{ + _vShader() = glCreateShader(GL_VERTEX_SHADER); + loadVertShader( vShader()); + + _fShader() = glCreateShader(GL_FRAGMENT_SHADER); + loadFragShader( fShader()); + + if(!compileShader(vShader(),eVertShader)) + { + MGlobal::displayError("Shave GLSL: can not compile vertex shader\n"); + return false; + } + if(!compileShader(fShader(),eFragShader)) + { + MGlobal::displayError("Shave GLSL: can not compile fragment shader\n"); + return false; + } + + _program() = glCreateProgram(); + + + glAttachShader(program(),vShader()); + glAttachShader(program(),fShader()); + + + if(!linkProgram(program())) + { + MGlobal::displayError("Shave GLSL: can not link program\n"); + return false; + } + + return true; +} + +void glslGenericProgram::UnLoadShaders() +{ + if(vShader() && program()) + { + glDetachShader(program(),vShader()); + glDeleteShader(vShader()); + } + if(fShader() && program()) + { + glDetachShader(program(),fShader()); + glDeleteShader(fShader()); + } + if(program()) + { + glUseProgram(0); + glDeleteProgram(program()); + } +} + +bool glslGenericProgram::IsVadlid() const +{ + GLint validStatus; + glValidateProgram(program()); + + glGetProgramiv(program(), GL_VALIDATE_STATUS, &validStatus); + return (validStatus == GL_TRUE); +} + +bool glslGenericProgram::UseProgram() +{ + if(!program() || !vShader() || !fShader()) + return false; + + GLint validStatus; + glValidateProgram(program()); + + glGetProgramiv(program(), GL_VALIDATE_STATUS, &validStatus); + if(validStatus != GL_TRUE) + { + MGlobal::displayError("Shave GLSL: program is not valid"); + + UnLoadShaders(); + LoadShaders(); + + //weird but we can not retrieve log + //char* infoLog; + //GLint infoLogLength; + //glGetProgramiv(program(), GL_INFO_LOG_LENGTH, &infoLogLength); + //if(infoLogLength) + //{ + // infoLog = (char*) malloc(infoLogLength); + + // glGetProgramInfoLog(program(), infoLogLength, NULL, infoLog); + + // MGlobal::displayInfo(MString("Info Log:\n\n") + MString(infoLog)); + + // free(infoLog); + //} + } + glUseProgram(program()); + + //if we do not share resouces between contexts it can happen that shader was + //compiled when different context was ative so it becomes unusable for current one. + //we need to recompile shaders in this case for active context -- dub|16-02-2009 + if(!setupUniformVariables()) + { + UnLoadShaders(); + LoadShaders(); + glUseProgram(program()); + setupUniformVariables(); + } + + return true; +} + +bool glslGenericProgram::UnUseProgram() +{ + glUseProgram(0); + + return true; +} + +bool glslGenericProgram::AddLight() +{ + if(numLights() < eMaxNumLights) + { + _numLights()++; + return true; + } + return false; +} + +bool glslGenericProgram::SetLightDir(int idx, float x, float y, float z) +{ + if(idx < numLights()) + { + _lightDir(idx).x = x; + _lightDir(idx).y = y; + _lightDir(idx).z = z; + + _lightDir(idx).normalize(); + + return true; + } + return false; +} + +bool glslGenericProgram::SetLightDir(int idx, const MVector &p) +{ + if(idx < numLights()) + { + + _lightDir(idx) = p; + _lightDir(idx).normalize(); + return true; + } + return false; +} + +bool glslGenericProgram::SetLightPos(int idx, float x, float y, float z) +{ + if(idx < numLights()) + { + _lightPos(idx).x = x; + _lightPos(idx).y = y; + _lightPos(idx).z = z; + + return true; + } + return false; +} + +bool glslGenericProgram::SetLightPos(int idx, const MVector &p) +{ + if(idx < numLights()) + { + _lightPos(idx) = p; + return true; + } + return false; +} + + +int glslGenericProgram::GetNumLigths() const +{ + return numLights(); +} + +const MVector& glslGenericProgram::GetLightDir(int idx) const +{ + return lightDir(idx); +} + + +const MVector& glslGenericProgram::GetLightPos(int idx) const +{ + return lightPos(idx); +} +/* +| protected methods +*/ + +bool glslGenericProgram::loadShader( GLuint shader, char* fn) +{ + FILE *fp; + GLubyte *buf; + GLint length; + bool ret = true; + + if (!(fp = fopen(fn,"rb"))) + { + return false; + } + + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buf = new GLubyte[length+1]; + + fread( buf, 1, length, fp); + buf[length] = '\0'; // make it a regular C string so str* functions work + + glShaderSource( shader, 1, (const char**)&buf, &length); + + if (glGetError() != 0) + { + ret = false; + } + + fclose(fp); + + delete []buf; + + return ret; +} + + +bool glslGenericProgram::compileShader(GLuint shader, int vert_or_frag) +{ + char *infoLog; + GLint infoLogLength = 0; + GLint compileStatus; + + glCompileShader(shader); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); + glGetShaderiv(shader, GL_INFO_LOG_LENGTH,&infoLogLength); + + if(compileStatus == GL_TRUE) + return true; + + + if((vert_or_frag == eVertShader && !s_vertexErrDumped) || + (vert_or_frag == eFragShader && !s_fragErrDumped)) + { + MGlobal::displayInfo(MString("Shave GLSL: Compile status: ") + compileStatus + "\n\n"); + + if (infoLogLength) + { + infoLog = (char*)malloc(infoLogLength); + + glGetShaderInfoLog(shader, infoLogLength, NULL, infoLog); + + MGlobal::displayInfo(MString("Info Log:\n\n") + MString(infoLog)); + + free(infoLog); + } + if(vert_or_frag == eVertShader) + s_vertexErrDumped = true; + + if(vert_or_frag == eFragShader) + s_fragErrDumped = true; + } + + return compileStatus != 0; +} + + + +bool glslGenericProgram::linkProgram(GLuint glprog) +{ + char* infoLog; + GLint infoLogLength = 0; + GLint linkStatus; + + glLinkProgram(glprog); + + glGetProgramiv(glprog, GL_LINK_STATUS, &linkStatus); + glGetProgramiv(glprog, GL_INFO_LOG_LENGTH, &infoLogLength); + + if(linkStatus == GL_TRUE) + return true; + + if(!s_linkErrDumped) + { + MGlobal::displayInfo(MString("Shave GLSL: Link status: ") + linkStatus + "\n\n"); + + if (infoLogLength) + { + infoLog = (char*) malloc(infoLogLength); + + glGetProgramInfoLog(glprog, infoLogLength, NULL, infoLog); + + MGlobal::displayInfo(MString("Info Log:\n\n") + MString(infoLog)); + + free(infoLog); + } + s_linkErrDumped = true; + } + return linkStatus != 0; +} + + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslHairProgram - default glsl shader program | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +glslHairProgram::glslHairProgram() +{ + _tangentLoc() = -1; + _specTint() = MColor(1.0f, 1.0f, 1.0f); + _specTint2() = MColor(0.0f, 0.0f, 0.0f); + _ambdiff() = 0.4f; + _specular() = 0.76f; + _specAmt() = 1.0f; + _useSSAO() = false; +} + +glslHairProgram::~glslHairProgram() +{ +} + +bool glslHairProgram::setupUniformVariables() +{ + //if(numLights() > 0) + { + GLint location = glGetUniformLocation(program(), "lightDir"); + + if(location != -1) + { + _lightDir(0).normalize(); + glUniform3f(location, (GLfloat)lightDir(0).x, (GLfloat)lightDir(0).y, (GLfloat)lightDir(0).z); + } + else + { + MGlobal::displayError("default - uniform variable 'lightDir' not found."); + + char* infoLog; + GLint infoLogLength = 0; + glGetProgramiv(program(), GL_INFO_LOG_LENGTH, &infoLogLength); + if(infoLogLength) + { + infoLog = (char*) malloc(infoLogLength); + + glGetProgramInfoLog(program(), infoLogLength, NULL, infoLog); + + MGlobal::displayInfo(MString("Info Log:\n\n") + MString(infoLog)); + + free(infoLog); + } + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"ambdiff"); + if(location != -1) + glUniform1f(location, ambdiff()); + else + { + MGlobal::displayError("uniform variable 'ambdiff' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"specular"); + if(location != -1) + glUniform1f(location, specular()/**0.1f*/); + else + { + MGlobal::displayError("uniform variable 'specular' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"specAmt"); + if(location != -1) + glUniform1f(location, specAmt()); + else + { + MGlobal::displayError("uniform variable 'specAmt' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"specTint"); + if(location != -1) + glUniform3f(location, specTint().r,specTint().g,specTint().b); + else + { + MGlobal::displayError("uniform variable 'specTint' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"specTint2"); + if(location != -1) + glUniform3f(location, specTint2().r,specTint2().g,specTint2().b); + else + { + MGlobal::displayError("uniform variable 'specTint2' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"useSsao"); + if(location != -1) + glUniform1i(location, useSSAO()); + else + { + MGlobal::displayError("uniform variable 'useSsao' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"viewWidth"); + if(location != -1) + glUniform1f(location, viewWidth()); + else + { + MGlobal::displayError("uniform variable 'viewWidth' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"viewHeight"); + if(location != -1) + glUniform1f(location, viewHeight()); + else + { + MGlobal::displayError("uniform variable 'viewHeight' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"useLights"); + if(location != -1) + glUniform1i(location, useLights() ? 1:0); + else + { + MGlobal::displayError("uniform variable 'useLights' not found."); + return false; + } + } + { + GLint location = glGetUniformLocation(program(),"numLights"); + if(location != -1) + glUniform1i(location, numLights()); + else + { + MGlobal::displayError("uniform variable 'numLights' not found."); + return false; + } + } + _tangentLoc() = glGetAttribLocation(program(), "tangent"); + if(tangentLoc() == -1) + { + MGlobal::displayError("attribute variable 'tangent' not found\n"); + return false; + } + + return true; +} + +void glslHairProgram::loadVertShader(GLuint shader) //loads internal vert shader +{ + char* buf = + "varying vec3 normal;\n\ + varying vec4 vert;\n\ + varying vec4 color;\n\ + varying vec3 tang;\n\ + varying vec3 view;\n\ + attribute vec3 tangent;\n\ + void main(){\n\ + color = gl_Color;\n\ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\ + normal = normalize(gl_NormalMatrix * gl_Normal);\n\ + vert = gl_ModelViewMatrix * vec4(gl_Vertex.xyz,1.0);\n\ + tang = gl_NormalMatrix * normalize(tangent);\n\ + view = -normalize(vert.xyz);\n\ + }\n\ + \0"; + + GLint length = (int)strlen(buf); + glShaderSource( shader, 1, (const char**)&buf, &length); + + +} + +void glslHairProgram::loadFragShader(GLuint shader) //loads internal frag shader +{ + char* buf = + "uniform vec3 lightDir;\n\ + uniform vec3 specTint;\n\ + uniform vec3 specTint2;\n\ + uniform float ambdiff;\n\ + uniform float specular;\n\ + uniform float specAmt;\n\ + uniform int useSsao;\n\ + uniform float viewWidth;\n\ + uniform float viewHeight;\n\ + uniform int useLights;\n\ + uniform int numLights;\n\ + uniform sampler2D ssaoTex;\n\ + varying vec3 normal;\n\ + varying vec4 vert;\n\ + varying vec4 color;\n\ + varying vec3 tang;\n\ + varying vec3 view;\n\ + float blur8x8flat(vec2 st, float d, sampler2D sampler){\n\ + float x = 0.0;\n\ + float y = 0.0;\n\ + float du = 0.0;\n\ + float dv = 0.0;\n\ + float s = 0.0;\n\ + for(y = -4.0; y < 4.0; y++)\n\ + {\n\ + du = y*d;\n\ + for(x = -4.0; x < 4.0; x++)\n\ + {\n\ + dv = x*d;\n\ + float r = texture2D(sampler, vec2(st.x + du, st.y + dv) ).r;\n\ + s += r;\n\ + }\n\ + }\n\ + s = s/64.0;\n\ + return s;\n\ + }\n\ + void main(){\n\ + vec3 D = vec3(0.0,0.0,0.0);\n\ + vec3 S = vec3(0.0,0.0,0.0);\n\ + vec3 S2 = vec3(0.0,0.0,0.0);\n\ + //vec3 N = normalize(normal);\n\ + //float D = max(0.0, dot(N,-lightDir));\n\ + if(useLights==1){\n\ + for(int i=0;i<numLights;i++){\n\ + vec3 ldir0 = gl_LightSource[i].position.xyz - vert.xyz;\n\ + float dist = length(ldir0);\n\ + vec3 ldir = ldir0/dist;\n\ + //direction light\n\ + if(gl_LightSource[i].position.w == 0.0){\n\ + ldir = gl_LightSource[i].position.xyz;//halfVector.xyz;\n\ + }\n\ + //diffuse \n\ + float diff = 0.3;\n\ + float ltdot = dot(ldir, tang);\n\ + float tsq = ltdot*ltdot;\n\ + float shd2 = sqrt(1.0 - tsq);\n\ + float shd = max(shd2, 0.0);\n\ + shd *= ambdiff;\n\ + shd += (1.0 - ambdiff);\n\ + D += shd*gl_LightSource[i].diffuse.rgb;\n\ + //specular \n\ + float K = 1.0/(3.0*(0.101 - specular));\n\ + //float kspec = 0.076;\n\ + float vtdot = dot(view, tang);\n\ + float sqr_vdot2 = sqrt(1.0 - vtdot*vtdot);\n\ + float sp = max(shd2*sqr_vdot2 - ltdot*vtdot, 0.0);\n\ + float spec = max(pow(sp, K), 0.0);\n\ + S += spec*gl_LightSource[i].specular.rgb;\n\ + //secondary specular\n\ + vec3 ldir2 = normalize(ldir*0.7+view*0.3);\n\ + float ltdot2 = dot(ldir2, tang);\n\ + float sp2 = max(shd2*sqr_vdot2 - ltdot2*vtdot, 0.0);\n\ + float spec2 = max(pow(sp2, K), 0.0);\n\ + S2 += spec2*gl_LightSource[i].specular.rgb;\n\ + //spotlight\n\ + if(gl_LightSource[i].spotCutoff != 180.0){\n\ + float spotEffect = dot(normalize(gl_LightSource[i].spotDirection), -ldir);\n\ + if (spotEffect > gl_LightSource[i].spotCosCutoff){\n\ + float spot = pow(spotEffect, gl_LightSource[i].spotExponent);\n\ + D *= spot;\n\ + S *= spot;\n\ + S2 *= spot;\n\ + }else{\n\ + D = vec3(0.0,0.0,0.0); S = vec3(0.0,0.0,0.0); S2 = vec3(0.0,0.0,0.0);\n\ + }\n\ + }\n\ + //attenuation\n\ + if(gl_LightSource[i].position.w != 0.0 ){\n\ + float decay = 1.0 / (gl_LightSource[i].constantAttenuation + \n\ + gl_LightSource[i].linearAttenuation * dist + \n\ + gl_LightSource[i].quadraticAttenuation * dist * dist);\n\ + D *= decay;\n\ + S *= decay;\n\ + S2 *= decay;\n\ + }\n\ + }\n\ + }else{\n\ + vec3 ldir = -lightDir;\n\ + //diffuse \n\ + float diff = 0.3;\n\ + float ltdot = dot(ldir, tang);\n\ + float tsq = ltdot*ltdot;\n\ + float shd2 = sqrt(1.0 - tsq);\n\ + float shd = max(shd2, 0.0);\n\ + shd *= ambdiff;\n\ + shd += (1.0 - ambdiff);\n\ + //D += shd*gl_LightSource[i].diffuse.rgb;\n\ + D += shd;\n\ + //specular \n\ + float K = 1.0/(3.0*(0.101 - specular));\n\ + //float kspec = 0.076;\n\ + float vtdot = dot(view, tang);\n\ + float sqr_vdot2 = sqrt(1.0 - vtdot*vtdot);\n\ + float sp = max(shd2*sqr_vdot2 - ltdot*vtdot, 0.0);\n\ + float spec = max(pow(sp, K), 0.0);\n\ + //S += spec*gl_LightSource[i].specular.rgb;\n\ + S += spec*vec3(1.0,1.0,1.0);\n\ + //secondary specular\n\ + vec3 ldir2 = normalize(ldir*0.7+view*0.3);\n\ + float ltdot2 = dot(ldir2, tang);\n\ + float sp2 = max(shd2*sqr_vdot2 - ltdot2*vtdot, 0.0);\n\ + float spec2 = max(pow(sp2, K), 0.0);\n\ + //S2 += spec2*gl_LightSource[i].specular.rgb;\n\ + S2 += spec2*vec3(1.0,1.0,1.0);\n\ + }\n\ + if(D.r > 1.0) D.r = 1.0;\n\ + if(D.g > 1.0) D.g = 1.0;\n\ + if(D.b > 1.0) D.b = 1.0;\n\ + if(S.r > 1.0) S.r = 1.0;\n\ + if(S.g > 1.0) S.g = 1.0;\n\ + if(S.b > 1.0) S.b = 1.0;\n\ + if(S2.r > 1.0) S2.r = 1.0;\n\ + if(S2.g > 1.0) S2.g = 1.0;\n\ + if(S2.b > 1.0) S2.b = 1.0;\n\ + //SSAO\n\ + float ssao = 1.0;\n\ + if(useSsao != 0){\n\ + vec2 uv = gl_FragCoord.xy;\n\ + uv.x /= viewWidth; \n\ + uv.y /= viewHeight; \n\ + ssao = 0.25 + 0.75*blur8x8flat(uv,0.001, ssaoTex);\n\ + ssao *= 1.15;\n\ + if (ssao>1.0) ssao=1.0;\n\ + }\n\ + gl_FragColor.xyz = (D*ssao)*color.xyz + specAmt*(S*specTint + S2*specTint2);\n\ + gl_FragColor.a = color.a;\n\ + }"; + + GLint length = (int)strlen(buf); + glShaderSource( shader, 1, (const char**)&buf, &length); + +} + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslSSAOdepthShader - for ssao pass 1) | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +bool glslSSAOdepthShader::setupUniformVariables() +{ + bool ok = true; + GLint location; + + + location = glGetUniformLocation(program(), "far"); + if(location != -1) + glUniform1f(location, zfar()); + else + { + ok &= false; + if(!s_varErrDumped) + MGlobal::displayError("uniform variable 'far' not found."); + } + location = glGetUniformLocation(program(), "near"); + if(location != -1) + glUniform1f(location, znear()); + else + { + ok &= false; + if(!s_varErrDumped) + MGlobal::displayError("uniform variable 'near' not found."); + } + + return ok; +} + +void glslSSAOdepthShader::loadVertShader(GLuint shader) //loads internal vert shader +{ + char* buf = "\n\ + uniform float far; \n\ + uniform float near; \n\ + varying float depth; // in eye space\n\ + void main( void ){\n\ + vec4 viewPos = gl_ModelViewMatrix * gl_Vertex;\n\ + depth = (-viewPos.z - near)/(far - near); \n\ + gl_Position = ftransform();\n\ + }\n\0"; + + GLint length = (int)strlen(buf); + glShaderSource( shader, 1, (const char**)&buf, &length); +} + +void glslSSAOdepthShader::loadFragShader(GLuint shader) //loads internal frag shader +{ + char* buf = "\n\ + varying float depth;\n\ + void main( void )\n\ + {\n\ + float R = depth*255.0;\n\ + int Ri = int(R); \n\ + R = float(Ri);\n\ + float G = depth*255.0 - R;\n\ + R /= 255.0;\n\ + gl_FragColor = vec4(R,G,0.0,depth);\n\ + }\n\0"; + + GLint length = (int)strlen(buf); + glShaderSource( shader, 1, (const char**)&buf, &length); +} + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslSSAOshader - (pass 2) shader used to render map used for SSAO | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +glslSSAOshader::glslSSAOshader() +{ + _radius() = 0.03f; + _samples() = 20; +} + +bool glslSSAOshader::setupUniformVariables() +{ + bool ok = true; + GLint location; + + location = glGetUniformLocation(program(), "rad"); + if(location != -1) + glUniform1f(location, radius()); + else + { + ok &= false; + if(!s_varErrDumped) + MGlobal::displayError("uniform variable 'rad' not found."); + } + + location = glGetUniformLocation(program(), "samples"); + if(location != -1) + glUniform1i(location, samples()); + else + { + ok &= false; + if(!s_varErrDumped) + MGlobal::displayError("uniform variable 'samples' not found."); + } + + //location = glGetUniformLocation(program(), "rnm"); + //if(location != -1) + // glUniform1i(location, 0); + //else + //{ + // ok &= false; + // if(!s_varErrDumped) + // MGlobal::displayError("uniform variable 'rnm' not found."); + //} + + location = glGetUniformLocation(program(), "normalMap"); + if(location != -1) + glUniform1i(location, 1); + else + { + ok &= false; + if(!s_varErrDumped) + MGlobal::displayError("uniform variable 'normalMap' not found."); + } + return ok; +} + +void glslSSAOshader::loadVertShader(GLuint shader) //loads internal vert shader +{ + char* buf = "\n\ + varying vec2 uv; \n\ + void main(void) \n\ + { \n\ + gl_Position = ftransform(); \n\ + gl_Position = sign( gl_Position ); \n\ + // Texture coordinate for screen aligned (in correct range): \n\ + //uv = (vec2( gl_Position.x, - gl_Position.y ) + vec2( 1.0 ) ) * 0.5; \n\ + uv = (vec2( gl_Position.x, gl_Position.y ) + vec2( 1.0 ) ) * 0.5; \n\ + }\n\0"; + + GLint length = (int)strlen(buf); + glShaderSource( shader, 1, (const char**)&buf, &length); +} + +void glslSSAOshader::loadFragShader(GLuint shader) //loads internal frag shader +{ + char* buf = "\n\ + uniform float rad; ///0.006; \n\ + uniform sampler2D rnm; \n\ + uniform sampler2D normalMap; \n\ + uniform int samples;\n\ + \n\ + varying vec2 uv; \n\ + void main(void) \n\ + { \n\ + int x = 0;\n\ + int y = 0;\n\ + int half_size = samples/2;\n\ + float du = 0.0;\n\ + float dv = 0.0;\n\ + int nsamp = 0;\n\ + int close = 0;\n\ + vec4 vd = texture2D(normalMap, uv ); //line 17\n\ + if(vd.a == 0.0)\n\ + {\n\ + gl_FragColor.r = 0.1;\n\ + return; \n\ + }\n\ + float D = vd.r + vd.g/255.0; \n\ + D -= 0.01; // out of loop \n\ + for(y = -half_size; y < half_size; y++)\n\ + {\n\ + du = float(y)*rad;\n\ + for(x = -half_size; x < half_size; x++)\n\ + {\n\ + dv = float(x)*rad;\n\ + vec4 zv = texture2D(normalMap, vec2(uv.x + du, uv.y + dv) ); \n\ + if(zv.a != 0.0)\n\ + {\n\ + float z = zv.r + zv.g/255.0;\n\ + nsamp++;\n\ + if(D < z) \n\ + close++;\n\ + }\n\ + }\n\ + }\n\ + float ssao = 1.0;\n\ + if(nsamp != 0) ssao = float(close)/float(nsamp);\n\ + //dither \n\ + gl_FragColor.r = ssao; \n\ + \n\ + }\n\0"; + + GLint length = (int)strlen(buf); + glShaderSource( shader, 1, (const char**)&buf, &length); +} + + + +/////////////////////////// SSAO ///////////////////////////////////////////// + +//utilities + +std::string convertInternalFormatToString(GLenum format) +{ + std::string formatName; + + switch(format) + { + case GL_STENCIL_INDEX: + formatName = "GL_STENCIL_INDEX"; + break; + case GL_DEPTH_COMPONENT: + formatName = "GL_DEPTH_COMPONENT"; + break; + case GL_ALPHA: + formatName = "GL_ALPHA"; + break; + case GL_RGB: + formatName = "GL_RGB"; + break; + case GL_RGBA: + formatName = "GL_RGBA"; + break; + case GL_LUMINANCE: + formatName = "GL_LUMINANCE"; + break; + case GL_LUMINANCE_ALPHA: + formatName = "GL_LUMINANCE_ALPHA"; + break; + case GL_ALPHA4: + formatName = "GL_ALPHA4"; + break; + case GL_ALPHA8: + formatName = "GL_ALPHA8"; + break; + case GL_ALPHA12: + formatName = "GL_ALPHA12"; + break; + case GL_ALPHA16: + formatName = "GL_ALPHA16"; + break; + case GL_LUMINANCE4: + formatName = "GL_LUMINANCE4"; + break; + case GL_LUMINANCE8: + formatName = "GL_LUMINANCE8"; + break; + case GL_LUMINANCE12: + formatName = "GL_LUMINANCE12"; + break; + case GL_LUMINANCE16: + formatName = "GL_LUMINANCE16"; + break; + case GL_LUMINANCE4_ALPHA4: + formatName = "GL_LUMINANCE4_ALPHA4"; + break; + case GL_LUMINANCE6_ALPHA2: + formatName = "GL_LUMINANCE6_ALPHA2"; + break; + case GL_LUMINANCE8_ALPHA8: + formatName = "GL_LUMINANCE8_ALPHA8"; + break; + case GL_LUMINANCE12_ALPHA4: + formatName = "GL_LUMINANCE12_ALPHA4"; + break; + case GL_LUMINANCE12_ALPHA12: + formatName = "GL_LUMINANCE12_ALPHA12"; + break; + case GL_LUMINANCE16_ALPHA16: + formatName = "GL_LUMINANCE16_ALPHA16"; + break; + case GL_INTENSITY: + formatName = "GL_INTENSITY"; + break; + case GL_INTENSITY4: + formatName = "GL_INTENSITY4"; + break; + case GL_INTENSITY8: + formatName = "GL_INTENSITY8"; + break; + case GL_INTENSITY12: + formatName = "GL_INTENSITY12"; + break; + case GL_INTENSITY16: + formatName = "GL_INTENSITY16"; + break; + case GL_R3_G3_B2: + formatName = "GL_R3_G3_B2"; + break; + case GL_RGB4: + formatName = "GL_RGB4"; + break; + case GL_RGB5: + formatName = "GL_RGB4"; + break; + case GL_RGB8: + formatName = "GL_RGB8"; + break; + case GL_RGB10: + formatName = "GL_RGB10"; + break; + case GL_RGB12: + formatName = "GL_RGB12"; + break; + case GL_RGB16: + formatName = "GL_RGB16"; + break; + case GL_RGBA2: + formatName = "GL_RGBA2"; + break; + case GL_RGBA4: + formatName = "GL_RGBA4"; + break; + case GL_RGB5_A1: + formatName = "GL_RGB5_A1"; + break; + case GL_RGBA8: + formatName = "GL_RGBA8"; + break; + case GL_RGB10_A2: + formatName = "GL_RGB10_A2"; + break; + case GL_RGBA12: + formatName = "GL_RGBA12"; + break; + case GL_RGBA16: + formatName = "GL_RGBA16"; + break; + default: + formatName = "Unknown Format"; + } + + return formatName; +} + + +std::string getTextureParameters(GLuint id) +{ + if(glIsTexture(id) == GL_FALSE) + return "Not texture object"; + + GLint width, height, format; + std::string formatName; + glBindTexture(GL_TEXTURE_2D, id); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); // get texture width + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); // get texture height + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); // get texture internal format + glBindTexture(GL_TEXTURE_2D, 0); + + formatName = convertInternalFormatToString(format); + + std::stringstream ss; + ss << width << "x" << height << ", " << formatName; + return ss.str(); +} + +std::string getRenderbufferParameters(GLuint id) +{ + if(glIsRenderbufferEXT(id) == GL_FALSE) + return "Not Renderbuffer object"; + + GLint width, height, format; + std::string formatName; + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id); + glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_WIDTH_EXT, &width); // get renderbuffer width + glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_HEIGHT_EXT, &height); // get renderbuffer height + glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_INTERNAL_FORMAT_EXT, &format); // get renderbuffer internal format + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + formatName = convertInternalFormatToString(format); + + std::stringstream ss; + ss << width << "x" << height << ", " << formatName; + return ss.str(); +} + +void glhDumpFBOinfo() +{ + std::cout << "\n***** FBO STATUS *****\n"; + + // print max # of colorbuffers supported by FBO + GLint colorBufferCount = 0; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &colorBufferCount); + std::cout << "Max Number of Color Buffer Attachment Points: " << colorBufferCount << std::endl; + + GLint objectType; + GLint objectId; + + // print info of the colorbuffer attachable image + for(int i = 0; i < colorBufferCount; ++i) + { + glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT+i, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT, + &objectType); + if(objectType != GL_NONE) + { + glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT+i, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, + &objectId); + + std::string formatName; + + std::cout << "Color Attachment " << i << ": "; + if(objectType == GL_TEXTURE) + std::cout << "GL_TEXTURE, " << getTextureParameters(objectId) << std::endl; + else if(objectType == GL_RENDERBUFFER_EXT) + std::cout << "GL_RENDERBUFFER_EXT, " << getRenderbufferParameters(objectId) << std::endl; + } + } + + // print info of the depthbuffer attachable image + glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT, + &objectType); + if(objectType != GL_NONE) + { + glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, + &objectId); + + std::cout << "Depth Attachment: "; + switch(objectType) + { + case GL_TEXTURE: + std::cout << "GL_TEXTURE, " << getTextureParameters(objectId) << std::endl; + break; + case GL_RENDERBUFFER_EXT: + std::cout << "GL_RENDERBUFFER_EXT, " << getRenderbufferParameters(objectId) << std::endl; + break; + } + } + + // print info of the stencilbuffer attachable image + glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT, + &objectType); + if(objectType != GL_NONE) + { + glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, + &objectId); + + std::cout << "Stencil Attachment: "; + switch(objectType) + { + case GL_TEXTURE: + std::cout << "GL_TEXTURE, " << getTextureParameters(objectId) << std::endl; + break; + case GL_RENDERBUFFER_EXT: + std::cout << "GL_RENDERBUFFER_EXT, " << getRenderbufferParameters(objectId) << std::endl; + break; + } + } + + std::cout << std::endl; +} + +bool glhCheckFBOstatus() +{ + // check FBO status + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + std::cout << "Framebuffer complete." << std::endl; + return true; + + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl; + return false; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + std::cout << "[ERROR] Unsupported by FBO implementation." << std::endl; + return false; + + default: + std::cout << "[ERROR] Unknow FBO error." << std::endl; + return false; + } +} + + + +//FBO +ssaoFBO::ssaoFBO(unsigned int w, unsigned int h) +{ + _texid() = 0; + _fboid() = 0; + _rboid() = 0; + _width() = w; + _height() = h; + + _origView() = new GLint[4]; + + _initOK() = false; + + //Init(); +} + +ssaoFBO::~ssaoFBO() +{ + delete [] origView(); +} + +void ssaoFBO::Init() +{ + // create a texture object + glGenTextures(1, &_texid()); + glBindTexture(GL_TEXTURE_2D, texid()); +#ifdef OSMac_ + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NONE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NONE); +#else + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#endif + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width(), height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + // create a framebuffer object, you need to delete them when program exits. + glGenFramebuffersEXT(1, &_fboid()); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboid()); + + // create a renderbuffer object to store depth info + // NOTE: A depth renderable image should be attached the FBO for depth test. + // If we don't attach a depth renderable image to the FBO, then + // the rendering output will be corrupted because of missing depth test. + // If you also need stencil test for your rendering, then you must + // attach additional image to the stencil attachement point, too. + glGenRenderbuffersEXT(1, &_rboid()); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboid()); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width(), height()); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // attach a texture to FBO color attachement point + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texid(), 0); + // attach a renderbuffer to depth attachment point + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboid()); + + + // check FBO status + //glhDumpFBOinfo(); + bool status = glhCheckFBOstatus(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + _initOK() = true; +} + + +void ssaoFBO::Destroy() +{ + if(texid()) + { + glDeleteTextures(1, &_texid()); + _texid() = 0; + } + // clean up FBO, RBO + if(fboid()) + { + glDeleteFramebuffersEXT(1, &_fboid()); + _fboid() = 0; + } + if(fboid()) + { + glDeleteRenderbuffersEXT(1, &_rboid()); + _rboid() = 0; + } +} + +bool ssaoFBO::MakeCurrent() +{ + // grab original matrices + glGetFloatv(GL_PROJECTION_MATRIX, _cameraProjectionMatrix()); + glGetFloatv(GL_MODELVIEW_MATRIX, _cameraViewMatrix()); + glGetIntegerv(GL_VIEWPORT, _origView()); + + glViewport(0, 0, width(), height()); //is it correct size for normal/depth map + + //Setup view + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(cameraProjectionMatrix()); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboid()); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(cameraViewMatrix()); + + //Set parmeters + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glPixelTransferf(GL_DEPTH_SCALE,1.0f); + glPixelTransferf(GL_DEPTH_BIAS, 0.0f); + + glShadeModel(GL_SMOOTH); + glColorMask(1, 1, 1, 1); + glDepthMask( GL_TRUE ); + + + glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); + + return true; +} + +bool ssaoFBO::ResetCurrent() +{ + ///////////////// + //glFlush(); + //////////////// + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // unbind + + ////////////////// + //glBindRenderbufferEXT(GL_FRAMEBUFFER_EXT, 0); // unbind + //////////////// + + glViewport(origView()[0],origView()[1],origView()[2],origView()[3]); + glShadeModel(GL_SMOOTH); + glColorMask(1, 1, 1, 1); + + //restore original matrices + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(cameraProjectionMatrix()); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(cameraViewMatrix()); + + return true; +} + +bool ssaoFBO::ActivateTexture(int samplerIdx) +{ + if(texid()) + { + glActiveTexture(GL_TEXTURE0 + samplerIdx); + glBindTexture(GL_TEXTURE_2D, texid()); +#ifdef OSMac_ + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NONE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NONE); +#else + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#endif + glMatrixMode(GL_TEXTURE); + glLoadIdentity (); + +#if 0 + ///////////////////// debug ssao depth ///////////////// + MImage mimage; + MStatus stat = mimage.create(width(),height(),4,MImage::kByte); + unsigned char* pb = mimage.pixels(); + unsigned char* p = (unsigned char*)malloc(width()*height()*4); + glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,p); + if(stat == MStatus::kSuccess) + { + int idx = 0; + for(unsigned int i = 0; i < width(); i++) + for(unsigned int j=0; j < height(); j++) + { + pb[idx] = (unsigned char)(p[idx]); + pb[idx+1] = (unsigned char)(p[idx+1]); + pb[idx+2] = (unsigned char)(p[idx+2]); + pb[idx+3] = (unsigned char)(p[idx+3]); + + idx+=4; + + } + + } + free(p); + MString mdir; + MGlobal::executeCommand("internalVar -userBitmapsDir",mdir); + MString path = mdir + samplerIdx + MString(".jpg"); + MGlobal::displayInfo(path); + mimage.writeToFile(path,"jpg"); + ///////////////// end debug //////////////////////// +#endif + + return true; + } + return false; +} + + + +////////////////////// LEGACY DISPLAY STUFF ////////////////////////////////// + +bool shaveHairUI::mBrushIsActive = false; + +static bool glslInited = false; +static glslHairProgram hairGlsl; +static glslSSAOdepthShader ssaodepthGlsl; +static glslSSAOshader ssaoGlsl; + +ssaoFBO ssaodepth; +ssaoFBO ssao; + + +bool shaveHairUI::InitGlsl() +{ + bool glIsOK = true; + if(!GLFunctionsInited()) + glIsOK = InitGLFunctionPointers(); + + if(glIsOK && !glslInited) + { + if(hairGlsl.LoadShaders() + && ssaodepthGlsl.LoadShaders() + && ssaoGlsl.LoadShaders()) + { + ssaodepth.Init(); + ssao.Init(); + glslInited = true; + } + } + return glslInited; +} + +void shaveHairUI::getDrawRequests( + const MDrawInfo& drawInfo, bool noActiveComponents, MDrawRequestQueue& queue +) +{ +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairUI::getDrawRequests", NULL); +#endif + + // If the object is invisible or intermediate, don't draw anything. + // + M3dView::DisplayStatus dStatus = drawInfo.displayStatus(); + M3dView::DisplayStyle dStyle = drawInfo.displayStyle(); + + if ((dStatus == M3dView::kInvisible) + || (dStatus == M3dView::kIntermediateObject)) + { + return; + } + + // Maya does not necessarily call us with a displayStatus of kInvisible + // when we're invisible. We need to do a more exhaustive check + // ourselves. + // + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + MObject shaveHairNode = nodePtr->thisMObject(); + MDagPath path; + MDagPath::getAPathTo(shaveHairNode, path); + + if (!areObjectAndParentsVisible(path, false, true, true)) return; + + // Similarly, Maya does not necessarily call us with a displayStatus of + // kTemplate when our template attribute is true, so we have to check + // that ourselves. + // + bool isTemplated; + + MPlug plug(shaveHairNode, shaveHairShape::isTemplated); + plug.getValue(isTemplated); + + if (isTemplated) dStatus = M3dView::kTemplate; + + // + // If we're displaying a bounding box, then that's all we will be + // displaying. + // + MDrawRequest request; + M3dView view = drawInfo.view(); + + if (dStyle == M3dView::kBoundingBox) + { + request = drawInfo.getPrototype(*this); + request.setToken(kDrawBBox); + setWireframeColor(request, view, dStatus); + queue.add(request); + } + else + { + // Are we displaying hairs? + // + shaveHairShape* shave = dynamic_cast<shaveHairShape*>(surfaceShape()); + if (shave->isHairDisplayEnabled()) + { + short hairDisplayMode; + + plug.setAttribute(shaveHairShape::dspyMode); + plug.getValue(hairDisplayMode); + + if (hairDisplayMode == shaveHairShape::kHairDisplayHair) + { + request = drawInfo.getPrototype(*this); + + ///////////// incremental update trick /////////////// + //numDisplayHairs = shave-> + ////////////////////////////////////////////////////// + + switch (dStyle) + { + case M3dView::kWireFrame: + request.setToken(kDrawHairs); + + // If the display status is kHilite then we are in + // component selection mode, in which case we want to + // display the hair as dormant and let the guides + // handle all of the burden of selection. + // + if (dStatus == M3dView::kHilite) + { + request.setColor( + DORMANT_SURFACE_COLOR, M3dView::kDormantColors + ); + } + else if (dStatus == M3dView::kLead) + { + request.setColor( + LEAD_HAIR_COLOR, M3dView::kActiveColors + ); + } + else if (dStatus == M3dView::kActive) + { + request.setColor( + ACTIVE_HAIR_COLOR, M3dView::kActiveColors + ); + } + else + setWireframeColor(request, view, dStatus); + + queue.add(request); + break; + + case M3dView::kFlatShaded: + case M3dView::kGouraudShaded: + { + if(!doFallbackGlob) + request.setToken(kDrawActiveShadedHairs); + else + switch (dStatus) + { + case M3dView::kHilite: + case M3dView::kLead: + case M3dView::kActive: + request.setToken(kDrawActiveShadedHairs); + break; + + default: + request.setToken(kDrawInactiveShadedHairs); + break; + } + + queue.add(request); + } + break; + + default: + // No hair display. + break; + } + } +//#ifdef NEW_INSTNACE_DISPLAY + else if (hairDisplayMode == shaveHairShape::kHairDisplayGeom) + { + shaveGlobals::Globals globals; + shaveGlobals::getGlobals(globals); + + if(glisntGlob) + { + request = drawInfo.getPrototype(*this); + + switch (dStyle) + { + case M3dView::kWireFrame: + request.setToken(kDrawInstancedWire); + + // + // If the display status is kHilite then we are in + // component selection mode, in which case we want to + // display the hair as dormant and let the guides + // handle all of the burden of selection. + // + if (dStatus == M3dView::kHilite) + { + request.setColor( + DORMANT_SURFACE_COLOR, M3dView::kDormantColors + ); + } + else if (dStatus == M3dView::kLead) + { + request.setColor( + LEAD_HAIR_COLOR, M3dView::kActiveColors + ); + } + else if (dStatus == M3dView::kActive) + { + request.setColor( + ACTIVE_HAIR_COLOR, M3dView::kActiveColors + ); + } + else + setWireframeColor(request, view, dStatus); + + queue.add(request); + break; + + case M3dView::kFlatShaded: + case M3dView::kGouraudShaded: + { + switch (dStatus) + { + case M3dView::kHilite: + case M3dView::kLead: + case M3dView::kActive: + { + + if (dStatus == M3dView::kLead) + { + request.setToken(kDrawInstancedShadedOffset); + + MDrawRequest request2 = drawInfo.getPrototype(*this); + request2.setColor(LEAD_HAIR_COLOR, M3dView::kActiveColors); + request2.setToken(kDrawInstancedWire); + queue.add(request2); + } + else + request.setToken(kDrawInstancedShaded); + + } + break; + + default: + request.setToken(kDrawInstancedShaded); + break; + } + + queue.add(request); + } + break; + + default: + // No hair display. + break; + } + } + else //we want to update texures + { + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + nodePtr->updateTexLookups(); + + } + } +//#endif + } + + bool componentSelectionMode = (!noActiveComponents + && (dStatus == M3dView::kHilite)); + + if (componentSelectionMode) + { + // We always display guides in component selection mode. + // Inactive guides are displayed in the standard hilite color + // while active ones are display in our own active guide color. + // + request = drawInfo.getPrototype(*this); + request.setToken(kDrawInactiveGuides); + request.setColor( + DORMANT_GUIDE_COMPONENT_COLOR, M3dView::kDormantColors + ); + queue.add(request); + + request = drawInfo.getPrototype(*this); + request.setToken(kDrawActiveGuides); + request.setColor( + ACTIVE_GUIDE_COMPONENT_COLOR, M3dView::kActiveColors + ); + + queue.add(request); + + // Get the guide selection type. + // + MString selectType; + + MGlobal::executeCommand( + "optionVar -q shaveBrushSelectMode", selectType + ); + + // Use the selection type to determine which guide vertices to + // display. + // + int activeDrawToken = kDrawNone; + int inactiveDrawToken = kDrawNone; + + if (selectType == "root") + { + activeDrawToken = kDrawActiveRoots; + inactiveDrawToken = kDrawInactiveRoots; + } + else if (selectType == "tip") + { + activeDrawToken = kDrawActiveTips; + inactiveDrawToken = kDrawInactiveTips; + } + else if (selectType == "vert") + { + activeDrawToken = kDrawActiveVerts; + inactiveDrawToken = kDrawInactiveVerts; + } + + if (activeDrawToken != kDrawNone) + { + request = drawInfo.getPrototype(*this); + request.setToken(inactiveDrawToken); + + request.setColor( + DORMANT_VERTEX_COLOR, M3dView::kDormantColors + ); + + queue.add(request); + + request = drawInfo.getPrototype(*this); + request.setToken(activeDrawToken); + + request.setColor( + ACTIVE_VERTEX_COLOR, M3dView::kActiveColors + ); + + queue.add(request); + } + } + // In object mode we display guides if the user has explicitly + // requested that we do so, or if the object is selected and a + // brush is active. + // + else + { + //bool displayGuides; + //plug.setAttribute(shaveHairShape::aDisplayGuides); + //plug.getValue(displayGuides); + + bool displayGuides = nodePtr->getDisplayGuides(); + + bool selected = (dStatus == M3dView::kActive) + || (dStatus == M3dView::kLead); + + if (displayGuides || (mBrushIsActive && selected)) + { + request = drawInfo.getPrototype(*this); + setWireframeColor(request, view, dStatus); + request.setToken(kDrawGuides); + queue.add(request); + } + } + } +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairUI::getDrawRequests - done", NULL); +#endif +} + +/// dummy implementation now +void SHAVEpre_draw() {} +void SHAVEpost_draw() {} + +void shaveHairUI::draw(const MDrawRequest& request, M3dView& view) const +{ +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("shaveHairUI::draw", NULL); +#endif + + int drawToken = request.token(); + + //vlad|03Jun2010 -- update shave camera + SHAVEpre_draw(); + + static int MBAAQ[] = {1,3,8,15,23}; + + MDagPath camPath = shaveRender::getRenderCamPath(); + shaveRender::SceneInfo si; + si.shaveRenderPixels = NULL; + si.shaveZBuffer = NULL; + si.aa = MBAAQ[renderQualityGlob]; + si.currentFrame = (float)MAnimControl::currentTime().value(); + si.haveTracedLights = false; + si.height = shaveMaya::imageHeight; + si.shaveTraceInitialized= false; + si.width = shaveMaya::imageWidth; + + shaveRender::doCamera(camPath,shaveConstant::kShutterOpen,si); + shaveRender::doCamera(camPath,shaveConstant::kShutterClose,si); + + view.beginGL(); + + // + // We do all of our drawing in worldSpace, so set the current + // transformation to worldSpace. + // + MMatrix worldInverse = request.multiPath().inclusiveMatrixInverse(); + + glPushMatrix(); + glMultMatrixd((double *)worldInverse.matrix); + + switch (drawToken) + { + case kDrawBBox: + drawBoundingBox(); + break; + + case kDrawGuides: + case kDrawActiveGuides: + case kDrawInactiveGuides: + drawGuides(drawToken, view); + break; + + case kDrawHairs: + case kDrawActiveShadedHairs: + case kDrawInactiveShadedHairs: + drawHairs(drawToken, view); + break; +#ifdef NEW_INSTNACE_DISPLAY + case kDrawInstancedWire: + case kDrawInstancedShaded: + case kDrawInstancedShadedOffset: + drawInstances(drawToken, view); + break; +#endif + case kDrawActiveRoots: + case kDrawActiveTips: + case kDrawActiveVerts: + case kDrawInactiveRoots: + case kDrawInactiveTips: + case kDrawInactiveVerts: + drawVerts(drawToken); + break; + + default: + break; + } + + glPopMatrix(); + view.endGL(); + + //vlad|03Jun2010 -- update shave camera + SHAVEpost_draw(); +#ifdef DO_PROFILE + Profile::ProfileDump("shaveHairUI::draw - done", NULL); +#endif +} + + +bool shaveHairUI::select( + MSelectInfo &selectInfo, + MSelectionList &selectionList, + MPointArray &worldSpaceSelectPts +) const +{ + // If the node is not visible, return immediately. + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + MDagPath path; + MDagPath::getAPathTo(nodePtr->thisMObject(), path); + + if (!areObjectAndParentsVisible(path, false, true, true)) return false; + + bool alreadyTriedGuides = false; + bool somethingSelected = false; + bool componentMode = (selectInfo.displayStatus() == M3dView::kHilite); + + // + // Load the proper hair shape into the Shave engine. + // + nodePtr->makeCurrent(); + + // + // We do all of our drawing in worldSpace, so set the current + // transformation to worldSpace. + // + M3dView view = selectInfo.view(); + MMatrix worldInverse = selectInfo.multiPath().inclusiveMatrixInverse(); + + view.beginGL(); + glPushMatrix(); + glMultMatrixd((double *)worldInverse.matrix); + view.endGL(); + + // If we're in component mode then try to get a hit on individual + // vertices or guides. + // + if (componentMode) + { + MString selectMode; + + MGlobal::executeCommand( + "optionVar -q shaveBrushSelectMode", selectMode + ); + + if (selectMode == "guide") + { + somethingSelected = selectGuides( + selectInfo, + selectionList, + worldSpaceSelectPts + ); + alreadyTriedGuides = true; + } + else + { + somethingSelected = selectVerts( + selectMode, + selectInfo, + selectionList, + worldSpaceSelectPts + ); + } + } + + // If we didn't get any component hits then try for an overall hit on the + // object. + // + if (!somethingSelected) + { + // Get the hair and guide display settings. + // + //bool displayGuides; + + short hairDisplayMode; + MPlug plug(surfaceShape()->thisMObject(), shaveHairShape::dspyMode); + plug.getValue(hairDisplayMode); + + //plug.setAttribute(shaveHairShape::aDisplayGuides); + //plug.getValue(displayGuides); + bool displayGuides = nodePtr->getDisplayGuides(); + + // If guides are being displayed, and we didn't already check them + // during component testing, try for a hit on them first. + // + if (!alreadyTriedGuides && (componentMode || displayGuides)) + { + somethingSelected = selectObjByGuides( + selectInfo, + selectionList, + worldSpaceSelectPts + ); + } + + // If we didn't get a hit on guides and hairs are being displayed, + // then try them. + // + if (!somethingSelected) + { + if(hairDisplayMode == shaveHairShape::kHairDisplayHair) + { + somethingSelected = selectObjByHairs( + selectInfo, + selectionList, + worldSpaceSelectPts + ); + } + else if(hairDisplayMode == shaveHairShape::kHairDisplayGeom) + { + somethingSelected = selectObjByGeom( + selectInfo, + selectionList, + worldSpaceSelectPts + ); + } + } + } + + view.beginGL(); + glPopMatrix(); + view.endGL(); + + return somethingSelected; +} + + +void shaveHairUI::setBrushActive(bool isActive) +{ + mBrushIsActive = isActive; +} + + +//------------------------------------------------------------- +// +// Utility Methods +// +//------------------------------------------------------------- + +void shaveHairUI::drawBoundingBox() + const +{ + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + MBoundingBox bbox = nodePtr->boundingBoxTemp(); + MPoint bbmin = bbox.min(); + MPoint bbmax = bbox.max(); + + glBegin(GL_LINES); + glVertex3d(bbmin.x, bbmin.y, bbmin.z); + glVertex3d(bbmax.x, bbmin.y, bbmin.z); + + glVertex3d(bbmin.x, bbmin.y, bbmin.z); + glVertex3d(bbmin.x, bbmax.y, bbmin.z); + + glVertex3d(bbmin.x, bbmin.y, bbmin.z); + glVertex3d(bbmin.x, bbmin.y, bbmax.z); + + glVertex3d(bbmax.x, bbmax.y, bbmax.z); + glVertex3d(bbmin.x, bbmax.y, bbmax.z); + + glVertex3d(bbmax.x, bbmax.y, bbmax.z); + glVertex3d(bbmax.x, bbmin.y, bbmax.z); + + glVertex3d(bbmax.x, bbmax.y, bbmax.z); + glVertex3d(bbmax.x, bbmax.y, bbmin.z); + + glVertex3d(bbmax.x, bbmin.y, bbmin.z); + glVertex3d(bbmax.x, bbmax.y, bbmin.z); + + glVertex3d(bbmax.x, bbmin.y, bbmin.z); + glVertex3d(bbmax.x, bbmin.y, bbmax.z); + + glVertex3d(bbmin.x, bbmax.y, bbmin.z); + glVertex3d(bbmax.x, bbmax.y, bbmin.z); + + glVertex3d(bbmin.x, bbmax.y, bbmin.z); + glVertex3d(bbmin.x, bbmax.y, bbmax.z); + + glVertex3d(bbmin.x, bbmin.y, bbmax.z); + glVertex3d(bbmax.x, bbmin.y, bbmax.z); + + glVertex3d(bbmin.x, bbmin.y, bbmax.z); + glVertex3d(bbmin.x, bbmax.y, bbmax.z); + glEnd(); +} + + +void shaveHairUI::drawGuides(int drawToken, M3dView& view) const +{ +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump(" shaveHairUI::drawGuides", NULL); +#endif + + // Make sure that this shaveNode is loaded into the engine. + // + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + + + const shaveHairShape::Guides& guides = nodePtr->getGuides().guides; + + + float w = nodePtr->getGuideThinkness(); + + + // Draw the guides. + // + int i; + shaveHairShape::Guides::const_iterator iter; + + if(w == 0) + { + for (iter = guides.begin(); iter != guides.end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + + if (!guide.hidden) + { + // Does this guide have any selected verts? + // + bool isActive = (guide.select != 0); + + // Skip this guide if it does not meet our drawing + // requirements. + // + if (((drawToken == kDrawActiveGuides) && !isActive) + || ((drawToken == kDrawInactiveGuides) && isActive)) + { + continue; + } + + glBegin(GL_LINE_STRIP); + + for (i = 0; i < SHAVE_VERTS_PER_GUIDE; i++) + glVertex3f(guide.verts[i].x, guide.verts[i].y, guide.verts[i].z); + + glEnd(); + } + } + } + else + { + float ext = nodePtr->getGuideExt(); + if(ext == 0.0f)//compute once + { + //printf("compute bbox\n"); + //MBoundingBox bbox = nodePtr->boundingBoxTemp(); + MBoundingBox bbox; + bbox.clear(); + + for (iter = guides.begin(); iter != guides.end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + if (!guide.hidden) + { + MPoint v = guide.verts[SHAVE_VERTS_PER_GUIDE-1];//tip only + bbox.expand(v); + } + } + ext = (bbox.max() - bbox.min()).length(); + nodePtr->setGuideExt(ext); + } + w *= (4.0f*ext)/1500.0f; + + MVector viewDir( 0.0, 0.0, 1.0); + MDagPath camPath; + MStatus stat = MStatus::kSuccess; + stat = view.getCamera(camPath); + + if(stat == MStatus::kSuccess) + { + MFnCamera camFn(camPath); + viewDir = camFn.viewDirection( MSpace::kWorld ); + viewDir.normalize(); + } + + // + // Draw the guides. + // + int v; + for (iter = guides.begin(); iter != guides.end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + + if (!guide.hidden) + { + // + // Does this guide have any selected verts? + // + bool isActive = (guide.select != 0); + + // + // Skip this guide if it does not meet our drawing + // requirements. + // + if (((drawToken == kDrawActiveGuides) && !isActive) + || ((drawToken == kDrawInactiveGuides) && isActive)) + { + continue; + } + + glBegin(GL_QUADS); + + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + int numS = SHAVE_VERTS_PER_GUIDE-1; + + for (v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + if(v == 0) //root polygon + { + p0.x = guide.verts[0].x; + p0.y = guide.verts[0].y; + p0.z = guide.verts[0].z; + p1.x = guide.verts[1].x; + p1.y = guide.verts[1].y; + p1.z = guide.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + tangent1 = tangent0; + } + else if(vv == numS) //tip poly + { + p0 = p1; + p1.x = guide.verts[vv].x; + p1.y = guide.verts[vv].y; + p1.z = guide.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + } + else + { + p0 = p1; + p1.x = guide.verts[vv].x; + p1.y = guide.verts[vv].y; + p1.z = guide.verts[vv].z; + + tangent0 = tangent1; + if(vvv < SHAVE_VERTS_PER_GUIDE) + { + p2.x = guide.verts[vvv].x; + p2.y = guide.verts[vvv].y; + p2.z = guide.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + } + if(p0 == p1) + continue; + + + MVector d0 = tangent0^viewDir; + MVector d1 = tangent1^viewDir; + d0.normalize(); + d1.normalize(); + + MVector v0 = p0 + d0*(float)w; + MVector v1 = p0 - d0*(float)w; + MVector v2 = p1 - d1*(float)w; + MVector v3 = p1 + d1*(float)w; + + + + glVertex3d(v0.x, v0.y, v0.z); + glVertex3d(v1.x, v1.y, v1.z); + + glVertex3d(v2.x, v2.y, v2.z); + glVertex3d(v3.x, v3.y, v3.z); + + } + glEnd(); + } + } + } +#ifdef DO_PROFILE + Profile::ProfileDump(" shaveHairUI::drawGuides -- done", NULL); +#endif +} + +class Group { +public: + MIntArray pids; + MIntArray hids; +}; + +struct Poly { + + MVector t[4]; + MVector v[4]; + MColor c[4]; +}; + +class PolyGroup { +public: + std::vector<Poly> p; +}; + + +bool incrementaldraw = false; + +void shaveHairUI::drawHairs(int drawToken, M3dView& view) const +{ + //printf("draw hairs\n"); fflush(stdout); +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump(" shaveHairUI::drawHairs", NULL); +#endif + + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + + // We won't be able to draw hair until the node has been properly + // initialized. We might arrive here before initialization if Maya + // triggers a refresh during file I/O. + // + if ((nodePtr == 0) || !nodePtr->nodeIsInitialized()) { + return; + } + + shaveDirties& dirties = nodePtr->dirties; + if(dirties.DIRTY_HAIRCOUNT) + { + //do something there + } + shaveGlobals::getGlobals(); + + +#ifndef NEW_DISPLAY // old lines code + + + const std::vector<shaveHairShape::DisplayHair>& + displayHairs = nodePtr->getDisplayHairs(); + + // + // Determine the number of hairs to be drawn. + // + unsigned numHairsToDraw = nodePtr->getNumDisplayHairs(false); + + if (numHairsToDraw > displayHairs.size()) + numHairsToDraw = displayHairs.size(); + + // + // Draw the hairs. + // + unsigned h; + unsigned s; + unsigned v; + + for (h = 0; h < numHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = displayHairs[h]; + + for (s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + glBegin(GL_LINE_STRIP); + + for (v = 0; v < strand.verts.length(); v++) + { + if (drawToken == kDrawActiveShadedHairs) + { + view.setDrawColor( + MColor( + strand.colors[v].r, + strand.colors[v].g, + strand.colors[v].b + ) + ); + } + else if (drawToken == kDrawInactiveShadedHairs) + { + view.setDrawColor( + MColor( + strand.colors[v].r * 0.5f, + strand.colors[v].g * 0.5f, + strand.colors[v].b * 0.5f + ) + ); + } + + glVertex3d( + strand.verts[v].x, strand.verts[v].y, strand.verts[v].z + ); + } + + glEnd(); + } + } +#else //new poly hair code + + bool doxpar = nodePtr->getDoHairXparency(); + float xpar = nodePtr->getHairXparency(); + bool tipfade = nodePtr->getDoTipfade(); + bool fallback = nodePtr->getDoFallback(); + int passes = nodePtr->getNumPasses(); + float invPasses = 1.0f/(float)passes; +#ifdef DO_PROFILE + { + unsigned numToDisplay = nodePtr->getNumDisplayHairs(false); + char buf[300]={'\0'}; sprintf(buf,"Going to get cache %i %s", numToDisplay, IsMouseDown()?"mouse down":"mose up"); + Profile::ProfileDump(/*"Going to get cache"*/ buf, NULL); + } +#endif + +#if 1 + //this does not work, event are not coming + //ClearEvents(); + //qApp->flush(); //gah, event are present in queue + unsigned numToDisplay = nodePtr->getNumDisplayHairs(false); + if(numToDisplay==0) + return; + + unsigned bunchSize = nodePtr->getNumDisplayHairs(true); + if(bunchSize == 0) bunchSize = numToDisplay; + + const shaveHairShape::DisplayHairCache& cache = nodePtr->getDisplayHairs(view,true); + int done=0; + //lets try to return there + //return; //15fps even for 2 hairs. so its related to not empty display cache +#if 1 +// printf("mousedown: %s, mousemove: %s\n", IsMouseDown()?"yes":"no", GetEventsHappen()?"yes":"no");fflush(stdout); + //qApp->flush(); +#ifdef GLOBAL_FALLBACK + ClearEvents(); +#endif + incrementaldraw = false; + while(/*cache.displayHair.capacity()*/ /*numToDisplay != cache.displayHair.size()*/ numToDisplay > cache.displayHair.size() +#ifdef GLOBAL_FALLBACK + && (done==0) // +#endif + ) + { +#ifdef GLOBAL_FALLBACK + if (QCoreApplication::hasPendingEvents()) + { + SetEventsHappen(); + incrementaldraw = true; + //if(!shaveStyleCmd::redrawOnIdlDone) + //{ + // printf("redrawOnIdle fired+\n");fflush(stdout); + // MGlobal::executeCommandOnIdle("shaveStyle -redrawOnIdle"); + //} + } + + // if (qApp->hasPendingEvents()) SetEventsHappen() ; + // qApp->processEvents(); + // qApp->flush(); + /////////////////////////////////////////// + // incrementaldraw = true; + // qApp->processEvents(/*0x02*/ QEventLoop::ExcludeSocketNotifiers); + // incrementaldraw = false; + ////////////////////////////////////////// + + if((IsMouseDown() || GetEventsHappen()) && !liveModeGlob && fallback) + //if(GetEventsHappen()) + //qApp->checkQueueSize(); there is no such member + //if(qApp->hasPendingEvents()); + { + //printf("event\n"); fflush(stdout); + //no, lets draw + //if(doxpar && tipfade && (drawToken == kDrawActiveShadedHairs || drawToken == kDrawInactiveShadedHairs)) + //{ + // //nodePtr->clearDisplayHairs(); + // //return; + //} + //else + { + nodePtr->invalidateDisplayHairs(); +// printf("interrupt 01\n");fflush(stdout); + break; + } + } + if (GetEventsHappen()) done=1; + if (!GetEventsHappen()) +#endif + nodePtr->getDisplayHairs(view,false); + //Sleep(1); + } +#ifdef GLOBAL_FALLBACK + ClearEvents(); +#endif +#endif +#else + const shaveHairShape::DisplayHairCache& cache = nodePtr->getDisplayHairs(view); +#endif + //no computing + //const shaveHairShape::DisplayHairCache& cache = nodePtr->getDisplayHairs(); + + + //return; //just a test + + // + // Determine the number of hairs to be drawn. + // + unsigned numHairsToDraw = nodePtr->getNumDisplayHairs(false); + + + + if (numHairsToDraw > cache.displayHair.size()) + numHairsToDraw = (unsigned int)cache.displayHair.size(); + + // Grab view direction + + MMatrix worldToCam; + MVector viewDir( 0.0, 0.0, 1.0); + MDagPath camPath; + MStatus stat = MStatus::kSuccess; + stat = view.getCamera(camPath); + if(stat == MStatus::kSuccess) + { + MFnCamera camFn(camPath); + viewDir = camFn.viewDirection( MSpace::kWorld ); + viewDir.normalize(); + + worldToCam = camPath.inclusiveMatrixInverse(); + + } + + /////////////////////////////////////////////// + // + // Draw the hairs. + // +#ifdef DO_PROFILE + Profile::ProfileDump("Going to draw hairs", NULL); +#endif + if(doxpar && (tipfade || passes > 1) && (drawToken == kDrawActiveShadedHairs || drawToken == kDrawInactiveShadedHairs)) + { + unsigned int h; + unsigned int s; + int v; + + MPoint centerPt(cache.center); + MPoint nearPt = centerPt - viewDir*cache.radius; + MPoint farPt = centerPt + viewDir*cache.radius; + + MPoint nearSc = nearPt*worldToCam; + MPoint farSc = farPt*worldToCam; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + + //glEnable(GL_COLOR_MATERIAL); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //////////// GLSL /////////////// + bool doGlsl = false; + if(glslInited) + { + doGlsl = true; + + glEnable ( GL_LIGHTING ); + + //count OpengGL lights + bool uselights = false; + int nlights = 0; + //MString lightmode = ""; + //MGlobal::executeCommand("string $editor = `modelEditor`; modelEditor -q -displayLights $editor;",lightmode); + //MGlobal::executeCommand("modelEditor -q -displayLights modelEditor1;",lightmode); + //MGlobal::displayInfo(MString("mode ") + lightmode); + + M3dView::LightingMode lightmode = M3dView::kLightDefault; + view.getLightingMode(lightmode); + if(lightmode == M3dView::kLightActive || + lightmode == M3dView::kLightAll) + { + uselights = true; + + GLint maxlights; + glGetIntegerv(GL_MAX_LIGHTS,&maxlights); + for(int k=0; k < maxlights; k++) + { + GLboolean on; + glGetBooleanv(GL_LIGHT0+k,&on); + if(on) + nlights++; + } + //MGlobal::displayInfo(MString("max lights ")+maxlights+" numlight " + nlights); + } + hairGlsl.SetUseLights(uselights,nlights); + + hairGlsl.SetAmbDiff(nodePtr->getAmbDiff()); + hairGlsl.SetSpecular( nodePtr->getGloss()); + hairGlsl.SetSpecularAmt(nodePtr->getSpecular()); + hairGlsl.SetSpecularTint(nodePtr->getSpecularTint()); + hairGlsl.SetSpecularTint2(nodePtr->getSpecularTint2()); + hairGlsl.SetUseSSAO(false); + hairGlsl.UseProgram(); + + if(hairGlsl.GetUseSSAO()) + ssao.ActivateTexture(0); + } + else + glEnable(GL_COLOR_MATERIAL); + ///////////////////////////////// + + bool dobreak = false; + unsigned int ingroups = 0; + int numS = 1; + std::vector<PolyGroup> groups; + groups.resize(400); + for(int g = (int)groups.size()-1; g >= 0; g--) + groups[g].p.reserve(300); + + for (h = 0; h < numHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = cache.displayHair[h]; + for (s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + float r0; + float r1; + numS = strand.verts.length()-1; + float inumS = 1.0f/(float)numS; + + for (v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + float t1 = vv*inumS; + if(v == 0) //root polygon + { + p0.x = strand.verts[0].x; + p0.y = strand.verts[0].y; + p0.z = strand.verts[0].z; + p1.x = strand.verts[1].x; + p1.y = strand.verts[1].y; + p1.z = strand.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + + tangent1 = tangent0; + + r0 = strand.rootrad; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + else if(vv == numS) //tip poly + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + + + r0 = r1; + r1 = strand.tiprad; + } + else + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + if(vvv < (int)strand.verts.length()) + { + p2.x = strand.verts[vvv].x; + p2.y = strand.verts[vvv].y; + p2.z = strand.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + + r0 = r1; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + if(p0 == p1) + continue; + + + MVector d0 = tangent0^viewDir; + MVector d1 = tangent1^viewDir; + d0.normalize(); + d1.normalize(); + + MVector v0 = p0 + d0*r0; + MVector v1 = p0 - d0*r0; + MVector v2 = p1 - d1*r1; + MVector v3 = p1 + d1*r1; + + MColor col = strand.colors[v]; + if (drawToken == kDrawInactiveShadedHairs) + { + col.r *= 0.75f; + col.g *= 0.75f; + col.b *= 0.75f; + } + //we do get 1.0 in WFTYPE.alpha[] at the moment, lets fake it with tipfade + //col.a = 1.0f - vv*inumS; + col.a = 1.0f; + if(tipfade) + col.a -= v*inumS; + if(passes) + col.a *= invPasses; + + + Poly P; + + P.v[0] = v0; + P.c[0] = col; + + P.v[1] = v1; + P.c[1] = col; + P.t[0] = P.t[1] = tangent0; + + col = strand.colors[vv]; + if (drawToken == kDrawInactiveShadedHairs) + { + col.r *= 0.75f; + col.g *= 0.75f; + col.b *= 0.75f; + } + //we do get 1.0 in WFTYPE.alpha[] at the moment, lets fake it with tipfade + col.a = 1.0f - vv*inumS; + + col.a = 1.0f; + if(tipfade) + col.a -= vv*inumS; + if(passes) + col.a *= invPasses; + + P.v[2] = v2; + P.c[2] = col; + + P.v[3] = v3; + P.c[3] = col; + P.t[2] = P.t[3] = tangent1; + + //for Joe: what I am doing wrong with z-sort? + MPoint V(0.5f*(p0.x+p1.x), 0.5f*(p0.y+p1.y), 0.5f*(p0.z+p1.z));//a middle of poly + MPoint S = V*worldToCam; //to camera space + float d = (S.z-nearSc.z)/(farSc.z-nearSc.z); + //if(d > 1.0f) d = 1.0f; + //if(d < 0.0f) d = 0.0f; + int pos = (int)(d*(float)groups.size()); + if(pos >= groups.size()) pos = (int)groups.size()-1; + if(pos < 0) pos = 0; + + groups[pos].p.push_back(P); + } + ingroups++; + + //if(ingroups%bunchSize==0) //no, bazilion of issues + //{ + // qApp->processEvents(); + //} +#ifdef GLOBAL_FALLBACK + if((IsMouseDown() || GetEventsHappen()) && !liveModeGlob && fallback) + { + //Sleep(1); + if(ingroups >= bunchSize) + { + dobreak = true; +// printf("interrupt 02\n");fflush(stdout); + break; + } + } +#endif + } + if(dobreak) + break; + } +#ifdef DO_PROFILE + Profile::ProfileDump("xparency groups", NULL); +#endif + dobreak = false; + unsigned drawn = 0; + glBegin(GL_QUADS); + for(int g = (int)groups.size()-1; g >= 0; g--) + { + const PolyGroup& gr = groups[g]; + unsigned int gs = (unsigned int)gr.p.size(); + for(unsigned int i = 0; i < gs; i++) + { + const Poly& p = gr.p[i]; + if(doGlsl) glVertexAttrib3f(hairGlsl.GetTangentLoc(),p.t[0].x, p.t[0].y, p.t[0].z); + glColor4f(p.c[0].r,p.c[0].g, p.c[0].b, p.c[0].a*xpar); + glVertex3d(p.v[0].x, p.v[0].y, p.v[0].z); + + if(doGlsl) glVertexAttrib3f(hairGlsl.GetTangentLoc(),p.t[1].x, p.t[1].y, p.t[1].z); + glColor4f(p.c[1].r,p.c[1].g, p.c[1].b, p.c[1].a*xpar); + glVertex3d(p.v[1].x, p.v[1].y, p.v[1].z); + + if(doGlsl) glVertexAttrib3f(hairGlsl.GetTangentLoc(),p.t[2].x, p.t[2].y, p.t[2].z); + glColor4f(p.c[2].r,p.c[2].g, p.c[2].b, p.c[2].a*xpar); + glVertex3d(p.v[2].x, p.v[2].y, p.v[2].z); + + if(doGlsl) glVertexAttrib3f(hairGlsl.GetTangentLoc(),p.t[3].x, p.t[3].y, p.t[3].z); + glColor4f(p.c[3].r,p.c[3].g, p.c[3].b, p.c[3].a*xpar); + glVertex3d(p.v[3].x, p.v[3].y, p.v[3].z); + + drawn++; + +// if( (drawn>bunchSize)&&((drawn/numS)%bunchSize==0) ) //no, bazilion of issues + // { + // if (qApp->hasPendingEvents()) SetEventsHappen() ; +// } + + // if((IsMouseDown() || GetEventsHappen()) && !liveModeGlob && fallback) + // { + // if(drawn/numS >= bunchSize) + // { + // dobreak = true; + // printf("interrupt 03\n");fflush(stdout); + // break; + // } + // } + } + //if(dobreak) + //break; + } + glEnd(); +#ifdef DO_PROFILE + Profile::ProfileDump(" shaveHairUI::drawHairs xparent -- done", NULL); +#endif + //////////// END GLSL /////////////// + if(doGlsl) + { + hairGlsl.UnUseProgram(); + } + glBindTexture(GL_TEXTURE_2D, 0); + ///////////////////////////////// + + glPopClientAttrib(); + glPopAttrib(); + + } + else + { + unsigned int h; + unsigned int s; + int v; + + //////////// GLSL /////////////// + bool doGlsl = false; + if(glslInited && (drawToken == kDrawActiveShadedHairs || drawToken == kDrawInactiveShadedHairs)) + { + doGlsl = true; + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + + glEnable ( GL_LIGHTING ); + + hairGlsl.SetAmbDiff(nodePtr->getAmbDiff()); + // hairGlsl.SetSpecular(nodePtr->getSpecular()); + // hairGlsl.SetSpecularAmt(nodePtr->getGloss()); + hairGlsl.SetSpecular( nodePtr->getGloss()); + hairGlsl.SetSpecularAmt(nodePtr->getSpecular()); + hairGlsl.SetSpecularTint(nodePtr->getSpecularTint()); + hairGlsl.SetSpecularTint2(nodePtr->getSpecularTint2()); + hairGlsl.SetUseSSAO(false); + hairGlsl.SetViewSize(view.portWidth(),view.portHeight()); + hairGlsl.UseProgram(); + + if(hairGlsl.GetUseSSAO()) + ssao.ActivateTexture(0); + } + ///////////////////////////////// + + for (h = 0; h < numHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = cache.displayHair[h]; + + for (s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + glBegin(GL_QUADS); + + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + float r0; + float r1; + int numS = strand.verts.length()-1; + float inumS = 1.0f/(float)numS; + + for (v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + float t1 = vv*inumS;; + if(v == 0) //root polygon + { + p0.x = strand.verts[0].x; + p0.y = strand.verts[0].y; + p0.z = strand.verts[0].z; + p1.x = strand.verts[1].x; + p1.y = strand.verts[1].y; + p1.z = strand.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + + tangent1 = tangent0; + + r0 = strand.rootrad; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + else if(vv == numS) //tip poly + { + p0 = p1; + //p0.x = strand.verts[v].x; + //p0.y = strand.verts[v].y; + //p0.z = strand.verts[v].z; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + + + r0 = r1; + r1 = strand.tiprad; + } + else + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + if(vvv < (int)strand.verts.length()) + { + p2.x = strand.verts[vvv].x; + p2.y = strand.verts[vvv].y; + p2.z = strand.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + + r0 = r1; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + if(p0 == p1) + continue; + + + MVector d0 = tangent0^viewDir; + MVector d1 = tangent1^viewDir; + d0.normalize(); + d1.normalize(); + + MVector v0 = p0 + d0*r0; + MVector v1 = p0 - d0*r0; + MVector v2 = p1 - d1*r1; + MVector v3 = p1 + d1*r1; + + if(doGlsl) + { + MColor c1(strand.colors[v].r,strand.colors[v].g,strand.colors[v].b); + MColor c2(strand.colors[vv].r,strand.colors[vv].g,strand.colors[vv].b); + + if (drawToken == kDrawInactiveShadedHairs) + { + c1 *= 0.5; + c2 *= 0.5; + } + glVertexAttrib3f(hairGlsl.GetTangentLoc(),tangent0.x,tangent0.y,tangent0.z); + glColor4f(c1.r, c1.g, c1.b, 1.0f); + glVertex3d(v0.x, v0.y, v0.z); + + glVertexAttrib3f(hairGlsl.GetTangentLoc(),tangent0.x,tangent0.y,tangent0.z); + glColor4f(c1.r, c1.g, c1.b, 1.0f); + glVertex3d(v1.x, v1.y, v1.z); + + glVertexAttrib3f(hairGlsl.GetTangentLoc(),tangent1.x,tangent1.y,tangent1.z); + glColor4f(c2.r, c2.g, c2.b, 1.0f); + glVertex3d(v2.x, v2.y, v2.z); + + glVertexAttrib3f(hairGlsl.GetTangentLoc(),tangent1.x,tangent1.y,tangent1.z); + glColor4f(c2.r, c2.g, c2.b, 1.0f); + glVertex3d(v3.x, v3.y, v3.z); + } + else + { + if (drawToken == kDrawActiveShadedHairs) + { + view.setDrawColor( + MColor( + strand.colors[v].r, + strand.colors[v].g, + strand.colors[v].b + ) + ); + } + else if (drawToken == kDrawInactiveShadedHairs) + { + view.setDrawColor( + MColor( + strand.colors[v].r * 0.5f, + strand.colors[v].g * 0.5f, + strand.colors[v].b * 0.5f + ) + ); + } + + glVertex3d(v0.x, v0.y, v0.z); + glVertex3d(v1.x, v1.y, v1.z); + + if (drawToken == kDrawActiveShadedHairs) + { + view.setDrawColor( + MColor( + strand.colors[vv].r, + strand.colors[vv].g, + strand.colors[vv].b + ) + ); + } + else if (drawToken == kDrawInactiveShadedHairs) + { + view.setDrawColor( + MColor( + strand.colors[vv].r * 0.5f, + strand.colors[vv].g * 0.5f, + strand.colors[vv].b * 0.5f + ) + ); + } + + glVertex3d(v2.x, v2.y, v2.z); + glVertex3d(v3.x, v3.y, v3.z); + } + + + //glVertex3d( + // strand.verts[v].x, strand.verts[v].y, strand.verts[v].z + //); + } + + glEnd(); + + } + } + //////////// END GLSL /////////////// + if(doGlsl) + { + hairGlsl.UnUseProgram(); + + glPopClientAttrib(); + glPopAttrib(); + } + glBindTexture(GL_TEXTURE_2D, 0); + ///////////////////////////////// + + //ClearEvents(); + +#ifdef DO_PROFILE + Profile::ProfileDump(" shaveHairUI::drawHairs -- done", NULL); +#endif + } +#endif + // nodePtr->dirties.BRUSH_JUST_MOVED = 0; //// crraaaaap! +} + +void shaveHairUI::drawSsaoDepth( int drawToken, M3dView& view, const shaveHairShape::DisplayHairCache& cache, const MVector& viewDir ) const +{ + + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + unsigned int numHairsToDraw = nodePtr->getNumDisplayHairs(false); + + if (numHairsToDraw > cache.displayHair.size()) + numHairsToDraw = (unsigned int)cache.displayHair.size(); + + Matrix4 m; + glGetFloatv(GL_MODELVIEW_MATRIX, m); //already set up by maya + + float maxz = -1000000.f; + float minz = 1000000.f; + float m4x4[4][4]; + memcpy(m4x4,m.entries,16*sizeof(float)); + MMatrix mm(m4x4); + + MBoundingBox b = nodePtr->boundingBoxTemp(); + b.transformUsing(mm); + + MPoint bmin = b.min(); + MPoint bmax = b.max(); + float z1 = (float)(-bmin.z); + float z2 = (float)(-bmax.z); + float znear = ( z1 < z2 ) ? z1 : z2; + float zfar = ( z1 > z2 ) ? z1 : z2; + ssaodepthGlsl.SetFarNear(zfar,znear); + + ssaodepth.MakeCurrent(); + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + + glEnable( GL_LIGHTING ); + glEnable( GL_DEPTH_TEST ); + glDisable( GL_CULL_FACE ); + + ssaodepthGlsl.UseProgram(); + + for (unsigned int h = 0; h < numHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = cache.displayHair[h]; + + for (unsigned int s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + glBegin(GL_QUADS); + + MVector tangent0; + MVector tangent1; + MVector p0; + MVector p1; + MVector p2; + float r0; + float r1; + int numS = strand.verts.length()-1; + + for (int v = 0; v < numS; v++) + { + int vv = v+1; + int vvv = v+2; + float t1 = vv/(float)numS; + if(v == 0) //root polygon + { + p0.x = strand.verts[0].x; + p0.y = strand.verts[0].y; + p0.z = strand.verts[0].z; + p1.x = strand.verts[1].x; + p1.y = strand.verts[1].y; + p1.z = strand.verts[1].z; + + tangent0 = p1 - p0; + tangent0.normalize(); + + tangent1 = tangent0; + + r0 = strand.rootrad; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + else if(vv == numS) //tip poly + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + tangent1 = p1 - p0; + tangent1.normalize(); + + + r0 = r1; + r1 = strand.tiprad; + } + else + { + p0 = p1; + p1.x = strand.verts[vv].x; + p1.y = strand.verts[vv].y; + p1.z = strand.verts[vv].z; + + tangent0 = tangent1; + if(vvv < (int)strand.verts.length()) + { + p2.x = strand.verts[vvv].x; + p2.y = strand.verts[vvv].y; + p2.z = strand.verts[vvv].z; + tangent1 = p2 - p0; + } + else + { + tangent1 = p1 - p0; + } + tangent1.normalize(); + + r0 = r1; + r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; + + } + if(p0 == p1) + continue; + + + MVector d0 = tangent0^viewDir; + MVector d1 = tangent1^viewDir; + d0.normalize(); + d1.normalize(); + + MVector v0 = p0 + d0*r0; + MVector v1 = p0 - d0*r0; + MVector v2 = p1 - d1*r1; + MVector v3 = p1 + d1*r1; + + glVertex3d(v0.x, v0.y, v0.z); + glVertex3d(v1.x, v1.y, v1.z); + glVertex3d(v2.x, v2.y, v2.z); + glVertex3d(v3.x, v3.y, v3.z); + + } + glEnd(); + } + } + + ssaodepthGlsl.UnUseProgram(); + + + glPopClientAttrib(); + glPopAttrib(); + + ssaodepth.ResetCurrent(); //FBO + +} + +void shaveHairUI::drawSsao( int drawToken, M3dView& view, const shaveHairShape::DisplayHairCache& cache, const MVector& viewDir ) const +{ + + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + + ///// BFO ///// + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + + glEnable( GL_LIGHTING ); + glEnable(GL_TEXTURE_2D); + + drawSsaoDepth( drawToken, view, cache, viewDir); + + + ssao.MakeCurrent(); + + +// if(ssaoGlsl.GetProgram() == 0 || !ssaoGlsl.IsVadlid()) +// { +// if(ssaoGlsl.GetProgram() != 0) +// ssaoGlsl.UnLoadShaders(); +// ssaoGlsl.LoadShaders(); +// } + + //float ssaorad = 0.007; + //int samples = 20; + + + ssaoGlsl.SetRadius(1.0f/(float)view.portWidth()); + ssaoGlsl.SetSamples(25); + + + + ssaoGlsl.UseProgram(); + ssaodepth.ActivateTexture(1); + + + //draw full screen quad + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + glLoadIdentity (); + glMatrixMode (GL_PROJECTION); + glPushMatrix (); + glLoadIdentity (); + + glBegin (GL_QUADS); + glVertex3i (-1, -1, -1); + glVertex3i (1, -1, -1); + glVertex3i (1, 1, -1); + glVertex3i (-1, 1, -1); + glEnd (); + + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + + ssaoGlsl.UnUseProgram(); + + glBindTexture(GL_TEXTURE_2D, 0); + + glPopClientAttrib(); + glPopAttrib(); + + ssao.ResetCurrent(); //BFO +} + + +#ifdef NEW_INSTNACE_DISPLAY +static GLfloat material_ambient[] = {0.1f, 0.1f, 0.1f, 1.0f}; +static GLfloat material_specular[]= {0.1f, 0.1f, 0.1f, 1.0f}; +static GLfloat material_shininess[]={.1f}; +static GLfloat material_diffuse[] = {0.82f, 0.82f, 0.82f, 1.0f}; + + + +void shaveHairUI::drawInstances(int drawToken, M3dView& view) const +{ +#if defined(WIN32) || defined(LINUX) +#ifndef GL_ACTIVE_TEXTURE + if(!glActiveTexture) + { + glActiveTexture = (PFNGLATCIVETEXTUREPROC) getGlFncPtr("glActiveTexture"); + if(!glActiveTexture) +#ifdef WIN32 + MGlobal::displayInfo(MString("glActiveTexture not found (error")+ GetLastError() + ")"); +#else + MGlobal::displayInfo(MString("glActiveTexture not found")); +#endif + } +#endif +#endif + MStatus stat = MStatus::kSuccess; + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + + shaveDirties& dirties = nodePtr->dirties; + if(dirties.DIRTY_HAIRCOUNT) + { + //do something there + } + + /*const*/ shaveInstanceDisplay& cache = nodePtr->getInstanceDisplay(); + + //printf("draw instance for node 0x%X nf %i\n",nodePtr,cache.faceCounts.length());fflush(stdout); + + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + + if(drawToken == kDrawInstancedShaded || + drawToken == kDrawInstancedShadedOffset ) + { + if(drawToken == kDrawInstancedShadedOffset) + glEnable( GL_POLYGON_OFFSET_FILL ); + + glEnable ( GL_LIGHTING ); + glEnable(GL_SMOOTH); + glEnable( GL_DEPTH_TEST ); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if(cache.diffuse != 0 /*|| cache.xparenc != 0*/) + glEnable(GL_TEXTURE_2D); + + glPolygonMode (GL_FRONT, GL_FILL); + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); + + glMaterialfv(GL_FRONT,GL_AMBIENT,material_ambient); + glMaterialfv(GL_FRONT,GL_SPECULAR,material_specular); + glMaterialfv(GL_FRONT,GL_SHININESS,material_shininess); + + + if(cache.diffuse != 0) + { + if(glActiveTexture) + glActiveTexture(GL_TEXTURE0); + + glBindTexture(GL_TEXTURE_2D, cache.diffuse); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + + } + else + glMaterialfv(GL_FRONT,GL_DIFFUSE,material_diffuse); + + int oo=0; + glBegin(GL_TRIANGLES); + if(cache.hasXparency) + { + MDagPath camPath; + MStatus stat = view.getCamera(camPath); + MMatrix worldToCam = camPath.inclusiveMatrixInverse(); + + MFnCamera camFn(camPath); + MVector viewDir = camFn.viewDirection( MSpace::kWorld ); + MPoint centerPt(cache.center); + MPoint nearPt = centerPt - viewDir*cache.radius; + MPoint farPt = centerPt + viewDir*cache.radius; + + MPoint p = nearPt*worldToCam; + MFloatPoint nearSc((float)p.x, (float)p.y, (float)p.z); + + p = farPt*worldToCam; + MFloatPoint farSc((float)p.x, (float)p.y, (float)p.z); + + std::vector<Group> groups; + groups.resize(100); + + for(unsigned int i = 0; i < cache.faceCounts.length(); i++) + { + + int fc = cache.faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = cache.faceStart[i]; + int fv = cache.faceVerts[fs]; + const MPoint& p = cache.points[fv]; + MPoint s = p*worldToCam; + //float d = s.z-nearSc.z* (farSc.z-nearSc.z/(float)groups.size()); + float d = (s.z-nearSc.z)/(farSc.z-nearSc.z); + groups[(int)(d*(float)groups.size())].pids.append(i); + } + for(int xx = (int)groups.size()-1; xx >= 0; xx--) + //for(int xx = 0; xx < groups.size(); xx++) + { + for(unsigned int zz = 0; zz < groups[xx].pids.length(); zz++) + { + int i = groups[xx].pids[zz]; + int fc = cache.faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = cache.faceStart[i]; + int fe = cache.faceEnd[i]; + int jj = 0; + for(int j = fs; j < fe; j++) + { + int k = cache.faceVerts[j]; + const MFloatPoint& p = cache.points[k]; + const MFloatPoint& t = cache.uvs[/*oo*/i*3 + jj]; + const MFloatPoint& n = cache.normals[k]; + //something is weird with this, normals are inverted... + glNormal3f(-n.x, -n.y, -n.z ); + glTexCoord2f(t.x, t.y); + glVertex3f(p.x,p.y, p.z); + //oo++; + jj++; + } + } + } + } + else + { + for(unsigned int i = 0; i < cache.faceCounts.length(); i++) + { + + int fc = cache.faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = cache.faceStart[i]; + int fe = cache.faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int k = cache.faceVerts[j]; + const MFloatPoint& p = cache.points[k]; + const MFloatPoint& t = cache.uvs[oo]; + const MFloatPoint& n = cache.normals[k]; + //something is weird with this, normals are inverted... + glNormal3f(-n.x, -n.y, -n.z ); + glTexCoord2f(t.x, t.y); + glVertex3f(p.x,p.y, p.z); + oo++; + } + } + } + glEnd(); + + if(cache.diffuse != 0) + { + glBindTexture(GL_TEXTURE_2D, 0); + } + } + else if(drawToken == kDrawInstancedWire) + { + //glEnable( GL_POLYGON_OFFSET_LINE ); + + glBegin( GL_LINES ); + for(unsigned int i = 0; i < cache.faceCounts.length(); i++) + { + + int fc = cache.faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = cache.faceStart[i]; + int fe = cache.faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int jj = (j < fe-1) ? j + 1 : fs; + int k = cache.faceVerts[j]; + int kk = cache.faceVerts[jj]; + const MPoint& p = cache.points[k]; + const MPoint& pp = cache.points[kk]; + glVertex3f(p.x,p.y, p.z); + glVertex3f(pp.x,pp.y, pp.z); + } + } + glEnd(); + } + + + glPopClientAttrib(); + glPopAttrib(); + +} +#endif + + +void shaveHairUI::drawVerts(int drawToken) const +{ + // + // Which verts will we be drawing? + // + bool drawActive = false; + int startVert = 0; + int endVert = SHAVE_VERTS_PER_GUIDE - 1; + + switch (drawToken) + { + case kDrawActiveRoots: + drawActive = true; + case kDrawInactiveRoots: + startVert = endVert = 0; + break; + + case kDrawActiveTips: + drawActive = true; + case kDrawInactiveTips: + startVert = endVert = SHAVE_VERTS_PER_GUIDE - 1; + break; + + case kDrawActiveVerts: + drawActive = true; + case kDrawInactiveVerts: + startVert = 0; + endVert = SHAVE_VERTS_PER_GUIDE - 1; + break; + + default: + // + // We should never get here, but if somehow we do then there's + // nothing for us to draw. + // + return; + } + + // + // Get the guides. + // + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + + const shaveHairShape::Guides& guides = nodePtr->getGuides().guides; + + // + // Save the old point size for later restoration, and set the point + // size that we'll be using. + // + float oldPointSize; + glGetFloatv(GL_POINT_SIZE, &oldPointSize); + glPointSize(POINT_SIZE); + + // + // Draw the guide vertices. + // + glBegin(GL_POINTS); + + int i; + shaveHairShape::Guides::const_iterator iter; + + for (iter = guides.begin(); iter != guides.end(); iter++) + { + const shaveHairShape::Guide& guide = *iter; + + if (!guide.hidden) + { + for (i = startVert; i <= endVert ; i++) + { + // + // Is this vert active? + // + bool isActive = ((guide.select & (1 << i)) != 0); + + // + // If the vert meets our drawing requirements, draw it. + // + if (drawActive == isActive) + { + glVertex3f( + guide.verts[i].x, guide.verts[i].y, guide.verts[i].z + ); + } + } + } + } + + glEnd(); + + glPointSize(oldPointSize); +} + + +bool shaveHairUI::selectGuides( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts +) const +{ + // + // Create a component to store the selected guides. + // + MFnSingleIndexedComponent compFn; + MObject compObj; + + compObj = compFn.create(shaveHairShape::kShaveGuideComponent); + + // + // In single-selection mode we'll use the alignmentMatrix to determine + // which of multiple hits is closest to the viewer. + // + MMatrix alignmentMatrix; + MPoint singlePoint; + bool singleSelection = selectInfo.singleSelection(); + MDagPath path = selectInfo.multiPath(); + + if (singleSelection) + { + alignmentMatrix = selectInfo.getAlignmentMatrix(); + + // + // I don't know for sure, but I'm willing to bet that the + // alignmentMatrix is expecting local-space points. Our points are + // all in worldSpace to we have to multiply in a localSpace + // transformation for it to work properly. + // + alignmentMatrix = path.inclusiveMatrixInverse() * alignmentMatrix; + } + + // + // Make sure that this shaveNode is loaded into the engine. + // + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + nodePtr->makeCurrent(); + + // + // Check the guide segments for selection hits. + // + SOFTGUIDE guide; + int guideIndex; + int i; + int closestGuideIndex = -1; + MPoint closestPt; + float closestXformedZ = 0.0f; + M3dView view = selectInfo.view(); + + for (guideIndex = 0; + SHAVEfetch_guide(guideIndex, &guide) != -1; + guideIndex++) + { + if (guide.hidden) continue; + + view.beginSelect(); + glBegin(GL_LINES); + + // To speed things up, we skip over the even verts. + for (i = 0; i < SHAVE_VERTS_PER_GUIDE-2; i += 2) + { + glVertex3f( + guide.guide[i].x, guide.guide[i].y, guide.guide[i].z + ); + glVertex3f( + guide.guide[i+2].x, guide.guide[i+2].y, guide.guide[i+2].z + ); + } + + glEnd(); + + if (view.endSelect() > 0) + { + // + // We got a hit so store it. + // + if (singleSelection) + { + // + // On singleSelection we only store the hit closest to + // the viewer. We'll use the midpoint of the guide + // as our hit point. (We used to test each segment + // individually, which gave us a more accurate result, but + // was much slower.) + // + MPoint currentPt( + (guide.guide[0].x + + guide.guide[SHAVE_VERTS_PER_GUIDE-1].x) / 2.0, + (guide.guide[0].y + + guide.guide[SHAVE_VERTS_PER_GUIDE-1].y) / 2.0, + (guide.guide[0].z + + guide.guide[SHAVE_VERTS_PER_GUIDE-1].z) / 2.0 + ); + MPoint xformedPt = currentPt; + + xformedPt.homogenize(); + xformedPt *= alignmentMatrix; + + float xformedZ = (float)xformedPt.z; + + if ((closestGuideIndex < 0) + || (xformedZ < closestXformedZ)) + { + closestGuideIndex = guideIndex; + closestPt = currentPt; + closestXformedZ = xformedZ; + } + } + else + { + // + // On multiple selection we store all guides which are + // hit. + // + compFn.addElement(guideIndex); + } + } + } + + // + // If we were doing singleSelection then store the closest hit that we + // got. + // + if (closestGuideIndex >= 0) + { + compFn.addElement(closestGuideIndex); + + // + // Convert the hit point to worldspace. + // + closestPt *= path.inclusiveMatrix(); + } + + + if (!compFn.isEmpty()) + { + MSelectionList newSelections; + + newSelections.add(path, compObj); + + MSelectionMask mask(MSelectionMask::kSelectComponentsMask); + + selectInfo.addSelection( + newSelections, + closestPt, + selectionList, + worldSpaceSelectPts, + mask, + true + ); + } + + return !compFn.isEmpty(); +} + + +bool shaveHairUI::selectObjByGuides( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts +) const +{ + SOFTGUIDE guide; + int guideIndex; + int i; + M3dView view = selectInfo.view(); + MPoint samplePt; + + view.beginSelect(); + + for (guideIndex = 0; + SHAVEfetch_guide(guideIndex, &guide) != -1; + guideIndex++) + { + if (guide.hidden) continue; + + // In single-selection mode we need to return the point on our + // object which is "closest to the viewer". However, to really do + // that for hair is extremely expensive and slows down the picking + // of everything. So instead we just return the first first of + // the first guide. That means that if there are other objects + // within the pickbox then they will likely win, but the user can + // avoid that by using the Outliner or zooming in for a more + // accurate pick. + if (guideIndex == 0) + { + samplePt.x = (double)guide.guide[0].x; + samplePt.y = (double)guide.guide[0].y; + samplePt.z = (double)guide.guide[0].z; + } + + glBegin(GL_LINE_STRIP); + + for (i = 0; i < SHAVE_VERTS_PER_GUIDE; i++) + glVertex3f(guide.guide[i].x, guide.guide[i].y, guide.guide[i].z); + + glEnd(); + } + + if (view.endSelect() > 0) + { + MSelectionMask priorityMask(MSelectionMask::kSelectNurbsSurfaces); + MSelectionList newItem; + MDagPath path = selectInfo.selectPath(); + + newItem.add(path); + + selectInfo.addSelection( + newItem, + samplePt, + selectionList, + worldSpaceSelectPts, + priorityMask, + false + ); + + return true; + } + + return false; +} + + +bool shaveHairUI::selectObjByHairs( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts +) const +{ + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + M3dView view = selectInfo.view(); + + //const std::vector<shaveHairShape::DisplayHair>& displayHairs = nodePtr->getDisplayHairs(); + //M3dView view = M3dView::active3dView(); + const shaveHairShape::DisplayHairCache& cache = nodePtr->getDisplayHairs(view); + + // + // Determine the number of hairs to be drawn. + // + unsigned numHairsToDraw = nodePtr->getNumDisplayHairs(false); + + if (numHairsToDraw > cache.displayHair.size()) + numHairsToDraw = (unsigned int)cache.displayHair.size(); + + // + // Draw the hairs. + // + unsigned h; + unsigned s; + unsigned v; + MPoint samplePt; + + view.beginSelect(); + + for (h = 0; h < numHairsToDraw; h++) + { + const shaveHairShape::DisplayHair& hair = cache.displayHair[h]; + + for (s = 0; s < hair.size(); s++) + { + const shaveHairShape::DisplayStrand& strand = hair[s]; + + // In single-selection mode we need to return the point on our + // object which is "closest to the viewer". However, to + // really do that for hair is extremely expensive and slows + // down the picking of everything. So instead we just return + // the first first of the first guide. That means that if + // there are other objects within the pickbox then they will + // likely win, but the user can avoid that by using the + // Outliner or zooming in for a more accurate pick. + if ((h == 0) && (s == 0)) + { + samplePt.x = strand.verts[0].x; + samplePt.y = strand.verts[0].y; + samplePt.z = strand.verts[0].z; + } + + glBegin(GL_LINE_STRIP); + + for (v = 0; v < strand.verts.length(); v++) + { + glVertex3d( + strand.verts[v].x, strand.verts[v].y, strand.verts[v].z + ); + } + + glEnd(); + } + } + + if (view.endSelect() > 0) + { + MSelectionMask priorityMask(MSelectionMask::kSelectNurbsSurfaces); + MSelectionList newItem; + MDagPath path = selectInfo.selectPath(); + + newItem.add(path); + + selectInfo.addSelection( + newItem, + samplePt, + selectionList, + worldSpaceSelectPts, + priorityMask, + false + ); + + return true; + } + + return false; +} + +bool shaveHairUI::selectObjByGeom( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts +) const +{ + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + M3dView view = selectInfo.view(); + + shaveInstanceDisplay& cache = nodePtr->getInstanceDisplay(); + + // + // Draw the hairs. + // + MPoint samplePt; + + view.beginSelect(); + + glBegin( GL_LINES ); + for(unsigned int i = 0; i < cache.faceCounts.length(); i++) + { + + int fc = cache.faceCounts[i]; + if(fc != 3) + continue; //trianglular faces expected + + int fs = cache.faceStart[i]; + int fe = cache.faceEnd[i]; + for(int j = fs; j < fe; j++) + { + int jj = (j < fe-1) ? j + 1 : fs; + int k = cache.faceVerts[j]; + int kk = cache.faceVerts[jj]; + const MPoint& p = cache.points[k]; + const MPoint& pp = cache.points[kk]; + glVertex3f(p.x,p.y, p.z); + glVertex3f(pp.x,pp.y, pp.z); + } + } + glEnd(); + + if (view.endSelect() > 0) + { + MSelectionMask priorityMask(MSelectionMask::kSelectNurbsSurfaces); + MSelectionList newItem; + MDagPath path = selectInfo.selectPath(); + + newItem.add(path); + + selectInfo.addSelection( + newItem, + samplePt, + selectionList, + worldSpaceSelectPts, + priorityMask, + false + ); + + return true; + } + + return false; +} + + + + +bool shaveHairUI::selectVerts( + MString selectMode, + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts +) const +{ + // Use the guide selection mode to determine which vertices to + // check. + // + int startVert = 0; + int endVert = SHAVE_VERTS_PER_GUIDE - 1; + bool entireGuide = false; + + if (selectMode == "root") + { + startVert = endVert = 0; + + // In 'root' mode we are selecting entire guides by their root + // verts. + // + entireGuide = true; + } + else if (selectMode == "tip") + { + startVert = endVert = SHAVE_VERTS_PER_GUIDE - 1; + } + else if (selectMode == "vert") + { + startVert = 0; + endVert = SHAVE_VERTS_PER_GUIDE - 1; + } + else + return false; + + // Create a component to store the selected verts/guides. + // + MFnSingleIndexedComponent guideCompFn; + MFnDoubleIndexedComponent vertCompFn; + MObject compObj; + + if (entireGuide) + compObj = guideCompFn.create(shaveHairShape::kShaveGuideComponent); + else + compObj = vertCompFn.create(shaveHairShape::kShaveGuideVertComponent); + + // In single-selection mode we'll use the alignmentMatrix to determine + // which of multiple hits is closest to the viewer. + // + MMatrix alignmentMatrix; + MPoint singlePoint; + bool singleSelection = selectInfo.singleSelection(); + MDagPath path = selectInfo.multiPath(); + + if (singleSelection) + { + alignmentMatrix = selectInfo.getAlignmentMatrix(); + + // I don't know for sure, but I'm willing to bet that the + // alignmentMatrix is expecting local-space points. Our points are + // all in worldSpace to we have to multiply in a localSpace + // transformation for it to work properly. + // + alignmentMatrix = path.inclusiveMatrixInverse() * alignmentMatrix; + } + + // Save the old point size for later restoration, and set the point + // size that we'll be using. + // + M3dView view = selectInfo.view(); + float oldPointSize; + + view.beginGL(); + glGetFloatv(GL_POINT_SIZE, &oldPointSize); + glPointSize(POINT_SIZE); + view.endGL(); + + // Make sure that this shaveNode is loaded into the engine. + // + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(surfaceShape()); + nodePtr->makeCurrent(); + + // Check the guide vertices for selection hits. + // + SOFTGUIDE guide; + int guideIndex; + int i; + int closestGuideIndex = -1; + int closestVertIndex = -1; + MPoint closestPt; + float closestXformedZ = 0.0f; + + for (guideIndex = 0; + SHAVEfetch_guide(guideIndex, &guide) != -1; + guideIndex++) + { + if (guide.hidden) continue; + + for (i = startVert; i <= endVert ; i++) + { + view.beginSelect(); + glBegin(GL_POINTS); + glVertex3f(guide.guide[i].x, guide.guide[i].y, guide.guide[i].z); + glEnd(); + + if (view.endSelect() > 0) + { + // We got a hit so store it. + // + if (singleSelection) + { + // On singleSelection we only store the hit closest to + // the viewer. + // + MPoint currentPt( + guide.guide[i].x, + guide.guide[i].y, + guide.guide[i].z + ); + MPoint xformedPt = currentPt; + + xformedPt.homogenize(); + xformedPt *= alignmentMatrix; + + float xformedZ = (float)xformedPt.z; + + if ((closestGuideIndex < 0) + || (xformedZ < closestXformedZ)) + { + closestGuideIndex = guideIndex; + closestVertIndex = i; + closestPt = currentPt; + closestXformedZ = xformedZ; + } + } + else + { + // On multiple selection we store all hits. + // + if (entireGuide) + { + guideCompFn.addElement(guideIndex); + break; + } + else + vertCompFn.addElement(guideIndex, i); + } + } + } + } + + // If we were doing singleSelection then store the closest hit that we + // got. + // + if (closestGuideIndex >= 0) + { + if (entireGuide) + guideCompFn.addElement(closestGuideIndex); + else + vertCompFn.addElement(closestGuideIndex, closestVertIndex); + + // Convert the hit point to worldspace. + // + closestPt *= path.inclusiveMatrix(); + } + + bool haveSelection = !guideCompFn.isEmpty() || !vertCompFn.isEmpty(); + + if (haveSelection) + { + MSelectionList newSelections; + + newSelections.add(path, compObj); + + MSelectionMask mask(MSelectionMask::kSelectComponentsMask); + + selectInfo.addSelection( + newSelections, + closestPt, + selectionList, + worldSpaceSelectPts, + mask, + true + ); + } + + // Restore the point size. + // + view.beginGL(); + glPointSize(oldPointSize); + view.endGL(); + + return haveSelection; +} + + +// +// Maya Bug: In Maya 6.5 MDrawRequest::setDisplayStatus() doesn't work, +// so we have to pass the display status as a separate arg +// rather than the caller setting it in 'request'. +// +void shaveHairUI::setWireframeColor( + MDrawRequest& request, M3dView& view, M3dView::DisplayStatus dStatus +) const +{ + M3dView::ColorTable colorTable = M3dView::kDormantColors; + int colorIndex = 0; + + switch (dStatus) + { + case M3dView::kActive: + colorIndex = ACTIVE_SURFACE_COLOR; + colorTable = M3dView::kActiveColors; + break; + + case M3dView::kActiveComponent: + colorIndex = DORMANT_SURFACE_COLOR; + colorTable = M3dView::kDormantColors; + break; + + case M3dView::kHilite: + colorIndex = HILITE_COLOR; + colorTable = M3dView::kActiveColors; + break; + + case M3dView::kTemplate: + colorIndex = 0; + colorTable = M3dView::kTemplateColor; + break; + + case M3dView::kActiveTemplate: + colorIndex = ACTIVE_TEMPLATE_COLOR; + colorTable = M3dView::kActiveColors; + break; + + case M3dView::kLead: + colorIndex = LEAD_COLOR; + colorTable = M3dView::kActiveColors; + break; + + case M3dView::kActiveAffected: + colorIndex = ACTIVE_AFFECTED_COLOR; + colorTable = M3dView::kActiveColors; + break; + + case M3dView::kLive: + // + // %%% This is not correct. If we think we might allow + // hair to become a live construction surface, then we + // should determine the correct settings. + // + colorIndex = ACTIVE_SURFACE_COLOR; + colorTable = M3dView::kActiveColors; + break; + + case M3dView::kDormant: + { + // + // If our drawing overrides are on, then use the specified + // override colour. + // + MFnDependencyNode nodeFn(surfaceShape()->thisMObject()); + bool colourOverridden; + + colorIndex = -1; + + MPlug plug = nodeFn.findPlug("overrideEnabled"); + plug.getValue(colourOverridden); + + if (colourOverridden) + { + plug = nodeFn.findPlug("overrideColor"); + plug.getValue(colorIndex); + + // + // The colour index contained in the 'overrideColor' + // attribute is one greater than the actual colour index + // (so that 0 can represent the override colour being + // disabled). To get the correct colour index, we must + // decrement the value just retrieved by 1. + // + --colorIndex; + } + + // + // If no override colour was specified, then check to + // see if we've been assigned a user-defined colour. + // + if (colorIndex == -1) + { + plug.setAttribute(shaveHairShape::useObjectColor); + plug.getValue(colourOverridden); + + if (colourOverridden) + { + int userIndex; + + plug.setAttribute(shaveHairShape::objectColor); + plug.getValue(userIndex); + + colorIndex = view.userDefinedColorIndex( + userIndex + ); + } + else + { + // + // None of the various overrides are in use, so + // just draw the wireframe in the default + // dormant surface colour. + // + colorIndex = DORMANT_SURFACE_COLOR; + } + } + + colorTable = M3dView::kDormantColors; + } + break; + + case M3dView::kInvisible: + case M3dView::kIntermediateObject: + case M3dView::kNoStatus: + default: + // Nothing to draw. + return; + } + + request.setColor(colorIndex, colorTable); +} diff --git a/mayaPlug/shaveHairUI.h b/mayaPlug/shaveHairUI.h new file mode 100644 index 0000000..739d37f --- /dev/null +++ b/mayaPlug/shaveHairUI.h @@ -0,0 +1,713 @@ +#ifndef shaveHairUI_h +#define shaveHairUI_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxSurfaceShapeUI.h> + +#ifdef OSMac_MachO_ +# include <OpenGL/gl.h> +# include <OpenGL/glu.h> +#elif defined LINUX +# include <GL/gl.h> +# include <GL/glu.h> +//# include <GL/glext.h> +# include <GL/glx.h> +#else +# include <GL/gl.h> +# include <GL/glu.h> +#endif + +#if MAYA_API_VERSION < 20180000 +class M3dView; +class MDrawInfo; +class MDrawRequest; +#endif + +#define NEW_DISPLAY +#define NEW_INSTNACE_DISPLAY + +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif + +#ifndef GL_REPEAT +#define GL_REPEAT 0x2901 +#endif + +#ifndef GL_TEXTURE0 +#define GL_TEXTURE0 0x84C0 +#endif + +/// glsl defines + +#ifndef GL_VERTEX_SHADER +#define GL_VERTEX_SHADER 0x8B31 +#endif + +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif + +#ifndef GL_COMPILE_STATUS +#define GL_COMPILE_STATUS 0x8B81 +#endif + +#ifndef GL_VALIDATE_STATUS +#define GL_VALIDATE_STATUS 0x8B83 +#endif + +#ifndef GL_INFO_LOG_LENGTH +#define GL_INFO_LOG_LENGTH 0x8B84 +#endif + +#ifndef GL_LINK_STATUS +#define GL_LINK_STATUS 0x8B82 +#endif + +//BFO defines + +#ifndef GL_FRAMEBUFFER_EXT +#define GL_FRAMEBUFFER_EXT 0x8D40 +#endif + +#ifndef GL_RENDERBUFFER_EXT +#define GL_RENDERBUFFER_EXT 0x8D41 +#endif + +#ifndef GL_COLOR_ATTACHMENT0_EXT +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#endif + +#ifndef GL_COLOR_ATTACHMENT1_EXT +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#endif + +#ifndef GL_COLOR_ATTACHMENT2_EXT +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#endif + +#ifndef GL_COLOR_ATTACHMENT3_EXT +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#endif + +#ifndef GL_COLOR_ATTACHMENT4_EXT +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#endif + +#ifndef GL_DEPTH_ATTACHMENT_EXT +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#endif + +#ifndef GL_STENCIL_ATTACHMENT_EXT +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#endif + +#ifndef GL_MAX_COLOR_ATTACHMENTS_EXT +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#endif + +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#endif + +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#endif + +#ifndef GL_RENDERBUFFER_WIDTH_EXT +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#endif + +#ifndef GL_RENDERBUFFER_HEIGHT_EXT +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#endif + +#ifndef GL_RENDERBUFFER_INTERNAL_FORMAT_EXT +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#endif + +#ifndef GL_FRAMEBUFFER_COMPLETE_EXT +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#endif + +#ifndef GL_FRAMEBUFFER_UNSUPPORTED_EXT +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#endif + +#ifndef GL_READ_FRAMEBUFFER_EXT +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#endif + +#ifndef GL_DRAW_FRAMEBUFFER_EXT +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#endif + +#ifdef NEW_INSTNACE_DISPLAY + +#if defined(WIN32) || defined(LINUX) +#ifndef GL_ACTIVE_TEXTURE +typedef void (APIENTRY * PFNGLATCIVETEXTUREPROC) (GLenum texture); +extern PFNGLATCIVETEXTUREPROC glActiveTexture; +#endif +#endif + +#if defined WIN32 +# define getGlFncPtr(name) wglGetProcAddress((LPCSTR)name) +#elif defined OSMac_ +// nothing is defined for osx +// # define getGlFncPtr(name) aglGetProcAddress(name) +#elif defined LINUX +# ifdef GLX_GLXEXT_LEGACY +# define getGlFncPtr(name) (*glXGetProcAddressARB)((const GLubyte*)name) +# else +# define getGlFncPtr(name) (*glXGetProcAddress)((const GLubyte*)name) +# endif +#endif + +#endif + +///////////////////////////// Profiling ////////////////////////////////////// + +//#define DO_PROFILE + +#ifdef DO_PROFILE +namespace Profile { +class LStrFile; +LStrFile* OpenDiagFile(); +LStrFile* GetDiagFile(); +void ProfileStart(LStrFile* f); +void ProfileDump(char* msg, LStrFile* f); +void ProfileEnd(LStrFile* f); +} +#endif +///////////////////////////////////////////////////////////////////////////// + +bool GLFunctionsInited(); +bool InitGLFunctionPointers(); + +////////////////////////////// GLSL /////////////////////////////////////////// + +class glslShader { +public: + virtual ~glslShader(){} + + virtual bool LoadShaders() = 0; + virtual void UnLoadShaders() = 0; + + virtual bool UseProgram() = 0; + virtual bool UnUseProgram() = 0; +}; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslGenericProgram - generic glsl shader program | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +class glslGenericProgram : public glslShader { +public: + + + glslGenericProgram(); + virtual ~glslGenericProgram(); + + bool IsVadlid() const; + + //loads shaders from external files and preparing the stuff for run + bool LoadShaders(char* vShaderFile, char* fShaderFile); + + //loads build-in shaders and prepares stuff for run; + bool LoadShaders(); + void UnLoadShaders(); + + bool UseProgram(); + bool UnUseProgram(); + + //misc parameters + bool AddLight(); + bool SetLightDir(int idx, float x, float y, float z); + bool SetLightDir(int idx, const MVector& p); + bool SetLightPos(int idx, float x, float y, float z); + bool SetLightPos(int idx, const MVector& p); + + int GetNumLigths() const; + const MVector& GetLightDir(int idx) const; + const MVector& GetLightPos(int idx) const; + + GLuint GetProgram() const {return program();} + + enum _e_parameters + { + eMaxNumLights = 6 + }; + +protected: + + bool s_vertexErrDumped; + bool s_fragErrDumped; + bool s_linkErrDumped; + bool s_varErrDumped; + + + //shader and program generation + + enum + { + eVertShader, + eFragShader + }; + bool loadShader( GLuint shader, char* fn); //loades shader from external file + bool compileShader(GLuint shader, int vert_or_frag);//vert_or_frags is used to reduce error dumps + bool linkProgram (GLuint glprog); + + virtual void loadVertShader(GLuint shader) = 0; //loads internal vert shader + virtual void loadFragShader(GLuint shader) = 0; //loads internal frag shader + + //setups + virtual bool setupUniformVariables() = 0; + + + //const member acces + inline const MVector& lightDir(int i) const {return m_lightDir[i];} + inline int numLights() const {return m_numLights;} + inline const MVector& lightPos(int i) const {return m_lightPos[i];} + inline GLuint vShader() const {return m_vShader;} + inline GLuint fShader() const {return m_fShader;} + inline GLuint program() const {return m_program;} + + //member access + inline MVector& _lightDir(int i) {return m_lightDir[i];} + inline int& _numLights() {return m_numLights;} + inline MVector& _lightPos(int i) {return m_lightPos[i];} + inline GLuint& _vShader() {return m_vShader;} + inline GLuint& _fShader() {return m_fShader;} + inline GLuint& _program() {return m_program;} + +private: + + GLuint m_vShader; //view render shader + GLuint m_fShader; //view render shader + GLuint m_program; //veiw render program + + MVector m_lightDir[eMaxNumLights]; + int m_numLights; + + MVector m_lightPos[eMaxNumLights]; +}; + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslHairProgram - default glsl shader program | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +class glslHairProgram : public glslGenericProgram { +public: + glslHairProgram(); + ~glslHairProgram(); + + inline GLint GetTangentLoc() const {return tangentLoc();} + + void SetSpecularTint(const MColor& c) {_specTint() = c;} + void SetSpecularTint2(const MColor& c) {_specTint2() = c;} + + //void SetUseSpecular2 (bool b) {_useSpec2() = b;} + //bool GetUseSpecular2() {return useSpec2();} + + void SetAmbDiff (float f) {_ambdiff() = f;} + float GetAmbDiff() {return ambdiff();} + + void SetSpecular (float f) { + _specular() = f; + //printf("specular (glossines) %f\n",f);fflush(stdout); + } + float GetSpecular() {return specular();} + + void SetSpecularAmt (float f) { + _specAmt() = f; + //printf("specularAmt %f\n",f);fflush(stdout); + } + float GetSpecularAmt() {return specAmt();} + + inline void SetUseSSAO(bool use){_useSSAO() = use;} + inline bool GetUseSSAO() const { return useSSAO(); } + + inline void SetViewSize(int w, int h){_viewWidth()=(float)w; _viewHeight()=(float)h;} + inline void SetUseLights(bool use, int num) {_useLights() = use; _numLights() = num;} + +protected: + //from glslGenericProgram + + void loadVertShader(GLuint shader); //loads internal vert shader + void loadFragShader(GLuint shader); //loads internal frag shader + void loadVertShadowShader(GLuint shader); //loads internal vert shader + void loadFragShadowShader(GLuint shader); //loads internal frag shader + + bool setupUniformVariables(); + + inline const MColor& specTint() const {return m_specTint;} + inline const MColor& specTint2() const {return m_specTint2;} + inline bool useSpec2() const {return m_useSpec2;} + inline float ambdiff() const {return m_ambdiff;} + inline float specular() const {return m_specular;} + inline float specAmt() const {return m_specAmt;} + inline bool useSSAO() const {return m_useSSAO;} + inline float viewWidth() const {return m_viewWidth;} + inline float viewHeight() const {return m_viewHeight;} + inline bool useLights() const {return m_useLights;} + inline int numLights() const {return m_numLights;} + + inline GLint tangentLoc() const {return m_tangentLoc;} + + inline MColor& _specTint() {return m_specTint;} + inline MColor& _specTint2() {return m_specTint2;} + inline bool& _useSpec2() {return m_useSpec2;} + inline float& _ambdiff() {return m_ambdiff;} + inline float& _specular() {return m_specular;} + inline float& _specAmt() {return m_specAmt;} + inline bool& _useSSAO() {return m_useSSAO;} + inline float& _viewWidth() {return m_viewWidth;} + inline float& _viewHeight(){return m_viewHeight;} + inline bool& _useLights() {return m_useLights;} + inline int& _numLights() {return m_numLights;} + inline GLint& _tangentLoc() {return m_tangentLoc;} + +private: + MColor m_specTint; + MColor m_specTint2; + bool m_useSpec2; + float m_ambdiff; + float m_specular; + float m_specAmt; + bool m_useSSAO; + float m_viewWidth; + float m_viewHeight; + bool m_useLights; + int m_numLights; + GLint m_tangentLoc; +}; + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslSSAOdepthShader - SSAO pass 1 | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +class glslSSAOdepthShader : public glslGenericProgram { +public: + glslSSAOdepthShader(){} + virtual ~glslSSAOdepthShader(){} + + void SetFarNear(GLfloat f, GLfloat n){_zfar() = f; _znear() = n;} + +protected: + //from glslGenericProgram + // + void loadVertShader(GLuint shader); //loads internal vert shader + void loadFragShader(GLuint shader); //loads internal frag shader + + bool setupUniformVariables(); + + //const member access + inline GLfloat zfar() const {return m_zfar;} + inline GLfloat znear() const {return m_znear;} + + //member access + inline GLfloat& _zfar() {return m_zfar;} + inline GLfloat& _znear() {return m_znear;} + +private: + GLfloat m_zfar; + GLfloat m_znear; +}; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| glslSSAOshader - for ssao pass 2 | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +class glslSSAOshader : public glslGenericProgram { +public: + glslSSAOshader(); + virtual ~glslSSAOshader(){} + + void SetRadius (GLfloat r) {_radius() = r;} + void SetSamples(GLint s) {_samples() = s;} + +protected: + //from glslGenericProgram + // + void loadVertShader(GLuint shader); //loads internal vert shader + void loadFragShader(GLuint shader); //loads internal frag shader + + bool setupUniformVariables(); + + ////const member accesss + inline GLfloat radius() const {return m_radius;} + inline GLint samples()const {return m_samples;} + + ////member access + inline GLfloat& _radius() {return m_radius;} + inline GLint& _samples(){return m_samples;} + +private: + GLfloat m_radius; + GLint m_samples; +}; + +//////////////////////////// SSAO //////////////////////////////////////////// + +class Matrix4 { +public: + float entries[16]; + + Matrix4(){ LoadIdentity(); } + Matrix4(float e0, float e1, float e2, float e3, + float e4, float e5, float e6, float e7, + float e8, float e9, float e10, float e11, + float e12, float e13, float e14, float e15) + { + entries[0]=e0; + entries[1]=e1; + entries[2]=e2; + entries[3]=e3; + entries[4]=e4; + entries[5]=e5; + entries[6]=e6; + entries[7]=e7; + entries[8]=e8; + entries[9]=e9; + entries[10]=e10; + entries[11]=e11; + entries[12]=e12; + entries[13]=e13; + entries[14]=e14; + entries[15]=e15; + } + Matrix4(const float * rhs) + { + memcpy(entries, rhs, 16*sizeof(float)); + } + Matrix4(const Matrix4& rhs) + { + memcpy(entries, rhs.entries, 16*sizeof(float)); + } + void LoadIdentity() + { + memset(entries, 0, 16*sizeof(float)); + entries[0]=1.0f; + entries[5]=1.0f; + entries[10]=1.0f; + entries[15]=1.0f; + } + void LoadZero() + { + memset(entries, 0, 16*sizeof(float)); + } + //cast to pointer to a (float *) for glGetFloatv etc + operator float* () const {return (float*) this;} + operator const float* () const {return (const float*) this;} +}; + +class ssaoFBO { +public: + ssaoFBO(unsigned int w = 512, unsigned int h = 512); + ~ssaoFBO(); + + void Init(); + + bool MakeCurrent(); + + bool ResetCurrent(); + bool ActivateTexture(int samplerIdx); + + void Destroy(); + + GLuint GetTexHandle() const { return texid(); } + unsigned int GetWidth() const { return width(); } + unsigned int GetHeight() const { return height(); } + bool IsInitOK() const { return initOK(); } + +protected: + + //const membe access + inline unsigned int width() const {return m_width;} + inline unsigned int height() const {return m_height;} + inline GLuint texid() const {return m_texid;} + inline GLuint fboid() const {return m_fboid;} + inline GLuint rboid() const {return m_rboid;} + + inline const Matrix4& cameraProjectionMatrix()const {return m_cameraProjectionMatrix; } + inline const Matrix4& cameraViewMatrix() const {return m_cameraViewMatrix; } + inline GLint* origView() const {return m_origView;} + inline bool initOK() const {return m_initOK;} + + //member access + inline unsigned int& _width() {return m_width;} + inline unsigned int& _height() {return m_height;} + inline GLuint& _texid() {return m_texid;} + inline GLuint& _fboid() {return m_fboid;} + inline GLuint& _rboid() {return m_rboid;} + + inline Matrix4& _cameraProjectionMatrix(){return m_cameraProjectionMatrix; } + inline Matrix4& _cameraViewMatrix() {return m_cameraViewMatrix; } + inline GLint*& _origView() {return m_origView;} + inline bool& _initOK() {return m_initOK;} + +private: + + unsigned int m_width; + unsigned int m_height; + GLuint m_texid; + GLuint m_fboid; //frame buffer object id + GLuint m_rboid; //render buffer object id + + GLuint m_depthRB; //render buffer object id + GLuint m_colorRB; //render buffer object id + GLuint m_resolveFB; //render buffer object id + + + Matrix4 m_cameraProjectionMatrix; + Matrix4 m_cameraViewMatrix; + + GLint* m_origView; + bool m_initOK; + +}; + + + +////////////////////// LEGACY DISPLAY STUFF ////////////////////////////////// +extern bool incrementaldraw; + +class shaveHairUI : public MPxSurfaceShapeUI +{ +public: + enum + { + kDrawNone, + kDrawBBox, + kDrawGuides, + kDrawActiveGuides, + kDrawInactiveGuides, + kDrawHairs, + kDrawActiveShadedHairs, + kDrawInactiveShadedHairs, + kDrawActiveRoots, + kDrawInactiveRoots, + kDrawActiveTips, + kDrawInactiveTips, + kDrawActiveVerts, + kDrawInactiveVerts, + kDrawInstancedWire, + kDrawInstancedShaded, + kDrawInstancedShadedOffset, + //////// incremental update trick /////// + kFirstCacheUpdate, + kNextCacheUpdate + }; + + static void* creator() + { + InitGlsl(); + return new shaveHairUI; + } + static bool InitGlsl(); + + + virtual void draw(const MDrawRequest& request, M3dView& view) const; + + virtual void getDrawRequests( + const MDrawInfo& drawInfo, + bool objAndActive, + MDrawRequestQueue& requests + ); + + virtual bool select( + MSelectInfo &selectInfo, + MSelectionList &selectionList, + MPointArray &worldSpaceSelectPts + ) const; + + static void setBrushActive(bool isActive); + +protected: + // + // Utility Methods + // + void drawBoundingBox() const; + void drawGuides(int drawToken, M3dView& view) const; + void drawHairs(int drawToken, M3dView& view) const; + //ssao passes + void drawSsaoDepth( int drawToken, M3dView& view, const shaveHairShape::DisplayHairCache& cache, const MVector& viewDir) const; + void drawSsao( int drawToken, M3dView& view, const shaveHairShape::DisplayHairCache& cache, const MVector& viewDir ) const ; +#ifdef NEW_INSTNACE_DISPLAY + void drawInstances(int drawToken, M3dView& view) const; +#endif + void drawVerts(int drawToken) const; + + bool selectGuides( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts + ) const; + + bool selectObjByGuides( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts + ) const; + + bool selectObjByHairs( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts + ) const; + + bool selectObjByGeom( + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts + ) const; + + bool selectVerts( + MString selectMode, + MSelectInfo& selectInfo, + MSelectionList& selectionList, + MPointArray& worldSpaceSelectPts + ) const; + + void setWireframeColor( + MDrawRequest& request, + M3dView& view, + M3dView::DisplayStatus dStatus + ) const; + + static bool mBrushIsActive; + + +}; + +#endif diff --git a/mayaPlug/shaveHaircut-vs11.sln b/mayaPlug/shaveHaircut-vs11.sln new file mode 100644 index 0000000..e597f59 --- /dev/null +++ b/mayaPlug/shaveHaircut-vs11.sln @@ -0,0 +1,53 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaveHaircut-vs11", "shaveHaircut-vs11.vcxproj", "{059E163A-F149-48FD-AE3A-000B2BD00725}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libShave-vs11", "libShave-vs11.vcxproj", "{2CCDF057-5380-4CE8-ABD7-3D6B21032B49}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libShaveAPI-vs11", "libShaveAPI-vs11.vcxproj", "{233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug20170|x64 = Debug20170|x64 + RelDbg20170|x64 = RelDbg20170|x64 + Release|x64 = Release|x64 + Release20170|x64 = Release20170|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug|x64.ActiveCfg = Debug20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug|x64.Build.0 = Debug20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug20170|x64.ActiveCfg = Debug20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug20170|x64.Build.0 = Debug20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.RelDbg20170|x64.ActiveCfg = RelDbg20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.RelDbg20170|x64.Build.0 = RelDbg20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release|x64.ActiveCfg = Release|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release|x64.Build.0 = Release|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release20170|x64.ActiveCfg = Release20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release20170|x64.Build.0 = Release20170|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug|x64.ActiveCfg = Debug|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug|x64.Build.0 = Debug|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug20170|x64.ActiveCfg = Debug20170|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug20170|x64.Build.0 = Debug20170|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.RelDbg20170|x64.ActiveCfg = RelDbg20170|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.RelDbg20170|x64.Build.0 = RelDbg20170|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release|x64.ActiveCfg = Release|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release|x64.Build.0 = Release|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release20170|x64.ActiveCfg = Release20170|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release20170|x64.Build.0 = Release20170|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug|x64.ActiveCfg = Debug|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug|x64.Build.0 = Debug|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug20170|x64.ActiveCfg = Debug20170|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug20170|x64.Build.0 = Debug20170|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.RelDbg20170|x64.ActiveCfg = RelDbg20170|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.RelDbg20170|x64.Build.0 = RelDbg20170|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release|x64.ActiveCfg = Release|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release|x64.Build.0 = Release|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release20170|x64.ActiveCfg = Release20170|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release20170|x64.Build.0 = Release20170|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mayaPlug/shaveHaircut-vs11.vcxproj b/mayaPlug/shaveHaircut-vs11.vcxproj new file mode 100644 index 0000000..4b5836e --- /dev/null +++ b/mayaPlug/shaveHaircut-vs11.vcxproj @@ -0,0 +1,234 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug20170|x64"> + <Configuration>Debug20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelDbg20170|x64"> + <Configuration>RelDbg20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20170|x64"> + <Configuration>Release20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClCompile Include="plugin.cpp" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{059E163A-F149-48FD-AE3A-000B2BD00725}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>shaveHaircutvs10</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\debug\20170\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SHAVEHAIRCUTVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEHAIRCUTVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\release\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\release\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\debug\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/shaveHaircut-vs11.vcxproj.filters b/mayaPlug/shaveHaircut-vs11.vcxproj.filters new file mode 100644 index 0000000..ef776a7 --- /dev/null +++ b/mayaPlug/shaveHaircut-vs11.vcxproj.filters @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="plugin.cpp"> + <Filter>Source</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/mayaPlug/shaveHaircut-vs14.sln b/mayaPlug/shaveHaircut-vs14.sln new file mode 100644 index 0000000..198b1b1 --- /dev/null +++ b/mayaPlug/shaveHaircut-vs14.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaveHaircut-vs14", "shaveHaircut-vs14.vcxproj", "{059E163A-F149-48FD-AE3A-000B2BD00725}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libShave-vs14", "libShave-vs14.vcxproj", "{2CCDF057-5380-4CE8-ABD7-3D6B21032B49}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libShaveAPI-vs14", "libShaveAPI-vs14.vcxproj", "{233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug20180|x64 = Debug20180|x64 + RelDbg20180|x64 = RelDbg20180|x64 + Release|x64 = Release|x64 + Release20180|x64 = Release20180|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug|x64.ActiveCfg = Debug20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug|x64.Build.0 = Debug20170|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug20180|x64.ActiveCfg = Debug20180|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Debug20180|x64.Build.0 = Debug20180|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.RelDbg20180|x64.ActiveCfg = RelDbg20180|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.RelDbg20180|x64.Build.0 = RelDbg20180|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release|x64.ActiveCfg = Release|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release|x64.Build.0 = Release|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release20180|x64.ActiveCfg = Release20180|x64 + {059E163A-F149-48FD-AE3A-000B2BD00725}.Release20180|x64.Build.0 = Release20180|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug|x64.ActiveCfg = Debug|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug|x64.Build.0 = Debug|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug20180|x64.ActiveCfg = Debug20180|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Debug20180|x64.Build.0 = Debug20180|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.RelDbg20180|x64.ActiveCfg = RelDbg20180|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.RelDbg20180|x64.Build.0 = RelDbg20180|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release|x64.ActiveCfg = Release|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release|x64.Build.0 = Release|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release20180|x64.ActiveCfg = Release20180|x64 + {2CCDF057-5380-4CE8-ABD7-3D6B21032B49}.Release20180|x64.Build.0 = Release20180|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug|x64.ActiveCfg = Debug|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug|x64.Build.0 = Debug|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug20180|x64.ActiveCfg = Debug20180|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Debug20180|x64.Build.0 = Debug20180|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.RelDbg20180|x64.ActiveCfg = RelDbg20180|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.RelDbg20180|x64.Build.0 = RelDbg20180|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release|x64.ActiveCfg = Release|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release|x64.Build.0 = Release|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release20180|x64.ActiveCfg = Release20180|x64 + {233456CC-4A2B-4DD9-A2B4-80B4DCD0255C}.Release20180|x64.Build.0 = Release20180|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mayaPlug/shaveHaircut-vs14.vcxproj b/mayaPlug/shaveHaircut-vs14.vcxproj new file mode 100644 index 0000000..37b54d2 --- /dev/null +++ b/mayaPlug/shaveHaircut-vs14.vcxproj @@ -0,0 +1,339 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug20170|x64"> + <Configuration>Debug20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug20180|x64"> + <Configuration>Debug20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelDbg20180|x64"> + <Configuration>RelDbg20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20170|x64"> + <Configuration>Release20170</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release20180|x64"> + <Configuration>Release20180</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClCompile Include="plugin.cpp" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{059E163A-F149-48FD-AE3A-000B2BD00725}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>shaveHaircutvs10</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20170\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20180\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\release\20180\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\debug\20170\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\raw64\debug\20180\</OutDir> + <TargetName>shaveNode</TargetName> + <TargetExt>.mll</TargetExt> + <IntDir>$(Platform)\$(Configuration)\tmp3\</IntDir> + <PostBuildEventUseInBuild>false</PostBuildEventUseInBuild> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SHAVEHAIRCUTVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>Use</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEHAIRCUTVS10_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\release\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2018\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2018\lib;..\raw64\release\20180\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelDbg20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2018\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2018\lib;..\raw64\release\20180\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20170|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2017\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2017\lib;..\raw64\debug\20170\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug20180|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>false</IntrinsicFunctions> + <PreprocessorDefinitions>REQUIRE_IOSTREAM;NDEBUG;WIN32;_WINDOWS;NT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.;..\libexe\sample\include;$(AUTODESK_LOCATION)\Maya2018\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <StringPooling>true</StringPooling> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(IntDir);$(AUTODESK_LOCATION)\Maya2018\lib;..\raw64\debug\20180\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>libShave.lib;Foundation.lib;OpenMaya.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalOptions>/export:initializePlugin /export:uninitializePlugin %(AdditionalOptions)</AdditionalOptions> + </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/shaveIO.h b/mayaPlug/shaveIO.h new file mode 100644 index 0000000..1bd99f9 --- /dev/null +++ b/mayaPlug/shaveIO.h @@ -0,0 +1,32 @@ +#ifndef shaveIO_h +#define shaveIO_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MFStream.h> +#include <maya/MIOStream.h> + +// +// Carbon apps on OSX don't understand 'cerr', so we have to work around it. +// +#ifdef OSMac_ +#include <maya/MString.h> + +class shaveCERR +{ +public: + shaveCERR& operator<<(ostream& (*pf)(ostream&)) { return *this; } + shaveCERR& operator<<(const char* str) { return *this; } + shaveCERR& operator<<(MString str) { return *this; } + shaveCERR& operator<<(int) { return *this; } + shaveCERR& operator<<(unsigned int) { return *this; } + shaveCERR& operator<<(float) { return *this; } +}; + +static shaveCERR cmsg; +#else +#define cmsg cerr +#endif + +#endif diff --git a/mayaPlug/shaveIcon.cpp b/mayaPlug/shaveIcon.cpp new file mode 100644 index 0000000..6c1aaf7 --- /dev/null +++ b/mayaPlug/shaveIcon.cpp @@ -0,0 +1,98 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#ifndef OSMac_ +#include <sys/types.h> +#else +#include "shaveMacCarbon.h" +#endif + +#include <sys/stat.h> + +#ifndef _WIN32 +#include <unistd.h> +#endif + +#include <maya/MIntArray.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> + +#include "shaveIcon.h" + +MStringArray shaveIcon::mIcons; +MIntArray shaveIcon::mTimestamps; + + +// +// See header file for documentation. +// +bool shaveIcon::needsReload(MString iconFile) +{ +#ifdef _WIN32 + struct _stat fileInfo; + bool fileExists = (_stat(iconFile.asChar(), &fileInfo) == 0); +#else + struct stat fileInfo; + +# if defined(OSMac_) && !defined(OSMac_MachO_) + MString hfsFileName = shaveMacCarbon::makeMacFilename(iconFile); + bool fileExists = (stat(hfsFileName.asChar(), &fileInfo) == 0); +#else + bool fileExists = (stat(iconFile.asChar(), &fileInfo) == 0); +# endif +#endif + bool reload = false; + + unsigned int i; + unsigned int numEntries = mIcons.length(); + + for (i = 0; i < numEntries; i++) + if (mIcons[i] == iconFile) break; + + if (fileExists) + { + if (i == numEntries) + { + // + // New icon. + // + mIcons.append(iconFile); + mTimestamps.append((long)fileInfo.st_mtime); + } + else + { + if (fileInfo.st_mtime != mTimestamps[i]) + { + // + // File has been modified since we last saw it, so update + // its timestamp and force a reload. + // + mTimestamps[i] = (long)fileInfo.st_mtime; + reload = true; + } + } + } + else + { + if (i < numEntries) + { + // + // This file used to exist, but no longer does, so force a + // reload and reset the timestamp to zero so that if it shows + // up again, we'll force another reload. + // + reload = true; + mTimestamps[i] = 0L; + } + } + + return reload; +} + + +void shaveIcon::cleanup() +{ + mIcons.clear(); + mTimestamps.clear(); +} diff --git a/mayaPlug/shaveIcon.h b/mayaPlug/shaveIcon.h new file mode 100644 index 0000000..0e78e7f --- /dev/null +++ b/mayaPlug/shaveIcon.h @@ -0,0 +1,40 @@ +#ifndef shaveIcon_h +#define shaveIcon_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MIntArray.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> + + +class shaveIcon +{ +public: + static void cleanup(); + + // + // Whenever Maya loads a new icon, it caches the image and never looks + // at the file again. This can be annoying when you are using icons as + // swatches which can be updated during the course of a session. + // + // Maya does provide a 'reloadImage' MEL command to reload an icon from + // its file, into a specific UI control. However, there is no way to + // determine whether the icon *needs* to be reloaded. So you're stuck + // with always doing the reload, which results in doubled load time the + // first time the icon is used, and an extra load even when it hasn't + // changed. + // + // This method gets around that by allowing you to register an icon + // whenever you use it. It compares timestamps and returns true if the + // icon needs to be reloaded. + // + static bool needsReload(MString iconFile); + +protected: + static MStringArray mIcons; + static MIntArray mTimestamps; +}; + +#endif diff --git a/mayaPlug/shaveIconCmd.cpp b/mayaPlug/shaveIconCmd.cpp new file mode 100644 index 0000000..9e76256 --- /dev/null +++ b/mayaPlug/shaveIconCmd.cpp @@ -0,0 +1,65 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MGlobal.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +#include "shaveGlobals.h" +#include "shaveIcon.h" +#include "shaveIconCmd.h" + + +const MString shaveIconCmd::commandName("shaveIcon"); + +static const char* kNeedsReloadLong = "-needsReload"; +static const char* kNeedsReloadShort = "-nr"; + + +shaveIconCmd::shaveIconCmd() {} +shaveIconCmd::~shaveIconCmd() {} + + +void* shaveIconCmd::createCmd() +{ + return new shaveIconCmd(); +} + + +MSyntax shaveIconCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(true); + + syntax.addFlag(kNeedsReloadShort, kNeedsReloadLong, MSyntax::kString); + + return syntax; +} + + +MStatus shaveIconCmd::doIt(const MArgList& argList) +{ + MStatus status; + MArgDatabase args(syntax(), argList, &status); + + if (!status) return status; + + shaveGlobals::getGlobals(); + + if (args.isFlagSet(kNeedsReloadShort)) + { + MString iconFile; + + args.getFlagArgument(kNeedsReloadShort, 0, iconFile); + setResult(shaveIcon::needsReload(iconFile)); + } + + return status; +} diff --git a/mayaPlug/shaveIconCmd.h b/mayaPlug/shaveIconCmd.h new file mode 100644 index 0000000..e2fea4a --- /dev/null +++ b/mayaPlug/shaveIconCmd.h @@ -0,0 +1,35 @@ +#ifndef shaveIconCmd_h +#define shaveIconCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +class shaveIconCmd : public MPxCommand +{ +public: + shaveIconCmd(); + virtual ~shaveIconCmd(); + + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + + static void* createCmd(); + static MSyntax createSyntax(); + + static const MString commandName; + +private: +}; + + +inline bool shaveIconCmd::isUndoable() const +{ return false; } + +#endif + diff --git a/mayaPlug/shaveInfo.cpp b/mayaPlug/shaveInfo.cpp new file mode 100644 index 0000000..6e98b21 --- /dev/null +++ b/mayaPlug/shaveInfo.cpp @@ -0,0 +1,171 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MGlobal.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveInfo.h" +#include "shaveNode.h" +#include "shaveSDK.h" + +static const char* defaultGlobalsFlagShort = "-dg"; +static const char* defaultGlobalsFlagLong = "-defaultGlobals"; +static const char* globalsFlagShort = "-g"; +static const char* globalsFlagLong = "-globals"; +static const char* globalsVersionFlagShort = "-gv"; +static const char* globalsVersionFlagLong = "-globalsVersion"; +static const char* quietFlagShort = "-qt"; +static const char* quietFlagLong = "-quiet"; +static const char* shaveHairVersionFlagShort = "-shv"; +static const char* shaveHairVersionFlagLong = "-shaveHairVersion"; +static const char* shaveNodeVersionFlagShort = "-snv"; +static const char* shaveNodeVersionFlagLong = "-shaveNodeVersion"; +static const char* statDirFlagShort = "-sd"; +static const char* statDirFlagLong = "-statDir"; +static const char* versionFlagShort = "-v"; +static const char* versionFlagLong = "-version"; + + +const MString shaveInfo::commandName("shaveInfo"); + + +shaveInfo::shaveInfo() {} +shaveInfo::~shaveInfo() {} + + +void* shaveInfo::createCmd() +{ + return new shaveInfo(); +} + + +MSyntax shaveInfo::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(false); + + syntax.addFlag(defaultGlobalsFlagShort, defaultGlobalsFlagLong); + syntax.addFlag(globalsFlagShort, globalsFlagLong, MSyntax::kSelectionItem); + syntax.addFlag(globalsVersionFlagShort, globalsVersionFlagLong); + syntax.addFlag(quietFlagShort, quietFlagLong); + syntax.addFlag(shaveHairVersionFlagShort, shaveHairVersionFlagLong); + syntax.addFlag(shaveNodeVersionFlagShort, shaveNodeVersionFlagLong); + syntax.addFlag(statDirFlagShort, statDirFlagLong); + syntax.addFlag(versionFlagShort, versionFlagLong); + + return syntax; +} + + +MStatus shaveInfo::doIt(const MArgList& argList) +{ + MStatus status; + MArgDatabase args(syntax(), argList, &status); + + if (!status) return status; + + if (args.isFlagSet(globalsVersionFlagShort)) + { + setResult(shaveGlobals::kNodeVersion); + } + else if (args.isFlagSet(shaveHairVersionFlagShort)) + { + setResult(shaveHairShape::kNodeVersion); + } + else if (args.isFlagSet(shaveNodeVersionFlagShort)) + { + setResult(shaveNode::kNodeVersion); + } + else if (args.isFlagSet(versionFlagShort)) + { + setResult(SHAVEquery_version()); + } + else if (args.isFlagSet(statDirFlagShort)) + { + MString shaveStatDir; + MGlobal::executeCommand("shaveGetStatDir", shaveStatDir); + setResult(shaveStatDir); + } + else if (args.isFlagSet(globalsFlagShort)) + { + // + // Get the specified node. + // + MSelectionList selection; + + args.getFlagArgument(globalsFlagShort, 0, selection); + + MObject node(MObject::kNullObj); + + if (!selection.isEmpty()) selection.getDependNode(0, node); + + MFnDependencyNode nodeFn(node, &status); + + // + // Make sure that it's a valid shaveGlobals node. + // + if (node.isNull() + || !status + || (nodeFn.typeId() != shaveGlobals::id)) + { + status = MS::kInvalidParameter; + + if (!args.isFlagSet(quietFlagShort)) + { + MGlobal::displayError( + "The specified object is not a valid shaveGlobals node." + ); + } + } + else + { + // dump attrs + + MGlobal::displayWarning( + "Sorry, this function is not yet supported." + ); + } + } + else if (args.isFlagSet(defaultGlobalsFlagShort)) + { + MSelectionList list; + list.add(shaveGlobals::defaultNodeName); + + MObject node(MObject::kNullObj); + list.getDependNode(0, node); + + MFnDependencyNode nodeFn(node, &status); + + if (node.isNull() || !status || (nodeFn.typeId() != shaveGlobals::id)) + { + if (!args.isFlagSet(quietFlagShort)) + { + MGlobal::displayError( + "There is no default shaveGlobals node in the scene." + ); + } + + status = MS::kFailure; + } + else + { + // dump attrs + + MGlobal::displayWarning( + "Sorry, this function is not yet supported." + ); + } + } + + return status; +} diff --git a/mayaPlug/shaveInfo.h b/mayaPlug/shaveInfo.h new file mode 100644 index 0000000..2586c1b --- /dev/null +++ b/mayaPlug/shaveInfo.h @@ -0,0 +1,33 @@ +#ifndef shaveInfo_h +#define shaveInfo_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +class shaveInfo : public MPxCommand +{ +public: + shaveInfo(); + virtual ~shaveInfo(); + + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + + static void* createCmd(); + static MSyntax createSyntax(); + + static const MString commandName; +}; + + +inline bool shaveInfo::isUndoable() const +{ return false; } + +#endif + diff --git a/mayaPlug/shaveItHair.cpp b/mayaPlug/shaveItHair.cpp new file mode 100644 index 0000000..dd07c0b --- /dev/null +++ b/mayaPlug/shaveItHair.cpp @@ -0,0 +1,29 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MObjectArray.h> +#include <maya/MStatus.h> + +#include "shaveAPI.h" +#include "shaveItHair.h" +#include "shaveItHairImpl.h" + + +MStatus shaveItHair::clear() +{ return shaveItHairImpl::clear(); } + +MStatus shaveItHair::init(bool instances, bool renderableOnly) +{ return shaveItHairImpl::init(instances, renderableOnly); } + +MStatus shaveItHair::init(bool instances, MObjectArray& shaveNodes) +{ return shaveItHairImpl::init(instances, shaveNodes); } + +MStatus shaveItHair::nextHair(shaveAPI::HairInfo* hairInfo) +{ return shaveItHairImpl::nextHair(hairInfo); } + +MStatus shaveItHair::nextHairCounts(shaveAPI::HairInfo* hairInfo) +{ return shaveItHairImpl::nextHairCounts(hairInfo); } + +MStatus shaveItHair::reset() +{ return shaveItHairImpl::reset(); } diff --git a/mayaPlug/shaveItHair.h b/mayaPlug/shaveItHair.h new file mode 100644 index 0000000..2e62867 --- /dev/null +++ b/mayaPlug/shaveItHair.h @@ -0,0 +1,108 @@ +#ifndef shaveItHair_h +#define shaveItHair_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MObjectArray.h> +#include <maya/MStatus.h> + +#include "shaveAPI.h" + + +class LIBSHAVEAPI_API shaveItHair +{ +public: + // + // Clear the iterator and free up its resources. + // + // Status Value: + // + // Currently always returns MS::kSuccess + // + static MStatus clear(); + + // + // Set the iterator to iterate over all the hairs in the scene. + // + // If 'instances' is true then only shaveNodes with instancing will be + // included in the iteration. If false, then only non-instanced + // shaveNodes will be included. + // + // If 'renderable' is true then only hairs from renderable shaveNodes + // will be included in the iteration. + // + // Status Value: + // + // MS::kSuccess Success. + // + // MS::kNotFound The scene contains no shaveNodes, or only + // contains shaveNodes which do not match the + // restrictions implied by the 'instances' and + // 'renderableOnly' parameters. + // + static MStatus init(bool instances, bool renderableOnly); + + // + // Set the iterator to iterate over all the hairs from the shaveNodes + // in the 'shaveNodes' array. + // + // If 'instances' is true then only those shaveNodes in the array which + // have instance geometry will be included in the iteration. If false + // then only those shaveNodes which are *not* instanced will be + // included. + // + // Status Value: + // + // MS::kSuccess Success. + // + // MS::kNotFound The 'shaveNodes' array is empty or only + // contains node which do not correspond to + // the 'instances' parameter. + // + // MS::kInvalidParameter + // One or more of the objects in the + // 'shaveNodes' array is not a shaveNode. + // + static MStatus init(bool instances, MObjectArray& shaveNodes); + + // + // Returns a HairInfo structure for all of the next hair's strands. + // + // The vertex lists will contain either a single curve for each strand, + // or a set of polys for each strand. This is determined as follows: + // + // If the current iterator is operating on instanced shaveNodes, or if + // it is operating on non-instanced nodes but the render mode in Shave + // Globals is set to 'Geometry', then polys will be returned. + // + // Otherwise the current iterator must be operating on non-instanced + // nodes with a render mode of 'Buffer' which case curves will be returned. + // + static MStatus nextHair(shaveAPI::HairInfo* hairInfo); + + // + // Returns a HairInfo structure for the next hair which contains the + // counts for all of the hair's strands. The only fields in 'hairInfo' + // which will be valid are 'numHairs', 'numVertices' and + // 'numHairVertices', with 'numHairs' containing the number of strands + // in the hair, as determined by the shaveNode's multistrand setting. + // + // While this method does not return as much information as + // nextHairCurves(), it is must faster, so use it if you only need a + // hair's counts. + // + // Note that the vertex counts given are only correct for hairs + // returned as curves, not as polys. + // + static MStatus nextHairCounts(shaveAPI::HairInfo* hairInfo); + + // + // Resets the iterator back to the very first hair of the very first + // shaveNode. + // + static MStatus reset(); +}; + +#endif diff --git a/mayaPlug/shaveItHairImpl.cpp b/mayaPlug/shaveItHairImpl.cpp new file mode 100644 index 0000000..3cfaae0 --- /dev/null +++ b/mayaPlug/shaveItHairImpl.cpp @@ -0,0 +1,273 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MFloatArray.h> +#include <maya/MItMeshFaceVertex.h> +#include <maya/MObjectArray.h> +#include <maya/MStatus.h> + +#include "shaveAPI.h" +#include "shaveAPIimpl.h" +#include "shaveConstant.h" +#include "shaveGlobals.h" +#include "shaveIO.h" +#include "shaveItHairImpl.h" +#include "shaveHairShape.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveSDK.h" +#include "shaveUtil.h" + + +bool shaveItHairImpl::mDoingInstances = false; + +shaveConstant::RenderMode + shaveItHairImpl::mHairRenderMode = shaveConstant::kNoRender; + +MFloatArray shaveItHairImpl::mInstanceUs; +MFloatArray shaveItHairImpl::mInstanceVs; +int shaveItHairImpl::mLastNodeIndex = -1; +shaveRenderer* shaveItHairImpl::mRenderer = NULL; + + +MStatus shaveItHairImpl::clear() +{ + shaveAPIimpl::clearHairStack(); + mInstanceUs.clear(); + mInstanceVs.clear(); + mLastNodeIndex = -1; + + return MS::kSuccess; +} + + +MStatus shaveItHairImpl::doInit(bool instances, MObjectArray& shaveHairShapes) +{ + // + // Get the render mode. + // + mHairRenderMode = mRenderer->getRenderMode(); + + // + // If 'instances' is true, remove any non-instanced shaveHairShapes. + // If it's false, remove any instanced ones. + // + unsigned int i; + + for (i = 0; i < shaveHairShapes.length();) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + if (nodePtr->isInstanced() != instances) + shaveHairShapes.remove(i); + else + i++; + } + + if (shaveHairShapes.length() == 0) return MS::kNotFound; + + mDoingInstances = instances; + + return shaveAPIimpl::createHairStack( + shaveHairShapes, shaveRender::getFrameGlobals() + ); +} + + +MStatus shaveItHairImpl::init(bool instances, bool renderableOnly) +{ + shaveRender::saveFrameGlobals(); + + mRenderer = shaveRender::getRenderer(); + + MObjectArray shaveHairShapes; + + if (renderableOnly) + mRenderer->getRenderableShaveNodes(shaveHairShapes); + else + shaveUtil::getShaveNodes(shaveHairShapes); + + return doInit(instances, shaveHairShapes); +} + + +MStatus shaveItHairImpl::init(bool instances, MObjectArray& shaveHairShapes) +{ + shaveRender::saveFrameGlobals(); + + mRenderer = shaveRender::getRenderer(); + + unsigned int i; + + for (i = 0; i < shaveHairShapes.length(); i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + + if (nodeFn.typeId() != shaveHairShape::id) return MS::kInvalidParameter; + } + + return doInit(instances, shaveHairShapes); +} + + +MStatus shaveItHairImpl::nextHair(shaveAPI::HairInfo* hairInfo) +{ + if (hairInfo == NULL) return MS::kInvalidParameter; + + int nodeIndex; + + // + // If we're doing instanced hair, or if the we're doing non-instanced + // hair but the render mode is Geom, then get polys. Otherwise get + // curves. + // + if (mDoingInstances + || (mHairRenderMode == shaveConstant::kGeometryRender)) + { + nodeIndex = SHAVEexport_poly_iterator((HAIRTYPE*)hairInfo); + + // + // If we're doing instances then Shave won't provide the UVs. + // Instead we'll have to get those from shaveHairShape's instance input + // surface. + // + if (mDoingInstances) + { + shaveHairShape* nodePtr = shaveAPIimpl::getNodeFromStack(nodeIndex); + + if (nodePtr) + { + // + // If this is a new node then cache its UVs. + // + if (nodeIndex != mLastNodeIndex) + { + MPlug plug( + nodePtr->thisMObject(), + shaveHairShape::instanceMesh + ); + MObject meshObj; + + plug.getValue(meshObj); + + // + // We cannot just grab the UV array from the mesh and + // stuff it into hairInfo->uvws because the mesh uses a + // shared vertex list while hairInfo currently does not. + // + // So we have to iterate through the vertices, + // hopefully in the same order as was used when the + // instance mesh was passed to Shave, and assign the + // UVs individually. + // + float2 uv; + MItMeshFaceVertex faceVertIter(meshObj); + + mInstanceUs.clear(); + mInstanceVs.clear(); + + for (; !faceVertIter.isDone(); faceVertIter.next()) + { + faceVertIter.getUV(uv); + mInstanceUs.append(uv[0]); + mInstanceVs.append(uv[1]); + } + } + + // + // Copy uvs from the cache to each vertex of each strand. + // + // Note that for instanced hair 'numHairs' is neither the + // number of hairs nor the the number of strands, but the + // number of faces in the polymesh for all of the hair's + // strands. So if the hair has 3 strands of 10 polys each + // then 'numHairs' will be 30. + // + // So we have to work backward to the strand count. + // + int numVertsPerStrand = mInstanceUs.length(); + int numStrands; + + if (numVertsPerStrand > 0) + numStrands = hairInfo->numHairVertices / numVertsPerStrand; + else + numStrands = 0; + + int i = 0; + int strand; + int v; + + for (strand = 0; strand < numStrands; strand++) + { + for (v = 0; v < numVertsPerStrand; v++) + { + hairInfo->uvws[i].x = mInstanceUs[v]; + hairInfo->uvws[i].y = 1.0f-mInstanceVs[v]; + hairInfo->uvws[i].z = 0.0f; + i++; + } + } + } + } + } + else + { + nodeIndex = SHAVEexport_iterator((HAIRTYPE*)hairInfo); + } + + if (nodeIndex == -1) return MS::kNotFound; + + mLastNodeIndex = nodeIndex; + + return MS::kSuccess; +} + + +MStatus shaveItHairImpl::nextHairCounts(shaveAPI::HairInfo* hairInfo) +{ + if (hairInfo == NULL) return MS::kInvalidParameter; + + int nodeIndex = SHAVEexport_iteratorROOT((HAIRTYPE*)hairInfo); + + // + // A return of -1 indicates that we've run out of hairs. + // + if (nodeIndex == -1) return MS::kNotFound; + + // + // The returned hairInfo will not contain correct counts for the number + // of vertices and hair-vertices in this hair. So we need to calculate + // for ourselves, using the shaveHairShape's segment count. + // + shaveHairShape* nodePtr; + + nodePtr = shaveAPIimpl::getNodeFromStack((unsigned)nodeIndex); + + hairInfo->numVertices = 0; + hairInfo->numHairVertices = 0; + + if (nodePtr != NULL) + { + SHAVENODE* hairNode = nodePtr->getHairNode(); + + if (hairNode != NULL) + { + int numSegs = hairNode->shavep.segs[nodePtr->getHairGroup()]; + + hairInfo->numVertices = hairInfo->numHairs * numSegs; + hairInfo->numHairVertices = hairInfo->numVertices; + } + } + + return MS::kSuccess; +} + + +MStatus shaveItHairImpl::reset() +{ + SHAVEreset_iterator(); + + return MS::kSuccess; +} diff --git a/mayaPlug/shaveItHairImpl.h b/mayaPlug/shaveItHairImpl.h new file mode 100644 index 0000000..caaad1b --- /dev/null +++ b/mayaPlug/shaveItHairImpl.h @@ -0,0 +1,39 @@ +#ifndef shaveItHairImpl_h +#define shaveItHairImpl_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MFloatArray.h> +#include <maya/MObjectArray.h> +#include <maya/MStatus.h> + +#include "libShave.h" +#include "shaveAPI.h" +#include "shaveConstant.h" + +class shaveRenderer; + + +class LIBSHAVE_API shaveItHairImpl +{ +public: + static MStatus clear(); + static MStatus init(bool instances, bool renderableOnly); + static MStatus init(bool instances, MObjectArray& shaveNodes); + static MStatus nextHair(shaveAPI::HairInfo* hairInfo); + static MStatus nextHairCounts(shaveAPI::HairInfo* hairInfo); + static MStatus reset(); + +protected: + static bool mDoingInstances; + static shaveConstant::RenderMode mHairRenderMode; + static MFloatArray mInstanceUs; + static MFloatArray mInstanceVs; + static int mLastNodeIndex; + static shaveRenderer* mRenderer; + + static MStatus doInit(bool instances, MObjectArray& shaveNodes); +}; + +#endif diff --git a/mayaPlug/shaveMacCarbon.cpp b/mayaPlug/shaveMacCarbon.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mayaPlug/shaveMacCarbon.cpp diff --git a/mayaPlug/shaveMacCarbon.h b/mayaPlug/shaveMacCarbon.h new file mode 100644 index 0000000..0f28773 --- /dev/null +++ b/mayaPlug/shaveMacCarbon.h @@ -0,0 +1,22 @@ +#ifndef shaveMacCarbon_h +#define shaveMacCarbon_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#if defined(OSMac_MachO_) +#include <Carbon/Carbon.h> +#else +#include <Carbon.h> +#endif + +#include <maya/MString.h> + + +class shaveMacCarbon +{ +public: +}; + +#endif diff --git a/mayaPlug/shaveMaya.cpp b/mayaPlug/shaveMaya.cpp new file mode 100644 index 0000000..630b571 --- /dev/null +++ b/mayaPlug/shaveMaya.cpp @@ -0,0 +1,321 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MAnimControl.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MObject.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> +#include <maya/MSelectionList.h> +#include <maya/MStatus.h> +#include <maya/MTime.h> + +#include "shaveMaya.h" +#include "shaveRender.h" + +#define USE_GLOBALS 0 + +MString shaveMaya::outFormat; +MString shaveMaya::outFormatString; +MString shaveMaya::outExt; + +bool shaveMaya::animation; +MString shaveMaya::animExt; +MString shaveMaya::baseFileName; +float shaveMaya::deviceAspectRatio; +bool shaveMaya::deviceAspectRatioLocked; +int shaveMaya::doCameraMotion; +int shaveMaya::doDef; +short shaveMaya::doExtensionPadding; +bool shaveMaya::doMotionBlur; +int shaveMaya::doSingleFile; +MString shaveMaya::extension; +double shaveMaya::fov_ratio; +int shaveMaya::frameFirst; +int shaveMaya::frameLast; +unsigned int shaveMaya::frameBy; +bool shaveMaya::ignoreFilmGate; +unsigned int shaveMaya::imageDepth; +unsigned int shaveMaya::imageHeight; +unsigned int shaveMaya::imageWidth; +MString shaveMaya::imageName; + +short shaveMaya::outPadding; +short shaveMaya::outFormatControl; + +int shaveMaya::pixelSamples; +double shaveMaya::motionBlurByFrame; +bool shaveMaya::renderAllCameras; + + +int shaveMaya::mb2smoothValue; +double shaveMaya::mb2blurSharpness; +double shaveMaya::mb2blurLength; +bool shaveMaya::motionBlurType; +bool shaveMaya::useFrameExt; + + +// +// Get MAYA's render globals information +// +MStatus shaveMaya::getRenderGlobals() +{ + MStatus status = MS::kSuccess; + + doMotionBlur = false; + imageWidth = 0; + imageHeight = 0; + + // + // Get the render globals node + // + MSelectionList renderGlobalsList; + renderGlobalsList.add("defaultRenderGlobals"); + + if (renderGlobalsList.length() < 1) return MS::kFailure; + + MObject renderGlobalsNode; + + renderGlobalsList.getDependNode(0, renderGlobalsNode); + + MFnDependencyNode fnRenderGlobals(renderGlobalsNode); + + // + // Get its resolution node. + // + MPlugArray connectedPlugs; + MPlug resPlug = fnRenderGlobals.findPlug("resolution"); + + resPlug.connectedTo( + connectedPlugs, + true, // asDestination + false, // asSource + &status + ); + + if (!status || (connectedPlugs.length() != 1)) return MS::kFailure; + + MObject resNode = connectedPlugs[0].node(&status); + + if (!status) return status; + + MFnDependencyNode fnRes(resNode); + + // + // If we're doing an interactive render, then the width and height + // of the output image are determined by the test resolution, + // otherwise they're determined by the resolution node. + // + if (MGlobal::mayaState() == MGlobal::kInteractive) + { + MIntArray res; + MGlobal::executeCommand("shave_getTestResolution", res); + + if (res.length() == 2) + { + imageWidth = res[0]; + imageHeight = res[1]; + } + else + status = MS::kFailure; + } + else + { + short res_width; + short res_height; + + fnRes.findPlug("width").getValue(res_width); + fnRes.findPlug("height").getValue(res_height); + + imageWidth = (unsigned int)res_width; + imageHeight = (unsigned int)res_height; + } + + // + // Determine the output image format + // + enum + { + kGIF = 0, + kSoftimage = 1, + kRLA = 2, + kTiff = 3, + kTiff16 = 4, + kSGI = 5, + kAlias = 6, + kMaya = 7, + kJPEG = 8, + kEPS = 9, + kMaya16 = 10, + kCineon = 11 + }; + + short outputId; + fnRenderGlobals.findPlug("imageFormat").getValue(outputId); + + switch (outputId) + { + case kSGI: + outFormat = "sgif"; + outExt = "sgi"; + break; + + case kAlias: + outFormat = "alias"; + outExt = "als"; + break; + + case kEPS: + outFormat = "cpostscript"; + outExt = "ps"; + break; + + case kCineon: + outFormat = "cineon"; + outExt = "cin"; + break; + + default: + outFormat = "tiff"; + outExt = "tif"; + break; + } + + // Whether "outExt" is to be included in file name + fnRenderGlobals.findPlug("outFormatControl").getValue(outFormatControl); + + if (outFormatControl == 2) + { + fnRenderGlobals.findPlug("outFormatExt").getValue(outFormatString); + } + + // + // Get MOBLURS STUFF + // + fnRenderGlobals.findPlug("motionBlur").getValue(doMotionBlur); + fnRenderGlobals.findPlug("motionBlurByFrame").getValue(motionBlurByFrame); + fnRenderGlobals.findPlug("blurLength").getValue(mb2blurLength); + fnRenderGlobals.findPlug("blurSharpness").getValue(mb2blurSharpness); + fnRenderGlobals.findPlug("smoothValue").getValue(mb2smoothValue); + fnRenderGlobals.findPlug("motionBlurType").getValue(motionBlurType); + + // + // Get own blur setting for Vray -- vlad|15Apr2010 + // + if (shaveRender::rendererIsVray()) + { + renderGlobalsList.clear(); + renderGlobalsList.add("vraySettings"); + + if (renderGlobalsList.length() > 0) + { + MObject vraySettingsNode; + renderGlobalsList.getDependNode(0, vraySettingsNode); + + MFnDependencyNode depFn(vraySettingsNode); + + int mbOn = 0; + MPlug mbOnPlug = depFn.findPlug("cam_mbOn"); + if(!mbOnPlug.isNull()) + mbOnPlug.getValue(mbOn); + else + MGlobal::displayError("shave: can not find vraySettings.cam_mbOn plug."); + + doMotionBlur = (mbOn != 0); + } + else + MGlobal::displayError("shave: can not get V-Ray settings."); + } + // + // Check if there is animation + // + fnRenderGlobals.findPlug("animation").getValue(animation); + + MTime startFrame; + MTime endFrame; + float byFrame; + + if (animation) + { + // Check if the time-slider or renderGlobals is used for + // the frame range + // + short animRange; + fnRenderGlobals.findPlug("animationRange").getValue(animRange); + + if (USE_GLOBALS == animRange) + { + fnRenderGlobals.findPlug("startFrame").getValue(startFrame); + fnRenderGlobals.findPlug("endFrame").getValue(endFrame); + fnRenderGlobals.findPlug("byFrameStep").getValue(byFrame); + + frameFirst = (int)startFrame.as(MTime::uiUnit()); + frameLast = (int)endFrame.as(MTime::uiUnit()); + frameBy = (unsigned int)byFrame; + } + else // USE_TIMESLIDER + { + startFrame = MAnimControl::minTime(); + endFrame = MAnimControl::maxTime(); + + frameFirst = (int)startFrame.as(MTime::uiUnit()); + frameLast = (int)endFrame.as(MTime::uiUnit()); + } + + // Check if motionBlur is turned on. + // (This is matrix motion blur) + // BIJAN + } + else // No animation + { + frameFirst = (int)MAnimControl::currentTime().as(MTime::uiUnit()); + frameLast = (int)MAnimControl::currentTime().as(MTime::uiUnit()); + frameBy = 1; + //doMotionBlur = false; // BIJAN + + // Whether the frame number is to be incded in the file name + fnRenderGlobals.findPlug("useFrameExt").getValue(useFrameExt); + } + + fnRenderGlobals.findPlug("extensionPadding").getValue(outPadding); + + if (outPadding > 99) outPadding = 99; + + // + // If a film gate is used this tells us whether the image is + // blacked out in regions outside the film-fit region. + // In our case we crop the image to the size of the region. + // + fnRenderGlobals.findPlug("ignoreFilmGate").getValue(ignoreFilmGate); + + // + // Check if we should render all cameras, or only the active one + // + fnRenderGlobals.findPlug("renderAll").getValue(renderAllCameras); + + // + // Get the device aspect ratio from the resolution node. + // + fnRes.findPlug("deviceAspectRatio").getValue(deviceAspectRatio); + fnRes.findPlug("lockDeviceAspectRatio").getValue(deviceAspectRatioLocked); + + // + // Two of Maya's preset device aspect ratios (1.333 and 1.777) + // don't have enough precision for high-definition rendering, so + // let's adjust those appropriately. + // + if (((deviceAspectRatio - 1.33f) <= 0.003f) + && ((deviceAspectRatio - 1.333f) >= -0.0f)) + { + deviceAspectRatio = 1.333333f; + } + else if (((deviceAspectRatio - 1.77f) <= 0.007f) + && ((deviceAspectRatio - 1.777f) >= -0.0f)) + { + deviceAspectRatio = 1.777777f; + } + + return MS::kSuccess; +} diff --git a/mayaPlug/shaveMaya.h b/mayaPlug/shaveMaya.h new file mode 100644 index 0000000..01684a2 --- /dev/null +++ b/mayaPlug/shaveMaya.h @@ -0,0 +1,57 @@ +#ifndef shaveMaya_h +#define shaveMaya_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MStatus.h> +#include <maya/MString.h> + +class shaveMaya +{ +public: + static MStatus getRenderGlobals(); + +public: + static MString outFormat; + static MString outFormatString; + static MString outExt; + + static bool animation; + static bool useFrameExt; + static MString animExt; + static MString baseFileName; + static MString extension; + static MString imageName; + static int frameFirst; + static int frameLast; + static unsigned int frameBy; + static unsigned int imageWidth; + static unsigned int imageHeight; + static unsigned int imageDepth; + static float deviceAspectRatio; + static bool deviceAspectRatioLocked; + + static short outPadding; + static short outFormatControl; + + static bool doMotionBlur; + static int doDef; // Motion blur for deforming objects + static int doCameraMotion; // Motion blur for moving cameras + static int doSingleFile; // Write all frames to a single file + static short doExtensionPadding; + static int pixelSamples; + static double motionBlurByFrame; + static bool renderAllCameras; // Render all cams, or only active? + + static bool ignoreFilmGate; + static double fov_ratio; + + static int mb2smoothValue; + static double mb2blurSharpness; + static double mb2blurLength; + static bool motionBlurType; // 2D or 3D? +}; + +#endif diff --git a/mayaPlug/shaveMayaRenderer.cpp b/mayaPlug/shaveMayaRenderer.cpp new file mode 100644 index 0000000..1ea2a8c --- /dev/null +++ b/mayaPlug/shaveMayaRenderer.cpp @@ -0,0 +1,393 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MAnimControl.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MItDependencyNodes.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> + +#include "shaveConstant.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveMaya.h" +#include "shaveMayaRenderer.h" +#include "shaveRenderCallback.h" +#include "shaveUtil.h" +#include "shaveVertexShader.h" + + +shaveMayaRenderer::shaveMayaRenderer() +: mHaveVolumeShader(false) +, mRenderCallback(NULL) +, mRenderCamera("") +, mTimeState(shaveMayaRenderer::kAwaitingNothing) +, mWarningGivenUnsupportedHairMode(false) +, mWarningGivenUnsupportedInstanceMode(false) +{ +} + + +shaveMayaRenderer::~shaveMayaRenderer() +{} + + +void shaveMayaRenderer::frameStart(const shaveGlobals::Globals& g) +{ + shaveRenderer::frameStart(g); + + // + // We only need the volume shader if we're doing 3D compositing or handling + // reflections/refractions for non-geometry hair. + // + mHaveVolumeShader = false; + + if ((g.doCompositing && !g.composite2d) + || (g.visibleInReflections && (mNonGeometryNodes.length() > 0))) + { + // + // If the render camera has changed, remove the volume shader from + // the old one and add it to the new one. + // + MString cameraName; + MGlobal::executeCommand("shave_getRenderCamera()", cameraName); + + if (cameraName != mRenderCamera) + { + MGlobal::executeCommand("shave_cleanupVolumeShader "); + + MGlobal::executeCommand( + MString("shave_setupVolumeShader ") + cameraName + ); + + mHaveVolumeShader = true; + mRenderCamera = cameraName; + } + } + + // + // At the moment, the Maya renderer will have us in the center of the + // frame to rendered. If motion blur is off, that's great. But if + // motion blur is on, we only care about the shutter open and close + // times. So we'll have to wait for them. + // + if (shaveMaya::doMotionBlur) + mTimeState = kAwaitingShutterOpen; + else + { + MTime curTime = MAnimControl::currentTime(); + + doShutter(curTime, shaveConstant::kShutterBoth); + mTimeState = kAwaitingNothing; + } +} + + +void shaveMayaRenderer::frameEnd(const shaveGlobals::Globals& g) +{ + mRenderCallback->disableCamRender(); + shaveRenderer::frameEnd(g); +} + + +float shaveMayaRenderer::getPixelAspect() const +{ + return shaveRenderer::getPixelAspect(); +} + + +shaveConstant::RenderMode shaveMayaRenderer::getBaseRenderMode(bool* renderInstances) + const +{ + // + // Let the base class do its stuff first. + // + shaveConstant::RenderMode hairRenderMode; + + hairRenderMode = shaveRenderer::getBaseRenderMode(renderInstances); + + // + // The base class will have verified that the render modes are valid + // for *some* renderer, but we must make sure that they're valid for + // *this* renderer. + // + switch (hairRenderMode) + { + case shaveConstant::kNoRender: + case shaveConstant::kBufferRender: + case shaveConstant::kGeometryRender: + break; + + default: + if(!shaveRender::rendererIsPrMan())//not clear why for rendermane we do not have separate renderer class - dub|22Jun2012 + { + if (!mWarningGivenUnsupportedHairMode) + { + MGlobal::displayWarning( + MString("Shave: The Maya renderer does not support ") + + shaveRender::getRenderModeName(hairRenderMode) + + " hair render mode. We'll use " + + shaveRender::getRenderModeName(shaveConstant::kBufferRender) + + " render mode instead." + ); + mWarningGivenUnsupportedHairMode = true; + } + } + hairRenderMode = shaveConstant::kBufferRender; + break; + } + + // + // Return the render mode to the caller. + // + return hairRenderMode; +} + + +// +// Is this node "rendered" using normal Maya geometry? +// +bool shaveMayaRenderer::isGeomNode(const shaveHairShape* nodePtr) const +{ + // + // Instances in Maya are always rendered using normal geometry, + // so if instances are being rendered then this one will. + // + if (nodePtr->isInstanced()) return true; + + // + // If the render mode is kGeometryRender then this node will be + // rendered using Maya geometry, otherwise it will not. + // + shaveConstant::RenderMode hairRenderMode = getRenderMode(); + + return (hairRenderMode == shaveConstant::kGeometryRender); +} + + +bool shaveMayaRenderer::needVertexColours() const +{ + // + // If there are any vertex shaders in the scene, then we'll need vertex + // colour info. + // + MItDependencyNodes iter(MFn::kPluginDependNode); + + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + MFnDependencyNode nodeFn(node); + + if (nodeFn.typeId() == shaveVertexShader::id) return true; + } + + return false; +} + + +void shaveMayaRenderer::render( + float frame, shaveConstant::ShutterState shutter, const MDagPath& camera +) +{ + if (mNonGeometryNodes.length() > 0) + { + // + // If native illumination is on, the camera render will call + // SHAVEapply_illuminationWF() which will need to sample the lights + // in the scene. Unfortunately, light sampling only works from + // within an MRenderCallback. That means that when native + // illumination is on we cannot do the camera render here but must + // instead do it in shaveRenderCallback. + // + // But wait, there's more. Our volumetric compositing runs before + // shaveRenderCallback is called and it needs the output from the + // render. So if volumetric compositing is on then we need to do + // the render now, not later. + // + // That means that we cannot support both native illumination and + // volumetric compositing at the same time. So if they're both on + // then volumetric compositing wins. + // + // That just leaves us with the case where native illumination and + // volumetric compositing are both off. In that case we can do the + // render either here or in the callback. We choose to do it in the + // callback because that way we can progressively display tiles in + // the Render View as they render. + bool doCamRenderNow = (doShaveCompsGlob && !doShaveComp2dGlob); + + shaveRender::bufferRender( + shutter, camera, frame, true, true, doCamRenderNow, true + ); + + if (!doCamRenderNow + && ((shutter == shaveConstant::kShutterClose) + || (shutter == shaveConstant::kShutterBoth))) + { + mRenderCallback->enableCamRender(camera); + } + + // + // Set up shaders on the lights to make them cast shadows for our + // hair. + // + if (((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + && (getShadowSource() == shaveConstant::kShaveShadows) + && !shaveShadowMatteGlob) + { + setupShadowShaders(); + } + } +} + + +void shaveMayaRenderer::renderEnd() +{ + if (mHaveVolumeShader) + { + if (visibleInReflectionsGlob || !doShaveComp2dGlob) + MGlobal::executeCommand("shave_cleanupVolumeShader"); + } + + // + // Remove the compositing callback. + // + if (mRenderCallback) + { + MRenderCallback::removeCallback(mRenderCallback); + delete mRenderCallback; + mRenderCallback = NULL; + } + + // + // Undo the shadow matte setup. + // + if (shaveShadowMatteGlob + && (getRenderMode() == shaveConstant::kBufferRender)) + { + MGlobal::executeCommand("shave_cleanupShadowMatte"); + } + + // + // Reset warning flags so that the next render will get warnings again. + // + mWarningGivenUnsupportedHairMode= false; + mWarningGivenUnsupportedInstanceMode = false; + + shaveRenderer::renderEnd(); +} + + +void shaveMayaRenderer::renderInterrupted() +{ + postCompositeCleanup(); +} + + +void shaveMayaRenderer::renderStart() +{ + mTimeState = kAwaitingNothing; + shaveRenderer::renderStart(); + + // + // A shadow matte pass is only useful when there is buffered hair in + // the scene. If that's the case, and a shadow matte pass has been + // requested, then set it up. + // + if (shaveShadowMatteGlob + && (getRenderMode() == shaveConstant::kBufferRender)) + { + MGlobal::executeCommand("shave_setupShadowMatte"); + } + + // + // Set up a render callback to handle compositing buffer renders onto + // the final image. + // + // NOTE: There's a bug in Maya: it will crash if you reuse an + // instance of a render callback object. So we have to + // allocate a fresh one each time. + // + mRenderCallback = new shaveRenderCallback(this); + + MRenderCallback::addCallback(mRenderCallback, 255); +} + + +void shaveMayaRenderer::timeChange(const MTime& newTime) +{ + switch (mTimeState) + { + case kAwaitingShutterOpen: + doShutter(newTime, shaveConstant::kShutterOpen); + + if (shaveRenderCancelled) + mTimeState = kAwaitingNothing; + else + mTimeState = kAwaitingCenterFrame; + break; + + case kAwaitingCenterFrame: + mTimeState = kAwaitingShutterClose; + break; + + case kAwaitingShutterClose: + doShutter(newTime, shaveConstant::kShutterClose); + + mTimeState = kAwaitingNothing; + break; + + default: + break; + } +} + + +//************************************************ +// +// Helper Methods +// +//************************************************ + +void shaveMayaRenderer::postCompositeCleanup() +{ + // + // Remove any shadow shaders we may have created. + // + if (!shaveShadowMatteGlob + && (getShadowSource() == shaveConstant::kShaveShadows)) + { + MGlobal::executeCommand("shave_disconnectShadowShaders()"); + } + + // + // It's now safe to free up Shave's render buffers. + // + shaveRender::bufferRenderCleanup(); +} + + +// +// Hook up shadow handler nodes to the lights. +// +void shaveMayaRenderer::setupShadowShaders() +{ + unsigned int i; + unsigned int numLights = (unsigned int)shaveUtil::globalLightList.size(); + + + for (i = 0; i < numLights; i++) + { + MDagPath lightPath = shaveUtil::globalLightList[i].path; + MString cmd = "shave_connectShadowShader " + + lightPath.fullPathName() + " "; + + // + // MString supplies a '+=' operator for ints, but not a '+' operator. + // + cmd += (int)i; + MGlobal::executeCommand(cmd); + } +} diff --git a/mayaPlug/shaveMayaRenderer.h b/mayaPlug/shaveMayaRenderer.h new file mode 100644 index 0000000..a4eba01 --- /dev/null +++ b/mayaPlug/shaveMayaRenderer.h @@ -0,0 +1,71 @@ +#ifndef shaveMayaRenderer_h +#define shaveMayaRenderer_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveConstant.h" +#include "shaveRenderCallback.h" +#include "shaveRenderer.h" + +class shaveHairShape; + + +class shaveMayaRenderer : public shaveRenderer +{ +public: + shaveMayaRenderer(); + virtual ~shaveMayaRenderer(); + + virtual void frameEnd(const shaveGlobals::Globals& g); + virtual void frameStart(const shaveGlobals::Globals& g); + virtual float getPixelAspect() const; + + virtual shaveConstant::RenderMode + getBaseRenderMode(bool* renderInstances = NULL) const; + + virtual bool isGeomNode(const shaveHairShape* nodePtr) const; + virtual bool needVertexColours() const; + virtual void postCompositeCleanup(); + + virtual void render( + float frame, + shaveConstant::ShutterState shutter, + const MDagPath& camera + ); + + virtual void renderEnd(); + virtual void renderInterrupted(); + virtual void renderStart(); + virtual void timeChange(const MTime& newTime); + + +//************************************************ +// +// Helper Methods +// +//************************************************ + +protected: + void setupShadowShaders(); + + +private: + enum TimeState + { + kAwaitingNothing, + kAwaitingShutterOpen, + kAwaitingCenterFrame, + kAwaitingShutterClose + }; + + bool mHaveVolumeShader; + shaveRenderCallback* mRenderCallback; + MString mRenderCamera; + TimeState mTimeState; + mutable bool mWarningGivenUnsupportedHairMode; + mutable bool mWarningGivenUnsupportedInstanceMode; +}; + +#endif diff --git a/mayaPlug/shaveNode.cpp b/mayaPlug/shaveNode.cpp new file mode 100644 index 0000000..149fe45 --- /dev/null +++ b/mayaPlug/shaveNode.cpp @@ -0,0 +1,1226 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveBlindData.h" +#include <maya/MFnStringData.h> +#include <maya/MFnCompoundAttribute.h> +#include <maya/MFnEnumAttribute.h> +#include <maya/MFnGenericAttribute.h> +#include <maya/MFnMessageAttribute.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MFnUnitAttribute.h> + +#include "shaveDebug.h" +#include "shaveError.h" +#include "shaveNode.h" +#include "shaveRender.h" +#include "shaveSDK.h" +#include "shaveUtil.h" + +#if defined(OSMac_) && !defined(OSMac_MachO_) +#include "shaveMacCarbon.h" +#endif + +const MString shaveNode::nodeTypeName("shaveNode"); +const int shaveNode::kNodeVersion = 13; + + +MObject shaveNode::blockDynamics; +MObject shaveNode::collisionObjectsAttr; +MObject shaveNode::collisionObjectsGroupIDAttr; +MObject shaveNode::displayHairMaxAttr; +MObject shaveNode::displayHairRatioAttr; +MObject shaveNode::displayNodeAttr; +MObject shaveNode::dspyMode; +MObject shaveNode::growthObjectsGroupIDAttr; + +MObject shaveNode::hairColorTexture, + shaveNode::hairColorTextureR, + shaveNode::hairColorTextureG, + shaveNode::hairColorTextureB; + +MObject shaveNode::inputCurve; +MObject shaveNode::inputMesh; +MObject shaveNode::inputSurf; +MObject shaveNode::instanceMesh; +MObject shaveNode::instanceMeshChanged; +MObject shaveNode::instancingStatusChanged; +MObject shaveNode::isActive; + +MObject shaveNode::mutantHairColorTexture, + shaveNode::mutantHairColorTextureR, + shaveNode::mutantHairColorTextureG, + shaveNode::mutantHairColorTextureB; + +MObject shaveNode::neverBeenInstanced; +MObject shaveNode::nodeVersionAttr; +MObject shaveNode::outputMesh; +MObject shaveNode::renderStateAttr; +MObject shaveNode::ribInsert; + +MObject shaveNode::rootHairColorTexture, + shaveNode::rootHairColorTextureR, + shaveNode::rootHairColorTextureG, + shaveNode::rootHairColorTextureB; + +MObject shaveNode::runDynamics; +MObject shaveNode::shaveBlindHair; +MObject shaveNode::shaveBlindState; +MObject shaveNode::shaveNodeCommand; +MObject shaveNode::shaveParamAmbDiff; // slider 6 +MObject shaveNode::shaveParamAnimSpeed; // slider 33 +MObject shaveNode::shaveParamCollide; +MObject shaveNode::shaveParamCollisionMethod; +MObject shaveNode::shaveParamDampening; // slider 40 +MObject shaveNode::shaveParamDisplacement; // slider 43 +MObject shaveNode::shaveParamFrizzAnim; // slider 32 + +MObject shaveNode::shaveParamFrizzAnimDir, + shaveNode::shaveParamFrizzAnimDirX, + shaveNode::shaveParamFrizzAnimDirY, + shaveNode::shaveParamFrizzAnimDirZ; + +MObject shaveNode::shaveParamFrizzFreqX; // slider 1 +MObject shaveNode::shaveParamFrizzFreqY; // slider 30 +MObject shaveNode::shaveParamFrizzFreqZ; // slider 31 +MObject shaveNode::shaveParamFrizzRoot; // slider 0 +MObject shaveNode::shaveParamFrizzTip; // slider 24 +MObject shaveNode::shaveParamGeomShadow; +MObject shaveNode::shaveParamGloss; // slider 5 +MObject shaveNode::shaveParamGravity; + +MObject shaveNode::shaveParamHairColor, + shaveNode::shaveParamHairRed, // slider 9 + shaveNode::shaveParamHairGreen, // slider 10 + shaveNode::shaveParamHairBlue; // slider 11 + +MObject shaveNode::shaveParamHaircount; +MObject shaveNode::shaveParamHueVariation; // slider 12 +MObject shaveNode::shaveParamInstancingStatus; +MObject shaveNode::shaveParamKinkFreqX; // slider 3 +MObject shaveNode::shaveParamKinkFreqY; // slider 34 +MObject shaveNode::shaveParamKinkFreqZ; // slider 35 +MObject shaveNode::shaveParamKinkRoot; // slider 38 +MObject shaveNode::shaveParamKinkTip; // slider 2 +MObject shaveNode::shaveParamMultStrand; // slider 25 + +MObject shaveNode::shaveParamMutantColor, + shaveNode::shaveParamMutantRed, // slider 13 + shaveNode::shaveParamMutantGreen, // slider 14 + shaveNode::shaveParamMutantBlue; // slider 15 + +MObject shaveNode::shaveParamMutantPercent; // slider 16 +MObject shaveNode::shaveParamNoInterp; +MObject shaveNode::shaveParamPainted; +MObject shaveNode::shaveParamPasses; +MObject shaveNode::shaveParamRandomizeMulti; // slider 42 +MObject shaveNode::shaveParamRandScale; // slider 36 + +MObject shaveNode::shaveParamRootColor, + shaveNode::shaveParamRootRed, // slider 17 + shaveNode::shaveParamRootGreen, // slider 18 + shaveNode::shaveParamRootBlue; // slider 19 + +MObject shaveNode::shaveParamRootStiffness; // slider 21 +MObject shaveNode::shaveParamScale; // slider 41 +MObject shaveNode::shaveParamSegs; +MObject shaveNode::shaveParamSelfShadow; // slider 7 +MObject shaveNode::shaveParamSpecular; // slider 4 +MObject shaveNode::shaveParamSplayRoot; // slider 26 +MObject shaveNode::shaveParamSplayTip; // slider 27 +MObject shaveNode::shaveParamMultAsp; // slider 44 -- vlad|05July2010 +MObject shaveNode::shaveParamOffset; // slider 45 -- vlad|09July2010 +MObject shaveNode::shaveParamAspect; // slider 46 -- vlad|09July2010 +MObject shaveNode::shaveParamStiffness; // slider 8 +MObject shaveNode::shaveParamThickRoot; // slider 20 +MObject shaveNode::shaveParamThickTip; // slider 37 +MObject shaveNode::shaveParamTotalGuides; +MObject shaveNode::shaveParamValueVariation; // slider 39 +MObject shaveNode::shaveTexMutantColor; +MObject shaveNode::shaveTextureAttr; +MObject shaveNode::surfTessU; +MObject shaveNode::surfTessV; +MObject shaveNode::textureCacheUpdatedAttr; +MObject shaveNode::timeAttr; +MObject shaveNode::triggerAttr; +MObject shaveNode::overrideGeomShaderAttr; +MObject shaveNode::uvSetAssignmentsAttr; +MObject shaveNode::uvSetNameAttr; +MObject shaveNode::uvSetTexturesAttr; +MObject shaveNode::versionLockAttr; + +MObject shaveNode::flyawayPerc; +MObject shaveNode::flyawayStren; +MObject shaveNode::messStren; + +MObject shaveNode::clumps; +MObject shaveNode::clumpsStren; +MObject shaveNode::clumpsColStren; +MObject shaveNode::clumpsRotStren; +MObject shaveNode::clumpsRotOffset; + + +// Deprecated Attributes +MObject shaveNode::birthName; +MObject shaveNode::closeRibFilename; +MObject shaveNode::growthSet; +MObject shaveNode::instanceGeoAttr; +MObject shaveNode::nodeNameAttr; +MObject shaveNode::openRibFilename; +MObject shaveNode::ribDumper; +MObject shaveNode::shaveBlindShaveParams; +MObject shaveNode::shaveParamThickness; +MObject shaveNode::shaveUvSets; +MObject shaveNode::signatureAttr; +MObject shaveNode::skullSet; +MObject shaveNode::sourceType; + +MTypeId shaveNode::id( 0x00106500 ); + + + +void* shaveNode::creator() +{ + return new shaveNode; +} + + +MStatus shaveNode::initialize() +{ + MFnUnitAttribute unitAttr; + MFnTypedAttribute tAttr; + MFnNumericAttribute nAttr; + MFnCompoundAttribute compAttr; + MFnEnumAttribute eAttr; + MFnGenericAttribute gAttr; + MFnMessageAttribute mesgAttr; + MStatus stat; + + + //*************************************************************** + // + // REGULAR ATTRIBUTES + // + //*************************************************************** + + blockDynamics = nAttr.create( + "disableDynamics", + "disabled", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(true); + addAttribute(blockDynamics); + + collisionObjectsAttr = gAttr.create("collisionObjects", "co"); + gAttr.setArray (true); + gAttr.setHidden(true); + gAttr.setStorable(false); + gAttr.setDisconnectBehavior(MFnAttribute::kDelete); + gAttr.addAccept(MFnData::kMesh); + gAttr.addAccept(MFnData::kNurbsCurve); + gAttr.addAccept(MFnData::kNurbsSurface); + gAttr.addAccept(MFnData::kSubdSurface); + addAttribute(collisionObjectsAttr); + + collisionObjectsGroupIDAttr = + nAttr.create("collisionObjectsGroupID", "cmgi", MFnNumericData::kLong); + nAttr.setArray(true); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setDisconnectBehavior(MFnAttribute::kDelete); + addAttribute(collisionObjectsGroupIDAttr); + + displayNodeAttr = mesgAttr.create("displayNode", "dn"); + mesgAttr.setStorable(false); + mesgAttr.setHidden(true); + addAttribute(displayNodeAttr); + + dspyMode = eAttr.create("displayAs", "dspyas", 1); + eAttr.addField("guides", kHairDisplayGuides); + eAttr.addField("hairs", kHairDisplayHair); + eAttr.addField("geometry", kHairDisplayGeom); + eAttr.setStorable(true); + addAttribute(dspyMode); + + growthObjectsGroupIDAttr = + nAttr.create("growthObjectsGroupID", "gmgi", MFnNumericData::kLong); + nAttr.setArray(true); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setDisconnectBehavior(MFnAttribute::kDelete); + addAttribute(growthObjectsGroupIDAttr); + + hairColorTextureR = + nAttr.create("hairColorTextureR", "hctr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + addAttribute(hairColorTextureR); + + hairColorTextureG = + nAttr.create("hairColorTextureG", "hctg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + addAttribute(hairColorTextureG); + + hairColorTextureB = + nAttr.create("hairColorTextureB", "hctb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + addAttribute(hairColorTextureB); + + hairColorTexture = nAttr.create( + "hairColorTexture", + "htc", + hairColorTextureR, + hairColorTextureG, + hairColorTextureB + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(false); + nAttr.setKeyable(true); + addAttribute(hairColorTexture); + + inputCurve = tAttr.create("inputCurve", "incrv", MFnData::kNurbsCurve); + tAttr.setArray (true); + tAttr.setHidden(true); + tAttr.setStorable(false); + tAttr.setDisconnectBehavior(MFnAttribute::kDelete); + addAttribute(inputCurve); + + inputMesh = tAttr.create ("inputMesh", "input", MFnData::kMesh); + tAttr.setArray (true); + tAttr.setHidden(true); + tAttr.setStorable(false); + tAttr.setDisconnectBehavior(MFnAttribute::kDelete); + addAttribute(inputMesh); + + inputSurf = gAttr.create("inputSurface", "insrf"); + gAttr.setArray (true); + gAttr.setHidden(true); + gAttr.setStorable(false); + gAttr.addAccept(MFnData::kNurbsSurface); + gAttr.addAccept(MFnData::kSubdSurface); + gAttr.setDisconnectBehavior(MFnAttribute::kDelete); + addAttribute(inputSurf); + + instanceMesh = tAttr.create("instanceMesh", "im", MFnData::kMesh); + tAttr.setHidden(true); + tAttr.setStorable(true); + addAttribute(instanceMesh); + + isActive = nAttr.create( "active", "act", MFnNumericData::kBoolean, true); + nAttr.setStorable(true); + addAttribute(isActive); + + mutantHairColorTextureR = nAttr.create( + "mutantHairColorTextureR", + "mhctr", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + addAttribute(mutantHairColorTextureR); + + mutantHairColorTextureG = nAttr.create( + "mutantHairColorTextureG", + "mhctg", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + addAttribute(mutantHairColorTextureG); + + mutantHairColorTextureB = nAttr.create( + "mutantHairColorTextureB", + "mhctb", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + addAttribute(mutantHairColorTextureB); + + mutantHairColorTexture = nAttr.create( + "mutantHairColorTexture", + "mhtc", + mutantHairColorTextureR, + mutantHairColorTextureG, + mutantHairColorTextureB + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(false); + nAttr.setKeyable(true); + addAttribute(mutantHairColorTexture); + + neverBeenInstanced = nAttr.create( + "neverBeenInstanced", + "nbi", + MFnNumericData::kBoolean, + true + ); + nAttr.setConnectable(false); + nAttr.setHidden(true); + nAttr.setStorable(true); + addAttribute(neverBeenInstanced); + + nodeVersionAttr = nAttr.create("nodeVersion", "nv", MFnNumericData::kLong); + nAttr.setDefault(0); + nAttr.setHidden(true); + nAttr.setWritable(true); + nAttr.setStorable(true); + addAttribute(nodeVersionAttr); + + outputMesh = tAttr.create("outputMesh", "out", MFnData::kMesh); + tAttr.setStorable(false); + tAttr.setHidden(true); + tAttr.setWritable(false); + addAttribute(outputMesh); + + overrideGeomShaderAttr = nAttr.create( + "overrideGeomShader", + "ogsh", + MFnNumericData::kBoolean, + true + ); + nAttr.setStorable(true); + addAttribute(overrideGeomShaderAttr); + + renderStateAttr = + eAttr.create("renderState", "rs", shaveRender::kRenderNone); + eAttr.addField("None", shaveRender::kRenderNone); + eAttr.addField("Rendering", shaveRender::kRendering); + eAttr.addField("Rendering Frame", shaveRender::kRenderingFrame); + eAttr.setStorable(false); + eAttr.setHidden(true); + addAttribute(renderStateAttr); + + ribInsert = tAttr.create("ribStuff", "rbs", MFnStringData::kString); + tAttr.setStorable(true); + addAttribute(ribInsert); + + rootHairColorTextureR = nAttr.create( + "rootHairColorTextureR", + "rhctr", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + addAttribute(rootHairColorTextureR); + + rootHairColorTextureG = nAttr.create( + "rootHairColorTextureG", + "rhctg", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + addAttribute(rootHairColorTextureG); + + rootHairColorTextureB = nAttr.create( + "rootHairColorTextureB", + "rhctb", + MFnNumericData::kFloat, + 0 + ); + nAttr.setStorable(true); + addAttribute(rootHairColorTextureB); + + rootHairColorTexture = nAttr.create( + "rootHairColorTexture", + "rhtc", + rootHairColorTextureR, + rootHairColorTextureG, + rootHairColorTextureB + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(false); + nAttr.setKeyable(true); + addAttribute(rootHairColorTexture); + + runDynamics = nAttr.create("runDynamics", "rd", MFnNumericData::kLong, 0); + nAttr.setStorable(true); + nAttr.setHidden(true); + addAttribute(runDynamics); + + shaveBlindHair = tAttr.create( + "blindShaveData", "BSD", blindShaveData::dataTypeId + ); + tAttr.setHidden(true); + tAttr.setStorable(true); + addAttribute(shaveBlindHair); + + shaveNodeCommand = tAttr.create("evalOption", "evo", MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(shaveNodeCommand); + + + //*************************************************************** + // + // ATTRIBUTES FOR SHAVE ENGINE PARAMETERS + // + // These parameters represent values which get mapped directly to the + // SHAVEPARMS structure passed to the Shave engine. + // + //*************************************************************** + + shaveParamAmbDiff = + nAttr.create("amb/diff", "spambdif", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + nAttr.setKeyable(true); + addAttribute(shaveParamAmbDiff); + + shaveParamCollide = nAttr.create( + "enableCollision", + "spec", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(true); + addAttribute(shaveParamCollide); + + shaveParamCollisionMethod = + nAttr.create("collisionMethod", "spcm", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setHidden(true); + addAttribute(shaveParamCollisionMethod); + + shaveParamDampening = + nAttr.create("dampening", "damp", MFnNumericData::kFloat, 0.0); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + addAttribute(shaveParamDampening); + + shaveParamFrizzAnim = + nAttr.create("frizzAnim", "spfa", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzAnim); + + shaveParamFrizzAnimDirX = + nAttr.create("animDirX", "spadx", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzAnimDirX); + + shaveParamFrizzAnimDirY = + nAttr.create("animDirY", "spady", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzAnimDirY); + + shaveParamFrizzAnimDirZ = + nAttr.create("animDirZ", "spadz", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzAnimDirZ); + + shaveParamFrizzAnimDir = nAttr.create( + "frizzAnimDir", + "spad", + shaveParamFrizzAnimDirX, + shaveParamFrizzAnimDirY, + shaveParamFrizzAnimDirZ + ); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzAnimDir); + + shaveParamAnimSpeed = + nAttr.create("animSpeed", "spas", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamAnimSpeed); + + shaveParamDisplacement = + nAttr.create("displacement", "disp", MFnNumericData::kFloat, 0.0f); + nAttr.setStorable(true); + nAttr.setKeyable(true); + nAttr.setSoftMin(-10.0); + nAttr.setSoftMax(10.0); + addAttribute(shaveParamDisplacement); + + shaveParamFrizzFreqX = + nAttr.create("frizzXFrequency", "spffx", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzFreqX); + + shaveParamFrizzFreqY = + nAttr.create("frizzYFrequency", "spffy", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzFreqY); + + shaveParamFrizzFreqZ = + nAttr.create("frizzZFrequency", "spffz", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzFreqZ); + + shaveParamFrizzRoot = + nAttr.create("rootFrizz", "sprf", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzRoot); + + shaveParamFrizzTip = + nAttr.create("tipFrizz", "sptf", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamFrizzTip); + + shaveParamGeomShadow = + nAttr.create("geomShadow", "spgs", MFnNumericData::kFloat, 1.0); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + nAttr.setStorable(true); + addAttribute(shaveParamGeomShadow); + + shaveParamGloss = + nAttr.create("gloss", "spgloss", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(0.099999); + nAttr.setKeyable(true); + addAttribute(shaveParamGloss); + + shaveParamHairRed = + nAttr.create("hairRed", "sphr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamHairRed); + + shaveParamHairGreen = + nAttr.create("hairGreen", "sphg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamHairGreen); + + shaveParamHairBlue = + nAttr.create("hairBlue", "sphb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamHairBlue); + + shaveParamHairColor = nAttr.create( + "hairColor", + "sphcol", + shaveParamHairRed, + shaveParamHairGreen, + shaveParamHairBlue + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + addAttribute(shaveParamHairColor); + + shaveParamHaircount = + nAttr.create("hairCount", "sphc", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(30000); + addAttribute(shaveParamHaircount); + + shaveParamHueVariation = + nAttr.create("hueVariation", "sphv", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(100); + nAttr.setKeyable(true); + addAttribute(shaveParamHueVariation); + + shaveParamInstancingStatus = nAttr.create( + "instancingStatus", + "spis", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(true); + addAttribute(shaveParamInstancingStatus); + + shaveParamKinkFreqX = + nAttr.create("kinkXFrequency", "spkfx", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamKinkFreqX); + + shaveParamKinkFreqY = + nAttr.create("kinkYFrequency", "spkfy", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamKinkFreqY); + + shaveParamKinkFreqZ = + nAttr.create("kinkZFrequency", "spkfz", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.001); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamKinkFreqZ); + + shaveParamKinkRoot = + nAttr.create("rootKink", "spkr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamKinkRoot); + + shaveParamKinkTip = + nAttr.create("tipKink", "spkt", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamKinkTip); + + shaveParamMultStrand = + nAttr.create("multiStrandCount", "spms", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(10); + addAttribute(shaveParamMultStrand); + + shaveParamMutantRed = + nAttr.create("mutantHairRed", "spmhr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + addAttribute(shaveParamMutantRed); + + shaveParamMutantGreen = + nAttr.create("mutantHairGreen", "spmhg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + addAttribute(shaveParamMutantGreen); + + shaveParamMutantBlue = + nAttr.create("mutantHairBlue", "spmhb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + addAttribute(shaveParamMutantBlue); + + shaveParamMutantColor = nAttr.create( + "mutantHairColor", + "spmhc", + shaveParamMutantRed, + shaveParamMutantGreen, + shaveParamMutantBlue + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + addAttribute(shaveParamMutantColor); + + shaveParamMutantPercent = + nAttr.create("percentMutantHairs", "spmp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(100); + nAttr.setKeyable(true); + addAttribute(shaveParamMutantPercent); + + shaveParamNoInterp = nAttr.create( + "interpolateGuides", + "dointerp", + MFnNumericData::kBoolean, + true + ); + nAttr.setStorable(true); + addAttribute(shaveParamNoInterp); + + shaveParamPasses = + nAttr.create("hairPasses", "sphp", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(20); + addAttribute(shaveParamPasses); + + shaveParamRandomizeMulti = + nAttr.create("randomizeMulti", "sprm", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + addAttribute(shaveParamRandomizeMulti); + + shaveParamRandScale = + nAttr.create("randScale", "sprs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + nAttr.setKeyable(true); + addAttribute(shaveParamRandScale); + + shaveParamRootRed = + nAttr.create("rootRed", "sprr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamRootRed); + + shaveParamRootGreen = + nAttr.create("rootGreen", "sprg", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamRootGreen); + + shaveParamRootBlue = + nAttr.create("rootBlue", "sprb", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setKeyable(true); + addAttribute(shaveParamRootBlue); + + shaveParamRootColor = nAttr.create( + "rootColor", + "sprtc", + shaveParamRootRed, + shaveParamRootGreen, + shaveParamRootBlue + ); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + addAttribute(shaveParamRootColor); + + shaveParamRootStiffness = + nAttr.create("rootStiffness", "rstf", MFnNumericData::kFloat, 1.0); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + addAttribute(shaveParamRootStiffness); + + shaveParamScale = nAttr.create("scale", "sc", MFnNumericData::kFloat, 1.0); + nAttr.setMin(0.01); + nAttr.setSoftMax(1.0); + addAttribute(shaveParamScale); + + shaveParamSegs = + nAttr.create("hairSegments", "spsegs", MFnNumericData::kShort, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setSoftMax(50); + addAttribute(shaveParamSegs); + + shaveParamSelfShadow = + nAttr.create("selfShadow", "spss", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + addAttribute(shaveParamSelfShadow); + + shaveParamSpecular = + nAttr.create("specular", "spspec", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(1); + nAttr.setKeyable(true); + addAttribute(shaveParamSpecular); + + shaveParamSplayRoot = + nAttr.create("rootSplay", "sprsp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamSplayRoot); + + shaveParamSplayTip = + nAttr.create("tipSplay", "sptsp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamSplayTip); + + shaveParamSplayTip = + nAttr.create("multAsp", "mlasp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamSplayTip); + + shaveParamStiffness = + nAttr.create("stiffness", "spstiff", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1); + nAttr.setKeyable(true); + addAttribute(shaveParamStiffness); + + shaveParamThickRoot = + nAttr.create("rootThickness", "sptr", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(2); + nAttr.setKeyable(true); + addAttribute(shaveParamThickRoot); + + shaveParamThickTip = + nAttr.create("tipThickness", "sptt", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(2); + nAttr.setKeyable(true); + addAttribute(shaveParamThickTip); + + shaveParamTotalGuides = + nAttr.create("totalGuides", "sptg", MFnNumericData::kShort, 0); + nAttr.setHidden(true); + addAttribute(shaveParamTotalGuides); + + shaveParamValueVariation = + nAttr.create("valueVariation", "spvv", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(100); + nAttr.setKeyable(true); + addAttribute(shaveParamValueVariation); + + //vlad|05July2010 + shaveParamMultAsp = + nAttr.create("multAsp", "mlasp", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamMultAsp); + + + //vlad|09July2010 + shaveParamOffset = + nAttr.create("Offset", "offs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamOffset); + + //vlad|09July2010 + shaveParamAspect = + nAttr.create("Asepect", "aspc", MFnNumericData::kFloat, 1.0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(5); + nAttr.setKeyable(true); + addAttribute(shaveParamAspect); + + + //*************************************************************** + // + // MORE REGULAR ATTRIBUTES + // + //*************************************************************** + + shaveTextureAttr = nAttr.create("shaveTex", "shtx", MFnNumericData::kFloat); + nAttr.setArray(true); + nAttr.setIndexMatters(true); + nAttr.setStorable(true); + addAttribute(shaveTextureAttr); + + surfTessU = + nAttr.create("uSubdivisions", "tusd", MFnNumericData::kShort, 2); + nAttr.setStorable(true); + addAttribute(surfTessU); + + surfTessV = + nAttr.create("vSubdivisions", "tvsd", MFnNumericData::kShort, 2); + nAttr.setStorable(true); + addAttribute(surfTessV); + + timeAttr = unitAttr.create("time", "tm", MFnUnitAttribute::kTime, 0.0); + unitAttr.setHidden(true); + addAttribute(timeAttr); + + triggerAttr = nAttr.create("trigger", "trg", MFnNumericData::kFloat, 0); + nAttr.setHidden(true); + nAttr.setWritable(false); + nAttr.setStorable(false); + addAttribute(triggerAttr); + + uvSetNameAttr = tAttr.create("uvSetName", "uvsn", MFnStringData::kString); + tAttr.setHidden(true); + tAttr.setDisconnectBehavior(MFnAttribute::kReset); + + uvSetTexturesAttr = mesgAttr.create("uvSetTextures", "uvst"); + mesgAttr.setStorable(false); + mesgAttr.setHidden(true); + mesgAttr.setArray(true); + mesgAttr.setIndexMatters(false); + mesgAttr.setDisconnectBehavior(MFnAttribute::kDelete); + + uvSetAssignmentsAttr = compAttr.create("uvSetAssignments", "uvsa"); + compAttr.setHidden(true); + compAttr.setArray(true); + compAttr.setIndexMatters(false); + compAttr.setDisconnectBehavior(MFnAttribute::kDelete); + compAttr.addChild(uvSetNameAttr); + compAttr.addChild(uvSetTexturesAttr); + addAttribute(uvSetAssignmentsAttr); + + versionLockAttr = nAttr.create("versionLock", "vl", MFnNumericData::kLong); + nAttr.setHidden(true); + nAttr.setStorable(true); + addAttribute(versionLockAttr); + + displayHairMaxAttr = nAttr.create( + "displayHairMax", + "dhm", + MFnNumericData::kLong, + 10000.0 + ); + nAttr.setMin(0.0); + nAttr.setSoftMax(25000.0); + addAttribute(displayHairMaxAttr); + + displayHairRatioAttr = nAttr.create( + "displayHairRatio", + "dhr", + MFnNumericData::kFloat, + 1.0 + ); + nAttr.setMin(0.0); + nAttr.setMax(1.0); + addAttribute(displayHairRatioAttr); + + textureCacheUpdatedAttr = nAttr.create( + "textureCacheUpdated", + "tau", + MFnNumericData::kBoolean, + false + ); + nAttr.setStorable(false); + addAttribute(textureCacheUpdatedAttr); + + flyawayPerc = + nAttr.create("flyawayPerc", "fwpc", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setMin(1); + nAttr.setMax(100); + stat = addAttribute(flyawayPerc); + MChkErr(stat, "can't add 'flyawayPerc' attribute."); + + flyawayStren = + nAttr.create("flyawayStren", "fwst", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1.0); + nAttr.setKeyable(true); + stat = addAttribute(flyawayStren); + MChkErr(stat, "can't add 'flyawayStren' attribute."); + + messStren = + nAttr.create("messStren", "mest", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + stat = addAttribute(messStren); + MChkErr(stat, "can't add 'messStren' attribute."); + + + clumps = + nAttr.create("clumps", "clp", MFnNumericData::kInt, 0); + nAttr.setStorable(true); + nAttr.setMin(0); + nAttr.setMax(1000); + stat = addAttribute(clumps); + MChkErr(stat, "can't add 'clumps' attribute."); + + clumpsStren = + nAttr.create("clumpsStrength", "cpst", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + stat = addAttribute(clumpsStren); + MChkErr(stat, "can't add 'clumpsStrength' attribute."); + + clumpsColStren = + nAttr.create("clumpsColStren", "clcs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + stat = addAttribute(clumpsColStren); + MChkErr(stat, "can't add 'clumpsColStren' attribute."); + + clumpsRotStren = + nAttr.create("clumpsRotStren", "clrs", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + stat = addAttribute(clumpsRotStren); + MChkErr(stat, "can't add 'clumpsRotStren' attribute."); + + clumpsRotOffset = + nAttr.create("clumpsRotOffset", "clro", MFnNumericData::kFloat, 0); + nAttr.setStorable(true); + nAttr.setSoftMin(0.0); + nAttr.setSoftMax(1.0); + nAttr.setKeyable(true); + stat = addAttribute(clumpsRotOffset); + MChkErr(stat, "can't add 'clumpsRotOffset' attribute."); + + + //*************************************************************** + // + // INTERMEDIATE ATTRIBUTES FOR TESTING DIRTY INPUTS + // + // When compute() is called for an output plug, it is sometimes useful + // to know which input plug(s) are dirty. As there's no direct way of + // determining this, we use intermediate attrs which are affected by + // the input attr we're interested in and nothing else. If, when we + // query them, they are recomputed, then we know that the input attr + // has changed. + // + //*************************************************************** + + // + // Whenever the instancing attrs change, we need to update the blind + // data, but we don't want to have to do that every time the outputMesh + // is dirty, so let's create some intermediate attributes to tell us + // when the instancing attrs change. + // + + //vlad|10Mar2010 - I do not see anything about it in Docs, probably Autodesk did + //not update it yet. But they added some attribute named "isc" to MPxNode + //so we can not add attribute with name already used. + // + //Solution: rename shave's attribute to "isc1" and "instancingStatusChanged1" + //it is safe because: + //1) attribute is not saved ( not storable ) so will not affect older scenes + //2) we do not access *this* attribute by name using MFnDependencyNode::findPlug + instancingStatusChanged = nAttr.create( + "instancingStatusChanged1", + "isc1", //vlad|10Mar2010 - let's try another name + MFnNumericData::kBoolean, + /*false*/0 + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setWritable(false); + + stat = addAttribute(instancingStatusChanged); + MChkErr(stat, "can't add 'instancingStatusChanged' attribute."); //vlad|10Mar2010 + attributeAffects(shaveParamInstancingStatus, instancingStatusChanged); + + instanceMeshChanged = nAttr.create( + "instanceMeshChanged", + "imc", + MFnNumericData::kBoolean + ); + nAttr.setHidden(true); + nAttr.setStorable(false); + nAttr.setWritable(false); + + addAttribute(instanceMeshChanged); + attributeAffects(instanceMesh, instanceMeshChanged); + + + //*************************************************************** + // + // DEPRECATED ATTRIBUTES + // + // These attributes are no longer used. They are only retained + // so that old scenes can still be loaded. + // + //*************************************************************** + birthName = tAttr.create( "birthName", "bnme", MFnStringData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(birthName); + + shaveBlindShaveParams = + tAttr.create("blindShaveParams", "BSP", blindShaveData::dataTypeId); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(shaveBlindShaveParams); + + shaveBlindState = + tAttr.create("blindShaveState", "BSS", blindShaveData::dataTypeId); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(shaveBlindState); + + growthSet = mesgAttr.create("growthSet", "sgrs"); + mesgAttr.setHidden(true); + mesgAttr.setStorable(false); + addAttribute(growthSet); + + instanceGeoAttr = mesgAttr.create("instanceSource", "instsrc"); + mesgAttr.setHidden(true); + mesgAttr.setStorable(false); + addAttribute(instanceGeoAttr); + + nodeNameAttr = tAttr.create( "nodeName", "nme", MFnStringData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(nodeNameAttr); + + ribDumper = + nAttr.create("ribDumper", "ribd", MFnNumericData::kBoolean, false); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(ribDumper); + + openRibFilename = tAttr.create("ribFilenameClose", "rfno",MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(openRibFilename); + + closeRibFilename = tAttr.create("ribFilenameOpen", "rfnc",MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(closeRibFilename); + + signatureAttr = tAttr.create( + "dynamicsSeeds", "dysd", blindShaveData::dataTypeId + ); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(signatureAttr); + + shaveParamGravity = + nAttr.create("shaveGravity", "spgrv", MFnNumericData::kFloat, 0); + nAttr.setStorable(false); + nAttr.setHidden(true); + addAttribute(shaveParamGravity); + + shaveUvSets = tAttr.create("shaveUvSets", "uvsts", MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + tAttr.setArray(true); + addAttribute(shaveUvSets); + + skullSet = mesgAttr.create("skullSet", "ssks"); + mesgAttr.setHidden(true); + mesgAttr.setStorable(false); + addAttribute(skullSet); + + sourceType = tAttr.create("sourceType", "stp", MFnData::kString); + tAttr.setHidden(true); + tAttr.setStorable(false); + addAttribute(sourceType); + + shaveParamThickness = + nAttr.create("thickness", "spthk", MFnNumericData::kFloat, 0.0); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(shaveParamThickness); + + return MS::kSuccess; +} diff --git a/mayaPlug/shaveNode.h b/mayaPlug/shaveNode.h new file mode 100644 index 0000000..7ebf0b5 --- /dev/null +++ b/mayaPlug/shaveNode.h @@ -0,0 +1,202 @@ +#ifndef _SHAVENODE_H_ +#define _SHAVENODE_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDGModifier.h> +#include <maya/MFnMesh.h> +#include <maya/MPxNode.h> +#include <maya/MPlug.h> +#include <maya/MSelectionList.h> + +#include "shaveSDK.h" +#include "shaveObjExporter.h" + + +class shaveNode : public MPxNode +{ +public: + enum HairDisplayMode + { + kHairDisplayGuides = 0, + kHairDisplayHair = 1, + kHairDisplayGeom = 2 + }; + + static void* creator(); + static MStatus initialize(); + + static MTypeId id; + static const MString nodeTypeName; + static const int kNodeVersion; + + + //******************************************************************* + // + // Attributes + // + //******************************************************************* +private: + static MObject blockDynamics; + static MObject collisionObjectsAttr; + static MObject collisionObjectsGroupIDAttr; + static MObject displayHairMaxAttr; + static MObject displayHairRatioAttr; + static MObject displayNodeAttr; + static MObject dspyMode; + static MObject growthObjectsGroupIDAttr; + + static MObject hairColorTexture; + static MObject hairColorTextureR; + static MObject hairColorTextureG; + static MObject hairColorTextureB; + + static MObject inputCurve; + static MObject inputMesh; + static MObject inputSurf; + static MObject instanceMesh; + static MObject instanceMeshChanged; + static MObject instancingStatusChanged; + static MObject isActive; + + static MObject mutantHairColorTexture; + static MObject mutantHairColorTextureR; + static MObject mutantHairColorTextureG; + static MObject mutantHairColorTextureB; + + static MObject neverBeenInstanced; + static MObject nodeVersionAttr; + static MObject outputMesh; + static MObject renderStateAttr; + static MObject ribDumper; + static MObject ribInsert; + static MObject runDynamics; + + static MObject rootHairColorTexture; + static MObject rootHairColorTextureR; + static MObject rootHairColorTextureG; + static MObject rootHairColorTextureB; + + static MObject shaveBlindHair; + static MObject shaveBlindState; + static MObject shaveInfo; + static MObject shaveNodeCommand; + static MObject shaveParamAmbDiff; // slider 6 + static MObject shaveParamAnimSpeed; // slider 33 + static MObject shaveParamCollide; + static MObject shaveParamCollisionMethod; + static MObject shaveParamDampening; // slider 40 + static MObject shaveParamDensity; // slider 28 + static MObject shaveParamDisplacement; // slider 43 + + static MObject shaveParamFrizzAnimDir; + static MObject shaveParamFrizzAnimDirX; + static MObject shaveParamFrizzAnimDirY; + static MObject shaveParamFrizzAnimDirZ; + + static MObject shaveParamFrizzAnim; // slider 32 + static MObject shaveParamFrizzFreqX; // slider 1 + static MObject shaveParamFrizzFreqY; // slider 30 + static MObject shaveParamFrizzFreqZ; // slider 31 + static MObject shaveParamFrizzRoot; // slider 0 + static MObject shaveParamFrizzTip; // slider 24 + static MObject shaveParamGeomShadow; + static MObject shaveParamGloss; // slider 5 + static MObject shaveParamGravity; + + static MObject shaveParamHairColor; + static MObject shaveParamHairRed; // slider 9 + static MObject shaveParamHairGreen; // slider 10 + static MObject shaveParamHairBlue; // slider 11 + + static MObject shaveParamHaircount; + static MObject shaveParamHueVariation; // slider 12 + static MObject shaveParamInstancingStatus; + static MObject shaveParamKinkFreqX; // slider 3 + static MObject shaveParamKinkFreqY; // slider 34 + static MObject shaveParamKinkFreqZ; // slider 35 + static MObject shaveParamKinkRoot; // slider 38 + static MObject shaveParamKinkTip; // slider 2 + static MObject shaveParamMultStrand; // slider 25 + + static MObject shaveParamMutantColor; + static MObject shaveParamMutantRed; // slider 13 + static MObject shaveParamMutantGreen; // slider 14 + static MObject shaveParamMutantBlue; // slider 15 + + static MObject shaveParamMutantPercent; // slider 16 + + static MObject shaveParamNoInterp; + static MObject shaveParamPainted; + static MObject shaveParamPasses; + static MObject shaveParamRandScale; // slider 36 + static MObject shaveParamRandomizeMulti; // slider 42 + + static MObject shaveParamRootColor; + static MObject shaveParamRootRed; // slider 17 + static MObject shaveParamRootGreen; // slider 18 + static MObject shaveParamRootBlue; // slider 19 + + static MObject shaveParamRootStiffness; // slider 21 + static MObject shaveParamScale; // slider 41 + static MObject shaveParamSegs; + static MObject shaveParamSelfShadow; // slider 7 + static MObject shaveParamSpecular; // slider 4 + static MObject shaveParamSplayRoot; // slider 26 + static MObject shaveParamSplayTip; // slider 27 + static MObject shaveParamMultAsp; // slider 44 - vlad|05July2010 + static MObject shaveParamOffset; // slider 45 - vlad|09July2010 + static MObject shaveParamAspect; // slider 46 - vlad|09July2010 + static MObject shaveParamStiffness; // slider 8 + static MObject shaveParamThickRoot; // slider 20 + static MObject shaveParamThickTip; // slider 37 + static MObject shaveParamTotalGuides; + static MObject shaveParamValueVariation; // slider 39 + static MObject shaveTexMutantColor; + static MObject shaveTextureAttr; + static MObject surfTessU; + static MObject surfTessV; + static MObject textureCacheUpdatedAttr; + static MObject timeAttr; + static MObject triggerAttr; + static MObject overrideGeomShaderAttr; + + static MObject uvSetAssignmentsAttr; + static MObject uvSetNameAttr; + static MObject uvSetTexturesAttr; + + //obsolete here + static MObject flyawayPerc; + static MObject flyawayStren; + static MObject messStren; + + static MObject clumps; + static MObject clumpsStren; + static MObject clumpsColStren; + static MObject clumpsRotStren; + static MObject clumpsRotOffset; + + static MObject versionLockAttr; + +private: + // + // Deprecated Attributes + // + static MObject birthName; // Removed node version 5 or earlier. + static MObject closeRibFilename; // Removed node version 5 or earlier. + static MObject growthSet; // Removed node version 5 or earlier. + static MObject instanceGeoAttr; // Removed node version 6, Shave 2.0v39 + static MObject nodeNameAttr; // Removed node version 5 or earlier. + static MObject openRibFilename; // Removed node version 5 or earlier. + static MObject shaveBlindShaveParams;// Removed node version 7, Shave 2.4v29 + static MObject shaveParamThickness; // Removed node version 5 or earlier. + static MObject shaveUvSets; // Removed node version 5 or earlier. + static MObject signatureAttr; + static MObject skullSet; // Removed node version 5 or earlier. + static MObject sourceType; // Removed node version 5 or earlier. +}; + +#endif + diff --git a/mayaPlug/shaveNodeCmd.cpp b/mayaPlug/shaveNodeCmd.cpp new file mode 100644 index 0000000..d124507 --- /dev/null +++ b/mayaPlug/shaveNodeCmd.cpp @@ -0,0 +1,1003 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDagModifier.h> +#include <maya/MDGModifier.h> +#include <maya/MDoubleArray.h> +#include <maya/MFnAttribute.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnNurbsCurve.h> +#include <maya/MGlobal.h> +#include <maya/MObjectArray.h> +#include <maya/MPlugArray.h> +#include <maya/MPoint.h> +#include <maya/MPointArray.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> +#include <maya/MTime.h> +#include <maya/MVector.h> +#include <maya/MVectorArray.h> + +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveNodeCmd.h" +#include "shaveSDK.h" +#include "shaveUtil.h" + + +static const char* flCollisionList = "-collisionList"; +static const char* fsCollisionList = "-cl"; +static const char* flCopyHair = "-copyHair"; +static const char* fsCopyHair = "-cph"; +static const char* flCurve = "-curve"; +static const char* fsCurve = "-crv"; +static const char* flDump = "-dump"; +static const char* fsDump = "-d"; +static const char* flGrowthList = "-growthList"; +static const char* fsGrowthList = "-gl"; +static const char* flGuidesToCurves = "-guidesToCurves"; +static const char* fsGuidesToCurves = "-gtc"; +static const char* flHairsToCurves = "-hairsToCurves"; +static const char* fsHairsToCurves = "-htc"; +static const char* flHairsToPolygons = "-hairsToPolygons"; +static const char* fsHairsToPolygons = "-htp"; +static const char* flParent = "-parent"; +static const char* fsParent = "-p"; +static const char* flRecomb = "-recomb"; +static const char* fsRecomb = "-rc"; +static const char* flRepair = "-repair"; +static const char* fsRepair = "-rep"; +static const char* flSplineLock = "-splineLock"; +static const char* fsSplineLock = "-slk"; + + +const MString shaveNodeCmd::commandName("shaveNode"); + + +shaveNodeCmd::shaveNodeCmd() {} +shaveNodeCmd::~shaveNodeCmd() {} + + +void* shaveNodeCmd::createCmd() +{ + return new shaveNodeCmd(); +} + + +MSyntax shaveNodeCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(true); + syntax.enableQuery(true); + + syntax.addFlag(fsCollisionList, flCollisionList); + syntax.addFlag(fsCopyHair, flCopyHair, MSyntax::kSelectionItem); + + syntax.addFlag(fsCurve, flCurve, MSyntax::kSelectionItem); + syntax.makeFlagMultiUse(fsCurve); + + syntax.addFlag(fsDump, flDump); + syntax.addFlag(fsGrowthList, flGrowthList); + syntax.addFlag(fsGuidesToCurves, flGuidesToCurves); + syntax.addFlag(fsHairsToCurves, flHairsToCurves); + syntax.addFlag(fsHairsToPolygons, flHairsToPolygons); + syntax.addFlag(fsParent, flParent, MSyntax::kSelectionItem); + syntax.addFlag(fsRecomb, flRecomb); + syntax.addFlag(fsRepair, flRepair); + syntax.addFlag(fsSplineLock, flSplineLock); + + syntax.setObjectType(MSyntax::kSelectionList, 1, 1); + syntax.useSelectionAsDefault(false); + + return syntax; +} + + +MStatus shaveNodeCmd::copyInputConnections( + MDGModifier& dgMod, MObject srcNode, MObject destNode, MObject attr +) +{ + MPlugArray conns; + MPlug destPlug(destNode, attr); + unsigned int i; + MPlug srcPlug(srcNode, attr); + + srcPlug.connectedTo(conns, true, false); + + if (conns.length() > 0) + dgMod.connect(conns[0], destPlug); + + // + // If this is a compound attribute, do its children recursively. + // + if (srcPlug.numChildren() > 0) + { + unsigned int i; + + for (i = 0; i < srcPlug.numChildren(); i++) + { + copyInputConnections( + dgMod, srcNode, destNode, destPlug.child(i).attribute() + ); + } + } + + // + // If this is an array attribute, then do its elements as well. + // Note that we don't do them recursively because this method does not + // properly handle the children of an array element. At the time that + // this was written, the shaveHairShape didn't have any such + // attributes, so it wasn't necessary to support them. + // + // Maya Bug: I used to just get numConnectedElements() then step + // through them using connectionByPhysicalIndex(). But + // the latter continues to report connections after + // they've been broken, which puts it out of synch with + // numConnectedElements(). So I now run through every + // element and check for connections. + // + unsigned int numElements = srcPlug.evaluateNumElements(); + + for (i = 0; i < numElements; i++) + { + MPlug element = srcPlug.elementByPhysicalIndex(i); + + element.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + unsigned int logicalIndex = element.logicalIndex(); + + dgMod.connect( + conns[0], destPlug.elementByLogicalIndex(logicalIndex) + ); + } + } + + return MS::kSuccess; +} + + +MStatus shaveNodeCmd::doEditFlags( + const MArgDatabase& args, shaveHairShape* nodePtr +) +{ + MStatus st; + + if (args.isFlagSet(fsCopyHair)) + { + MSelectionList list; + MObject srcNode; + + args.getFlagArgument(fsCopyHair, 0, list); + list.getDependNode(0, srcNode); + + MFnDependencyNode srcFn(srcNode, &st); + + if (srcNode.isNull() + || !st + || (srcFn.typeId() != shaveHairShape::id)) + { + displayError( + "-cp/-copyParams flag requires a shaveHairShape as" + " its argument." + ); + + return MS::kInvalidParameter; + } + + // If any of the source node's params are being supplied by + // connections, then make the same connections to the destination + // node's params. + MDGModifier dgMod; + MObject node = nodePtr->thisMObject(); + + copyInputConnections( + dgMod, srcNode, node, shaveHairShape::hairColorTexture + ); + + copyInputConnections( + dgMod, srcNode, node, shaveHairShape::mutantHairColorTexture + ); + + copyInputConnections( + dgMod, srcNode, node, shaveHairShape::rootHairColorTexture + ); + + copyInputConnections( + dgMod, srcNode, node, shaveHairShape::shaveTextureAttr + ); + + dgMod.doIt(); + + // Get a pointer to the source node's instance. + shaveHairShape* srcNodePtr = (shaveHairShape*)srcFn.userNode(); + + // Copy the internal hairnode. + SHAVEcopy_node( + &(nodePtr->hairnode), &(srcNodePtr->hairnode), nodePtr->getShaveID() + ); + + // Copy the param values. + SHAVEset_parms(&(srcNodePtr->hairnode.shavep)); + SHAVEfetch_parms(&(nodePtr->hairnode.shavep)); + + // Update the destination's node's plugs to reflect its new + // parameter values. + nodePtr->updatePlugsFromParams(); + } + else if (args.isFlagSet(fsRepair) + && !args.isFlagSet(fsCollisionList) + && !args.isFlagSet(fsGrowthList)) + { + displayError( + "-rep/-repair must be used with either -cl/-collisionList or " + "-gl/-growthList." + ); + + return MS::kInvalidParameter; + } + else if (args.isFlagSet(fsCollisionList)) + { + if (args.isFlagSet(fsRepair)) + { + st = nodePtr->repairGeomList( + shaveHairShape::aCollisionSet, + shaveHairShape::collisionObjectsGroupIDAttr + ); + + if (!st) + { + displayWarning( + MString("Shave: could not repair collision list for ") + + nodePtr->name() + "." + ); + + st = MS::kSuccess; + } + } + else + { + MSelectionList list; + + MGlobal::getActiveSelectionList(list); + nodePtr->setCollisionList(list); + } + } + else if (args.isFlagSet(fsGrowthList)) + { + if (args.isFlagSet(fsRepair)) + { + st = nodePtr->repairGeomList( + shaveHairShape::aGrowthSet, + shaveHairShape::growthObjectsGroupIDAttr + ); + + if (!st) + { + displayWarning( + MString("Shave: could not repair growth list for ") + + nodePtr->name() + "." + ); + + st = MS::kSuccess; + } + } + else + { + MSelectionList list; + + MGlobal::getActiveSelectionList(list); + nodePtr->setGrowthList(list); + } + } + else if (args.isFlagSet(fsGuidesToCurves)) + { + MDagModifier dm; + MDagPath group = getParentTransform(args, dm, st); + + if (!st) return st; + + MObject curve; + MFnNurbsCurve curveFn; + SOFTGUIDE guide; + + int guideIndex; + int hairGroup = nodePtr->getHairGroup(); + int numCurveSegs = nodePtr->hairnode.shavep.segs[hairGroup]; + int numCurveVerts = numCurveSegs + 1; + int numGuideSegs = SHAVE_VERTS_PER_GUIDE - 1; + + numCurveSegs = numCurveSegs > numGuideSegs ? numGuideSegs : numCurveSegs; + numCurveVerts = numCurveVerts > SHAVE_VERTS_PER_GUIDE ? SHAVE_VERTS_PER_GUIDE : numCurveVerts; + + double guideSegsPerHairSeg = (float)numGuideSegs/(float)(numCurveSegs); + + //printf("numCurveSegs %i numCurveVerts %i numGuideSets %i \n",numCurveSegs,numCurveVerts,numGuideSegs);fflush(stdout); + + int guideVertIndex; + int i; + MDoubleArray knots(numCurveVerts); + MPointArray points(numCurveVerts); + MVector p1, p2, p3, p4; + double u; + MVector vert; + + for (guideIndex = 0; + SHAVEfetch_guide(guideIndex, &guide) != -1; + guideIndex++) + { + MVectorArray guideVerts(SHAVE_VERTS_PER_GUIDE); + + for (i = 0; i < SHAVE_VERTS_PER_GUIDE; i++) + { + guideVerts.set( + MVector( + guide.guide[i].x, + guide.guide[i].y, + guide.guide[i].z + ), + i + ); + } + + // Make sure that the first curve vert lies precisely on + // the first guide vert. + points.set(0, guideVerts[0].x, guideVerts[0].y, guideVerts[0].z); + knots.set(0.0, 0); + + for (i = 1; i < numCurveVerts - 1; i++) + { + // Interpolate the curve point along the guide. + u = guideSegsPerHairSeg * (double)i; + guideVertIndex = (int)u; + u -= (double)guideVertIndex; + + p2 = guideVerts[guideVertIndex]; + p3 = guideVerts[guideVertIndex+1]; + + if (guideVertIndex > 0) + p1 = guideVerts[guideVertIndex-1]; + else + p1 = p2; + + if (guideVertIndex < numGuideSegs) + p4 = guideVerts[guideVertIndex+2]; + else + p4 = p3; + + vert = interpolate(p1, p2, p3, p4, u); + + points.set(i, vert.x, vert.y, vert.z); + knots.set((double)i, i); + } + + // Make sure that the last curve vert lies precisely on + // the last guide vert. + points.set( + numCurveSegs, + guideVerts[numGuideSegs].x, + guideVerts[numGuideSegs].y, + guideVerts[numGuideSegs].z + ); + knots.set((double)numCurveSegs, numCurveSegs); + + curve = curveFn.create( + points, + knots, + 1, + MFnNurbsCurve::kOpen, + false, + true, + MObject::kNullObj, + &st + ); + + if (!st) + { + displayError( + commandName + ": error creating curve: " + st.errorString() + ); + return st; + } + + st = dm.reparentNode(curve, group.node()); + + if (!st) + { + displayError( + commandName + + ": error reparenting curve under group transform: " + + st.errorString() + ); + return st; + } + } + + if (guideIndex == 0) + { + displayError( + commandName + ": shave node contains no guides." + ); + + return MS::kFailure; + } + + st = dm.doIt(); + + if (!st) + { + displayError( + commandName + ": error committing changes to DAG: " + + st.errorString() + ); + return st; + } + + MSelectionList list; + + list.add(group); + MGlobal::setActiveSelectionList(list); + + setResult(group.partialPathName()); + } + else if (args.isFlagSet(fsHairsToCurves)) + { + MDagModifier dm; + MDagPath group = getParentTransform(args, dm, st); + + if (!st) return st; + + CURVEINFO ci; + MObject curve; + MFnNurbsCurve curveFn; + bool gotACurve = false; + int hairNum; + int hairGroup = nodePtr->getHairGroup(); + SHAVENODE* hairnode = nodePtr->getHairNode(); + int numHairs = hairnode->shavep.haircount[hairGroup]; + int numSegs = hairnode->shavep.segs[hairGroup]; + WFTYPE wf; + + init_geomWF(&wf); + + for (hairNum = 0; hairNum < numHairs; hairNum++) + { + SHAVEmake_a_curve(0, hairGroup, hairNum, numSegs, &wf, &ci); + + if (wf.totalverts > 0) + { + int face; + + for (face = 0; face < wf.totalfaces; face++) + { + MDoubleArray knots; + MPointArray points; + + int vert; + + for (vert = wf.face_start[face]; vert < wf.face_end[face]; vert++) + { + points.append( + wf.v[wf.facelist[vert]].x, + wf.v[wf.facelist[vert]].y, + wf.v[wf.facelist[vert]].z + ); + knots.append((double)(vert - wf.face_start[face])); + } + + gotACurve = true; + + curve = curveFn.create( + points, + knots, + 1, + MFnNurbsCurve::kOpen, + false, + true, + MObject::kNullObj, + &st + ); + + if (!st) + { + displayError( + commandName + ": error creating curve: " + + st.errorString() + ); + return st; + } + + st = dm.reparentNode(curve, group.node()); + + if (!st) + { + displayError( + commandName + + ": error reparenting curve under group transform: " + + st.errorString() + ); + return st; + } + } + } + } + + free_geomWF(&wf); + + if (!gotACurve) + { + displayError( + commandName + ": shave node contains no hairs." + ); + + return MS::kFailure; + } + + st = dm.doIt(); + + if (!st) + { + displayError( + commandName + ": error committing changes to DAG: " + + st.errorString() + ); + return st; + } + + MSelectionList list; + + list.add(group); + MGlobal::setActiveSelectionList(list); + + setResult(group.partialPathName()); + } + else if (args.isFlagSet(fsHairsToPolygons)) + { + MDagModifier dm; + MDagPath parent = getParentTransform(args, dm, st); + + if (!st) return st; + + MObject meshNode = nodePtr->createExternalMesh(parent.node(), &st); + + if (!st) + { + displayError( + commandName + ": could not create hair mesh: " + st.errorString() + ); + return st; + } + + st = dm.doIt(); + + if (!st) + { + displayError( + commandName + ": error committing changes to DAG: " + + st.errorString() + ); + return st; + } + + MSelectionList list; + + list.add(parent); + MGlobal::setActiveSelectionList(list); + + setResult(parent.partialPathName()); + } + else if (args.isFlagSet(fsRecomb)) + { + MDagPathArray curves; + st = getCurves(args, curves); + + if (st) + { + if (curves.length() == 0) + { + displayError( + MString("The ") + flRecomb + " flag requires that you " + + "specify one or more curves using the " + flCurve + + " flag." + ); + st = MS::kInvalidParameter; + } + else + { + // Copy the curves into an object array. + MObjectArray curveObjs; + + for (unsigned int i = 0; i < curves.length(); ++i) + { + MFnNurbsCurve curveFn(curves[i]); + curveObjs.append(curveFn.object()); + } + + // Shave wants points on the curve, not cvs. Since + // Shave reparameterizes all curves to + // SHAVE_VERTS_PER_GUIDE points, we might as well just + // give it that many points per curve in the first + // place. + WFTYPE curveData; + + shaveUtil::sampleCurves( + curveObjs, SHAVE_VERTS_PER_GUIDE, curveData + ); + + nodePtr->recomb(curveData); + free_geomWF(&curveData); + } + } + } + else if (args.isFlagSet(fsSplineLock)) + { + MDagPathArray curves; + st = getCurves(args, curves); + + if (st) + { + if (curves.length() == 0) + nodePtr->clearSplineLocks(); + else + nodePtr->setSplineLocks(curves); + } + } + else + { + displayError("No editable flags specified."); + st = MS::kInvalidParameter; + } + + return st; +} + + +MStatus shaveNodeCmd::doIt(const MArgList& argList) +{ + MStatus st; + MArgDatabase args(syntax(), argList, &st); + + if (!st) return st; + + shaveHairShape* nodePtr = getShaveNode(args); + if (nodePtr == NULL) return MS::kInvalidParameter; + + if (args.isQuery()) + { + st = doQueryFlags(args, nodePtr); + } + else if (args.isEdit()) + { + st = doEditFlags(args, nodePtr); + } + else + { + displayError(commandName + ": must specify one of -edit or -query."); + st = MS::kInvalidParameter; + } + + return st; +} + + +MStatus shaveNodeCmd::doQueryFlags( + const MArgDatabase& args, shaveHairShape* nodePtr +) +{ + MStatus st; + + MObject node = nodePtr->thisMObject(); + + if (args.isFlagSet(fsDump)) + { + MPlugArray connections; + MTime timeVal; + MFnAttribute attrFn; + MString indent(" "); + + cout << "Warning: The -dump flag is not yet fully implemented." + << endl + << " Here's what we have so far..." + << endl; + +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cout << "Attribute values for '" << nodePtr->name().asChar() << "':" << endl; +#else + cout << "Attribute values for '" << nodePtr->name() << "':" << endl; +#endif + + // + // time + // + MPlug plug(node, shaveHairShape::timeAttr); + plug.getValue(timeVal); + + attrFn.setObject(shaveHairShape::timeAttr); + +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cout << indent.asChar() << attrFn.name().asChar() << ": " << timeVal.value() + << endl; +#else + cout << indent << attrFn.name() << ": " << timeVal.value() + << endl; +#endif + + displayInputConnection(node, shaveHairShape::inputCurve, indent); + displayInputConnection(node, shaveHairShape::inputMesh, indent); + displayInputConnection(node, shaveHairShape::inputSurf, indent); + displayInputConnection(node, shaveHairShape::collisionObjectsAttr, indent); + } + else if (args.isFlagSet(fsCollisionList)) + { + MSelectionList collisionList; + nodePtr->getCollisionList(collisionList); + + MStringArray collisionObjectNames; + collisionList.getSelectionStrings(collisionObjectNames); + setResult(collisionObjectNames); + } + else if (args.isFlagSet(fsGrowthList)) + { + MSelectionList growthList; + nodePtr->getGrowthList(growthList); + + MStringArray growthObjectNames; + growthList.getSelectionStrings(growthObjectNames); + setResult(growthObjectNames); + } + else + { + displayError("No queryable flags specified."); + st = MS::kInvalidParameter; + } + + return st; +} + + +void shaveNodeCmd::displayInputConnection( + MObject node, MObject attr, MString indent +) +{ + MPlug plug(node, attr); + MFnAttribute attrFn(attr); + MPlugArray connections; + MString otherPlugName; + + if (plug.isArray()) + { + // + // Maya Bug: I used to just get numConnectedElements() then step + // through them using connectionByPhysicalIndex(). But + // the latter continues to report connections after + // they've been broken, which puts it out of synch with + // numConnectedElements(). So I now run through every + // element and check for connections. + // + unsigned int numElements = plug.evaluateNumElements(); + unsigned int i; + bool foundConnections = false; + + for (i = 0; i < numElements; i++) + { + MPlug element = plug.elementByPhysicalIndex(i); + + element.connectedTo(connections, true, false); + + if (connections.length() > 0) + { + MString elementName = element.name(); + + // + // Strip the node name from the start of the element name. + // +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cout << indent.asChar() << stripNode(element.name()).asChar() << ": " + << connections[0].name().asChar() << endl; +#else + cout << indent << stripNode(element.name()) << ": " + << connections[0].name() << endl; +#endif + + foundConnections = true; + } + } + + if (!foundConnections) + { +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cout << indent.asChar() << stripNode(plug.name()).asChar() << ": No Connections" + << endl; +#else + cout << indent << stripNode(plug.name()) << ": No Connections" + << endl; +#endif + } + } + else + { + plug.connectedTo(connections, true, false); + + if (connections.length() > 0) + otherPlugName = connections[0].name(); + else + otherPlugName = "Not Connected"; + +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cout << indent.asChar() << stripNode(plug.name()).asChar() << ": " + << otherPlugName.asChar() << endl; +#else + cout << indent << stripNode(plug.name()) << ": " << otherPlugName + << endl; +#endif + } +} + + +MStatus shaveNodeCmd::getCurves(const MArgDatabase& args, MDagPathArray& curves) +{ + MDagPath curve; + MArgList flagArgs; + MSelectionList list; + unsigned int numCurves = args.numberOfFlagUses(flCurve); + + for (unsigned int i = 0; i < numCurves; i++) + { + list.clear(); + + // + // Get a DAG path to this flag's curve. + // + args.getFlagArgumentList(fsCurve, i, flagArgs); + list.add(flagArgs.asString(0)); + list.getDagPath(0, curve); + curve.extendToShape(); + + if (!curve.isValid() || !curve.hasFn(MFn::kNurbsCurve)) + { + displayError( + MString("'") + flagArgs.asString(0) + "' is not a NURBS curve." + ); + curves.clear(); + return MS::kInvalidParameter; + } + + curves.append(curve); + } + + return MS::kSuccess; +} + + +MDagPath shaveNodeCmd::getParentTransform( + const MArgDatabase& args, MDagModifier& dm, MStatus& st +) +{ + MDagPath parent; + + if (args.isFlagSet(fsParent)) + { + MSelectionList list; + + args.getFlagArgument(fsParent, 0, list); + + list.getDagPath(0, parent); + + if (!parent.isValid()) + { + displayError( + commandName + ": argument of '" + flParent + + "' flag is not a transform." + ); + st = MS::kInvalidParameter; + } + } + else + { + MObject node = dm.createNode("transform", MObject::kNullObj, &st); + + if (!st) + { + displayError( + commandName + ": could not create group transform: " + + st.errorString() + ); + } + else + { + if (args.isFlagSet(flHairsToPolygons)) + dm.renameNode(node, "shaveHairMesh#"); + else + dm.renameNode(node, "shaveCurveGroup#"); + + st = MDagPath::getAPathTo(node, parent); + + if (!st) + { + displayError( + commandName + ": error getting path to group transform: " + + st.errorString() + ); + } + } + } + + return parent; +} + + +shaveHairShape* shaveNodeCmd::getShaveNode(const MArgDatabase& args) +{ + MStatus st; + shaveHairShape* nodePtr = NULL; + + // + // Get the shaveHairShape to be operated on. + // + MSelectionList selection; + args.getObjects(selection); + + MObject node(MObject::kNullObj); + selection.getDependNode(0, node); + + MFnDependencyNode nodeFn(node, &st); + + if (node.isNull() || !st || (nodeFn.typeId() != shaveHairShape::id)) + { + st = MS::kInvalidParameter; + + displayError("No shaveHairShape specified."); + } + else + { + nodePtr = (shaveHairShape*)nodeFn.userNode(); + + // Make sure that this is the shaveHairShape which is loaded into the + // engine. + if (nodePtr) + { + nodePtr->makeCurrent(); + nodePtr->getHairNode(); + } + } + + return nodePtr; +} + + +MVector shaveNodeCmd::interpolate( + const MVector& p1, + const MVector& p2, + const MVector& p3, + const MVector& p4, + double u +) +{ + double u3, u2; + + if (u >= 1.0) return p3; + if (u <= 0.0) return p2; + + u3 = u * u * u; + u2 = u * u; + + return ((-u3 + (2.0 * u2) - u) * p1 + + (3.0 * u3 - 5.0 * u2 + 2.0) * p2 + + (-3.0 * u3 + (4.0 * u2) + u) * p3 + + (u3 + -u2) * p4) / 2.0; +} + + +MString shaveNodeCmd::stripNode(MString plugName) +{ + return plugName.substring(plugName.index('.')+1, plugName.length()-1); +} diff --git a/mayaPlug/shaveNodeCmd.h b/mayaPlug/shaveNodeCmd.h new file mode 100644 index 0000000..abafa14 --- /dev/null +++ b/mayaPlug/shaveNodeCmd.h @@ -0,0 +1,84 @@ +#ifndef shaveNodeCmd_h +#define shaveNodeCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +#if MAYA_API_VERSION < 20180000 +class MArgDatabase; +class MDagModifier; +class MDGModifier; +#endif + +class shaveHairShape; + + +class shaveNodeCmd : public MPxCommand +{ +public: + shaveNodeCmd(); + virtual ~shaveNodeCmd(); + + static MStatus copyInputConnections( + MDGModifier& dgMod, + MObject srcNode, + MObject destNode, + MObject attr + ); + + static void* createCmd(); + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + static MSyntax createSyntax(); + + + static const MString commandName; + +private: + void displayInputConnection( + MObject node, MObject attr, MString indent + ); + + MStatus doEditFlags( + const MArgDatabase& args, shaveHairShape* nodePtr + ); + + MStatus doQueryFlags( + const MArgDatabase& args, shaveHairShape* nodePtr + ); + + // Fills 'curves' with dag paths for all of the curves passed to + // the command using the '-curve' flag. If any of the curves are + // invalid then 'curves' will be empty and MS::kInvalidParameter will + // be returned. + MStatus getCurves(const MArgDatabase& args, MDagPathArray& curves); + + MDagPath getParentTransform( + const MArgDatabase& args, MDagModifier& dm, MStatus& st + ); + + shaveHairShape* getShaveNode(const MArgDatabase& args); + + static MVector interpolate( + const MVector& p1, + const MVector& p2, + const MVector& p3, + const MVector& p4, + double u + ); + + MString stripNode(MString plugName); +}; + + +inline bool shaveNodeCmd::isUndoable() const +{ return false; } + +#endif + diff --git a/mayaPlug/shaveObjExporter.cpp b/mayaPlug/shaveObjExporter.cpp new file mode 100644 index 0000000..c982550 --- /dev/null +++ b/mayaPlug/shaveObjExporter.cpp @@ -0,0 +1,2054 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include "shaveHairShape.h" +#include "shaveSDK.h" +#include "shaveObjExporter.h" +#include "shaveUtil.h" +#include <maya/MDagModifier.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFnMesh.h> +#include <maya/MFnNurbsSurface.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MIntArray.h> +#include <maya/MPlugArray.h> +#include <maya/MRenderUtil.h> +#include <maya/MUint64Array.h> + +#ifdef OSMac_ +#include "shaveMacCarbon.h" +#endif + + +#define MEMFILE_CHUNKSIZE 1024 + +static const int numSamplesPerCurve = 30; + + +// We've got a few globals we need to make sure are initialized. +shaveObjTranslator::shaveObjTranslator() +: space(MSpace::kWorld) +{ +} + + +void* shaveObjTranslator::creator() +{ + return new shaveObjTranslator; +} + + +//! export all of the objects we need to build hair for this shaveHairShape. +/** +exportThis is called by shaveHairShape and shaveRender to export geometry to +.obj files and the WFType structure. +*/ +MStatus shaveObjTranslator::exportThis( + MDataBlock& block, ShaveExportData& exportData +) +{ + MFnDependencyNode nodeFn(exportData.shaveHairShape); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + MObjectArray tesselations; + +#if GET_GEOM_FROM_DATABLOCK + nodePtr->getGrowthGeom(block, hairObjects, hairComponents); + nodePtr->getCollisionGeom(block, skullObjects, skullComponents); +#else + nodePtr->getGrowthList(hairSelections); + nodePtr->getCollisionList(skullSelections); +#endif + + int uTess = exportData.uSubdivs; + int vTess = exportData.vSubdivs; + + int subdDepth = exportData.subdDepth; + int subdSamples = exportData.subdSamples; + + newtopo = exportData.newtopo; + + objListInit(); + wfInit(uTess, vTess, subdDepth, subdSamples, exportData, tesselations); + doWFType(exportData.theObj, exportData.uvs, tesselations); + + // + // If we detected new topology, then generate an OBJ file which the + // caller can pass to Shave in a SHAVExplant() call, to let it know the + // new geometry. + // + // However, if the caller did not pass us a filename for the OBJ file, + // then they don't want us to generate an OBJ file. + // + if (newtopo && (exportData.objFileName != "")) + { + bool** growthFaces = NULL; + bool** collisionFaces = NULL; + + mtlLookupInit(growthFaces, collisionFaces); + + doExport( + exportData.objFileName, tesselations, growthFaces, collisionFaces + ); + + mtlLookupCleanup(growthFaces, collisionFaces); + } + + exportData.newtopo = newtopo; + + tesselations.clear(); + fullHairObjects.clear(); + partialHairObjects.clear(); + fullSkullObjects.clear(); + partialSkullObjects.clear(); +#if GET_GEOM_FROM_DATABLOCK + partialHairComponents.clear(); + partialSkullComponents.clear(); +#else + hairSelections.clear(); + skullSelections.clear(); +#endif + + return MS::kSuccess; +} + + +MStatus shaveObjTranslator::objListInit() +{ + MStatus st; + + // + // Some growth or collision surfaces may encompass the entire object + // while others may only involve a subset of the object's faces. + // + // In the case of a subset of faces, we have to do extra processing to + // keep track of which faces are involved, and which are not. The + // first step of that is to separate the full and partial objects into + // separate lists. + // + st = separateOutPartialMeshes( +#if GET_GEOM_FROM_DATABLOCK + hairObjects, + hairComponents, + fullHairObjects, + partialHairObjects, + partialHairComponents +#else + hairSelections, fullHairObjects, partialHairObjects +#endif + ); + + if (st) + { + st = separateOutPartialMeshes( +#if GET_GEOM_FROM_DATABLOCK + skullObjects, + skullComponents. + fullSkullObjects, + partialSkullObjects, + partialSkullComponents +#else + skullSelections, fullSkullObjects, partialSkullObjects +#endif + ); + } + + return st; +} + + +#if GET_GEOM_FROM_DATABLOCK +MStatus shaveObjTranslator::separateOutPartialMeshes( + MObjectArray& objects, + MObjectArray& components, + MObjectArray& fullObjects, + MObjectArray& partialMeshes, + MObjectArray& partialMeshComponents +) +{ + fullObjects.clear(); + partialMeshes.clear(); + partialMeshComponents.clear(); + + unsigned i; + + for (i = 0; i < objects.length(); i++) + { + if (!components[i].isNull() && objects[i].hasFn(MFn::kMesh)) + { + partialMeshes.append(objects[i]); + partialMeshComponents.append(components[i]); + } + else + fullObjects.append(objects[i]); + } + + return MS::kSuccess; +} +#else +MStatus shaveObjTranslator::separateOutPartialMeshes( + MSelectionList& objectList, + MDagPathArray& fullObjects, + MSelectionList& partialMeshes +) +{ + MObject component; + MFnDagNode nodeFn; + MDagPath nodePath; + MDagPath shapePath; + + // + // Separate out the objects into those which are full objects, and + // those which are just a subset of a mesh. + // + fullObjects.clear(); + partialMeshes.clear(); + + MItSelectionList iter(objectList); + + for (iter.reset(); !iter.isDone(); iter.next()) + { + iter.getDagPath(nodePath, component); + + // + // 'nodePath' may be pointing to a transform, so get a path to + // the actual shape node beneath it. + // + shapePath = nodePath; + shapePath.extendToShape(); + + if (shapePath.hasFn(MFn::kMesh)) + { + // + // If there's no component specified, then the entire mesh is + // being used. + // + if (component.isNull()) + fullObjects.append(shapePath); + else + partialMeshes.add(nodePath, component); + } + else if (shapePath.hasFn(MFn::kNurbsCurve) + || shapePath.hasFn(MFn::kNurbsSurface) + || shapePath.hasFn(MFn::kSubdiv)) + { + // + // We don't allow partial NURBS curves or surfaces, or + // partial subdivision surfaces, so they always go on the + // 'Full' list. + // + fullObjects.append(shapePath); + } + } + + return MS::kSuccess; +} +#endif + + +MStatus shaveObjTranslator::mtlLookupInit( + bool**& growthMtlTbl, bool**& collisionMtlTbl +) +{ + MStatus st; + + st = createMtlLookup(partialHairObjects, growthMtlTbl); + + if (st) st = createMtlLookup(partialSkullObjects, collisionMtlTbl); + + return st; +} + + +void shaveObjTranslator::mtlLookupCleanup( + bool**& growthMtlTbl, bool**& collisionMtlTbl +) +{ + deleteMtlLookup(partialHairObjects, growthMtlTbl); + deleteMtlLookup(partialSkullObjects, collisionMtlTbl); +} + + +// +// For a mesh, it is possible that only some of its faces are being used +// to grow or collide with hair. So we need to build lookup tables to tell +// use which faces those are. +// +MStatus shaveObjTranslator::createMtlLookup( + MSelectionList& objectList, bool**& lookupTbl +) +{ + // + // Set up a lookup table for partial hair meshes. + // + unsigned int objectCount = objectList.length(); + + if (objectCount > 0) + { + MObject component; + unsigned int i; + int numFaces; + MDagPath shapePath; + + lookupTbl = new bool*[objectCount]; + + for (i = 0; i < objectCount; i++) + { + objectList.getDagPath(i, shapePath, component); + + MItMeshPolygon fullPolyIter(shapePath); + + // + // Allocate an array of flags for all the faces. + // + numFaces = fullPolyIter.count(); + lookupTbl[i] = new bool[numFaces]; + + if (lookupTbl[i] == NULL) return MS::kFailure; + + // + // Initialize all the table entries to false, which means that + // those faces will use the 'default' material. + // + for (int k = 0; k < numFaces; k++) + lookupTbl[i][k] = false; + + // + // For those faces listed in the component, set their table + // entries to true, which means that they will use the 'hair' + // material. + // + MItMeshPolygon partialPolyIter(shapePath, component); + + for (partialPolyIter.reset(); + !partialPolyIter.isDone(); + partialPolyIter.next()) + { + int faceID = partialPolyIter.index(); + + // + // It is possible that because of changes in the topology + // of the growth surface since the hair was assigned, our + // component list will contain faces which no longer exist. + // Oddly enough, the poly iterator will still return those + // faces to us, so we have to be careful to discard any + // which no longer exist. + // + if (faceID < numFaces) + lookupTbl[i][faceID] = true; + } + } + } + + return MS::kSuccess; +} + + +void shaveObjTranslator::deleteMtlLookup( + MSelectionList& objectList, bool**& lookupTbl +) +{ + if (lookupTbl != NULL) + { + unsigned int i; + + for (i = 0; i < objectList.length(); i++) + delete [] lookupTbl[i]; + + delete [] lookupTbl; + } +} + + +// +// Export an array of objects which are completely active. (i.e. hair grows +// from the entire object, or the entire object can collide with hair, etc). +// +void shaveObjTranslator::exportFullObjects( + FILE* exportFile, +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& objects, +#else + MDagPathArray& objects, +#endif + MObjectArray tesselations, + const char* surfaceMaterial, + const char* curveMaterial, + int& objectIndex, + int& totalNumVerts, + int& totalNumFaces +) +{ + int i; + int j; + int objectCount = objects.length(); + int vertCount; +#if GET_GEOM_FROM_DATABLOCK + MObject object; +#else + MDagPath object; +#endif + MPoint p; + + for (i = 0; i < objectCount; i++) + { + unsigned int objStartVertIndex = totalNumVerts; + + object = objects[i]; + + if (object.hasFn(MFn::kMesh)) + { + // + // Output the vertex positions. + // + MItMeshPolygon polyIter(object); + MItMeshVertex vtxIter(object); + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next()) + { + p = vtxIter.position(space); + + fprintf( + exportFile, + "v %f %f %f\n", + (float)p.x, + (float)p.y, + (float)p.z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl %s\n", surfaceMaterial); + + // + // Output the face information. + // + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + fprintf(exportFile, "f "); + + vertCount = polyIter.polygonVertexCount(); + + for(j = 0; j < vertCount; j++) + { + fprintf( + exportFile, + "%d ", + objStartVertIndex + polyIter.vertexIndex(j) + 1 + ); + } + + fprintf(exportFile, "\n"); + + totalNumFaces++; + } + + totalNumVerts += vtxIter.count(); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kNurbsSurface)) +#else + else if (object.hasFn(MFn::kNurbsSurface) + || object.hasFn(MFn::kSubdiv)) +#endif + { + // + // Output the vertex positions of the temporary mesh which + // contains this surface's tesselation. + // + MItMeshVertex vtxIter(tesselations[objectIndex]); + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next() ) + { + p = vtxIter.position(space); + + fprintf( + exportFile, + "v %f %f %f\n", + (float)p.x, + (float)p.y, + (float)p.z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl %s\n", surfaceMaterial); + + // + // Output the face information. + // + MItMeshPolygon faceIter(tesselations[objectIndex]); + + for (faceIter.reset(); !faceIter.isDone(); faceIter.next()) + { + fprintf(exportFile, "f "); + + vertCount = faceIter.polygonVertexCount(); + + for(j = 0; j < vertCount; j++) + { + fprintf( + exportFile, + "%d ", + objStartVertIndex + faceIter.vertexIndex(j) + 1 + ); + } + + fprintf(exportFile, "\n"); + totalNumFaces++; + } + + totalNumVerts += vtxIter.count(); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kSubdiv)) + { + // + // Output a list of all the vertices for the triangulated level + // 1 faces of the subd. + // + MPointArray objVerts; + MIntArray objVertIndices; + MUint64Array quadIDs; + unsigned int v; + + shaveUtil::getSubdQuads( + objectPath, + quadIDs, + objVerts, + objVertIndices + ); + + for (v = 0; v < objVerts.length(); v++) + { + fprintf( + exportFile, + "v %f %f %f\n", + (float)objVerts[v].x, + (float)objVerts[v].y, + (float)objVerts[v].z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl %s\n", surfaceMaterial); + + // + // Output the face information. + // + unsigned int numQuads = quadIDs.length(); + unsigned int q; + + v = 0; + + for (q = 0; q < numQuads; q++) + { + // + // Each quad is split into two triangles: one using verts + // 0, 1 and 2, the other using 0, 2 and 3. + // + // Vertex numbering in the OBJ file begins at 1 but our + // internal indices are all 0-based, so we have to add one + // to the indices before writing them to the file. + // + fprintf( + exportFile, + "f %d %d %d\n", + objStartVertIndex + objVertIndices[v] + 1, + objStartVertIndex + objVertIndices[v+1] + 1, + objStartVertIndex + objVertIndices[v+2] + 1 + ); + + fprintf( + exportFile, + "f %d %d %d\n", + objStartVertIndex + objVertIndices[v] + 1, + objStartVertIndex + objVertIndices[v+2] + 1, + objStartVertIndex + objVertIndices[v+3] + 1 + ); + + v += 4; + } + + totalNumVerts += objVerts.length(); + totalNumFaces += numQuads * 2; + } +#endif + else if (object.hasFn(MFn::kNurbsCurve) && (curveMaterial != NULL)) + { + MFloatPointArray samples; + + getCurveSamples(object, samples); + + unsigned int s; + + for (s = 0; s < samples.length(); s++) + { + fprintf( + exportFile, + "v %f %f %f\n", + samples[s].x, + samples[s].y, + samples[s].z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl spline\n"); + + // + // Output the entire curve as a single face. + // + fprintf(exportFile, "f "); + + for (s = samples.length(); s > 0; s--) + { + // + // You may notice that in storeObjectInWFTYPE() we have a + // similar loop which subtracts 1 from the vertex index + // while here we don't. The reason is that the vertex + // indices in an OBJ file are 1-based, while the arrays in + // the WFTYPE are 0-based. + // + fprintf(exportFile, "%d ", objStartVertIndex + s); + } + + fprintf(exportFile, "\n"); + + totalNumFaces++; + totalNumVerts += samples.length(); + } + + fprintf(exportFile, "# end of object.\n"); + objectIndex++; + } +} + + +// +// Export a list of meshes which have some active and some inactive faces. +// +void shaveObjTranslator::exportPartialMeshes( + FILE* exportFile, +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& meshList, +#else + MSelectionList& meshList, +#endif + bool** activeFaceMap, + const char* activeFaceMaterial, + int& objectIndex, + int& vertexOffset, + int& faceOffset +) +{ + int objectCount = meshList.length(); + int i; +#if !GET_GEOM_FROM_DATABLOCK + MDagPath shapePath; +#endif + MPoint p; + + for (i = 0; i < objectCount; i++) + { +#if !GET_GEOM_FROM_DATABLOCK + meshList.getDagPath(i, shapePath); +#endif + + // + // Write out the vertices. + // +#if GET_GEOM_FROM_DATABLOCK + MItMeshVertex vtxIter(meshList[i]); +#else + MItMeshVertex vtxIter(shapePath); +#endif + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next()) + { + p = vtxIter.position(space); + + fprintf( + exportFile, "v %f %f %f\n", (float)p.x, (float)p.y, (float)p.z + ); + } + + // + // Write out the face information. + // + bool firstTime = true; + bool faceIsActive = false; + bool prevFaceIsActive = true; + + MItMeshPolygon polyIter(shapePath); + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + faceIsActive = activeFaceMap[i][polyIter.index()]; + + if (firstTime || (faceIsActive != prevFaceIsActive)) + { + fprintf( + exportFile, + "usemtl %s\n", + faceIsActive ? activeFaceMaterial : "default" + ); + + prevFaceIsActive = faceIsActive; + firstTime = false; + } + + fprintf(exportFile, "f "); + + int vertCount = polyIter.polygonVertexCount(); + + for (int j = 0; j < vertCount; j++) + { + fprintf( + exportFile, "%d ", polyIter.vertexIndex(j)+1+vertexOffset + ); + } + + fprintf(exportFile, "\n"); + faceOffset++; + } + + fprintf(exportFile, "# end of mesh.\n"); + + vertexOffset += vtxIter.count(); + objectIndex++; + } +} + + +// +// Export all surfaces and splines associated with the current shaveHairShape +// into the specified object file. +// +MStatus shaveObjTranslator::doExport( + MString fileName, + MObjectArray tesselations, + bool** growthFaces, + bool** collisionFaces +) +{ + FILE* fp = NULL; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + fileName = shaveMacCarbon::makeMacFilename(fileName); +#endif + + if (fileName == "") + { + cerr << "Shave: internal error: no OBJ file supplied to doExport()." + << endl; + return MS::kFailure; + } + + // + // Open the obj file for writing. Bail as gracefully as possible if + // this fails. + // + fp = fopen(fileName.asChar(), "w"); + + if (fp == NULL) + { + cerr << "Shave: doExport() could not write to '" +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + << fileName.asChar() +#else + << fileName +#endif + << "'." << endl; + + return MS::kFailure; + } + + int objectIndex = 0; + int faceOffset = 0; + int vertOffset = 0; + + // + // Export the hair objects. + // + exportFullObjects( + fp, + fullHairObjects, + tesselations, + "hair", + "spline", + objectIndex, + vertOffset, + faceOffset + ); + + exportPartialMeshes( + fp, + partialHairObjects, + growthFaces, + "hair", + objectIndex, + vertOffset, + faceOffset + ); + + // + // Export the skull objects. + // + exportFullObjects( + fp, + fullSkullObjects, + tesselations, + "skull", + NULL, + objectIndex, + vertOffset, + faceOffset + ); + + exportPartialMeshes( + fp, + partialSkullObjects, + collisionFaces, + "skull", + objectIndex, + vertOffset, + faceOffset + ); + + fflush(fp); + fclose(fp); + + return MS::kSuccess; +} + + +void shaveObjTranslator::getObjectCounts( +#if GET_GEOM_FROM_DATABLOCK + MObject& object, +#else + MDagPath& object, +#endif + GeomType geomType, + int& totalNumVerts, + int& totalNumFaces, + int& totalNumFaceVerts, + int uTess, + int vTess, + int sDept, + int sSamp, + ShaveExportData* exportData, + MObjectArray& tesselations, + bool includeDisplacements +) +{ + MObject tesselation = MObject::kNullObj; + + if (exportData) + { + exportData->meshes.append(object); + exportData->startFaces.append(totalNumFaces); + exportData->startVerts.append(totalNumVerts); + } + + if (object.hasFn(MFn::kMesh)) + { + MStatus st; + MItMeshPolygon* faceIter = 0; + MItMeshVertex* vtxIter = 0; + + // I used to have this inside the 'else' clause where it is used, + // except this was another of those cases where MObject ref + // counting screwed up, leaving the iterators invalid outside the + // else clause. So we put it here instead. + MObject meshDataObj; + + if (includeDisplacements && shaveUtil::isSurfaceDisplaced(object)) + { + tesselation = shaveUtil::getMesh(object, uTess, vTess, sDept, sSamp, true); + faceIter = new MItMeshPolygon(tesselation); + vtxIter = new MItMeshVertex(tesselation); + } + else + { +#if GET_GEOM_FROM_PLUGS + // See comments at top of shaveObjExporter.h + MFnMesh meshFn(object, &st); + MPlug worldMeshArray = meshFn.findPlug("worldMesh", true, &st); + MPlug worldMeshPlug = worldMeshArray.elementByLogicalIndex(object.instanceNumber(), &st); + + st = worldMeshPlug.getValue(meshDataObj); + + faceIter = new MItMeshPolygon(meshDataObj, &st); + vtxIter = new MItMeshVertex(meshDataObj, &st); +#else + faceIter = new MItMeshPolygon(object); + vtxIter = new MItMeshVertex(object); +#endif + } + + totalNumVerts += vtxIter->count(); + totalNumFaces += faceIter->count(); + + for (faceIter->reset(); !faceIter->isDone(); faceIter->next() ) + { + totalNumFaceVerts += faceIter->polygonVertexCount(); + } + + delete faceIter; + delete vtxIter; + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kNurbsSurface)) +#else + else if (object.hasFn(MFn::kNurbsSurface) || object.hasFn(MFn::kSubdiv)) +#endif + { + // + // Tesselate the surface into a temporary mesh. + // + tesselation = shaveUtil::getMesh( + object, uTess, vTess, sDept, sSamp, includeDisplacements + ); + + // + // Get the counts from the temporary mesh. + // + MItMeshPolygon faceIter(tesselation); + MItMeshVertex vtxIter(tesselation); + + totalNumVerts += vtxIter.count(); + totalNumFaces += faceIter.count(); + + for (faceIter.reset(); !faceIter.isDone(); faceIter.next() ) + { + totalNumFaceVerts += faceIter.polygonVertexCount(); + } + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kSubdiv)) + { + MUint64Array quadIDs; + MPointArray verts; + MIntArray vertIndices; + + shaveUtil::getSubdQuads(object, quadIDs, verts, vertIndices); + + unsigned int numTriangles = quadIDs.length() * 2; + + totalNumVerts += verts.length(); + totalNumFaceVerts += numTriangles * 3; + totalNumFaces += numTriangles; + } +#endif + // We can use curves as growth geometry, but we cannot collide with + // curves and they do not occlude. + // + else if (object.hasFn(MFn::kNurbsCurve) && (geomType == kGrowthGeom)) + { + totalNumVerts += numSamplesPerCurve; + totalNumFaceVerts += numSamplesPerCurve; + + // + // We treat a curve as a single face. + // + totalNumFaces += 1; + } + + tesselations.append(tesselation); +} + + +//! initialize variables needed when we fill the WFType struct. +/** +Here we assign values to totalverts, totalfverts, and totalfaces so that we know +how much memory to set aside when we go to fill the WFType struct. for this node. +Originally it was planned that all of this would only happen once on the initial +call to the exporter, but it turns out that because of the architecture, we end up +calling this every time we come through the exporter. It might make sense to re-scope +totalverts, totalfverts, and totalfaces, it makes no sense for them to be global any +more. +*/ + +MStatus shaveObjTranslator::wfInit( + int uTess, + int vTess, + int sDept, + int sSamp, + ShaveExportData& exportData, + MObjectArray& tesselations +) +{ + totalverts = 0; + totalfaces = 0; + totalfverts = 0; + MDagPath node; + MFnDagNode nodeFn; + MObject component; + MFnMesh meshFn; + + exportData.meshes.clear(); + exportData.startFaces.clear(); + exportData.startVerts.clear(); + tesselations.clear(); + + // + // In order to handle the material lookup, we need to get a count of + // which objects we will be exporting, and how many faces those objects + // have. Unfortunately, this means doing an iteration pass through the + // selections to get these counts. Once we know what we are dealing + // with, we can build the lookups on the next iteration through the + // selections. This first pass could be removed by doing a little + // extra bookkeeping during the selection process- it's not that big a + // deal though, since these lookups (and so this pre-process pass) only + // need to happen during the disk export, and so will not effect the + // time-critical WFTYPE creation. + // + MDagPath shapePath; + int objectCount; + int i; + + objectCount = fullHairObjects.length(); + + for (i = 0; i < objectCount; i++) + { + getObjectCounts( + fullHairObjects[i], + kGrowthGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + objectCount = partialHairObjects.length(); + + for (i = 0; i < objectCount; i++) + { + partialHairObjects.getDagPath(i, shapePath); + + getObjectCounts( + shapePath, + kGrowthGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + objectCount = fullSkullObjects.length(); + + for (i = 0; i < objectCount; i++) + { + getObjectCounts( + fullSkullObjects[i], + kCollisionGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + objectCount = partialSkullObjects.length(); + + for (i = 0; i < objectCount; i++) + { + partialSkullObjects.getDagPath(i, shapePath); + + getObjectCounts( + shapePath, + kCollisionGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + // Add an extra element to each of these so that we can easily figure + // out the number of verts/faces in the last object. + exportData.startFaces.append(totalfaces); + exportData.startVerts.append(totalverts); + + return MS::kSuccess; +} + + +void shaveObjTranslator::storeMeshInWFTYPE( + WFTYPE* wf, + WFTYPE* uvs, + MItMeshVertex& vertexIter, + MItMeshPolygon& faceIter, + int& vertexIndex, + int& faceIndex, + int& faceVertexIndex +) +{ + float2 vertUV; + int startOfMesh = vertexIndex; + + // + // Fill in the vertex table. + // + for (vertexIter.reset(); !vertexIter.isDone(); vertexIter.next()) + { + MPoint p = vertexIter.position(space); + + wf->v[vertexIndex].x = (float)p.x; + wf->v[vertexIndex].y = (float)p.y; + wf->v[vertexIndex].z = (float)p.z; + + // + // Workaround for Maya bug #178517 + // + // MItMeshVertex::getUV() will cause Maya to crash if the + // vertex has no UVs, so we check the UV count first. + // + int numUVs; + vertexIter.numUVs(numUVs); + + if (numUVs > 0) + { + vertexIter.getUV(vertUV); +// wf->uv[vertexIndex].x = (float)vertUV[0]; +// wf->uv[vertexIndex].y = (float)vertUV[1]; +// wf->uv[vertexIndex].z = 0.0f; + } + + vertexIndex++; + } + + // + // Fill in the face list. For each poly, iterate through its vertex + // references, and write them to the WFTYPE. voff serves as an index + // incremented each time we finish an iteration, provides the offset so + // we ref. the right verts. + // + if (newtopo) + { + for (faceIter.reset(); !faceIter.isDone(); faceIter.next()) + { + bool hasUVs = faceIter.hasUVs(); + int faceVertexCount = faceIter.polygonVertexCount(); + wf->face_start[faceIndex] = faceVertexIndex; + uvs->face_start[faceIndex] = faceVertexIndex; + + for (int vtx=0; vtx < faceVertexCount; vtx++) + { + wf->facelist[faceVertexIndex] = faceIter.vertexIndex(vtx) + + startOfMesh; + uvs->facelist[faceVertexIndex] = faceVertexIndex; + + if (hasUVs) + { + faceIter.getUV(vtx, vertUV); + uvs->v[faceVertexIndex].x = vertUV[0]; + uvs->v[faceVertexIndex].y = vertUV[1]; + wf->uv[faceVertexIndex].x = vertUV[0]; + wf->uv[faceVertexIndex].y = vertUV[1]; + + + } + + faceVertexIndex++; + } + + wf->face_end[faceIndex] = faceVertexIndex; + uvs->face_end[faceIndex] = faceVertexIndex; + faceIndex++; + } + } +} + + +void shaveObjTranslator::storeObjectInWFTYPE( + WFTYPE* wf, + WFTYPE* uvs, +#if GET_GEOM_FROM_DATABLOCK + MObject& object, +#else + MDagPath& object, +#endif + MObject tesselation, + int& totalNumVerts, + int& totalNumFaces, + int& totalNumFaceVerts +) +{ + if (object.hasFn(MFn::kMesh)) + { +#if GET_GEOM_FROM_PLUGS + // See comments at top of shaveObjExporter.h + MFnMesh meshFn(object); + MPlug worldMeshArray = meshFn.findPlug("worldMesh"); + MPlug worldMeshPlug = worldMeshArray.elementByLogicalIndex(object.instanceNumber()); + MObject meshDataObj; + + worldMeshPlug.getValue(meshDataObj); + + MItMeshPolygon faceIter(meshDataObj); + MItMeshVertex vtxIter(meshDataObj); +#else + MItMeshPolygon faceIter(object); + MItMeshVertex vtxIter(object); +#endif + + storeMeshInWFTYPE( + wf, + uvs, + vtxIter, + faceIter, + totalNumVerts, + totalNumFaces, + totalNumFaceVerts + ); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kNurbsSurface)) +#else + else if (object.hasFn(MFn::kNurbsSurface) || object.hasFn(MFn::kSubdiv)) +#endif + { + MItMeshVertex vtxIter(tesselation); + MItMeshPolygon faceIter(tesselation); + + ////// debug //////// + //MGlobal::displayInfo(MString("storeObjectInWFTYPE: tesselation ") + vtxIter.count() + " " + faceIter.count()); + ///////////////////// + + storeMeshInWFTYPE( + wf, + uvs, + vtxIter, + faceIter, + totalNumVerts, + totalNumFaces, + totalNumFaceVerts + ); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kSubdiv)) + { + MPointArray objVerts; + MIntArray objVertIndices; + MUint64Array quadIDs; + MDoubleArray vertUs; + MDoubleArray vertVs; + + shaveUtil::getSubdQuads( + object, quadIDs, objVerts, objVertIndices, &vertUs, &vertVs + ); + + // + // Save the vertex positions. + // + unsigned int i; + unsigned int objStartVertIndex = totalNumVerts; + + for (i = 0; i < objVerts.length(); i++) + { + wf->v[totalNumVerts].x = objVerts[i].x; + wf->v[totalNumVerts].y = objVerts[i].y; + wf->v[totalNumVerts].z = objVerts[i].z; + + if (newtopo) + { + wf->uv[totalNumVerts].x = vertUs[i]; + wf->uv[totalNumVerts].y = vertVs[i]; + } + + totalNumVerts++; + } + + if (newtopo) + { + // + // Save the per-face lists of vertex indices. Note that each + // quad becomes two triangular faces. + // + unsigned int numQuads = quadIDs.length(); + unsigned int objVertIndex = 0; + + for (i = 0; i < numQuads; i++) + { + // + // FIRST TRIANGLE + // + // Record the index of the triangle's first vertex. + // + wf->face_start[totalNumFaces] = totalNumFaceVerts; + uvs->face_start[totalNumFaces] = totalNumFaceVerts; + + // + // Record the indices of the triangle's three face-vertices. + // + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+1]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+2]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + // + // Record the index of the triangle's last vertex + // (actually, the index of the first vertex beyond the + // triangle's last vertex, since that's what Shave expects). + // + wf->face_end[totalNumFaces] = totalNumFaceVerts; + uvs->face_end[totalNumFaces] = totalNumFaceVerts; + + totalNumFaces++; + + // + // SECOND TRIANGLE + // + wf->face_start[totalNumFaces] = totalNumFaceVerts; + uvs->face_start[totalNumFaces] = totalNumFaceVerts; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+2]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+3]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->face_end[totalNumFaces] = totalNumFaceVerts; + uvs->face_end[totalNumFaces] = totalNumFaceVerts; + + totalNumFaces++; + + // + // Advance by 4 object verts to get to the next quad. + // + objVertIndex += 4; + } + } + } +#endif + else if (object.hasFn(MFn::kNurbsCurve)) + { + MFloatPointArray samples; + + getCurveSamples(object, samples); + + unsigned int x; + unsigned int objStartVertIndex = totalNumVerts; + + for (x = 0; x < samples.length(); x++) + { + wf->v[totalNumVerts].x = samples[x].x; + wf->v[totalNumVerts].y = samples[x].y; + wf->v[totalNumVerts].z = samples[x].z; + + if(newtopo) + { + wf->uv[totalNumVerts].x = 0.0f; + wf->uv[totalNumVerts].y = 0.0f; + } + + totalNumVerts++; + } + + wf->face_start[totalNumFaces] = totalNumFaceVerts; + uvs->face_start[totalNumFaces] = totalNumFaceVerts; + + for (x = samples.length(); x > 0; x--) + { + wf->facelist[totalNumFaceVerts] = objStartVertIndex + x - 1; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts].x = 0.0f; + uvs->v[totalNumFaceVerts].y = 0.0f; + + totalNumFaceVerts++; + } + + wf->face_end[totalNumFaces] = totalNumFaceVerts; + uvs->face_end[totalNumFaces] = totalNumFaceVerts; + + totalNumFaces++; + } +} + + +// Here we write the WFType. Will be careful in here, and offload any +// preprocessing to other places so that this segment can scream as quickly +// as possible. + +MStatus shaveObjTranslator::doWFType( + WFTYPE* wf, WFTYPE* uvs, MObjectArray tesselations +) +{ + // printf("totalverts is %d, totalfaces is %d.\n",totalverts, totalfaces); + // set aside a little memory within the WFTYPE so we can fill in the lists. + if((wf->totalverts != totalverts) || (wf->totalfaces != totalfaces)) + { + newtopo = true; + + free_geomWF(wf); + free_geomWF(uvs); + + wf->totalverts = totalverts; + wf->totalfaces = totalfaces; + wf->totalfverts = totalfverts; + + // We need the UVs to be per-face-vert, which means no sharing + // of UVs, so the total number of 'verts' must be the same as the + // number of face-verts. + uvs->totalverts = uvs->totalfverts = totalfverts; + uvs->totalfaces = totalfaces; + + alloc_geomWF(wf); + alloc_geomWF(uvs); + + if ((wf->v == NULL) + || (wf->face_start == NULL) + || (wf->face_end == NULL) + || (uvs->v == NULL) + || (uvs->face_start == NULL) + || (uvs->face_end == NULL)) + { + return MS::kFailure; + } + } + + int objectIndex = 0; + int faceIndex = 0; + int faceVertexIndex = 0; + int vertexIndex = 0; + int objectCount; + int i; +#if !GET_GEOM_FROM_DATABLOCK + MDagPath object; +#endif + + // + // Store the hair objects into myObj. + // + objectCount = fullHairObjects.length(); + + for(i = 0; i < objectCount; i++) + { + storeObjectInWFTYPE( + wf, + uvs, + fullHairObjects[i], + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + objectCount = partialHairObjects.length(); + + for(i = 0; i < objectCount; i++) + { +#if !GET_GEOM_FROM_DATABLOCK + partialHairObjects.getDagPath(i, object); +#endif + + storeObjectInWFTYPE( + wf, + uvs, +#if GET_GEOM_FROM_DATABLOCK + partialHairObjects[i], +#else + object, +#endif + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + // + // Store the skull objects into myObj. + // + objectCount = fullSkullObjects.length(); + + for(i = 0; i < objectCount; i++) + { + storeObjectInWFTYPE( + wf, + uvs, + fullSkullObjects[i], + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + objectCount = partialSkullObjects.length(); + + for(i = 0; i < objectCount; i++) + { +#if !GET_GEOM_FROM_DATABLOCK + partialSkullObjects.getDagPath(i, object); +#endif + + storeObjectInWFTYPE( + wf, + uvs, +#if GET_GEOM_FROM_DATABLOCK + partialSkullObjects[i], +#else + object, +#endif + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + return MS::kSuccess; +} + + +void shaveObjTranslator::exportInstanceObj( + MObject mesh, MObject shader, MString objFileName +) +{ + FILE* fp = NULL; + MFnMesh meshFn(mesh); + int vertCount; + int j; + MPoint p; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + objFileName = shaveMacCarbon::makeMacFilename(objFileName); +#endif + + if (objFileName == "") + { + cerr << "Shave: internal error: no OBJ file supplied to" + << " exportInstanceObj()." << endl; + return; + } + + // + // Open the obj file for writing. Bail as gracefully as possible if + // this fails. + // + fp = fopen (objFileName.asChar(), "w"); + + if(fp == NULL) + { + cerr << "Shave: exportInstanceObj() could not write to '" +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + << objFileName.asChar() +#else + << objFileName +#endif + << "'." << endl; + return; + } + + fprintf(fp, "# obj file created by shaveObjExporter.\n\n"); + + MItMeshPolygon polyIter(mesh); + MFloatPointArray pointArray; + meshFn.getPoints(pointArray); + // iterate through the vertex list, writing each vertex to the file. + + MFloatArray uArray; + MFloatArray vArray; + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + MFloatMatrix camMatrix; + meshFn.getUVs(uArray, vArray); + + MString texturePlugName(getShaderTexture(shader)); + int vertn; + + for(vertn = 0; vertn < (int)pointArray.length(); vertn++) + { + fprintf(fp,"v %f %f %f\n", pointArray[vertn].x, pointArray[vertn].y, pointArray[vertn].z); + } + + // + // If the instance's shader doesn't have a texture driving its colour, + // then we'll let shave determine the colour for itself using the root, + // tip, etc, parameters. + // + if (texturePlugName == "") + { + fprintf(fp, "usemtl default\n\n"); + } + else + { + MRenderUtil::sampleShadingNetwork( + texturePlugName, //plug to sample + (int)pointArray.length(), //number of samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL, //u tangents + NULL, //v tangents + NULL, //filter size + vertColorArray, //out color + vertTranspArray //out transparency + ); + + for(int vertn = 0; vertn < (int)vertColorArray.length(); vertn++) + { + fprintf(fp,"#vc %f %f %f\n", vertColorArray[vertn].x*255, vertColorArray[vertn].y*255, vertColorArray[vertn].z*255); + } + + fprintf(fp, "usemtl colorlock\n\n"); + } + + // + // Output the list of verts in each face. + // + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + fprintf(fp, "f "); + vertCount = polyIter.polygonVertexCount(); + for(j = 0; j < vertCount; j++) + { + fprintf(fp, "%d ",polyIter.vertexIndex(j)+1); + } + fprintf(fp, "\n"); + } + + // + // Output the list of normals for each face's verts. + // + MVector norm; + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + fprintf(fp, "n"); + vertCount = polyIter.polygonVertexCount(); + for(j = 0; j < vertCount; j++) + { + polyIter.getNormal(j, norm); + fprintf(fp, " %f %f %f", norm.x, norm.y, norm.z); + } + fprintf(fp, "\n"); + } + + fprintf(fp, "# end of obj file.\n\n"); + + fflush(fp); + fclose(fp); + + return; +} + + +MStatus shaveObjTranslator::exportOcclusion( + MDagPathArray& objects, WFTYPE * memObj +) +{ + MDagPath transformPath; + MDagPath shapePath; + unsigned int i; + unsigned int numObjects = objects.length(); + MObjectArray tesselations; + + // + // Before we can allocate the vertex arrays in the WFTYPE, we need to + // know how many vertices we have. So let's count them. + // + free_geomWF(memObj); + + totalverts = 0; + totalfaces = 0; + totalfverts = 0; + + for (i = 0; i < numObjects; i++) + { + shapePath = objects[i]; + shapePath.extendToShape(); + + // + // Passing in -1 for the tesselation values will cause the object's + // own tesselation values to be used. + // + getObjectCounts( + shapePath, + kOcclusionGeom, + totalverts, + totalfaces, + totalfverts, + -1, + -1, + 1, + 1, + NULL, + tesselations, + true + ); + } + + // + // Allocate whatever space we need to store all the vertices. + // + memObj->totalverts = totalverts; + memObj->totalfaces = totalfaces; + memObj->totalfverts = totalfverts; + + alloc_geomWF(memObj); + + if ((memObj->v == NULL) + || (memObj->face_start == NULL) + || (memObj->face_end == NULL)) + { + tesselations.clear(); + + return MS::kFailure; + } + + // + // Now we can actually store the vertices into the WFTYPE. + // + int globalFaceIndex = 0; + int globalFaceVtxIndex = 0; + int globalVtxIndex = 0; + int objStartVertex = 0; + + for (i = 0; i < numObjects; i++) + { + objStartVertex = globalVtxIndex; + + shapePath = objects[i]; + shapePath.extendToShape(); + transformPath = shapePath; + transformPath.pop(); + + if(shapePath.hasFn(MFn::kMesh)) + { + MItMeshPolygon* polyIter = 0; + MItMeshVertex* vtxIter = 0; + + if (tesselations[i].isNull()) + { + polyIter = new MItMeshPolygon(shapePath); + vtxIter = new MItMeshVertex(shapePath); + } + else + { + polyIter = new MItMeshPolygon(tesselations[i]); + vtxIter = new MItMeshVertex(tesselations[i]); + } + + // + // Store the vertices into the WFTYPE object which was passed + // to us. + // + for (vtxIter->reset(); !vtxIter->isDone(); vtxIter->next()) + { + MPoint p = vtxIter->position (space); + + memObj->v[globalVtxIndex].x = (float) p.x; + memObj->v[globalVtxIndex].y = (float) p.y; + memObj->v[globalVtxIndex].z = (float) p.z; + globalVtxIndex++; + } + + // + // Store the face-vertices into the WFTYPE object. + // + for (polyIter->reset(); !polyIter->isDone(); polyIter->next()) + { + int polyVertexCount = polyIter->polygonVertexCount(); + + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + for (int vtx=0; vtx < polyVertexCount; vtx++) + { + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + polyIter->vertexIndex(vtx); + } + + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + } + + delete polyIter; + delete vtxIter; + } +#ifdef EVALUATE_POSITION_FIXED + else if (shapePath.hasFn(MFn::kNurbsSurface)) +#else + else if (shapePath.hasFn(MFn::kNurbsSurface) + || shapePath.hasFn(MFn::kSubdiv)) +#endif + { + // + // Store the vertices into the WFTYPE object which was passed + // to us. + // + MItMeshVertex vtxIter(tesselations[i]); + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next()) + { + MPoint p = vtxIter.position(space); + memObj->v[globalVtxIndex].x = (float)p.x; + memObj->v[globalVtxIndex].y = (float)p.y; + memObj->v[globalVtxIndex].z = (float)p.z; + globalVtxIndex++; + } + + // + // Store the face-vertices into the WFTYPE object. + // + MItMeshPolygon polyIter(tesselations[i]); + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next()) + { + int polyVertexCount = polyIter.polygonVertexCount(); + + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + for (int vtx=0; vtx < polyVertexCount; vtx++) + { + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + polyIter.vertexIndex(vtx); + } + + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + } + } +#ifdef EVALUATE_POSITION_FIXED + else if (shapePath.hasFn(MFn::kSubdiv)) + { + MPointArray objVerts; + MIntArray objVertIndices; + MUint64Array quadIDs; + + shaveUtil::getSubdQuads( + shapePath, quadIDs, objVerts, objVertIndices + ); + + // + // Save the vertex positions. + // + unsigned int j; + + for (j = 0; j < objVerts.length(); j++) + { + memObj->v[globalVtxIndex].x = objVerts[j].x; + memObj->v[globalVtxIndex].y = objVerts[j].y; + memObj->v[globalVtxIndex].z = objVerts[j].z; + + globalVtxIndex++; + } + + // + // Save the per-face lists of vertex indices. Note that each + // quad becomes two triangles. + // + unsigned int numQuads = quadIDs.length(); + unsigned int objVertIndex = 0; + + for (j = 0; j < numQuads; j++) + { + // + // Record the index of the first triangle's first vertex. + // + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + // + // Record the indices of the first triangle's 3 vertices. + // + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+1]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+2]; + + // + // Record the index of the first vertex beyond the first + // triangle's last vertex. + // + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + + // + // Record the index of the second triangle's first vertex. + // + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + // + // Record the indices of the second triangle's 3 vertices. + // + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+2]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+3]; + + // + // Record the index of the first vertex beyond the second + // triangle's last vertex. + // + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + + // + // Advance to the next quad. + // + objVertIndex += 4; + } + } +#endif + } + + tesselations.clear(); + + return MS::kSuccess; +} + + +MObject shaveObjTranslator::getConnectedShader(MFnMesh meshFn) +{ + MObjectArray shaderList; + MIntArray indecies; + meshFn.getConnectedShaders(0, shaderList, indecies); + MObject ret(shaderList[0]); + return ret; +} + + +void shaveObjTranslator::getCurveSamples( +#if GET_GEOM_FROM_DATABLOCK + MObject curve, MFloatPointArray& samples +#else + MDagPath curve, MFloatPointArray& samples +#endif +) +{ + MFnNurbsCurve curveFn(curve); + double maxParam; + double minParam; + double param; + int s; + + curveFn.getKnotDomain(minParam, maxParam); + + double paramIncrement = (maxParam - minParam) + / (double)(numSamplesPerCurve - 1); + + samples.clear(); + + for (s = 0, param = minParam; + s < numSamplesPerCurve; + s++, param += paramIncrement) + { + if (s == numSamplesPerCurve - 1) param = maxParam; + + MPoint p ;; + curveFn.getPointAtParam(param, p, MSpace::kWorld ); + + samples.append((float)p.x, (float)p.y, (float)p.z); + } +} + + +MString shaveObjTranslator::getShaderTexture(MObject shadingGroup) +{ + MString retval = ""; + + if (!shadingGroup.isNull()) + { + MStatus status; + MFnDependencyNode mtlDependNode(shadingGroup, &status); + MPlug tmpPlug; + MPlugArray texPlugArray; + tmpPlug = mtlDependNode.findPlug("surfaceShader"); + tmpPlug.connectedTo(texPlugArray, true, false); + + MObject shader = texPlugArray[0].node(); + texPlugArray.clear(); + mtlDependNode.setObject(shader); + + tmpPlug = mtlDependNode.findPlug("color"); + + if (tmpPlug.isConnected()) + { + tmpPlug.connectedTo(texPlugArray, true, false); + retval = texPlugArray[0].name(); + } + } + + return retval; +} + diff --git a/mayaPlug/shaveObjExporter.h b/mayaPlug/shaveObjExporter.h new file mode 100644 index 0000000..2beb721 --- /dev/null +++ b/mayaPlug/shaveObjExporter.h @@ -0,0 +1,260 @@ +#ifndef _SHAVEOBJEXPORTER_H_ +#define _SHAVEOBJEXPORTER_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagPath.h> +#include <maya/MDagPathArray.h> +#include <maya/MFnMesh.h> +#include <maya/MFnNurbsCurve.h> +#include <maya/MFnNurbsSurface.h> +#include <maya/MGlobal.h> +#include <maya/MIntArray.h> +#include <maya/MItDag.h> +#include <maya/MItMeshEdge.h> +#include <maya/MItMeshPolygon.h> +#include <maya/MItMeshVertex.h> +#include <maya/MItSelectionList.h> +#include <maya/MPointArray.h> +#include <maya/MSelectionList.h> + +// We used to use MDagPaths to initialize function sets to access +// the input surfacs. That was always dangerous given that this +// code gets called during shaveHairShape::compute(). In Maya 8.0 +// that started to cause problems when it turned out that polygon +// smooth proxy meshes were not in an internally consistent state +// in the middle of a compute(): they could have verts but not yet +// any faces. +// +// Here we get around this by pulling the geometry from the mesh's +// plug to ensure that it updates correctly. The proper approach +// GET_GEOM_FROM_DATABLOCK represents the correct approach which is to +// avoid the MDagPaths altogether and get all input geometry from the +// compute's datablock. +// +// Unfortunately that's not finished yet (shaveTextureStore still uses +// the DAG paths for the surfaces) so GET_GEOM_FROM_PLUGS represents a +// temporary workaround in which we continue to use MDagPaths to access +// the nodes, but make sure to pull the geometry from the node's plugs +// beforehand to ensure that it is up to date. +// +// Since the old approach worked prior to Maya 8.0, I'm only enabling this +// new stuff in 8.0. +#define GET_GEOM_FROM_DATABLOCK 0 +#define GET_GEOM_FROM_PLUGS 1 + +#if MAYA_API_VERSION < 20180000 +class MDataBlock; +class MFloatPointArray; +#endif + + +typedef struct +{ + WFTYPE* theObj; + WFTYPE* uvs; + MDagPathArray meshes; // The exported meshes, in the order exported + MIntArray startFaces; // Face ID of first face of each mesh + MIntArray startVerts; // Vertex ID of first vert of each mesh + bool newtopo; + + int uSubdivs; + int vSubdivs; + int lastUSubdiv; + int lastVSubdiv; + + int subdDepth; + int subdSamples; + int lastSubdDepth; + int lastSubdSamples; + + MObject shaveHairShape; + MString objFileName; +}ShaveExportData; + + +class shaveObjTranslator { +public: + shaveObjTranslator (); + virtual ~shaveObjTranslator (){} + static void* creator(); + + // called from shaveHairShape in order to export .obj file for the + // current node. + MStatus exportThis(MDataBlock& block, ShaveExportData& data); + MStatus exportOcclusion(MDagPathArray&, WFTYPE *); + void exportInstanceObj(MObject instanceMesh, MObject shader, MString filename); + +private: + enum GeomType { + kGrowthGeom, + kCollisionGeom, + kOcclusionGeom + }; + + bool newtopo; + +#if GET_GEOM_FROM_DATABLOCK + MObjectArray hairObjects; + MObjectArray hairComponents; + MObjectArray fullHairObjects; + MObjectArray partialHairObjects; + MObjectArray partialHairComponents; + + MObjectArray skullObjects; + MObjectArray skullComponents; + MObjectArray fullSkullObjects; + MObjectArray partialSkullObjects; + MObjectArray partialSkullComponents; + +#else + MSelectionList hairSelections; + MSelectionList skullSelections; + + MDagPathArray fullHairObjects; + MSelectionList partialHairObjects; + + MDagPathArray fullSkullObjects; + MSelectionList partialSkullObjects; +#endif + + //! the total number of verts we'll write to the WFType. + int totalverts; + //! the total number of faces we'll write to the WFType. + int totalfaces; + //! the total number of fverts we'll write to the WFType. + int totalfverts; + + MSpace::Space space; + + MStatus objListInit(); + + MStatus separateOutPartialMeshes( +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& objects, + MObjectArray& components, + MObjectArray& fullObjects, + MObjectArray& partialMeshes, + MObjectArray& partialMeshComponents +#else + MSelectionList& objectList, + MDagPathArray& fullObjects, + MSelectionList& partialMeshes +#endif + ); + + MStatus mtlLookupInit(bool**& growthFaces, bool**& collisionFaces); + void mtlLookupCleanup(bool**& growthFaces, bool**& collisionFaces); + + MStatus createMtlLookup(MSelectionList& objectList, bool**& lookupTbl); + void deleteMtlLookup(MSelectionList& objectList, bool**& lookupTbl); + + void exportFullObjects( + FILE* exportFile, +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& objects, +#else + MDagPathArray& objects, +#endif + MObjectArray tesselations, + const char* surfaceMaterial, + const char* curveMaterial, + int& objectIndex, + int& vtxOffset, + int& faceOffset + ); + + void exportPartialMeshes( + FILE* exportFile, +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& meshList, +#else + MSelectionList& meshList, +#endif + bool** activeFaceMap, + const char* activeFaceMaterial, + int& objectIndex, + int& vertexOffset, + int& faceOffset + ); + + MStatus doExport( + MString fileName, + MObjectArray tesselations, + bool** growthFaces, + bool** collisionFaces + ); + + void storeMeshInWFTYPE( + WFTYPE* wf, + WFTYPE* uvs, + MItMeshVertex& vertexIter, + MItMeshPolygon& faceIter, + int& vertexIndex, + int& faceIndex, + int& faceVertexIndex + ); + + void storeObjectInWFTYPE( + WFTYPE* wf, + WFTYPE* uvs, +#if GET_GEOM_FROM_DATABLOCK + MObject& object, +#else + MDagPath& object, +#endif + MObject tempMesh, + int& vertexIndex, + int& faceIndex, + int& faceVertexIndex + ); + + void getObjectCounts( +#if GET_GEOM_FROM_DATABLOCK + MObject& object, +#else + MDagPath& object, +#endif + GeomType geomType, + int& numVertices, + int& numFaces, + int& numFaceVertices, + int uTess, + int vTess, + int sDept, + int sSamp, + ShaveExportData* exportData, + MObjectArray& tesselations, + bool includeDisplacements = false + ); + + MStatus wfInit( + int uTess, + int vTess, + int sDept, + int sSamp, + ShaveExportData& exportData, + MObjectArray& tesselations + ); + + bool checkExported(); + + MStatus doWFType(WFTYPE* wf, WFTYPE* uvs, MObjectArray tesselations); + + MObject getConnectedShader(MFnMesh); + + void getCurveSamples( +#if GET_GEOM_FROM_DATABLOCK + MObject curve, MFloatPointArray& samples +#else + MDagPath curve, MFloatPointArray& samples +#endif + ); + + MString getShaderTexture(MObject shadingGroup); +}; + +#endif + diff --git a/mayaPlug/shavePack2TexCmd.cpp b/mayaPlug/shavePack2TexCmd.cpp new file mode 100644 index 0000000..b9def1a --- /dev/null +++ b/mayaPlug/shavePack2TexCmd.cpp @@ -0,0 +1,334 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shavePack2TexCmd.cpp + + DESCRIPTION: command for packing hair data to image file + + HISTORY: created 23-04-2013 + + *> + **********************************************************************/ + +#include "shavePack2TexCmd.h" +#include "shaveHairShape.h" +#include "shaveAPI.h" +#include "shaveRender.h" + +#include <maya/MDagPath.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MDGModifier.h> +#include <maya/MArgDatabase.h> +#include <maya/MPlugArray.h> +#include <maya/MDagModifier.h> +#include <maya/MSelectionList.h> +#include <maya/MFnRenderLayer.h> +#include <maya/MItDependencyNodes.h> + + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Command | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MString shavePack2TexCmd::cmd = "shavePack2Tex"; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Flags | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +char* shavePack2TexCmd::sf_file = "f"; + +char* shavePack2TexCmd::lf_file = "file"; + +char* shavePack2TexCmd::sf_hair = "h"; + +char* shavePack2TexCmd::lf_hair = "hair"; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Methods | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MStatus shavePack2TexCmd::doIt(const MArgList &maList) +{ + MStatus stat; + + MArgDatabase parser(syntax(),maList,&stat); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shavePack2TexCmd: can not parse arguments."); + return stat; + } + + if(parser.isFlagSet(sf_file) && + parser.isFlagSet(sf_hair)) + { + MString file; + MString hair; + if(parser.getFlagArgument(sf_file,0,file) != MStatus::kSuccess) + { + MGlobal::displayError("shavePack2TexCmd: can not get 'file' argument."); + return MStatus::kFailure; + } + MStringArray fparts; + if(file.split('.',fparts) != MStatus::kSuccess || fparts.length() < 2) + { + MGlobal::displayError(MString("shavePack2TexCmd: can not get file extention from ") + file); + return MStatus::kFailure; + } + if(fparts[1] != "tga") + { + MGlobal::displayWarning("shavePack2TexCmd: you need 32bpp format without compression .tga"); + return MStatus::kFailure; + } + if(parser.getFlagArgument(sf_hair,0,hair) != MStatus::kSuccess) + { + MGlobal::displayError("shavePack2TexCmd: can not get 'hair' argument."); + return MStatus::kFailure; + } + MObject node; + if(!findNodeByName(hair,node)) + { + MGlobal::displayError(MString("shavePack2TexCmd: can not find ") + hair + " node"); + return MStatus::kFailure; + } + MFnDependencyNode dFn(node); + if(dFn.typeId() != shaveHairShape::id) + { + MGlobal::displayError(MString("shavePack2TexCmd: the node ") + hair + " is not a shaveHairShape"); + return MStatus::kFailure; + } + MImage img; + if(!copyToImage(node,img)) + { + MGlobal::displayError("shavePack2TexCmd: can not pack to image."); + return MStatus::kFailure; + } + + MString mdir; + MGlobal::executeCommand("internalVar -userWorkspaceDir",mdir); + + MString out = mdir+file; + MGlobal::displayInfo(MString("shavePack2TexCmd: saving file ") + out ); + + if(img.writeToFile(out,fparts[1]) != MStatus::kSuccess) + { + MGlobal::displayError(MString("shavePack2TexCmd: can not write ") + out ); + return MStatus::kFailure; + } + return MStatus::kSuccess; + } + else + { + MGlobal::displayError("shavePack2TexCmd: you should specify 'file' and 'hair' flags."); + return MStatus::kFailure; + } +} + +MSyntax shavePack2TexCmd::newSyntax() +{ + MSyntax syn; + + syn.addFlag(sf_file, lf_file, MSyntax::kString); + syn.addFlag(sf_hair, lf_hair, MSyntax::kString); + + return syn; +} +#define SIMPLE_PACK +static void i2c(unsigned char* c, unsigned int i) +{ +#ifdef SIMPLE_PACK + memcpy(c,&i,4); +#else + char b[4] = {0,0,0,0}; + b[3] = i/1000000; + i -= b[3]*1000000; + b[2] = i/10000; + i -= b[2]*10000; + b[1] = i/100; + i -= b[1]*100; + b[0] = i; + memcpy(c,b,4); +#endif +} +static void f2c(unsigned char* c, float f) +{ +#ifdef SIMPLE_PACK + memcpy(c,&f,4); +#else + char sign = f >= 0.0f ? 1 : 0; + f = fabs(f); + float af = floor(f); + float df = f - af; + if(df < 0.01f) + df = 0.0f; + + //int d = f * pow(10.0f,7) - a * pow(10.0f,7); + //while(d >= 100) + // d /= 10; + + int a = (int)af; + int d = (int)(df*100.0f); + + char b[4] = {0,0,0,0}; + b[3]=(char)(a/256); + b[2]=a%256; + b[1]=d; + b[0]=sign; + memcpy(c,b,4); +#endif +} + +bool shavePack2TexCmd::copyToImage(MObject node, MImage& image) const +{ + //MObjectArray nodes; + //nodes.append(node); + + //crap, unresolved!! + //shaveAPI::HairInfo h; + //shaveAPI::exportHair(nodes, &h); + + //MFnDependencyNode dFn(node); + //shaveHairShape* hairShape = (shaveHairShape*)dFn.userNode(); + //SHAVENODE* hairNode = hairShape->getHairNode(); + //hairShape->setStackIndex(0); + //SHAVEclear_stack(); + //SHAVEadd_hairOPEN2(hairNode, 0); + + MObjectArray nodes; + nodes.append(node); + shaveRender::buildHairStack(nodes, shaveConstant::kShutterBoth); + + + HAIRTYPE h; + SHAVEinit_hairtype(&h); + SHAVEexport_hairtype(&h); + +// if(h.numHairs == 0 || h.numVertices == 0 || h.numHairVertices == 0) +// return false; + + if(h.totalfaces == 0 || h.totalverts == 0 || h.totalfverts == 0) + { + MGlobal::displayError("shavePack2TexCmd: hairtype is empty."); + return false; + } + + struct header + { + unsigned char num_hairs[4]; + unsigned char num_knots[4]; + }; + struct hair + { + unsigned char start_vert[4]; + unsigned char tip_radi[4]; + unsigned char root_radi[4]; + + unsigned char root_color_r[4]; + unsigned char root_color_g[4]; + unsigned char root_color_b[4]; + + unsigned char tip_color_r[4]; + unsigned char tip_color_g[4]; + unsigned char tip_color_b[4]; + }; + struct vertex + { + unsigned char x[4]; + unsigned char y[4]; + unsigned char z[4]; + }; + //[header][hairs array][verts arra] + //int numknots = h.hairEndIndices[0] - h.hairStartIndices[0]; + int numknots = h.face_end[0] - h.face_start[0]; + int numhairs = h.totalfaces; + + header he; + //i2c(he.num_hairs,h.numHairs); + i2c(he.num_hairs,h.totalfaces); + + i2c(he.num_knots,numknots); + + + hair* har = (hair*) malloc(numhairs*sizeof(hair)); + vertex* var = (vertex*) malloc(numhairs*numknots*sizeof(vertex)); + for(int i = 0; i < numhairs; i++) + { + hair& hh = har[i]; + i2c(hh.start_vert,i*numknots); + + //f2c(hh.tip_radi,h.tipRadii[i]); + //f2c(hh.root_radi,h.rootRadii[i]); + + f2c(hh.tip_radi,h.radiustip[i]); + f2c(hh.root_radi,h.radiusroot[i]); + + + //f2c(hh.root_color_r,h.rootColors[i].r); + //f2c(hh.root_color_g,h.rootColors[i].g); + //f2c(hh.root_color_b,h.rootColors[i].b); + + f2c(hh.root_color_r,h.colorroot[i].x); + f2c(hh.root_color_g,h.colorroot[i].y); + f2c(hh.root_color_b,h.colorroot[i].z); + + + //f2c(hh.tip_color_r,h.tipColors[i].r); + //f2c(hh.tip_color_g,h.tipColors[i].g); + //f2c(hh.tip_color_b,h.tipColors[i].b); + + f2c(hh.tip_color_r,h.colortip[i].x); + f2c(hh.tip_color_g,h.colortip[i].y); + f2c(hh.tip_color_b,h.colortip[i].z); + + + for(int k = 0; k < numknots; k++) + { + //int vi = h.hairStartIndices[i] + k; + int vi = h.face_start[i] + k; + + vertex& vv = var[i*numknots + k]; + //f2c(vv.x, h.vertices[vi].x); + //f2c(vv.y, h.vertices[vi].y); + //f2c(vv.z, h.vertices[vi].z); + + f2c(vv.x, h.v[vi].x); + f2c(vv.y, h.v[vi].y); + f2c(vv.z, h.v[vi].z); + } + } + int npix = (sizeof(header) + numhairs*sizeof(hair) +numhairs*numknots*sizeof(vertex))/4; + int W = 1024; + int H = 1 + npix/W; + + image.create(W,H,4); + unsigned char* pix = image.pixels(); + memcpy(pix,&he,sizeof(header)); + memcpy(&pix[sizeof(header)],har,numhairs*sizeof(hair)); + memcpy(&pix[sizeof(header)+ numhairs*sizeof(hair)],var,numhairs*numknots*sizeof(vertex)); + free(har); + free(var); + return true; +} + +bool shavePack2TexCmd::findNodeByName(const MString& name, MObject& thenode) const +{ + if(name == "") + return false; + + MItDependencyNodes iter; + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + MFnDependencyNode dFn(node); + if(dFn.name() == name) + { + thenode = node; + return true; + } + } + return false; +} diff --git a/mayaPlug/shavePack2TexCmd.h b/mayaPlug/shavePack2TexCmd.h new file mode 100644 index 0000000..951454f --- /dev/null +++ b/mayaPlug/shavePack2TexCmd.h @@ -0,0 +1,56 @@ +#ifndef _HAIR_P2T_COMMAND_H_ +#define _HAIR_P2T_COMMAND_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shavePack2TexCmd.h + + DESCRIPTION: command for packing hair data to image file + + HISTORY: created 23-04-2013 + + *> + **********************************************************************/ + +#include <maya/MPxCommand.h> +#include <maya/MArgList.h> +#include <maya/MCommandResult.h> +#include <maya/MSyntax.h> +#include <maya/MImage.h> + + +class shavePack2TexCmd : public MPxCommand { +public: + // command + // + static MString cmd; + + // flags + // + //destinatin file + static char* sf_file; + static char* lf_file; + + //hair node name + static char* sf_hair; + static char* lf_hair; + + shavePack2TexCmd(){} + virtual ~shavePack2TexCmd(){} + + static void *creator() {return new shavePack2TexCmd;} + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList&); + +protected: + + bool findNodeByName(const MString& name, MObject& thenode) const; + bool copyToImage(MObject thenode, MImage& image) const; +}; + +#endif diff --git a/mayaPlug/shavePadCmd.cpp b/mayaPlug/shavePadCmd.cpp new file mode 100644 index 0000000..225328d --- /dev/null +++ b/mayaPlug/shavePadCmd.cpp @@ -0,0 +1,47 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MGlobal.h> +#include <maya/MPxCommand.h> +#include <maya/MString.h> + +#include "shavePadCmd.h" + + +const MString shavePadCmd::commandName("shavePadCmd"); + + +shavePadCmd::shavePadCmd() {} +shavePadCmd::~shavePadCmd() {} + + +void* shavePadCmd::createCmd() +{ + return new shavePadCmd(); +} + + +MStatus shavePadCmd::doIt(const MArgList& argList) +{ + MStatus st; + + if (argList.length() < 2) return MS::kInvalidParameter; + + MString cmd = argList.asString(0); + unsigned int numArgsRequired = (unsigned int)argList.asInt(1); + + unsigned int i; + + for (i = 2; i < argList.length(); i++) + cmd = cmd + " \"" + argList.asString(i) + "\""; + + for (; i < numArgsRequired + 2; i++) + cmd = cmd + " \"\""; + + st = MGlobal::executeCommand(cmd, false, true); + + return st; +} + diff --git a/mayaPlug/shavePadCmd.h b/mayaPlug/shavePadCmd.h new file mode 100644 index 0000000..a3802d9 --- /dev/null +++ b/mayaPlug/shavePadCmd.h @@ -0,0 +1,34 @@ +#ifndef shavePadCmd_h +#define shavePadCmd_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +class shavePadCmd : public MPxCommand +{ +public: + shavePadCmd(); + virtual ~shavePadCmd(); + + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + + static void* createCmd(); + static MSyntax createSyntax() { MSyntax syntax; return syntax; } + + static const MString commandName; + +private: +}; + + +inline bool shavePadCmd::isUndoable() const +{ return false; } + +#endif + diff --git a/mayaPlug/shaveProceduralsCmd.cpp b/mayaPlug/shaveProceduralsCmd.cpp new file mode 100644 index 0000000..132f01d --- /dev/null +++ b/mayaPlug/shaveProceduralsCmd.cpp @@ -0,0 +1,174 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveProceduralsCmd.cpp + + DESCRIPTION: command for registering procedurals + + HISTORY: created 15-06-2011 + + *> + **********************************************************************/ + +#include "shaveProceduralsCmd.h" +#include <maya/MArgDatabase.h> +#include <maya/MGlobal.h> + +#ifdef USE_PROCEDURALS + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Command | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MString shaveProceduralsCmd::cmd = "shaveProcedurals"; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Flags | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +char* shaveProceduralsCmd::sf_register = "r"; +char* shaveProceduralsCmd::lf_register = "register"; + +char* shaveProceduralsCmd::sf_unregister = "u"; +char* shaveProceduralsCmd::lf_unregister = "unregister"; + +char* shaveProceduralsCmd::sf_total = "t"; +char* shaveProceduralsCmd::lf_total = "total"; + +char* shaveProceduralsCmd::sf_name = "n"; +char* shaveProceduralsCmd::lf_name = "name"; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Members | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MStringArray shaveProceduralsCmd::procedurals; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Methods | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MStatus shaveProceduralsCmd::doIt(const MArgList &maList) +{ + MStatus stat; + + MArgDatabase parser(syntax(),maList,&stat); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveProceduralsCmd: can not parse arguments."); + return stat; + } + if(parser.isQuery()) + { + if(parser.isFlagSet(sf_total)) + { + setResult((int)procedurals.length()); + return MStatus::kSuccess; + } + else if(parser.isFlagSet(sf_name)) + { + unsigned int idx; + stat = parser.getFlagArgument(sf_name,0,idx); + if(stat == MStatus::kSuccess) + { + if(idx >= procedurals.length()) + { + setResult(""); + MGlobal::displayError("shaveProceduralsCmd: index is out of range."); + return MStatus::kFailure; + } + setResult(procedurals[idx]); + return MStatus::kSuccess; + } + else + { + setResult(""); + MGlobal::displayError("shaveProceduralsCmd: can not get name."); + return MStatus::kFailure; + } + } + + } + else + { + if(parser.isFlagSet(sf_register)) + { + MString name; + stat = parser.getFlagArgument(sf_register,0,name); + if(stat == MStatus::kSuccess && name != "") + { + bool duplicate = false; + for(unsigned int i = 0; i < procedurals.length(); i++) + if(name == procedurals[i]) + { + duplicate = true; + break; + } + if(duplicate) + { + MGlobal::displayWarning(MString("shaveProceduralsCmd: ") + name + " already registered."); + return MStatus::kFailure; + } + procedurals.append(name); + + //trigger shave shapes update there + MGlobal::displayInfo(MString("shaveProceduralsCmd: ") + name + " successfully registered."); + } + else + { + MGlobal::displayError("shaveProceduralsCmd: can not get name."); + return MStatus::kFailure; + } + } + if(parser.isFlagSet(sf_unregister)) + { + MString name; + stat = parser.getFlagArgument(sf_unregister,0,name); + if(stat == MStatus::kSuccess && name != "") + { + int idx = -1; + for(unsigned int i = 0; i < procedurals.length(); i++) + if(name == procedurals[i]) + { + idx = i; + break; + } + if(idx == -1) + { + MGlobal::displayWarning(MString("shaveProceduralsCmd: ") + name + " not found"); + return MStatus::kFailure; + } + procedurals.remove(idx); + + //trigger shave shapes update there + MGlobal::displayInfo(MString("shaveProceduralsCmd: ") + name + " successfully unregistered."); + } + else + { + MGlobal::displayError("shaveProceduralsCmd: can not get name."); + return MStatus::kFailure; + } + } + + } + return MStatus::kSuccess; +} + + +MSyntax shaveProceduralsCmd::newSyntax() +{ + MSyntax syn; + + syn.enableQuery(true); + syn.addFlag(sf_register, lf_register, MSyntax::kString); + syn.addFlag(sf_unregister, lf_unregister, MSyntax::kString); + syn.addFlag(sf_total, lf_total); + syn.addFlag(sf_name, lf_name, MSyntax::kUnsigned); + + return syn; +} + +#endif diff --git a/mayaPlug/shaveProceduralsCmd.h b/mayaPlug/shaveProceduralsCmd.h new file mode 100644 index 0000000..227fcf2 --- /dev/null +++ b/mayaPlug/shaveProceduralsCmd.h @@ -0,0 +1,66 @@ +#ifndef _HAIR_PROCEDURALS_COMMAND_H_ +#define _HAIR_PROCEDURALS_COMMAND_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveProceduralsCmd.h + + DESCRIPTION: command for registering procedurals + + HISTORY: created 15-06-2011 + + *> + **********************************************************************/ + +//#define USE_PROCEDURALS + +#ifdef USE_PROCEDURALS + +#include <maya/MPxCommand.h> +#include <maya/MArgList.h> +#include <maya/MCommandResult.h> +#include <maya/MSyntax.h> +#include <maya/MStringArray.h> + +class shaveProceduralsCmd : public MPxCommand { +public: + // command + // + static MString cmd; + + // flags + // + static char* sf_register; + static char* lf_register; + + static char* sf_unregister; + static char* lf_unregister; + + static char* sf_total; + static char* lf_total; + + static char* sf_name; + static char* lf_name; + + + shaveProceduralsCmd(){} + virtual ~ shaveProceduralsCmd(){} + + static void *creator() {return new shaveProceduralsCmd;} + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList&); + virtual bool isUndoable() const {return false;} + + static MStringArray procedurals; +}; + +#endif //USE_PROCEDURALS + +#endif + + diff --git a/mayaPlug/shaveRender.cpp b/mayaPlug/shaveRender.cpp new file mode 100644 index 0000000..a005cf2 --- /dev/null +++ b/mayaPlug/shaveRender.cpp @@ -0,0 +1,2504 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MAngle.h> +#include <maya/MAnimControl.h> +#include <maya/MColorArray.h> +#include <maya/MDagPath.h> +#include <maya/MDGMessage.h> +#include <maya/MFileIO.h> +#include <maya/MFnCamera.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnLight.h> +#include <maya/MFnMesh.h> +#include <maya/MFnSpotLight.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MIntArray.h> +#include <maya/MItDag.h> +#include <maya/MItDependencyNodes.h> +#include <maya/MMatrix.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> +#include <maya/MPoint.h> +#include <maya/MSelectionList.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> +#include <maya/MVector.h> + +#include "shaveCheckObjectVisibility.h" +#include "shaveDebug.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveIO.h" +#include "shaveMaya.h" +#include "shaveMayaRenderer.h" +#include "shaveObjExporter.h" +#include "shaveRender.h" +#include "shaveRenderCallback.h" +#include "shaveRenderer.h" +#include "shaveSDK.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" +#include "shaveVertexShader.h" +#include "shaveVrayRenderer.h" +#include "shaveXPM.h" + + +#ifndef M_PI +#define M_PI 3.14159926535 +#endif + +bool shaveRenderCancelled = false; +bool shaveEnableProgressBar = true; + + +// +// Static class members. +// +bool shaveRender::mCallbacksSet = false; +bool shaveRender::mExportActive = false; +unsigned shaveRender::mExportFileCompression = 0; +unsigned shaveRender::mExportFileFramePadding = 0; +MString shaveRender::mExportFileName = ""; +bool shaveRender::mExportFilePerFrame = false; + +shaveConstant::FileNameFormat + shaveRender::mExportFileNameFormat = shaveConstant::kNoFileNameFormat; + +shaveGlobals::Globals shaveRender::mFrameGlobals; + +shaveConstant::RenderMode shaveRender::mHairRenderMode = shaveConstant::kNoRender; +MObjectArray shaveRender::mHiddenNodes; +bool shaveRender::mRenderInstances = true; + +bool shaveRender::mNeedVertexColours = false; +shaveConstant::ShadowSource + shaveRender::mNormalShadows = shaveConstant::kNoShadows; + +shaveRenderer* shaveRender::mRenderer = NULL; +shaveRender::RenderState shaveRender::mRenderState = shaveRender::kRenderNone; +shaveRender::SceneInfo shaveRender::mSceneInfo; +MObjectArray shaveRender::mTemplatedNodes; + +MCallbackId shaveRender::mAfterFrameRenderCallback; +MCallbackId shaveRender::mAfterRenderCallback; +MCallbackId shaveRender::mBeforeFrameRenderCallback; +MCallbackId shaveRender::mBeforeRenderCallback; +MCallbackId shaveRender::mRenderInterruptedCallback; +MCallbackId shaveRender::mTimeChangeCallback; +shaveRenderCallback* shaveRender::mShaveCallback = NULL; + +// +// Normally, 'mFirstFrame' will be set true in renderStart() and false in +// frameEnd(). This ensures that it will be true during the first frame +// and false during subsequent frames within the same render. +// +// However, Shave API calls don't go through renderStart() and frameEnd(). +// Since the Shave API currently just does a single frame at a time, +// 'mFirstFrame' should always be true. +// +bool shaveRender::mFirstFrame = true; + + +void shaveRender::initSceneInfo(float currentFrame, bool allocateBuffers) +{ + if (mFrameGlobals.verbose) cerr << "setting up render." << endl; + + static int MBAAQ[] = {1,3,8,15,23}; + + shaveRenderCallback::setGeomRender(false); + + mSceneInfo.shaveRenderPixels = NULL; + mSceneInfo.shaveZBuffer = NULL; + + if (allocateBuffers) + { + mSceneInfo.shaveRenderPixels = (Pixel*)malloc( + (shaveMaya::imageWidth+1) * + (shaveMaya::imageHeight+1) * + sizeof(Pixel) + ); + + if (!mSceneInfo.shaveRenderPixels) + { + MGlobal::displayError( + "Shave Render: not enough memory available for pixel buffer." + ); + + shaveRenderCancelled = true; + + return; + } + + mSceneInfo.shaveZBuffer = (float *)malloc( + (shaveMaya::imageWidth+1) * + (shaveMaya::imageHeight+1) *\ + sizeof(float) + ); + + if (!mSceneInfo.shaveZBuffer) + { + MGlobal::displayError( + "Shave Render: not enough memory available for depth buffer." + ); + + shaveRenderCancelled = true; + + return; + } + } + + mSceneInfo.aa = MBAAQ[renderQualityGlob]; + mSceneInfo.currentFrame = currentFrame; + mSceneInfo.haveTracedLights = false; + mSceneInfo.height = shaveMaya::imageHeight; + mSceneInfo.shaveTraceInitialized= false; + mSceneInfo.width = shaveMaya::imageWidth; + + return; +} + + +void shaveRender::doShadows() +{ + // + // Note that here we check doHairShadowsGlob rather than checking the + // hair and instance shadow sources. This is because even if all the + // shadow sources are set to kNoShadows, if doHairShadowsGlob is true + // then we still want self-shadowing. + // + if (doHairShadowsGlob) + { + int renderReturnVal = 0; + + shadowRender = true; + + if (nativeIlluminationGlob) + { + // + // With native illumination, Maya will be rendering the shadows + // for those objects which are actually in the scene, so Shave + // doesn't have to. + // + // The only use that SHAVErender_shadows() makes of the WFTYPEs + // that we pass down to it is to render shadows for the + // geometry that they contain. So by passing down empty + // WFTYPEs, we can prevent it from redoing the shadows which + // Maya has already done. + // + WFTYPE empty; + + init_geomWF(&empty); + + renderReturnVal = SHAVErender_shadows(&empty, &empty, 1, 1); + } + else + { + renderReturnVal = SHAVErender_shadows( + &mSceneInfo.shutterOpenShadowScene, + (shaveMaya::doMotionBlur ? + &mSceneInfo.shutterCloseShadowScene : + &mSceneInfo.shutterOpenShadowScene), + 1, + 1 + ); + } + + // + // If there were any traced lights, then the shadow render will have + // called SHAVEtrace_init. It's an expensive operation, so let's + // set a flag so that we know not to do it in other places, such as + // where we set up for reflections and refractions. + // + mSceneInfo.shaveTraceInitialized |= mSceneInfo.haveTracedLights; + + if (shaveRenderCancelled) + { + MGlobal::displayInfo("Shave render cancelled by user."); + } + else if (renderReturnVal != 0) + { + cerr << "Shave light render failed, return value is " + << renderReturnVal << endl; + + shaveRenderCancelled = true; + } + else if (mFrameGlobals.verbose) + { + cerr << "Done with Shave light render." << endl; + } + + shadowRender = false; + + MGlobal::executeCommand("shave_closeProgressBar()"); + } + + return; +} + + +void shaveRender::bufferRender( + shaveConstant::ShutterState shutter, + const MDagPath& cameraPath, + float frame, + bool needBuffers, + bool doShadowRender, + bool doCameraRender, + bool doOcclusions +) +{ + // + // Do the initialization on shutter open so that everything's there for + // the rest of the render. + // + if (((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + && !shaveRenderCancelled) + { + initSceneInfo(frame, needBuffers); + } + + // + // If this is shutter close, then the actual frame time will be the + // average of the current frame and what was saved into + // mSceneInfo.currentFrame on shutter open. So calculate the real frame + // time. + // + if (shutter == shaveConstant::kShutterClose) + mSceneInfo.currentFrame = (mSceneInfo.currentFrame + frame) / 2.0f; + + if (!shaveRenderCancelled + && (doCameraRender || doShadowRender || doOcclusions)) + { + buildOcclusionLists(shutter); + } + + if (!shaveRenderCancelled) doCamera(cameraPath, shutter, mSceneInfo); + + // + // Lights are not motion-blurred, so we only do them on shutter open. + // + if (((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + && !shaveRenderCancelled) + { + doLights(); + } + + // + // Shadows & rendering are done on shutter close. + // + if ((shutter == shaveConstant::kShutterClose) + || (shutter == shaveConstant::kShutterBoth)) + { + if (!shaveRenderCancelled && doShadowRender) + doShadows(); + + if (!shaveRenderCancelled && doCameraRender) + { + if (renderCameraView(cameraPath) != 0) + shaveRenderCancelled = true; + } + } + + // + // If something went wrong, clean up. Otherwise cleanup will be done + // by shaveRenderCallback::renderCallback(), once it's through with the + // compositing. + // + if (shaveRenderCancelled) + { + bufferRenderCleanup(); + } + + return; +} + + +void shaveRender::bufferRenderCleanup() +{ + mSceneInfo.bufferValid = false; + + if (mSceneInfo.shaveRenderPixels) + { + free(mSceneInfo.shaveRenderPixels); + mSceneInfo.shaveRenderPixels = NULL; + } + + if (mSceneInfo.shaveZBuffer) + { + free(mSceneInfo.shaveZBuffer); + mSceneInfo.shaveZBuffer = NULL; + } + + SHAVEclear_stack(); + + return; +} + + +void shaveRender::geomRender(MObjectArray& shaveHairShapes) +{ + // + // Let shaveRenderCallback know that this is a geom render. + // + shaveRenderCallback::setGeomRender(true); + + // + // This code used to only set up texture lookups if there were vertex + // shaders in the scene, the logic being that they would need updated + // vertex colours. However, we still need to evaluate things such as + // cutlength and density which might be textured. So we need to do the + // lookups *every* time. + // +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapes, "", mFrameGlobals.verbose); +#else + initTexInfoLookup(shaveHairShapes, "", mFrameGlobals.verbose); +#endif + // + // Force each shaveHairShape to update its outputMesh. This is needed for + // two reasons: + // + // 1) When motion blur is off and we're rendering just a single frame, + // the last time change will have occurred just prior to + // renderStart(). So the outputMeshes won't have been updated since + // renderStart() set the mRenderAllNormalHairs/mRenderAllInstanceHairs + // flags, which will affect the output meshes. + // + // 2) We just now built the proper texture lookup for this frame, so + // the meshes need to pick up new values from that. + // + // %%% We could optimize this by only doing the loop below if motion + // blur is off and this is the first frame to be rendered, or if + // mNeedVertexColours is true. + // + unsigned int i; + unsigned int numShaveNodes = shaveHairShapes.length(); + + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode(); + + // + // Simply grabbing the hairNode will cause the output mesh to + // recompute. + // + theShaveNode->getHairNode(); + } + + // + // NOTE: We no longer need the texture lookup tables at this point + // because any info we needed from them is now embedded in the + // output meshes built by the getHairNode() calls in the loop + // above. So it's not a problem if someone else now calls + // initTexInfoLookup() with a different set of shaveHairShapes. + // + + return; +} + + +// +// Called at the start of each frame render. Note that for normal Maya +// renders we get called from a callback, but some renderers might call +// us via the shaveRender MEL command. +// +void shaveRender::frameStart(void * clientData) +{ + // + // We don't support IPR. + // + if (isIPRActive()) return; + + mRenderState = kRenderingFrame; + mSceneInfo.bufferValid = false; + shaveRenderCancelled = false; + + // We are transitioning from using a bunch of global variables to + // using a shaveGlobals::Globals instance to pass around the + // shaveGlobals values for the frame. During the transition period we + // have to continue to support both. + shaveGlobals::getGlobals(); + saveFrameGlobals(); + + shaveMaya::getRenderGlobals(); + + SHAVEset_verbose(mFrameGlobals.verbose ? 1 : 0); + SHAVEclear_stack(); + + // + // Do renderer-specific setup. + // + if (mRenderer) mRenderer->frameStart(mFrameGlobals); + + return; +} + + +// +// Called at the end of each frame render. Note that for normal Maya +// renders we get called from a callback but some renderers might call +// us via the shaveRender MEL command. +// +void shaveRender::frameEnd(void * clientData) +{ + // + // We don't support IPR. + // + if (isIPRActive()) return; + + // + // Do renderer-specific cleanup. + // + if (mRenderer) mRenderer->frameEnd(mFrameGlobals); + + if (shaveHairShape::getNumShaveNodes() > 0) + { + SHAVEclear_stack(); + } + + mRenderState = kRendering; + mFirstFrame = false; + + return; +} + + + + +/************************************************************************* + * LIGHTS + *************************************************************************/ +void shaveRender::doLights() +{ + MStatus st; + + MDagPath lightPath; + MTransformationMatrix lightWorldMatrix; + MVector lightTranslation; + double rot[3]; + VERT shaveDirection; + + MTransformationMatrix::RotationOrder order = MTransformationMatrix::kXYZ; + + // + // Get our lights + // + shaveUtil::buildLightList(); + + // + // Loop through lights, register them with Shave + // + unsigned i; + unsigned length = (unsigned int)shaveUtil::globalLightList.size(); + + for (i = 0; i < length; i++) + { + lightPath = shaveUtil::globalLightList[i].path; + + MFnLight lightFn(lightPath); + MColor lightColor = lightFn.color (&st); + float lightIntensity = lightFn.intensity(&st); + + if (nativeIlluminationGlob) + { + lightColor = MColor(1,1,1); + lightIntensity = 1; + } + + // + // Look for the per-light shadow parameters. Use defaults if + // this light doesn't have the parameters. + // + MPlug plug; + float shadowFuzz = 8.0f; + short shadowQuality = shaveGlobals::kHighShadowQuality; + int shadowRes = 450; + bool shadowTrace = false; + + if (lightPath.hasFn(MFn::kSpotLight)) shadowRes = 600; + + plug = lightFn.findPlug("shaveShadowFuzz", &st); + if (st) plug.getValue(shadowFuzz); + + plug = lightFn.findPlug("shaveShadowResolution", &st); + if (st) plug.getValue(shadowRes); + + plug = lightFn.findPlug("shaveShadowTrace", &st); + if (st) plug.getValue(shadowTrace); + + // + // Get the position of the light, in World space. + // + lightWorldMatrix = lightPath.inclusiveMatrix(); + lightTranslation = lightWorldMatrix.translation(MSpace::kWorld); + + VERT shaveLightPosition; + shaveLightPosition.x = (float)lightTranslation.x; + shaveLightPosition.y = (float)lightTranslation.y; + shaveLightPosition.z = (float)lightTranslation.z; + + MColor shadowColour = lightFn.shadowColor(); + + // + // Handle spotlights. + // + if (lightPath.hasFn(MFn::kSpotLight)) + { + // + // DIRECTION VECTOR + // + lightWorldMatrix.getRotation(rot, order); + + MVector mayaDirectionVector = MVector::zAxis.rotateBy(rot, order); + + shaveDirection.x = (float) -mayaDirectionVector.x; + shaveDirection.y = (float) -mayaDirectionVector.y; + shaveDirection.z = (float) -mayaDirectionVector.z; + + // + // UP VECTOR + // + MVector mayaUpVector = MVector::yAxis.rotateBy(rot, order); + + VERT shaveUpVector; + shaveUpVector.x = (float) mayaUpVector.x; + shaveUpVector.y = (float) mayaUpVector.y; + shaveUpVector.z = (float) mayaUpVector.z; + + // + // Cone Angle + // + const float kRad2degrees = (float)(180.0 / M_PI); + MFnSpotLight spotFn(lightPath); + + float lightConeAngle = (float) + (spotFn.coneAngle() + spotFn.penumbraAngle() * 2.0); + lightConeAngle = ((float)lightConeAngle) * kRad2degrees; + + Matrix shaveLightMatrix; + + SHAVEmake_view_matrix( + shaveLightMatrix, + shaveUpVector, + shaveDirection, + shaveLightPosition, + lightConeAngle + ); + + shaveUtil::globalLightList[i].minId = + SHAVEadd_light( + lightColor.r*lightIntensity, + lightColor.g*lightIntensity, + lightColor.b*lightIntensity, + shaveLightMatrix, + shaveLightPosition, + shadowRes, + shadowRes, + 1.0, + lightConeAngle, + shadowFuzz, + shadowQuality, + (float)shadowColour.r, + (float)shadowColour.g, + (float)shadowColour.b, + (shadowTrace ? 1 : 0), + 0.01f + ); + shaveUtil::globalLightList[i].maxId = shaveUtil::globalLightList[i].minId; + } + // + // Handle all other light types. + // + else + { + shaveUtil::globalLightList[i].minId = + SHAVEadd_point_light( + lightColor.r*lightIntensity, + lightColor.g*lightIntensity, + lightColor.b*lightIntensity, + shaveLightPosition, + shadowRes, + shadowRes, + shadowFuzz, + shadowQuality, + (float)shadowColour.r, + (float)shadowColour.g, + (float)shadowColour.b, + (shadowTrace ? 1 : 0), + 0.01f + ); + + // + // Internally, Shave generates 6 lights for each non-spotlight + // we register using the above function. + // + shaveUtil::globalLightList[i].maxId = shaveUtil::globalLightList[i].minId + 5; + } + + // + // Keep track of if we have any traced lights. + // + if (shadowTrace) mSceneInfo.haveTracedLights = true; + } + + return; +} + + +/************************************************************************* + * CAMERA + * + * A *LOT* of voodoo here. Maya Z and Shave Z are opposites. So we flip + * it on our end. This affects the handed-ness of rotations along + * X and Y (but not Z). Maya's FOV is horizontal, Shave's camera is vertical. + * Maya has the concept of a camera's viewing fustrum (aspect, FOV) being + * different than the rendering (globals) fustrum. This is because Maya looks + * at the FILM aperture (width & height) for calculating its camera FOV and + * aspect, while Shave uses the rendering globals. Can't we all just get along? + *************************************************************************/ + + + +/************************************************************************* + * SCENE GEOMETRY + *************************************************************************/ +void shaveRender::buildOcclusionLists(shaveConstant::ShutterState shutter) +{ + static bool firstTime = true; + + if (firstTime) + { + init_geomWF(&mSceneInfo.shutterOpenCamScene); + init_geomWF(&mSceneInfo.shutterCloseCamScene); + init_geomWF(&mSceneInfo.shutterOpenShadowScene); + init_geomWF(&mSceneInfo.shutterCloseShadowScene); + firstTime = false; + } + + static MDagPathArray camOcclusions; + static MDagPathArray shadowOcclusions; + shaveObjTranslator x; + + if ((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + { + camOcclusions.clear(); + shadowOcclusions.clear(); + + // + // When doing 2D compositing, we ignore transparency: the user must + // switch to 3D if zie wants to see hair through glass. + // + bool ignoreTransparency = doShaveComp2dGlob; + unsigned int i; + MStringArray occlusionNames; + MDagPath occlusionPath; + MSelectionList tmpSel; + + if (shaveSceneObjectsGlob == "all") + { + MGlobal::executeCommand("ls -g", occlusionNames); + + for (i = 0; i < occlusionNames.length(); i++) + { + tmpSel.clear(); + tmpSel.add(occlusionNames[i]); + tmpSel.getDagPath(0, occlusionPath); + + // + // If native illumination is on, then we only do + // occlusions for the cam pass, not the shadow pass. + // + if (nativeIlluminationGlob) + { + if (areObjectAndParentsVisible( + occlusionPath, true, false, ignoreTransparency)) + { + camOcclusions.append(occlusionPath); + } + } + else + { + // + // For shadows, we ignore transparency and primary + // visibility. + // + if (areObjectAndParentsVisible( + occlusionPath, true, true, true)) + { + shadowOcclusions.append(occlusionPath); + + // + // For the cam pass, we don't want objects whose + // primary visibility is off or which have any + // transparency to be treated as occlusions. + // + if (areObjectAndParentsVisible( + occlusionPath, true, false, ignoreTransparency)) + { + camOcclusions.append(occlusionPath); + } + } + } + } + } + else + { + shaveSceneObjectsGlob.split(' ', occlusionNames); + + for (i = 0; i < occlusionNames.length(); i++) + { + tmpSel.clear(); + tmpSel.add(occlusionNames[i]); + tmpSel.getDagPath(0, occlusionPath); + + camOcclusions.append(occlusionPath); + + if (!nativeIlluminationGlob) + shadowOcclusions.append(occlusionPath); + } + } + + if (mFrameGlobals.verbose) cerr << "built occlusion list." << endl; + + x.exportOcclusion(camOcclusions, &mSceneInfo.shutterOpenCamScene); + + if (!nativeIlluminationGlob) + { + x.exportOcclusion( + shadowOcclusions, &mSceneInfo.shutterOpenShadowScene + ); + } + } + + // + // If there's no motion blur then shutter open and close are identical + // and we can just use the same data for both. So we only need to + // gather occlusions for shutter close if motion blur is on. + // + if ((shaveMaya::doMotionBlur) + && ((shutter == shaveConstant::kShutterClose) + || (shutter == shaveConstant::kShutterBoth))) + { + x.exportOcclusion(camOcclusions, &mSceneInfo.shutterCloseCamScene); + + if (!nativeIlluminationGlob) + { + x.exportOcclusion( + shadowOcclusions, &mSceneInfo.shutterCloseShadowScene + ); + } + } + + return; +} + + +/* + * Modify our color projection map (for hair-to-object shadows) with the + * color of the light. I thought intensity would also need to be + * multiplied in, but I was wrong. + */ +void shaveRender::doColorCorrection (MColor colorMod, short mapWidth, short mapHeight, unsigned char* mapBuffer) +{ + + for (int x=0; x < mapWidth*mapHeight*4; x += 4) + { + // RED + mapBuffer[x+0] = (unsigned char) ((float) mapBuffer[x+0] * colorMod.r ); + //GREEN + mapBuffer[x+1] = (unsigned char) ((float) mapBuffer[x+1] * colorMod.g ); + //BLUE + mapBuffer[x+2] = (unsigned char) ((float) mapBuffer[x+2] * colorMod.b ); + } + +} + +/* + * Correct black levels, to control hair-on-skin shadow density. + */ +void shaveRender::doDensityCorrection (float density, short mapWidth, short mapHeight, unsigned char* mapBuffer) +{ + /* + * Tint values towards white. + * Density range is 0.0-1.0 + */ + for (int x=0; x < mapWidth*mapHeight*4; x += 4) + { + mapBuffer[x+0] = (unsigned char) + ((float)mapBuffer[x+0] * density + 255.*(1.0-density)); + + mapBuffer[x+1] = (unsigned char) + ((float)mapBuffer[x+1] * density + 255.*(1.0-density)); + + mapBuffer[x+2] = (unsigned char) + ((float)mapBuffer[x+2] * density + 255.*(1.0-density)); + } +} + + +// +// Called at the start of a render of a group of one or more frames. Note +// that for normal Maya renders we get called from a callback but some +// renderers may call us via the shaveRender MEL command. +// +void shaveRender::renderStart(void* clientData) +{ + // + // We don't support IPR. + // + if (isIPRActive()) return; + + // + // If a render crashes (e.g. due to insufficient memory) Maya won't + // send us the renderEnd event. So let's make sure that there isn't + // anything lying around from a previous render. + // + bufferRenderCleanup(); + + mRenderState = kRendering; + mFirstFrame = true; + + // + // Make sure we start out with a fresh renderer instance. + // + if (mRenderer != NULL) + { + delete mRenderer; + mRenderer = NULL; + } + + getRenderer(); + + // + // Make sure that the display nodes are all in the same render layer as + // at least on of their growth surfaces. + // + MGlobal::executeCommand("shave_assignRenderLayers"); + + + //******************************************************************* + // + // Get The Shave Nodes + // + //******************************************************************* + + shaveGlobals::getGlobals(); + + MObjectArray shaveHairShapes; + shaveUtil::getShaveNodes(shaveHairShapes); + + unsigned int numShaveNodes = shaveHairShapes.length(); + + + //******************************************************************* + // + // Get The Render Modes + // + //******************************************************************* + + mHairRenderMode = mRenderer->getFilteredRenderMode( + shaveHairShapes, &mRenderInstances + ); + + // + // Go no further if there's nothing to render. + // + if ((mHairRenderMode == shaveConstant::kNoRender) && !mRenderInstances) + { + delete mRenderer; + mRenderer = NULL; + + return; + } + + + //******************************************************************* + // + // Determine Shadow Sources + // + //******************************************************************* + + // + // Let's figure out where our shadows are coming from. + // + mNormalShadows = mRenderer->getShadowSource(); + + + //******************************************************************* + // + // Set Templating & Visibility + // + //******************************************************************* + + // + // We may not want display geometry showing up in the render, or we + // might only want it showing up in secondary effects, such as shadows, + // so ask the renderer what we should do with it. + // + // Note that we need to do it now, before the first call to frameStart, + // because we want the full geometry available at the start of the + // render, when the renderer does its automatic clipping plane + // calculations. Otherwise it might end up clipping some of our + // geometry later on. + // + bool hairPrimaryVisibility; + bool hairSecondaryVisibility; + bool instancePrimaryVisibility; + bool instanceSecondaryVisibility; + + mRenderer->getGeomVisibility( + hairPrimaryVisibility, + hairSecondaryVisibility, + instancePrimaryVisibility, + instanceSecondaryVisibility + ); + + unsigned int i; + bool shouldBeTemplated; + bool shouldBeVisible; + + mHiddenNodes.clear(); + mTemplatedNodes.clear(); + + for (i = 0; i < numShaveNodes; i++) + { + // + // Find the shaveHairShape's display node. + // + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + MObject displayNode = nodePtr->getDisplayShape(); + + if (!displayNode.isNull()) + { + // + // Set the display node's templating, as appropriate. + // + nodeFn.setObject(displayNode); + + if (nodePtr->isInstanced()) + { + shouldBeVisible = instancePrimaryVisibility; + shouldBeTemplated = (!instancePrimaryVisibility + && !instanceSecondaryVisibility); + } + else + { + shouldBeVisible = hairPrimaryVisibility; + shouldBeTemplated = (!hairPrimaryVisibility + && !hairSecondaryVisibility); + } + + bool isTemplated = false; + bool isVisible = false; + MPlug plug; + + plug = nodeFn.findPlug("template"); + plug.getValue(isTemplated); + + if (isTemplated != shouldBeTemplated) + { + plug.setValue(shouldBeTemplated); + mTemplatedNodes.append(displayNode); + } + + // + // If the display node is not templated we may still need to + // adjust its primary visibility. + // + if (!shouldBeTemplated) + { + plug = nodeFn.findPlug("primaryVisibility"); + plug.getValue(isVisible); + + if (isVisible != shouldBeVisible) + { + plug.setValue(shouldBeVisible); + mHiddenNodes.append(displayNode); + } + } + } + + // + // Let the shaveHairShape know about the new render state. + // + MPlug renderStatePlug( + shaveHairShapes[i], shaveHairShape::renderStateAttr + ); + + renderStatePlug.setValue(mRenderState); + } + + + //******************************************************************* + // + // Set Up Vertex Shaders + // + //******************************************************************* + + // + // If we're using geometry for hair, for shadows, or for instances, + // then we'll need to create/update vertex shaders for those shaveHairShapes + // with their geom shader override turned on. + // + if ((mHairRenderMode == shaveConstant::kGeometryRender) + || (mNormalShadows == shaveConstant::kMayaGeomShadows) + || mRenderInstances) + { + MGlobal::executeCommand("shave_prepareVertexShaders"); + } + + // + // Do we need to set up vertex colours? + // + mNeedVertexColours = mRenderer->needVertexColours(); + + + //******************************************************************* + // + // Turn Off Automatic Clipping Planes + // + //******************************************************************* + + // + // Automatic clipping planes cause us all kinds of problems because + // they're evaluated before frameStart() gets called, which means that + // any geometry created on a per-frame basis (e.g. the camera's render + // cone) may end up getting clipped. + // + // So let's just turn it off. We turn it off on all cameras just in + // case the render camera changes in mid-sequence. + // + mSceneInfo.autoClipCameras.clear(); + + if (numShaveNodes > 0) + { + MItDag dagIter(MItDag::kDepthFirst, MFn::kCamera); + AutoClipInfo camInfo; + + camInfo.nearClipChanged = false; + camInfo.farClipChanged = false; + + for (; !dagIter.isDone(); dagIter.next()) + { + dagIter.getPath(camInfo.cameraPath); + + MFnCamera cameraFn(camInfo.cameraPath); + MPlug plug = cameraFn.findPlug("bestFitClippingPlanes"); + bool autoClipOn; + + plug.getValue(autoClipOn); + + if (autoClipOn) + { + plug.setValue(false); + + camInfo.origNearClip = cameraFn.nearClippingPlane(); + camInfo.origFarClip = cameraFn.farClippingPlane(); + + // + // We shouldn't be doing this here since we don't know + // the render cam yet, but Joe doesn't want to pay to do + // it properly, so we're stuck with issuing spurious + // warnings. + // + if (!cameraFn.isOrtho()) + { + float diff; + + diff = (float)(camInfo.origFarClip - camInfo.origNearClip); + + if (diff == camInfo.origFarClip) + { + MGlobal::displayWarning( + "Shave does not support autoclip. The clipping" + " range is larger than Maya can support. Your" + " surfaces may appear black as a result." + ); + } + } + + mSceneInfo.autoClipCameras.push_front(camInfo); + } + } + } + + // + // Do any renderer-specific setup. + // + mRenderer->renderStart(); + + // + // Setup up a callback for time changes. + // + mTimeChangeCallback = MDGMessage::addForceUpdateCallback(timeChanged); + + return; +} + + +// +// Called at the end of a render of a group of one or more frames. Note +// that for normal Maya renders we get called from a callback but some +// renderers may call us via the shaveRender MEL command. +// +void shaveRender::renderEnd(void* clientData) +{ + // + // We don't support IPR. + // + if (isIPRActive()) return; + + mRenderState = kRenderNone; + + // + // If there's nothing to render, then there's nothing to undo. + // + if ((mHairRenderMode == shaveConstant::kNoRender) && !mRenderInstances) + { + return; + } + + MDGMessage::removeCallback(mTimeChangeCallback); + + // + // Do renderer-specific cleanup. + // + if (mRenderer) mRenderer->renderEnd(); + + // + // Restore automatic clipping planes to those cameras which had them + // turned on. + // + MPlug plug; + AutoClipListIter iter; + + for (iter = mSceneInfo.autoClipCameras.begin(); + iter != mSceneInfo.autoClipCameras.end(); + iter++) + { + MFnCamera cameraFn(iter->cameraPath); + + if (iter->nearClipChanged) + cameraFn.setNearClippingPlane(iter->origNearClip); + + if (iter->farClipChanged) + cameraFn.setFarClippingPlane(iter->origFarClip); + + // + // If we turn the camera's auto clipping back on right now, either + // by setting its plug or executing a 'setAttr' command, then for + // some reason Maya will reset both of the camera's clipping planes + // to 0.1. + // + // But if we do it as a deferred command, then it retains the + // clipping planes. + // + // Just more of the magic of Maya... + // + MGlobal::executeCommand( + MString("evalDeferred \"setAttr ") + + iter->cameraPath.fullPathName() + + ".bestFitClippingPlanes true \"" + ); + } + + mSceneInfo.autoClipCameras.clear(); + + shaveGlobals::getGlobals(); + + // + // Remove any vertex shaders we set up. + // + if ((mHairRenderMode == shaveConstant::kGeometryRender) + || (mNormalShadows == shaveConstant::kMayaGeomShadows) + || mRenderInstances) + { + MGlobal::executeCommand("shave_destroyVertexShaders"); + } + + // + // Toggle the templating of any display nodes which we changed, + // back to their original settings. + // + unsigned int i; + unsigned int numTemplatedNodes = mTemplatedNodes.length(); + + for (i = 0; i < numTemplatedNodes; i++) + { + MFnDependencyNode nodeFn(mTemplatedNodes[i]); + MPlug plug = nodeFn.findPlug("template"); + bool isTemplated; + + plug.getValue(isTemplated); + plug.setValue(!isTemplated); + } + + mTemplatedNodes.clear(); + + // + // Toggle the primary visibility of any display nodes which we changed, + // back to their original settings. + // + unsigned int numHiddenNodes = mHiddenNodes.length(); + + for (i = 0; i < numHiddenNodes; i++) + { + MFnDependencyNode nodeFn(mHiddenNodes[i]); + MPlug plug = nodeFn.findPlug("primaryVisibility"); + bool isHidden; + + plug.getValue(isHidden); + plug.setValue(!isHidden); + } + + mHiddenNodes.clear(); + + // + // Reset the first frame flag to true for the sake of Shave API calls. + // + // See the initialization of 'mFirstFrame' near the top of this file + // for more details on why we do this here. + // + mFirstFrame = true; + + // + // Let the shaveHairShapes know about the new render state. + // + MObjectArray shaveHairShapes; + shaveUtil::getShaveNodes(shaveHairShapes); + + unsigned int numShaveNodes = shaveHairShapes.length(); + + for (i = 0; i < numShaveNodes; i++) + { + MPlug plug(shaveHairShapes[i], shaveHairShape::renderStateAttr); + + plug.setValue(mRenderState); + } + + // + // Get rid of the renderer instance. + // + delete mRenderer; + mRenderer = NULL; + + return; +} + + +void shaveRender::renderInterrupted(void* clientData) +{ + // + // We don't support IPR. + // + if (isIPRActive()) return; + + if (mRenderer) mRenderer->renderInterrupted(); + + if (mRenderState == kRendering) + renderEnd(NULL); + else if (mRenderState != kRenderNone) + { + frameEnd(NULL); + renderEnd(NULL); + } + + return; +} + + +void shaveRender::setupCallbacks() +{ + if (!mCallbacksSet) + { + mBeforeRenderCallback = + MSceneMessage::addCallback( + MSceneMessage::kBeforeSoftwareRender, + renderStart + ); + + mAfterRenderCallback = + MSceneMessage::addCallback( + MSceneMessage::kAfterSoftwareRender, + renderEnd + ); + + mBeforeFrameRenderCallback = + MSceneMessage::addCallback( + MSceneMessage::kBeforeSoftwareFrameRender, + frameStart + ); + + mAfterFrameRenderCallback = + MSceneMessage::addCallback( + MSceneMessage::kAfterSoftwareFrameRender, + frameEnd + ); + + mRenderInterruptedCallback = + MSceneMessage::addCallback( + MSceneMessage::kSoftwareRenderInterrupted, + renderInterrupted + ); + + mCallbacksSet = true; + } +} + + +void shaveRender::cleanupCallbacks() +{ + if (mCallbacksSet) + { + MSceneMessage::removeCallback(mBeforeRenderCallback); + MSceneMessage::removeCallback(mAfterRenderCallback); + MSceneMessage::removeCallback(mBeforeFrameRenderCallback); + MSceneMessage::removeCallback(mAfterFrameRenderCallback); + MSceneMessage::removeCallback(mRenderInterruptedCallback); + + mCallbacksSet = false; + } +} + + +// vlad|15Apr2010 +// +bool shaveRender::rendererIsVray() +{ + MStatus status; + int isVray = false; + + status = MGlobal::executeCommand("shave_rendererIsVray", isVray); + + return (status && isVray); +} + +bool shaveRender::rendererIsPrMan() +{ + MStatus status; + int isPrMan = 0; + status = MGlobal::executeCommand("shave_curRendererIsPrMan", isPrMan); + + return (status && isPrMan); +} + + +int shaveRender::renderCameraView(const MDagPath& cameraPath) +{ + int renderReturnVal = 0; + + shaveGlobals::getGlobals(); + + SceneInfo* shaveData = getSceneInfo(); + + if (doShaveCompsGlob || keepHairRenderFilesGlob) + { + shadowRender = false; + + SHAVEcalibrate(0.0f, -.05f); +// SHAVEcalibrate(-.25f,0.0f); + + if (shaveMaya::doMotionBlur) + { + renderReturnVal = SHAVErender_cam( + &mSceneInfo.shutterOpenCamScene, + &mSceneInfo.shutterCloseCamScene, + mSceneInfo.aa, + (unsigned char*)mSceneInfo.shaveRenderPixels, + mSceneInfo.shaveZBuffer, + 0, + 0, + mSceneInfo.width-1, + mSceneInfo.height-1, + mSceneInfo.width, + mSceneInfo.height, + 3 + ); + } + else + { + renderReturnVal = SHAVErender_camNOBLUR( + &mSceneInfo.shutterOpenCamScene, + &mSceneInfo.shutterOpenCamScene, + mSceneInfo.aa, + (unsigned char*)mSceneInfo.shaveRenderPixels, + mSceneInfo.shaveZBuffer, + 0, + 0, + mSceneInfo.width-1, + mSceneInfo.height-1, + mSceneInfo.width, + mSceneInfo.height, + 3 + ); + } + + if (mFrameGlobals.verbose) + cerr << "Pixel render returned: " << renderReturnVal << endl; + + // + // Did this camera have automatic clipping planes on? + // + AutoClipListIter iter; + + for (iter = mSceneInfo.autoClipCameras.begin(); + iter != mSceneInfo.autoClipCameras.end(); + iter++) + { + if (iter->cameraPath == cameraPath) break; + } + + if (iter != mSceneInfo.autoClipCameras.end()) + { + // + // Find the min and max depth values in the image. + // + bool gotDepth = false; + int i; + int numPixels = mSceneInfo.height * mSceneInfo.width; + float depth; + float minDepth = 0.0f; + float maxDepth = 0.0f; + + for (i = 0; i < numPixels; i++) + { + depth = mSceneInfo.shaveZBuffer[i]; + + if (depth != SHAVE_FAR_CLIP) + { + if (!gotDepth) + { + minDepth = maxDepth = depth; + gotDepth = true; + } + else if (depth < minDepth) + minDepth = depth; + else if (depth > maxDepth) + maxDepth = depth; + } + } + + if (gotDepth) + { + // + // We want to make sure that the camera's clipping planes + // catch all of our generated pixels. So if a pixel lies + // outside of the volume bounded by the near and far + // clipping planes, then we will need to move them so that + // the pixel is included. + // + // Shave's depths represent linear distance from the + // camera while Maya's clip values are the Z coordinates of + // the clipping planes in camera space. + // + // Now let's look at two points in camera space: [0, 0, 5] + // and [4, 2, 4]. The first point is 5 units away from the + // camera while the second point is 6 units away from the + // camera. So the first point is closer to the camera. + // + // However, if we set Maya's near clipping plane to a value + // of 5, it will still clip the second point. That's + // because while the second point is farther from the + // camera than the first, it is still closer along the + // camera's Z-axis, and that's what counts when clipping. + // The camera does not clip at a uniform distance but + // rather at a uniform Z coordinate, which means that the + // clipping distance increases the further you get from the + // camera's Z-axis. + // + // To do the job properly, we should convert each non-empty + // Shave pixel to a camera-space position, then find the + // one with the lowest Z coord. However, that's a bit + // expensive in terms of computation, so instead we'll + // cheat a bit. + // + // The points on the clipping plane which are most distant + // from the camera are at the far corners of the plane. If + // we pull the near clipping plane in so that even those + // corner points are as close as the minimum depth in + // Shave's depth buffer, then we'll be guaranteed that our + // nearest pixel doesn't get clipped. + // + // For the far clipping plane, our job is easier. We want + // to push the far clipping plane out until its point + // nearest the camera is at least as far away as Shave's + // largest depth value. The point nearest the camera on + // the far clipping plane is directly along the Z-axis. So + // we can just move the far clipping plane to Shave's max + // depth value and that will work. + // + // I call all of this "cheating" because we'll frequently + // end up pulling the near clipping plane in closer to the + // camera, and pushing the far plane farther from the + // camera, than we really need to. But it's unlikely that + // anyone will care. If they do, then we can switch to the + // full-blown approach. + // + + // + // The first step is to get the camera's near and far + // clipping planes. + // + MFnCamera cameraFn(cameraPath); + double nearClip = cameraFn.nearClippingPlane(); + double farClip = cameraFn.farClippingPlane(); + + // + // If Shave's max depth is greater than the camera's far + // clip, then reset the clip. + // + if (maxDepth > farClip) + { + cameraFn.setFarClippingPlane((double)maxDepth); + iter->farClipChanged = true; + } + + // + // For the near clipping plane, find a corner of the + // frustum. + // + MPoint frustumCorners[4]; + + cameraFn.getFilmFrustum(1.0, frustumCorners); + + // + // Turn it into a normalized vector. + // + MVector vec( + frustumCorners[0].x, + frustumCorners[0].y, + frustumCorners[0].z + ); + + vec.normalize(); + + // + // Multiply it by our minimum depth value. This will give + // us the position vector for a corner point at the minimum + // depth. + // + vec *= minDepth; + + // + // If the resulting z-coord is smaller than the near clip + // value, then set the near clip to the z-coord value. + // + if (vec.z < nearClip) + { + cameraFn.setNearClippingPlane((double)vec.z); + iter->nearClipChanged = true; + } + } + } + + shaveData->bufferValid = true; + } + + MGlobal::executeCommand("shave_closeProgressBar()"); + + if (keepHairRenderFilesGlob) + { + MString outFilename; + int intFrame; + + if (mSceneInfo.currentFrame < 0) + intFrame = (int)(mSceneInfo.currentFrame - 0.5f); + else + intFrame = (int)(mSceneInfo.currentFrame + 0.5f); + + outFilename = hairFileNameGlob + "."; + outFilename += intFrame; + outFilename += ".tga"; + + if (mFrameGlobals.verbose) { +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cerr << "Outputting to " << outFilename.asChar() << endl; +#else + cerr << "Outputting to " << outFilename << endl; +#endif + } + + SHAVEwrite_targa( + (char *)outFilename.asChar(), + (short)mSceneInfo.width, + (short)mSceneInfo.height, + (unsigned char*)mSceneInfo.shaveRenderPixels + ); + } + + return renderReturnVal; +} + + +void shaveRender::doCamera( + const MDagPath& camPath, + shaveConstant::ShutterState shutter, + shaveRender::SceneInfo& shaveData +) +{ + Matrix camvm; + MVector mayaUpVector, mayaDirectionVector; + double rotationVector[3]; + VERT shaveUpVector, shaveDirection, shavePosition; + MVector unitDirectionVector(0., 0., 1.); + MVector unitUpVector(0., 1., 0.); + + MTransformationMatrix::RotationOrder order = MTransformationMatrix::kXYZ; + + MTransformationMatrix camWorldMatrix = camPath.inclusiveMatrix(); + MFnCamera fnCamera(camPath); + + // + // Get MAYA camera translation and rotation info + // + MVector translation = camWorldMatrix.translation(MSpace::kWorld); + + camWorldMatrix.getRotation(rotationVector, order); + + // + // Set SHAVE camera position in World space + // + shavePosition.x = (float) translation.x; + shavePosition.y = (float) translation.y; + shavePosition.z = (float) translation.z; + + // + // UP VECTOR + // + mayaUpVector = unitUpVector.rotateBy(rotationVector, order); + shaveUpVector.x = (float) mayaUpVector.x; + shaveUpVector.y = (float) mayaUpVector.y; + shaveUpVector.z = (float) mayaUpVector.z; + + // + // DIRECTION VECTOR + // + mayaDirectionVector = unitDirectionVector.rotateBy(rotationVector, order); + shaveDirection.x = (float) -mayaDirectionVector.x; + shaveDirection.y = (float) -mayaDirectionVector.y; + shaveDirection.z = (float) -mayaDirectionVector.z; + + // + // FIELD OF VIEW + // + float pixelAspect = mRenderer ? mRenderer->getPixelAspect() : 1.0f; + double hfovagain, vfovagain; + + portFieldOfView( + shaveMaya::imageWidth, + shaveMaya::imageHeight, + pixelAspect, + hfovagain, + vfovagain, + fnCamera + ); + + const float rad2degrees = (180.0f / (float)M_PI); + + vfovagain *= rad2degrees; + + if (mFrameGlobals.verbose) + { +#if 0 +#ifdef OSMac_ + // + // Panther broke cerr somehow so that it crashes if you use it on + // non-zero float or double values. So let's try our old pal + // stderr, instead. + // + if (stderr) fprintf(stderr, "pixel aspect is: %f\n", pixelAspect); +#else + cerr << "pixel aspect is: " << pixelAspect << endl; +#endif +#endif + } + + SHAVEmake_view_matrix( + camvm, shaveUpVector, shaveDirection, shavePosition, (float)vfovagain + ); + + + /* + * Register the camera with Shave + */ + if ((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + { + SHAVEset_cameraOPEN( + camvm, + shavePosition, + shaveData.width, + shaveData.height, + pixelAspect, + (float)vfovagain, + 0.01f + ); + } + + if ((shutter == shaveConstant::kShutterClose) + || (shutter == shaveConstant::kShutterBoth)) + { + SHAVEset_cameraCLOSE(camvm, shavePosition); + } + + return; +} + + +void shaveRender::computeViewingFrustum( + double image_aspect, + double& left, + double& right, + double& bottom, + double& top, + MFnCamera& cam +) +{ + double film_aspect = cam.aspectRatio(); + double aperture_x = cam.horizontalFilmAperture(); + double aperture_y = cam.verticalFilmAperture(); + double offset_x = cam.horizontalFilmOffset(); + double offset_y = cam.verticalFilmOffset(); + double focal_to_near = cam.nearClippingPlane() / + (cam.focalLength() * MM_TO_INCH); +// 88360 + + focal_to_near *= cam.cameraScale(); + + double scale_x = 1.0; + double scale_y = 1.0; + double translate_x = 0.0; + double translate_y = 0.0; + + switch (cam.filmFit()) { + case MFnCamera::kFillFilmFit: + // In Fill mode we want to keep the entire image within the + // viewing frustum. If the image's aspect ratio is greater + // than that of the film then we'll do a horizontal fit, + // otherwise we'll do a vertical fit. + if (image_aspect > film_aspect) + scale_y = film_aspect / image_aspect; + else + scale_x = image_aspect / film_aspect; + break; + + case MFnCamera::kHorizontalFilmFit: + scale_y = film_aspect / image_aspect; + + if (scale_y > 1.0) + { + translate_y = cam.filmFitOffset() * + (aperture_y - (aperture_y * scale_y)) / 2.0; + } + break; + + case MFnCamera::kVerticalFilmFit: + scale_x = image_aspect / film_aspect; + + if (scale_x > 1.0) + { + translate_x = cam.filmFitOffset() * + (aperture_x - (aperture_x * scale_x)) / 2.0; + } + break; + + case MFnCamera::kOverscanFilmFit: + // In Overscan mode we allow the edges of the image to extend + // beyond the viewing frustum. If the image's aspect ratio is + // greater than that of the film then we'll do a vertical fit, + // allowing the left and right edges to extend beyond the + // frustum. Otherwise we'll do a horizontal fit, allowing the + // top and bottom edges to extend beyond the frustum. + if (image_aspect > film_aspect) + scale_x = image_aspect / film_aspect; + else + scale_y = film_aspect / image_aspect; + break; + + case MFnCamera::kInvalid: + break; + } + + + left = focal_to_near * (-.5*aperture_x*scale_x+offset_x+translate_x); + right = focal_to_near * ( .5*aperture_x*scale_x+offset_x+translate_x); + bottom = focal_to_near * (-.5*aperture_y*scale_y+offset_y+translate_y); + top = focal_to_near * ( .5*aperture_y*scale_y+offset_y+translate_y); +} + + +void shaveRender::portFieldOfView( + int port_width, + int port_height, + double pixelAspect, + double& horizontal, + double& vertical, + MFnCamera& fnCamera +) +{ + double left, right, bottom, top; + double aspect = pixelAspect * (double)port_width / (double)port_height; + computeViewingFrustum(aspect,left,right,bottom,top,fnCamera); + + double neardb = fnCamera.nearClippingPlane(); + horizontal = atan(((right - left) * 0.5) / neardb) * 2.0; + vertical = atan(((top - bottom) * 0.5) / neardb) * 2.0; +} + + +void shaveRender::buildHairStack( + MObjectArray& shaveHairShapes, shaveConstant::ShutterState shutter +) +{ + unsigned int i; + unsigned int numShaveNodes = shaveHairShapes.length(); + + // + // Register the hair nodes. + // + if ((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + SHAVEclear_stack(); + + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode(); + SHAVENODE* hairNode = hairShape->getHairNode(); + + hairShape->setStackIndex(i); + + if ((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + { + // + // Remove any old UV sets and add the current ones. + // Note that this *must* be done on the add_hairOPEN call: + // add_hairCLOSE is too late. + // + hairShape->removeShaveUVSets(); + hairShape->addShaveUVSets(); + SHAVEadd_hairOPEN2(hairNode, (int)i); + } + + if ((shutter == shaveConstant::kShutterClose) + || (shutter == shaveConstant::kShutterBoth)) + { + SHAVEadd_hairCLOSE2(hairNode, (int)i); + } + } + + SHAVEset_stack_max(numShaveNodes); + + return; +} + + +void shaveRender::clearHairStack(MObjectArray& shaveHairShapes) +{ + unsigned int i; + unsigned int numShaveNodes = shaveHairShapes.length(); + + // + // Remove the UV sets from the nodes, so that they don't waste mem. + // + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode(); + + hairShape->removeShaveUVSets(); + } + + SHAVEclear_stack(); + + return; +} + + +void shaveRender::timeChanged(MTime& newTime, void* clientData) +{ + if (mRenderer) mRenderer->timeChange(newTime); + + return; +} + + +MStatus shaveRender::renderSwatch( + MObject shaveHairShapeObj, unsigned int res, MString fileName +) +{ + unsigned char* image = new unsigned char[res*res*4]; + float* zbuff = new float[res*res]; + + MFnDependencyNode nodeFn(shaveHairShapeObj); + shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode(); + SHAVENODE* hairNode = theShaveNode->getHairNode(); + + theShaveNode->makeCurrent(); + + // + // Let's not have the progress bar for such a short render. + // + shaveRenderCancelled = false; + shaveEnableProgressBar = false; + SHAVErender_swatch(image, zbuff, res, &hairNode->shavep); + shaveEnableProgressBar = true; + + delete [] zbuff; + + bool success = shaveXPM::write(res, res, image, fileName); + + delete [] image; + + if (!success) return MS::kFailure; + + return MS::kSuccess; +} + + +MStatus shaveRender::getShutterTimes(float& shutterOpen, float& shutterClose) +{ + // + // Get the current render camera. + // + MDagPath cameraPath = getRenderCamPath(); + + if (!cameraPath.isValid()) return MS::kUnknownParameter; + + // + // Get the camera's shutter angle. + // + MFnCamera cameraFn(cameraPath); + MAngle shutterAngle(cameraFn.shutterAngle(), MAngle::kRadians); + + // + // Calculate the amount of slider time needed for blurring. + // + float frameDelta = (float) + (shutterAngle.asDegrees()/360.0 * shaveMaya::motionBlurByFrame/2.0); + + float midFrame = 0.0f; + //if(rendererIsVray()) + //{ + // double open = 0.0; + // double close = 0.0; + // MGlobal::executeCommand("eval(\"shaveVrayShader -q -shutterOpen\")",open); + // MGlobal::executeCommand("eval(\"shaveVrayShader -q -shutterClose\")",close); + // shutterOpen = open; + // shutterClose= close; + //} + //else + //{ + midFrame = (float)MAnimControl::currentTime().value(); + shutterOpen = midFrame - frameDelta; + shutterClose = midFrame + frameDelta; + //} + MGlobal::displayInfo(MString(" sutterOpen ") + shutterOpen + MString(" shutterClose ") + shutterClose); + + return MS::kSuccess; +} + + +MStatus shaveRender::renderToDRAFile( + SHAVE_RENDER_HOST renderHost, + const MString& fileName, + int voxelResolution, + bool includeOcclusions +) +{ + MStatus st; + bool mustFreeRenderer = false; + MObjectArray shaveHairShapes; + float shutterOpen = 0.0f; + float shutterClose = 0.0f; + + shaveGlobals::getGlobals(); + + if (mRenderer == NULL) + { + getRenderer(); + mustFreeRenderer = true; + } + + mRenderer->getRenderableShaveNodesByRenderMode(NULL, &shaveHairShapes); + + bool doMotionBlur = shaveMaya::doMotionBlur; + + if (doMotionBlur) + { + st = getShutterTimes(shutterOpen, shutterClose); + if (!st) doMotionBlur = false; + } + + MGlobal::displayInfo(MString("blur ") + doMotionBlur + MString("sutterOpen ") + shutterOpen + MString(" shutterClose ") + shutterClose); + + st = renderToDRAFile( + renderHost, + fileName, + voxelResolution, + shaveHairShapes, + doMotionBlur, + shutterOpen, + shutterClose, + true, + includeOcclusions + ); + + if (mustFreeRenderer) + { + delete mRenderer; + mRenderer = NULL; + } + + return st; +} + + +MStatus shaveRender::renderToDRAFile( + SHAVE_RENDER_HOST renderHost, + const MString& fileName, + int voxelResolution, + MObjectArray shaveHairShapes, + bool doMotionBlur, + float shutterOpen, + float shutterClose, + bool updateTextureCache, + bool includeOcclusions +) +{ + bool mustFreeRenderer = false; + MStatus st; + + if (shaveHairShapes.length() == 0) return MS::kNotFound; + + saveFrameGlobals(); + shaveMaya::getRenderGlobals(); + + SHAVEset_verbose(mFrameGlobals.verbose ? 1 : 0); + SHAVEclear_stack(); + + SHAVEset_tile_limit( + mFrameGlobals.tileMemoryLimit, mFrameGlobals.transparencyDepth + ); + + if (mRenderer == NULL) + { + getRenderer(); + mustFreeRenderer = true; + } + + mHairRenderMode = mRenderer->getFilteredRenderMode( + shaveHairShapes, &mRenderInstances + ); + mNormalShadows = mRenderer->getShadowSource(); + + // + // Build the texture lookup tables. + // + if (updateTextureCache) +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapes, "", mFrameGlobals.verbose); +#else + initTexInfoLookup(shaveHairShapes, "", mFrameGlobals.verbose); +#endif + + // + // Do everything except the camera renders, as we won't be needing the + // rendered images. + // + shaveRenderCancelled = false; + mSceneInfo.bufferValid = false; + + MDagPath cameraPath = getRenderCamPath(); + + + + if (doMotionBlur) + { + //if(rendererIsVray()) //vlad|15Apr2010 + //{ + // shutterOpen += 0.5; + // shutterClose += 0.5; + //} + + float curFrame = (float)MAnimControl::currentTime().as(MTime::uiUnit()); + + if (curFrame != shutterOpen) shaveUtil::setFrame(shutterOpen); + + buildHairStack(shaveHairShapes, shaveConstant::kShutterOpen); + + bufferRender( + shaveConstant::kShutterOpen, + cameraPath, + shutterOpen, + false, + false, + false, + includeOcclusions + ); + + shaveUtil::setFrame(shutterClose); + buildHairStack(shaveHairShapes, shaveConstant::kShutterClose); + + bufferRender( + shaveConstant::kShutterClose, + cameraPath, + shutterClose, + false, + false, + false, + includeOcclusions + ); + } + else + { + //MTime ct = MAnimControl::currentTime(); + //if(rendererIsVray()) //vlad|15Apr2010 + //{ + // shaveUtil::setFrame((ct + 1.0).value()); + //} + buildHairStack(shaveHairShapes, shaveConstant::kShutterBoth); + + bufferRender( + shaveConstant::kShutterBoth, + cameraPath, + shutterClose, + false, + false, + false, + includeOcclusions + ); + //if(rendererIsVray()) //vlad|15Apr2010 + //{ + // shaveUtil::setFrame(ct.value()); + //} + } + +#if OCCLUSIONS_IN_DRA + if (includeOcclusions) + { + shaveUtil::setWFTYPEVelocities( + &mSceneInfo.shutterOpenCamScene, &mSceneInfo.shutterCloseCamScene + ); + + SHAVEadd_occlusions(&mSceneInfo.shutterOpenCamScene); + } +#endif + + // + // Export the current Shave state to a DRA file. + // + SHAVEset_render_host(renderHost); + SHAVEmult_vox_size(mFrameGlobals.voxelScaling); + SHAVEexport_archive((char*)fileName.asChar(), voxelResolution); + +#if OCCLUSIONS_IN_DRA + if (includeOcclusions) + { + free_geomWF(&mSceneInfo.shutterOpenCamScene); + free_geomWF(&mSceneInfo.shutterCloseCamScene); + } +#endif + + clearHairStack(shaveHairShapes); + + if (mustFreeRenderer) + { + delete mRenderer; + mRenderer = NULL; + } + + return MS::kSuccess; +} + + +MDagPath shaveRender::getRenderCamPath() +{ + // + // We have a dandy MEL proc which determines the current render camera + // for us, so let's use it. + // + MString cameraName; + MGlobal::executeCommand("shave_getRenderCamera()", cameraName); + + MDagPath cameraPath; + MSelectionList list; + + list.add(cameraName); + list.getDagPath(0, cameraPath); + + return cameraPath; +} + + +MString shaveRender::getRenderModeName(shaveConstant::RenderMode mode) +{ + // + // Yes, it would be nice to switch to some kind of dynamic registration + // system rather than these hard-coded strings, but that's not likely + // to happen unless we start supporting a new renderer, or I have a lot + // of idle time on my hands. + // + switch (mode) + { + case shaveConstant::kNoRender: + return "None"; + + case shaveConstant::kBufferRender: + return "Buffer"; + + case shaveConstant::kGeometryRender: + return "Geometry"; + + default: + break; + } + + return ""; +} + + +// +// The caller must not free the returned renderer. +// +shaveRenderer* shaveRender::getRenderer() +{ + // + // If we're in the middle of a render, then return the existing + // renderer (if there is one), otherwise return a new renderer. + // + if (mRenderState == kRenderNone) + { + if (mRenderer != NULL) + { + delete mRenderer; + mRenderer = NULL; + } + } + + if (mRenderer == NULL) + { +#ifdef USE_VRAY + if(rendererIsVray()) //vlad|16Apr2010 + mRenderer = new shaveVrayRenderer; + else +#endif + mRenderer = new shaveMayaRenderer; + + ///I do not see tha rpman case is trapped here -- vald|19Jan2012 + //int isPrMan = 0; + //status = MGlobal::executeCommand("shave_curRendererIsPrMan", isPrMan); + //if(isPrMan) + //{ + //} + } + + return mRenderer; +} + + +bool shaveRender::isIPRActive() +{ + bool isActive = false; + + MGlobal::executeCommand("shave_isIPRActive", isActive); + + return isActive; +} + + +void shaveRender::exportEnd() +{ + mExportActive = false; + + // + // Let the MEL scripts know that the export is over. + // + MGlobal::executeCommand("shave_exportEnd"); + + // + // Clear out the export filename info. + // + mExportFileFramePadding = 0; + mExportFileNameFormat = shaveConstant::kNoFileNameFormat; + mExportFileName = ""; + mExportFilePerFrame = false; + mExportFileCompression = 0; +} + + +void shaveRender::exportStart() +{ + // + // The caller didn't give us a name for the export file, so let's see + // if Maya can. + // + exportStart(MFileIO::beforeExportFilename()); +} + + +void shaveRender::exportStart(MString fileName) +{ + // These parameters were used for special handling of Mental Ray. We + // no longer support Mental Ray and none of the renderers we do + // support have need of these, so we just set them to default values + // for now. + // + bool filePerFrame = false; + shaveConstant::FileNameFormat nameFormat = shaveConstant::kNoFileNameFormat; + unsigned framePadding = 0; + unsigned compression = 0; + + exportStart(fileName, filePerFrame, nameFormat, framePadding, compression); +} + + +void shaveRender::exportStart( + MString exportFile, + bool filePerFrame, + shaveConstant::FileNameFormat nameFormat, + unsigned framePadding, + unsigned compression +) +{ + mExportFileFramePadding = framePadding; + mExportFileName = exportFile; + mExportFileNameFormat = nameFormat; + mExportFilePerFrame = filePerFrame; + mExportFileCompression = compression; + + mExportActive = true; + + // + // Let the MEL scripts know that an export has started. + // + MGlobal::executeCommand("shave_exportStart"); +} + + +void shaveRender::getExportFileInfo( + MString& exportFile, + bool& filePerFrame, + shaveConstant::FileNameFormat& nameFormat, + unsigned& framePadding, + unsigned& compression +) +{ + exportFile = mExportFileName; + filePerFrame = mExportFilePerFrame; + nameFormat = mExportFileNameFormat; + framePadding = mExportFileFramePadding; + compression = mExportFileCompression; + + return; +} + +void shaveRender::vrayKeyFrame() +{ + if (mRenderer) + { +#ifdef USE_VRAY + if(rendererIsVray()) + { + shaveVrayRenderer* vr = static_cast<shaveVrayRenderer*>( mRenderer ); + vr->vrayKeyframeCallback(); + } +#endif + } +} + +/////////////////////// +//void shaveRender::shutterOpen() +//{ +// shaveGlobals::getGlobals(); +// saveFrameGlobals(); +// +// shaveMaya::getRenderGlobals(); +// +// SHAVEset_verbose(mFrameGlobals.verbose ? 1 : 0); +// SHAVEclear_stack(); +// +// MTime curTime = MAnimControl::currentTime(); +// float frame = (float)curTime.as(MTime::uiUnit()); +// MGlobal::displayInfo(MString("shutter open: ") + frame); +// +// bool mustFreeRenderer = false; +// if (mRenderer == NULL) +// { +// getRenderer(); +// mustFreeRenderer = true; +// } +// +// MObjectArray shaveHairShapes; +// mRenderer->getRenderableShaveNodes(shaveHairShapes); +// MGlobal::displayInfo(MString("num nodes: ") + shaveHairShapes.length() ); +// buildHairStack(shaveHairShapes, shaveConstant::kShutterOpen); +// +// //if(mustFreeRenderer) +// //{ +// // delete mRenderer; +// // mRenderer = NULL; +// //} +// MGlobal::displayInfo("shaveRender::shutterOpen - OK"); +// +//} +//void shaveRender::shutterClose() +//{ +// shaveGlobals::getGlobals(); +// saveFrameGlobals(); +// +// shaveMaya::getRenderGlobals(); +// +// MTime curTime = MAnimControl::currentTime(); +// float frame = (float)curTime.as(MTime::uiUnit()); +// MGlobal::displayInfo(MString("shutter close: ") + frame); +// +// +// //bool mustFreeRenderer = false; +// //if (mRenderer == NULL) +// //{ +// // getRenderer(); +// // mustFreeRenderer = true; +// //} +// MObjectArray shaveHairShapes; +// mRenderer->getRenderableShaveNodes(shaveHairShapes); +// buildHairStack(shaveHairShapes, shaveConstant::kShutterClose); +// +// //if(mustFreeRenderer) +// //{ +// // delete mRenderer; +// // mRenderer = NULL; +// //} +//} +//void shaveRender::shutterExport(MString fileName) +//{ +// SHAVEmult_vox_size(mFrameGlobals.voxelScaling); +// SHAVEexport_archive((char*)fileName.asChar(), voxelResolutionGlob); +// +// //bool mustFreeRenderer = false; +// //if (mRenderer == NULL) +// //{ +// // getRenderer(); +// // mustFreeRenderer = true; +// //} +// MObjectArray shaveHairShapes; +// mRenderer->getRenderableShaveNodes(shaveHairShapes); +// clearHairStack(shaveHairShapes); +// +// //if(mustFreeRenderer) +// //{ +// if(mRenderer) +// { +// delete mRenderer; +// mRenderer = NULL; +// } +// //} +//} diff --git a/mayaPlug/shaveRender.h b/mayaPlug/shaveRender.h new file mode 100644 index 0000000..79e908e --- /dev/null +++ b/mayaPlug/shaveRender.h @@ -0,0 +1,305 @@ +#ifndef _SHAVERENDER_H_ +#define _SHAVERENDER_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <stdio.h> +#include <list> + +#include <maya/MColor.h> +#include <maya/MColorArray.h> +#include <maya/MDagPath.h> +#include <maya/MDagPathArray.h> +#include <maya/MFloatArray.h> +#include <maya/MIntArray.h> +#include <maya/MObjectArray.h> +#include <maya/MSceneMessage.h> +#include <maya/MSelectionList.h> +#include <maya/MStringArray.h> +#include <maya/MTime.h> + +#include "shaveConstant.h" +#include "shaveGlobals.h" +#include "shaveIO.h" +#include "shaveSDK.h" + +#define OCCLUSIONS_IN_DRA 1 + +#if MAYA_API_VERSION < 20180000 +class MDagPath; +class MFnCamera; +#endif + +class shaveRenderCallback; +class shaveRenderer; + + +class shaveRender +{ + //need a frank to update shave camera interactively + //during viewport draws -- vlad|03Jun2010 + friend class shaveHairUI; + +public: + typedef struct + { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } Pixel; + + typedef struct + { + MDagPath cameraPath; + bool nearClipChanged; + double origNearClip; + bool farClipChanged; + double origFarClip; + } AutoClipInfo; + + typedef std::list<AutoClipInfo> AutoClipList; + typedef std::list<AutoClipInfo>::iterator AutoClipListIter; + + typedef struct + { + WFTYPE shutterOpenCamScene; + WFTYPE shutterCloseCamScene; + WFTYPE shutterOpenShadowScene; + WFTYPE shutterCloseShadowScene; + Pixel* shaveRenderPixels; + float* shaveZBuffer; + int width; + int height; + int aa; + float currentFrame; + float frameOffset; + bool bufferValid; + AutoClipList autoClipCameras; + bool shaveTraceInitialized; + bool haveTracedLights; + } SceneInfo; + + enum RenderState + { + kRenderNone, + kRendering, + kRenderingFrame + }; + + static void bufferRender( + shaveConstant::ShutterState shutter, + const MDagPath& cameraPath, + float frame, + bool needBuffers, + bool doShadowRender, + bool doCameraRender, + bool doOcclusions + ); + + static void bufferRenderCleanup(); + + static void buildHairStack( + MObjectArray& shaveNodes, + shaveConstant::ShutterState shutter + ); + + static void buildOcclusionLists(shaveConstant::ShutterState shutter); + static void cleanupCallbacks(); + static void clearHairStack(MObjectArray& shaveHairShapes); + static bool exportIsActive() { return mExportActive; } + static void exportEnd(); + static void exportStart(); + static void exportStart(MString exportFile); + + static void exportStart( + MString exportFile, + bool filePerFrame, + shaveConstant::FileNameFormat nameFormat, + unsigned framePadding, + unsigned compression + ); + + static void frameEnd(void* clientData); + static void frameStart(void* clientData); + static void geomRender(MObjectArray& shaveNodes); + + static void getExportFileInfo( + MString& exportFile, + bool& filePerFrame, + shaveConstant::FileNameFormat& nameFormat, + unsigned& framePadding, + unsigned& compression + ); + + static const shaveGlobals::Globals& + getFrameGlobals() { return mFrameGlobals; } + + static MDagPath getRenderCamPath(); + static MString getRenderModeName(shaveConstant::RenderMode mode); + + static shaveRenderer* getRenderer(); + + static SceneInfo* getSceneInfo(); + + static MStatus getShutterTimes( + float& shutterOpen, float& shutterClose + ); + + static bool isFirstFrame(); + static int renderCameraView(const MDagPath& cameraPath); + static void renderCameraViewCleanup(); + static void renderEnd(void* clientData); + static bool rendererIsVray(); //vlad|15Apr2010 + static bool rendererIsPrMan(); + static void renderStart(void* clientData); + + static shaveRender::RenderState renderState(); + + static MStatus renderSwatch( + MObject shaveNode, + unsigned int resolution, + MString fileName + ); + + // This method will use Maya's motion blur settings. If you need + static MStatus renderToDRAFile( + SHAVE_RENDER_HOST renderHost, + const MString& fileName, + int voxelResolution, + bool includeOcclusions = false + ); + + static MStatus renderToDRAFile( + SHAVE_RENDER_HOST renderHost, + const MString& fileName, + int voxelResolution, + MObjectArray shaveNodes, + bool doMotionBlur, + float shutterOpen, + float shutterClose, + bool updateTextureCache, + bool includeOcclusions = false + ); + + static void saveFrameGlobals() + { shaveGlobals::getGlobals(mFrameGlobals); } + + static void setupCallbacks(); + + /////////////////////// + //static void shutterOpen(); + //static void shutterClose(); + //static void shutterExport(MString fileName); + /////////////////////// + + static void vrayKeyFrame(); + + static void portFieldOfView( + int width, + int height, + double pixelAspect, + double& horizontal, + double& vertical, + MFnCamera& fnCamera + ); + +protected: + static void computeViewingFrustum( + double window_aspect, + double &left, + double &right, + double &bottom, + double &top, + MFnCamera& cam + ); + + static void doCamera( + const MDagPath& camPath, + shaveConstant::ShutterState shutter, + SceneInfo& shaveData + ); + + static void doColorCorrection( + MColor colorMod, + short mapWidth, + short mapHeight, + unsigned char* mapBuffer + ); + + static void doDensityCorrection( + float density, + short mapWidth, + short mapHeight, + unsigned char* mapBuffer + ); + + static void doLights(); + static void doShadows(); + + static void initSceneInfo(float frame, bool allocateBuffers); + static bool isIPRActive(); + + + + static void renderInterrupted(void* clientData); + static void timeChanged(MTime& newTime, void* clientData); + + + +public: + static bool mHaveInstancedHair; + static bool mHaveNormalHair; + static MObjectArray mHiddenNodes; + static bool mNeedVertexColours; + static shaveConstant::ShadowSource mNormalShadows; + static RenderState mRenderState; + +protected: + static bool mCallbacksSet; + static bool mFirstFrame; + static shaveGlobals::Globals mFrameGlobals; + static MIntArray mDisplayModes; + static bool mExportActive; + static unsigned mExportFileCompression; + static unsigned mExportFileFramePadding; + static MString mExportFileName; + static shaveConstant::FileNameFormat mExportFileNameFormat; + static bool mExportFilePerFrame; + static shaveConstant::RenderMode mHairRenderMode; + static bool mRenderAllInstanceHairs; + static bool mRenderAllNormalHairs; + static shaveRenderer* mRenderer; + static bool mRenderInstances; + static SceneInfo mSceneInfo; + static MObjectArray mTemplatedNodes; + + // + // Callback Handles + // + static MCallbackId mAfterFrameRenderCallback; + static MCallbackId mAfterRenderCallback; + static MCallbackId mBeforeFrameRenderCallback; + static MCallbackId mBeforeRenderCallback; + static MCallbackId mRenderInterruptedCallback; + static MCallbackId mTimeChangeCallback; + static shaveRenderCallback* mShaveCallback; +}; + + +inline shaveRender::RenderState shaveRender::renderState() +{ return mRenderState; } + +inline shaveRender::SceneInfo* shaveRender::getSceneInfo() +{ return &mSceneInfo; } + +inline bool shaveRender::isFirstFrame() +{ return mFirstFrame; } + +extern bool shaveRenderCancelled; +extern bool shaveEnableProgressBar; + +#endif + diff --git a/mayaPlug/shaveRenderCallback.cpp b/mayaPlug/shaveRenderCallback.cpp new file mode 100644 index 0000000..2665824 --- /dev/null +++ b/mayaPlug/shaveRenderCallback.cpp @@ -0,0 +1,581 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <float.h> +#include <math.h> + +#include <maya/MDagPath.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnTransform.h> +#include <maya/MGlobal.h> +#include <maya/MItDag.h> +#include <maya/MMatrix.h> +#include <maya/MPoint.h> +#include <maya/MRenderUtil.h> +#include <maya/MRenderView.h> +#include <maya/MVector.h> + +#include "shaveDebug.h" +#include "shaveGlobals.h" +#include "shaveIO.h" +#include "shaveMaya.h" +#include "shaveMayaRenderer.h" +#include "shaveRender.h" +#include "shaveRenderCallback.h" + + +extern void lightBufferFileCleanup(); +extern void lightProjectionNodeCleanup(); + +// The different order of bytes passed by Maya to renderCallback() is +// OSX-specific and is not an artifact of the byte-ordering on different +// processors because both PPC and Intel on OSX use the same ordering, +// which is different from that used by Linux and Windows. +#ifdef OSMac_ +#define ALPHA 0 +#define BLUE 1 +#define GREEN 2 +#define RED 3 +#else +#define BLUE 0 +#define GREEN 1 +#define RED 2 +#define ALPHA 3 +#endif + +#ifndef M_PI +#define M_PI 3.14159926535 +#endif + + +bool shaveRenderCallback::mDoingGeomRender = false; +bool shaveRenderCallback::mDoTiles = false; +const MRenderData* shaveRenderCallback::mMayaRenderData = 0; +const shaveRender::Pixel* shaveRenderCallback::mShavePixels = 0; + + +void shaveRenderCallback::disableCamRender() +{ + ENTER(); + mDoingCamRender = false; + LEAVE(); +} + + +void shaveRenderCallback::enableCamRender(const MDagPath& renderCam) +{ + ENTER(); + mDoingCamRender = true; + mRenderCamera = renderCam; + + LEAVE(); +} + + +shaveRenderCallback::shaveRenderCallback(shaveMayaRenderer* renderer) +: mDoingCamRender(false) +, mRenderer(renderer) +{} + + +#if !defined(max) +inline float max(float first, float second) +{ + return(first >= second ? first : second); +} +#endif + +#if !defined(min) +inline float min(float first, float second) +{ + return(first <= second ? first : second); +} +#endif + + +bool shaveRenderCallback::shadowCastCallback (const MRenderShadowData &data) +{ + return true; +} + + +bool shaveRenderCallback::renderCallback (const MRenderData &data) +{ + ENTER(); + + if (mDoingGeomRender) + { + mDoingGeomRender = false; + } + else + { + shaveRender::SceneInfo* shaveData = shaveRender::getSceneInfo(); + const shaveGlobals::Globals& globals = shaveRender::getFrameGlobals(); + + if (mDoingCamRender && !shaveRenderCancelled) + { + initTileCallback(data, shaveData); + + if (shaveRender::renderCameraView(mRenderCamera) != 0) + shaveRenderCancelled = true; + + cleanupTileCallback(); + } + + if (globals.doCompositing + && !shaveRenderCancelled + && !shaveGlobals::getDefaultNode().isNull()) + { + float maxSZ = -SHAVE_FAR_CLIP; + float minSZ = 0.0f; + float maxMZ = -SHAVE_FAR_CLIP; + float minMZ = 0.0f; + float* depthData = data.depthArr; + + if (globals.composite2d) + { + unsigned char * imageData = data.rgbaArr; + + shaveMaya::getRenderGlobals(); + + shaveRender::Pixel* sPixel = NULL; + + if ((depthData != NULL) && (shaveData->shaveZBuffer != NULL)) + { + float* MZ; + float SZ; + float nSZ; + float tmp; + MFloatPoint newPoint; + for(int y = 0; y < data.ysize; y++) + { + for(int x = 0; x < data.xsize; x++) + { + MZ = &depthData[data.xsize*y+x]; + SZ = (float)shaveData->shaveZBuffer[ + data.resX*(y + data.bottom) + x + data.left]; + nSZ = shaveZtoMaya(SZ); + tmp = (*MZ == 0 ? 0:1.0f/(*MZ)); + if(tmp != 0) + { + maxMZ = max(maxMZ, tmp); + minMZ = min(minMZ, tmp); + } + if(nSZ != 0) + { + maxSZ = max(maxSZ, -SZ); + minSZ = min(minSZ, -SZ); + } + if((nSZ < *MZ && nSZ != 0.0f) || *MZ == 0.0f) + *MZ = (float)nSZ; + } + } + } + + if (shaveData->shaveRenderPixels) + { + unsigned int carryA; + unsigned int carryB; + unsigned int carryG; + unsigned int carryR; + int i; + unsigned int iALPHA = ALPHA * data.bytesPerChannel; + unsigned int iBLUE = BLUE * data.bytesPerChannel; + unsigned int iGREEN = GREEN * data.bytesPerChannel; + unsigned int iRED = RED * data.bytesPerChannel; + unsigned char* rPixel; + unsigned int temp; + + for (int y = 0; y < data.ysize; y++) + { + for (int x=0; x < data.xsize; x++) + { + rPixel = imageData + ((data.xsize*y+x)*4*data.bytesPerChannel); + sPixel = &shaveData->shaveRenderPixels[data.resX + * (y + data.bottom) + x + data.left]; + + float sAlpha = (float)sPixel->a / 255.0f; + float sTransp = 1.0f - sAlpha; + + // + // Our own version of infinite precision + // arithmetic. + // + carryA = carryB = carryG = carryR = 0; + +#if defined(IRIX) || defined(__ppc__) + for (i = data.bytesPerChannel-1; i >= 0; i--) +#else + for (i = 0; i < data.bytesPerChannel; i++) +#endif + { + temp = (unsigned int)sPixel->r + + (unsigned int)(sTransp*(float)rPixel[iRED+i]) + + carryR; + + carryR = temp >> 8; + + if ((data.bytesPerChannel == 1) && (temp > 255)) + rPixel[iRED+i] = 255; + else + rPixel[iRED+i] = (unsigned char)(temp & 0xff); + + temp = (unsigned int)sPixel->g + + (unsigned int)(sTransp*(float)rPixel[iGREEN+i]) + + carryG; + + carryG = temp >> 8; + + if ((data.bytesPerChannel == 1) && (temp > 255)) + rPixel[iGREEN+i] = 255; + else + rPixel[iGREEN+i] = (unsigned char)(temp & 0xff); + + temp = (unsigned int)sPixel->b + + (unsigned int)(sTransp*(float)rPixel[iBLUE+i]) + + carryB; + + carryB = temp >> 8; + + if ((data.bytesPerChannel == 1) && (temp > 255)) + rPixel[iBLUE+i] = 255; + else + rPixel[iBLUE+i] = (unsigned char)(temp & 0xff); + + temp = (unsigned int)sPixel->a + + (unsigned int)rPixel[iALPHA+i] + + carryA; + + carryA = temp >> 8; + + if ((data.bytesPerChannel == 1) && (temp > 255)) + rPixel[iALPHA+i] = 255; + else + rPixel[iALPHA+i] = (unsigned char)(temp & 0xff); + } + + // + // In theory, all of Shave's color values will + // lie in the range 0 - alpha, so it should be + // impossible for any of the values to + // overflow. However, let's be paranoid: if we + // got a carry out the MSB of any channel, then + // set that channel's MSB to its max value of + // 255. (We could set all of the bytes in the + // channel, but MSB should be sufficient.) + // +#if defined(IRIX) || defined(__ppc__) + i++; +#else + i--; +#endif +#if 0 + // + // This *should* have had the same effect as + // the more expensive 'temp > 255' checks in + // the loop above, but Joe says that replacing + // these with the checks got rid of 'pookies': + // multicolored artifacts at the ends of + // semi-transparent hairs. + // + if (carryR) rPixel[iRED+i] = 255; + if (carryG) rPixel[iGREEN+i] = 255; + if (carryB) rPixel[iBLUE+i] = 255; + if (carryA) rPixel[iALPHA+i] = 255; +#endif + } + } + } + } + // + // Even if the hair itself is composited into the image within + // a 3d volumetric shader, we still need to give Maya our depth + // values so that post-processes such as depth of field will + // work. + // + else + { + if ((depthData != NULL) + && (shaveData->shaveZBuffer != NULL) + && globals.doCompositing) + { + float* MZ; + float SZ; + float nSZ; + float tmp; + + for(int y = 0; y < data.ysize; y++) + { + for(int x = 0; x < data.xsize; x++) + { + MZ = &depthData[data.xsize*y+x]; + SZ = (float)shaveData->shaveZBuffer[ + data.resX*(y + data.bottom) + x + data.left]; + nSZ = shaveZtoMaya(SZ); + tmp = (*MZ == 0 ? 0:1.0f/(*MZ)); + if(tmp != 0) + { + maxMZ = max(maxMZ, tmp); + minMZ = min(minMZ, tmp); + } + if(nSZ != 0) + { + maxSZ = max(maxSZ, -SZ); + minSZ = min(minSZ, -SZ); + } + if((nSZ < *MZ && nSZ != 0.0f) || *MZ == 0.0f) + *MZ = (float)nSZ; + } + } + } + } + + if (globals.verbose) + { +#ifdef OSMac_ + // + // Panther broke cerr somehow so that it crashes if you use + // it to non-zero float or double values. So let's try our + // old pal stderr, instead. + // + if (stderr) + { + fprintf( + stderr, + "Z Bounds: Maya: %f, %f, Shave: %f, %f\n", + -maxMZ, -minMZ, -maxSZ, -minSZ + ); + } +#else + cerr << "Z Bounds: Maya: " << -maxMZ << ", " << -minMZ + << ", Shave: " << -maxSZ << ", " << -minSZ << endl; +#endif + } + } + } + + if (mRenderer) mRenderer->postCompositeCleanup(); + + RETURN(true); +} + + +bool shaveRenderCallback::postProcessCallback (const MRenderData &data) +{ + return true; +} + + +float shaveZtoMaya(float shaveVal) +{ + if(shaveVal == SHAVE_FAR_CLIP) + return 0.0f; + else + return -1.0f/shaveVal; +} + + +MStatus shaveRenderCallback::getRenderCamera( + const MRenderData& renderData, MDagPath& renderCamPath +) +{ + // + // Unfortunately, there's no way to simply ask Maya which camera is + // currently being rendered. So instead we have to walk through all + // available cameras and find out which one's parameters most closely + // match those which are available to use through the passed-in + // MRenderData object. + // + MItDag iter(MItDag::kDepthFirst, MFn::kCamera); + MDagPath testCamPath; + float aspectDiff; + float fovDiff; + const float kTolerance = 0.001f; + + for (; !iter.isDone(); iter.next()) + { + iter.getPath(testCamPath); + + MFnCamera cameraFn(testCamPath); + + // + // Make sure that the perspective/ortho and other settings match. + // + aspectDiff = renderData.aspectRatio - (float)cameraFn.aspectRatio(); + + fovDiff = renderData.fieldOfView + - (float)cameraFn.horizontalFieldOfView(); + + if ((cameraFn.isOrtho() != renderData.perspective) + && (fabs(aspectDiff) < kTolerance) + && (fabs(fovDiff) < kTolerance)) + { + // + // The preliminaries look good, now let's see if the camera's + // eye point is in the right place. + // + MPoint tempPoint = cameraFn.eyePoint(MSpace::kWorld); + + MFloatPoint eyePoint( + (float)tempPoint.x, + (float)tempPoint.y, + (float)tempPoint.z + ); + + // + // For some inexplicable reason, MRenderData::worldToEyeMatrix + // is an MFloatMatrix rather than a normal MMatrix. That means + // that we lose a lot of precision in the calculations below. + // Enough so that even our relatively generous kTolerance value + // may end up rejecting valid matches. + // + // So let's instead calculate a tolerance which is in keeping + // with the overall scale of the vectors involved. + // + float scaledTolerance = eyePoint.distanceTo(MFloatPoint::origin) + / 1000000.0f; + + // + // Convert it to the render camera's local space. + // + eyePoint *= renderData.worldToEyeMatrix; + if (eyePoint.isEquivalent(renderData.eyePoint, scaledTolerance)) + { + // + // The eye point is right, now let's check the viewing + // direction. + // + MFloatVector viewDir(cameraFn.viewDirection()); + + if (viewDir.isEquivalent( + renderData.viewDirection, scaledTolerance)) + { + renderCamPath = testCamPath; + + return MS::kSuccess; + } + } + } + } + + MGlobal::displayError( + "shaveRenderCallback::getRenderCamera - could not find the render camera." + ); + + return MS::kFailure; +} + + +void shaveRenderCallback::initTileCallback( + const MRenderData& mayaRenderData, shaveRender::SceneInfo* shaveRenderData +) +{ + mMayaRenderData = &mayaRenderData; + mShavePixels = shaveRenderData->shaveRenderPixels; + mDoTiles = true; +} + + +void shaveRenderCallback::cleanupTileCallback() +{ + mMayaRenderData = 0; + mShavePixels = 0; + mDoTiles = false; +} + + +// This gets called every time Shave has finished rendering a tile. +void shaveRenderCallback::tileRendered( + unsigned int left, unsigned int right, unsigned int bottom, unsigned int top +) +{ + if (mDoTiles && MRenderView::doesRenderEditorExist()) + { + // Find the intersection between Shave's tile and Maya's render + // region. + if (left < mMayaRenderData->left) + left = mMayaRenderData->left; + if (right >= (unsigned int)(mMayaRenderData->left + mMayaRenderData->xsize)) + right = mMayaRenderData->left + mMayaRenderData->xsize - 1; + if (bottom < mMayaRenderData->bottom) + bottom = mMayaRenderData->bottom; + if (top >= (unsigned int)(mMayaRenderData->bottom + mMayaRenderData->ysize)) + top = mMayaRenderData->bottom + mMayaRenderData->ysize - 1; + + if ((right < left) || (top < bottom)) return; + + const unsigned xsize = right - left + 1; + const unsigned ysize = top - bottom + 1; + + // Create an image buffer for the render view. + RV_PIXEL* viewPixels = new RV_PIXEL[xsize * ysize]; + + //--------------------------------------------------------------- + // Compose the shave pixels onto Maya's pixels and store in the + // render view buffer. + //--------------------------------------------------------------- + const unsigned int iALPHA = ALPHA * mMayaRenderData->bytesPerChannel; + const unsigned int iBLUE = BLUE * mMayaRenderData->bytesPerChannel; + const unsigned int iGREEN = GREEN * mMayaRenderData->bytesPerChannel; + const unsigned int iRED = RED * mMayaRenderData->bytesPerChannel; + const unsigned int mayaBytesPerPixel = 4 * mMayaRenderData->bytesPerChannel; + + // If there are multiple bytes per channel, we only care about the + // most significant byte. +#if defined(__ppc__) + unsigned int mayaMSBOffset = 0; +#else + unsigned int mayaMSBOffset = mMayaRenderData->bytesPerChannel - 1; +#endif + unsigned int mayaByteIdx = 0; + unsigned char* mayaBytes = mMayaRenderData->rgbaArr; + unsigned int shavePixelIdx = 0; + float temp; + unsigned int viewPixelIdx = 0; + + for (unsigned int y = bottom; y <= top; ++y) + { + shavePixelIdx = mMayaRenderData->resX * y + left; + mayaByteIdx = (mMayaRenderData->xsize * (y - mMayaRenderData->bottom) + + left - mMayaRenderData->left) * mayaBytesPerPixel + + mayaMSBOffset; + + for (unsigned int x = left; x <= right; ++x) + { + float shaveAlpha = (float)mShavePixels[shavePixelIdx].a / 255.0f; + float shaveTransp = 1.0f - shaveAlpha; + + temp = (float)mShavePixels[shavePixelIdx].r + + shaveTransp * (float)mayaBytes[mayaByteIdx + iRED]; + if (temp > 255.0f) temp = 255.0f; + viewPixels[viewPixelIdx].r = temp; + + temp = (float)mShavePixels[shavePixelIdx].g + + shaveTransp * (float)mayaBytes[mayaByteIdx + iGREEN]; + if (temp > 255.0f) temp = 255.0f; + viewPixels[viewPixelIdx].g = temp; + + temp = (float)mShavePixels[shavePixelIdx].b + + shaveTransp * (float)mayaBytes[mayaByteIdx + iBLUE]; + if (temp > 255.0f) temp = 255.0f; + viewPixels[viewPixelIdx].b = temp; + + temp = shaveAlpha + (float)mayaBytes[mayaByteIdx + iALPHA]; + if (temp > 255.0f) temp = 255.0f; + viewPixels[viewPixelIdx].a = temp; + + mayaByteIdx += mayaBytesPerPixel; + ++shavePixelIdx; + ++viewPixelIdx; + } + } + + // Update the render view with the new buffer. + MRenderView::updatePixels(left, right, bottom, top, viewPixels); + MRenderView::refresh(left, right, bottom, top); + + delete [] viewPixels; + } +} diff --git a/mayaPlug/shaveRenderCallback.h b/mayaPlug/shaveRenderCallback.h new file mode 100644 index 0000000..2c7a912 --- /dev/null +++ b/mayaPlug/shaveRenderCallback.h @@ -0,0 +1,78 @@ +#ifndef _SHAVERENDERCALLBACK_H_ +#define _SHAVERENDERCALLBACK_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagPath.h> +#include <maya/MObject.h> +#include <maya/MRenderCallback.h> +#include <maya/MFnCamera.h> +#include <maya/MRenderData.h> +#include <maya/MRenderShadowData.h> +#include <maya/MRenderUtil.h> + +#include "shaveRender.h" + +#ifndef MM_TO_INCH +# define MM_TO_INCH 0.03937 +#endif + +class shaveMayaRenderer; + + +class shaveRenderCallback : public MRenderCallback +{ +public: + shaveRenderCallback(shaveMayaRenderer* renderer); + ~shaveRenderCallback() {} + virtual bool shadowCastCallback (const MRenderShadowData &data); + virtual bool renderCallback (const MRenderData &data); + virtual bool postProcessCallback (const MRenderData &data); + + static void cleanupTileCallback(); + void disableCamRender(); + void enableCamRender(const MDagPath& camera); + + static MStatus getRenderCamera( + const MRenderData& renderData, MDagPath& renderCamPath + ); + + static void initTileCallback( + const MRenderData& mayaRenderData, + shaveRender::SceneInfo* shaveRenderData + ); + + static void setGeomRender(bool setIt); + + static void tileRendered( + unsigned int left, + unsigned int right, + unsigned int bottom, + unsigned int top + ); + +private: + unsigned int width, height; + bool ignoreFilmGate; + double fov_ratio; + int cam_width, cam_height; + bool mDoingCamRender; + static bool mDoingGeomRender; + static bool mDoTiles; + shaveMayaRenderer* mRenderer; + MDagPath mRenderCamera; + static const MRenderData* mMayaRenderData; + static const shaveRender::Pixel* mShavePixels; +}; + + +inline void shaveRenderCallback::setGeomRender(bool setIt) +{ mDoingGeomRender = setIt; } + + +float shaveZtoMaya(float); + +#endif + diff --git a/mayaPlug/shaveRenderCmd.cpp b/mayaPlug/shaveRenderCmd.cpp new file mode 100644 index 0000000..5096d2b --- /dev/null +++ b/mayaPlug/shaveRenderCmd.cpp @@ -0,0 +1,327 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MGlobal.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveRender.h" +#include "shaveRenderCmd.h" +#include "shaveUtil.h" + +#if defined(OSMac_) +#include "shaveMacCarbon.h" +#endif + + +const MString shaveRenderCmd::commandName("shaveRender"); + +static const char* kCreateDRAFileLong = "-createDRAFile"; +static const char* kCreateDRAFileShort = "-cdf"; +static const char* kExportEndLong = "-exportEnd"; +static const char* kExportEndShort = "-ee"; +static const char* kExportStartLong = "-exportStart"; +static const char* kExportStartShort = "-es"; +static const char* kOcclusionsLong = "-occlusionsInDRA"; +static const char* kOcclusionsShort = "-occ"; +static const char* kRenderStartLong = "-renderStart"; +static const char* kRenderStartShort = "-rs"; +static const char* kRenderEndLong = "-renderEnd"; +static const char* kRenderEndShort = "-re"; +static const char* kFrameStartLong = "-frameStart"; +static const char* kFrameStartShort = "-fs"; +static const char* kFrameEndLong = "-frameEnd"; +static const char* kFrameEndShort = "-fe"; +static const char* kSwatchLong = "-swatch"; +static const char* kSwatchShort = "-sw"; +static const char* kTargetRendererLong = "-targetRenderer"; +static const char* kTargetRendererShort= "-tr"; + +//static const char* kShutterOpenLong = "-shutterOpen"; +//static const char* kShutterOpenShort = "-so"; +// +//static const char* kShutterCloseLong = "-shutterClose"; +//static const char* kShutterCloseShort = "-sc"; +// +//static const char* kShutterExportLong = "-shutterExport"; +//static const char* kShutterExportShort = "-se"; + +static const char* kVrayKeyframeLong = "-vrayKeyframe"; +static const char* kVrayKeyframeShort = "-vr"; + + +shaveRenderCmd::shaveRenderCmd() {} +shaveRenderCmd::~shaveRenderCmd() {} + + +void* shaveRenderCmd::createCmd() +{ + return new shaveRenderCmd(); +} + + +MSyntax shaveRenderCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(true); + + syntax.addFlag(kCreateDRAFileShort, kCreateDRAFileLong, MSyntax::kString); + syntax.addFlag(kExportEndShort, kExportEndLong); + syntax.addFlag(kExportStartShort, kExportStartLong, MSyntax::kString); + syntax.addFlag(kFrameEndShort, kFrameEndLong); + syntax.addFlag(kFrameStartShort, kFrameStartLong); + syntax.addFlag(kOcclusionsShort, kOcclusionsLong); + syntax.addFlag(kRenderEndShort, kRenderEndLong); + syntax.addFlag(kRenderStartShort, kRenderStartLong); + syntax.addFlag(kTargetRendererShort, kTargetRendererLong, MSyntax::kUnsigned); + + syntax.addFlag( + kSwatchShort, + kSwatchLong, + MSyntax::kString, + MSyntax::kUnsigned, + MSyntax::kString + ); + + //syntax.addFlag(kShutterOpenShort, kShutterOpenLong); + //syntax.addFlag(kShutterCloseShort,kShutterCloseLong); + //syntax.addFlag(kShutterExportShort,kShutterExportLong, MSyntax::kString); + + syntax.addFlag(kVrayKeyframeShort, kVrayKeyframeLong); + + return syntax; +} + + +MStatus shaveRenderCmd::doIt(const MArgList& argList) +{ + MStatus status; + MArgDatabase args(syntax(), argList, &status); + + if (!status) return status; + + // + // Make sure that one, and only one, flag has been specified. + // + MStringArray flags; + + if (args.isFlagSet(kCreateDRAFileShort)) flags.append(kCreateDRAFileLong); + if (args.isFlagSet(kRenderStartShort)) flags.append(kRenderStartLong); + if (args.isFlagSet(kRenderEndShort)) flags.append(kRenderEndLong); + if (args.isFlagSet(kExportStartShort)) flags.append(kExportStartLong); + if (args.isFlagSet(kExportEndShort)) flags.append(kExportEndLong); + if (args.isFlagSet(kFrameStartShort)) flags.append(kFrameStartLong); + if (args.isFlagSet(kFrameEndShort)) flags.append(kFrameEndLong); + if (args.isFlagSet(kSwatchShort)) flags.append(kSwatchLong); + + //if (args.isFlagSet(kShutterOpenShort)) flags.append(kShutterOpenLong); + //if (args.isFlagSet(kShutterCloseShort)) flags.append(kShutterCloseLong); + //if (args.isFlagSet(kShutterExportShort)) flags.append(kShutterExportLong); + + if (args.isFlagSet(kVrayKeyframeShort)) flags.append(kVrayKeyframeLong); + + if (flags.length() == 0) + { + MGlobal::displayError( + commandName + ": no flags specified. Nothing to do." + ); + return MS::kNotFound; + } + else if (flags.length() > 1) + { + MGlobal::displayError( + commandName + ": the '" + flags[0] + "' and '" + flags[1] + + "' flags cannot be used together on the same command." + ); + return MS::kInvalidParameter; + } + + SHAVE_RENDER_HOST renderHost = kShaveHostPRMAN; + + if (args.isFlagSet(kTargetRendererShort)) + { + int val; + + args.getFlagArgument(kTargetRendererShort, 0, val); + + switch ((SHAVE_RENDER_HOST)val) + { + case kShaveHostPRMAN: + case kShaveHostBuffer: + renderHost = (SHAVE_RENDER_HOST)val; + break; + + default: + displayError( + commandName + ": invalid value for '" + kTargetRendererLong + + "' flag. Must be one of " + + kShaveHostPRMAN + " (PRMAN), " + + kShaveHostBuffer + " (buffer)." + ); + return MS::kInvalidParameter; + break; + } + } + + shaveGlobals::getGlobals(); + + // + // I used to enclose all the 'kRender*' flags in a test such that these + // commands would only be honoured during a render or export. + // However, some render cleanup code runs on an idle event + // and by the time it gets around to running, the flags telling us + // whether we're doing a render or export might no longer be set. + // So it's better to always honour these commands and rely on the + // callers to only call them when it's appropriate. + // + if (args.isFlagSet(kRenderStartShort)) + shaveRender::renderStart(NULL); + else if (args.isFlagSet(kRenderEndShort)) + shaveRender::renderEnd(NULL); + else if (args.isFlagSet(kExportStartShort)) + { + MString exportFile; + + args.getFlagArgument(kExportStartShort, 0, exportFile); + shaveRender::exportStart(exportFile); + } + else if (args.isFlagSet(kExportEndShort)) + shaveRender::exportEnd(); + else if (args.isFlagSet(kFrameStartShort)) + shaveRender::frameStart(NULL); + else if (args.isFlagSet(kFrameEndShort)) + shaveRender::frameEnd(NULL); + else if (args.isFlagSet(kSwatchShort)) + { + MString shaveHairShapeName; + + args.getFlagArgument(kSwatchShort, 0, shaveHairShapeName); + + MSelectionList list; + list.add(shaveHairShapeName); + + MObject shaveHairShape; + list.getDependNode(0, shaveHairShape); + + if (shaveHairShape.isNull()) + { + MGlobal::displayError( + MString("shaveRender: '") + shaveHairShapeName + + "' is not a shave hair node." + ); + + return MS::kInvalidParameter; + } + + MFnDependencyNode nodeFn(shaveHairShape); + + if (nodeFn.typeId() != shaveHairShape::id) + { + MGlobal::displayError( + MString("shaveRender: '") + shaveHairShapeName + + "' is not a shave hair node." + ); + + return MS::kInvalidParameter; + } + + unsigned int resolution; + + args.getFlagArgument(kSwatchShort, 1, resolution); + + if (resolution == 0) + { + MGlobal::displayError( + "shaveRender: resolution of swatch must greater than zero." + ); + + return MS::kInvalidParameter; + } + + MString fileName; + + args.getFlagArgument(kSwatchShort, 2, fileName); + +#if defined(OSMac_) && !defined(OSMac_MachO_) + // + // The filename will be coming to us in standard POSIX form + // (e.g. "/path/to/file"), but since this is the CFM version of + // Maya on OSX then we need to convert that to an HSF filename. + // + fileName = shaveMacCarbon::makeMacFilename(fileName); +#endif + + status = shaveRender::renderSwatch(shaveHairShape, resolution, fileName); + + if (!status) + { + MGlobal::displayError( + MString("shaveRender: error rendering swatch to '") + + fileName + "'." + ); + } + } + else if (args.isFlagSet(kCreateDRAFileShort)) + { + + MString fileName; + args.getFlagArgument(kCreateDRAFileShort, 0, fileName); + + bool includeOcclusions = args.isFlagSet(kOcclusionsShort); + +#if defined(OSMac_) && !defined(OSMac_MachO_) + // + // The filename will be coming to us in standard POSIX form + // (e.g. "/path/to/file"), but since this is the CFM version of + // Maya on OSX then we need to convert that to an HSF filename. + // + fileName = shaveMacCarbon::makeMacFilename(fileName); +#endif + + MGlobal::displayInfo(MString("exporting hair to '") + fileName + "'."); + + status = shaveRender::renderToDRAFile( + renderHost, + fileName, + voxelResolutionGlob, + includeOcclusions + ); + + if (status == MS::kNotFound) + { + MGlobal::displayError( + commandName + ": no renderable shaveHairShapes in scene," + + " no hair file generated." + ); + } + } + //else if(args.isFlagSet(kShutterOpenShort)) + //{ + // shaveRender::shutterOpen(); + //} + //else if(args.isFlagSet(kShutterCloseShort)) + //{ + // shaveRender::shutterClose(); + //} + //else if(args.isFlagSet(kShutterExportShort)) + //{ + // MString fileName; + // args.getFlagArgument(kShutterExportShort, 0, fileName); + // shaveRender::shutterExport(fileName); + //} + else if(args.isFlagSet(kVrayKeyframeShort)) + { + shaveRender::vrayKeyFrame(); + } + return status; +} + diff --git a/mayaPlug/shaveRenderCmd.h b/mayaPlug/shaveRenderCmd.h new file mode 100644 index 0000000..fdb025f --- /dev/null +++ b/mayaPlug/shaveRenderCmd.h @@ -0,0 +1,35 @@ +#ifndef shaveRenderCmd_h +#define shaveRenderCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +class shaveRenderCmd : public MPxCommand +{ +public: + shaveRenderCmd(); + virtual ~shaveRenderCmd(); + + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + + static void* createCmd(); + static MSyntax createSyntax(); + + static const MString commandName; + +private: +}; + + +inline bool shaveRenderCmd::isUndoable() const +{ return false; } + +#endif + diff --git a/mayaPlug/shaveRenderer.cpp b/mayaPlug/shaveRenderer.cpp new file mode 100644 index 0000000..6ebf6d1 --- /dev/null +++ b/mayaPlug/shaveRenderer.cpp @@ -0,0 +1,541 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MFnDependencyNode.h> +#include <maya/MGlobal.h> +#include <maya/MItDependencyNodes.h> +#include <maya/MObjectArray.h> + +#include "shaveDebug.h" + +#include "shaveCheckObjectVisibility.h" +#include "shaveConstant.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveMaya.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveSDKFUNCS.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" + + +shaveRenderer::shaveRenderer() +: mRendering(false) +, mWarningGivenInvalidHairMode(false) +, mWarningGivenInvalidInstanceMode(false) +{} + + +shaveRenderer::~shaveRenderer() +{} + + +void shaveRenderer::doShutter( + const MTime& curTime, shaveConstant::ShutterState shutter +) +{ + ENTER(); + + if ((mGeometryNodes.length() > 0) || (mNonGeometryNodes.length() > 0)) + { + float frame = (float)curTime.as(MTime::uiUnit()); + + shaveGlobals::getGlobals(); + shaveMaya::getRenderGlobals(); + + // + // We "render" geometry by having the shaveHairShape create its entire + // mesh. As such, we want it in the scene before anything else + // happens so that Maya will take it into account when doing + // bounding box calculations for the render. + // + if (mGeometryNodes.length() > 0) + shaveRender::geomRender(mGeometryNodes); + + MDagPath cameraPath = shaveRender::getRenderCamPath(); + + if (mNonGeometryNodes.length() > 0) + { + // + // Create the texture lookup table for this frame. + // + // We only do this on shutterOpen, not shutterClose. The + // reason for this is that Shave doesn't handle animation of + // any of its parameters: it simply takes their value at + // shutter close. So we only need to create the lookups once + // per frame, even when motion blur is on. + // + // That would argue that we should create the lookup on + // shutterClose, not shutterOpen. However, two things mitigate + // against that. First, to properly support vertex shaders, we + // need to have vertex-level colour info available at both + // shutterOpen and shutterClose. Second, there is a bug in + // Maya that if you sample a shading network on shutterClose, + // it screws up other, unrelated textures in the scene. + // + // Okay, so the last thing to discuss is why we don't create + // the lookup table in frameStart(). That's because there's a + // bug in Mental Ray For Maya such that the frame number at + // frameStart() is not correct during a batch render. So + // instead we have to wait for the actual time change before we + // can know which frame we are rendering. We no longer support + // Mental Ray, but it seems safer to leave it as-is in case + // any other renderers suffer from the same problem. + // + if ((shutter == shaveConstant::kShutterOpen) + || (shutter == shaveConstant::kShutterBoth)) + { +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2( + mNonGeometryNodes, + "", + shaveRender::getFrameGlobals().verbose + ); +#else + initTexInfoLookup( + mNonGeometryNodes, + "", + shaveRender::getFrameGlobals().verbose + ); +#endif + } + + shaveRender::buildHairStack(mNonGeometryNodes, shutter); + } + + // + // Call the overridden render() method. + // + render(frame, shutter, cameraPath); + } + + LEAVE(); +} + + +void shaveRenderer::frameEnd(const shaveGlobals::Globals& g) +{ + shaveRender::clearHairStack(mNonGeometryNodes); + mGeometryNodes.clear(); + mNonGeometryNodes.clear(); +} + + +void shaveRenderer::frameStart(const shaveGlobals::Globals& g) +{ + SHAVEset_tile_limit((int)g.tileMemoryLimit, (int)g.transparencyDepth); + + // + // Get the nodes to be rendered as geometry, and those to be rendered + // not as geometry, into two separate lists. + // + mGeometryNodes.clear(); + mNonGeometryNodes.clear(); + + getRenderableShaveNodesByRenderMode(&mGeometryNodes, &mNonGeometryNodes); + + // + // Changes in animated attributes may have changed the list of + // renderable shaveHairShapes. Let's update the flags telling us which + // types of shaveHairShapes we have. + // + bool tempHaveHair; + bool tempHaveInstances; + + shaveUtil::classifyShaveNodes(mGeometryNodes, tempHaveHair, tempHaveInstances); + shaveUtil::classifyShaveNodes(mNonGeometryNodes, mHaveHair, mHaveInstances); + + mHaveHair = (mHaveHair || tempHaveHair); + mHaveInstances = (mHaveInstances || tempHaveInstances); +} + + +// +// Return the base render modes, without caring if we actually have any of +// the relevant types of shaveHairShape. +// +shaveConstant::RenderMode shaveRenderer::getBaseRenderMode( + bool* renderInstances +) const +{ + shaveConstant::RenderMode hairRenderMode; + + hairRenderMode = normalRenderModeGlob; + + if (renderInstances) *renderInstances = enableInstanceGeometryGlob; + + // + // If the specified render mode does not exist, then switch to buffer. + // + if (shaveRender::getRenderModeName(hairRenderMode) == "") + { + if (!mWarningGivenInvalidHairMode) + { + MGlobal::displayWarning( + MString("Shave: Hair render mode ") + (double)hairRenderMode + + " is invalid. Using " + + shaveRender::getRenderModeName(shaveConstant::kBufferRender) + + " render mode instead." + ); + + mWarningGivenInvalidHairMode = true; + } + + hairRenderMode = shaveConstant::kBufferRender; + } + + return hairRenderMode; +} + + +shaveConstant::ShadowSource shaveRenderer::getBaseShadowSource() const +{ + ENTER(); + + shaveConstant::ShadowSource hairShadowSource = shaveConstant::kNoShadows; + shaveConstant::RenderMode hairRenderMode = getRenderMode(); + + if (doHairShadowsGlob) + { + switch (hairRenderMode) + { + case shaveConstant::kNoRender: + break; + + case shaveConstant::kGeometryRender: + hairShadowSource = shaveConstant::kMayaGeomShadows; + break; + + default: + if (shaveUseGeomShadowsGlob) + hairShadowSource = shaveConstant::kMayaGeomShadows; + else + hairShadowSource = shaveConstant::kShaveShadows; + break; + } + } + + // + // Return the shadow sources to the caller. + // + RETURN(hairShadowSource); +} + + +// +// Return the render modes applicable to the given array of shaveHairShapes. +// +shaveConstant::RenderMode shaveRenderer::getFilteredRenderMode( + const MObjectArray& shaveHairShapes, bool* renderInstances +) const +{ + // + // Get the base mode. + // + shaveConstant::RenderMode hairRenderMode = getRenderMode(renderInstances); + + // + // Do we have any hair? Any instances? + // + bool haveHair = false; + bool haveInstances = false; + + shaveUtil::classifyShaveNodes(shaveHairShapes, haveHair, haveInstances); + + if (!haveInstances && renderInstances) *renderInstances = false; + if (!haveHair) hairRenderMode = shaveConstant::kNoRender; + + return hairRenderMode; +} + + +void shaveRenderer::getGeomVisibility( + bool& hairPrimaryVisibility, + bool& hairSecondaryVisibility, + bool& instancePrimaryVisibility, + bool& instanceSecondaryVisibility +) +{ + bool renderInstances; + shaveConstant::RenderMode hairRenderMode; + shaveConstant::ShadowSource hairShadowSource; + + hairRenderMode = getRenderMode(&renderInstances); + hairShadowSource = getShadowSource(); + + switch (hairRenderMode) + { + case shaveConstant::kGeometryRender: + hairPrimaryVisibility = true; + hairSecondaryVisibility = true; + break; + + case shaveConstant::kBufferRender: + hairPrimaryVisibility = false; + hairSecondaryVisibility = + (hairShadowSource == shaveConstant::kMayaGeomShadows); + break; + + case shaveConstant::kNoRender: + default: + hairPrimaryVisibility = false; + hairSecondaryVisibility = false; + break; + } + + instancePrimaryVisibility = renderInstances; + instanceSecondaryVisibility = renderInstances; +} + + +float shaveRenderer::getPixelAspect() const +{ + return ((float)shaveMaya::imageHeight + / (float)shaveMaya::imageWidth) + * shaveMaya::deviceAspectRatio; +} + + +// Returns an array of those shaveHairShapes which are in an appropriate state +// to be rendered (e.g. active, visible, etc). +// +void shaveRenderer::getRenderableShaveNodes(MObjectArray& shaveHairShapes) +{ + shaveHairShapes.clear(); + + // Get the base render modes. + // + bool includeInstances; + shaveConstant::RenderMode renderMode = getRenderMode(&includeInstances); + + const bool includeHair = (renderMode != shaveConstant::kNoRender); + + // If the render modes are excluding both hair and instances, then we're + // done. + // + if (!includeHair && !includeInstances) return; + + // Run through all the shaveHairShapes in the scene and save the ones which + // are renderable. + // + MItDependencyNodes iter; + + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + MFnDependencyNode nodeFn(node); + + if (nodeFn.typeId() != shaveHairShape::id) continue; + + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + // + // Skip any nodes whose render mode means that they're not being + // rendered. + // + bool isInstanced = nodePtr->isInstanced(); + + if ((isInstanced && !includeInstances) + || (!isInstanced && !includeHair)) + { + continue; + } + + // Skip any nodes which are not visible. + // + // TODO: The user may have turned off visibility on the + // hair node or its display node. The display node + // only matters when we're displaying hair as geometry, + // but it would be confusing to force our users to + // switch back and forth between the hair and display + // nodes, depending upon which render mode they were + // using. To avoid that we go with a least common + // denominator approach: both the hair and display + // nodes must be visible for the hair to render. + // + MDagPath path; + MDagPath::getAPathTo(node, path); + + if (areObjectAndParentsVisible(path, false, true)) + { + // Find the shaveHairShape's display node. + // + MObject displayNode = nodePtr->getDisplayShape(); + + if (displayNode.isNull()) continue; + + // Is the display node in a renderable layer? + // + MFnDagNode dagNodeFn(displayNode); + MPlug plug = dagNodeFn.findPlug("layerRenderable"); + bool layerIsRenderable; + + plug.getValue(layerIsRenderable); + + if (!layerIsRenderable) continue; + + // Get a DAG path to the display node. At the moment we don't + // support instancing of shaveHairShapes, or their display + // nodes, so any path will do. + // + dagNodeFn.getPath(path); + + // We only render shaveHairShapes whose display nodes are + // visible. + // + // This method used to discard those shaveHairShapes whose + // display nodes had their primary visibility off. However, if + // someone turns off a node's primary visibility while leaving + // its regular visibility on, that means that they still want + // it to show up in shadows, reflections, refractions, etc. So + // we really do still need to render it. Admittedly we might + // not want it to be seen in a buffer render, but that's a + // larger issue than we can resolve here with a simply binary + // check of primary visibility. So now we ignore primary + // visibility when checking display nodes. + // + if (areObjectAndParentsVisible(path, false, true)) + shaveHairShapes.append(node); + } + } +} + + +// +// Returns those shaveHairShapes which are in an appropriate state to be +// rendered (e.g. active, visible, etc). The result is returned as two +// arrays, one containing those shaveHairShapes which are to be rendered as +// normal Maya geometry, and another containing those which require +// rendering by Shave. (The latter includes geometry shaders using Shave +// calls to generate render geometry.) +// +void shaveRenderer::getRenderableShaveNodesByRenderMode( + MObjectArray* geomNodes, MObjectArray* nonGeomNodes +) +{ + MObjectArray shaveHairShapes; + + getRenderableShaveNodes(shaveHairShapes); + + unsigned int i; + + for (i = 0; i < shaveHairShapes.length(); i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + // + // Ask the renderer-specific subclass whether this node should be + // put onto the geomNodes list. + // + if (isGeomNode(nodePtr)) + { + if (geomNodes) geomNodes->append(shaveHairShapes[i]); + } + else + { + if (nonGeomNodes) nonGeomNodes->append(shaveHairShapes[i]); + } + } +} + + +shaveConstant::RenderMode shaveRenderer::getRenderMode(bool* renderInstances) + const +{ + // + // If we're in the middle of a render, return the cached values. + // + if (mRendering) + { + if (renderInstances) *renderInstances = mRenderInstances; + + return mHairRenderMode; + } + + return getBaseRenderMode(renderInstances); +} + + +shaveConstant::ShadowSource shaveRenderer::getShadowSource() const +{ + // + // If we're in the middle of a render, return the cached value. + // + if (mRendering) return mHairShadowSource; + + return getBaseShadowSource(); +} + + +bool shaveRenderer::needVertexColours() const +{ + return false; +} + + +void shaveRenderer::renderEnd() +{ + // + // Clear the various warning flags so that the warnings will be issued + // on the next render. + // + mWarningGivenInvalidHairMode = false; + mWarningGivenInvalidInstanceMode = false; + + mRendering = false; +} + + +void shaveRenderer::renderInterrupted() +{} + + +void shaveRenderer::renderStart() +{ + // + // Cache the render and shadow modes. + // + MObjectArray shaveHairShapes; + + mHairRenderMode = getRenderMode(&mRenderInstances); + mHairShadowSource = getShadowSource(); + + // + // We have something of a problem here. We don't want to create, say, + // a buffer render shader if there are only instances in the scene. + // It's easy enough to check the list of shaveHairShapes in the scene, but + // it's possible that there might be shaveHairShapes which are present but + // not being rendered, because their 'active' flag is false, their + // instance geom is invisible, etc. We can't take any of that into + // account because the affect may be animated: thus an instance which + // is invisible now might be visible on the next frame, so we have to + // make sure that the shader for it is already in place. + // + // So we're stuck with just a static check of the nodes currently + // available. If subsequently during a frame we find that a particular + // type of shader is not needed we'll have to give it a flag telling it + // to act as a no-op for that frame. + // + shaveUtil::getShaveNodes(shaveHairShapes); + shaveUtil::classifyShaveNodes(shaveHairShapes, mHaveHair, mHaveInstances); + + if (!mHaveHair) + { + mHairRenderMode = shaveConstant::kNoRender; + mHairShadowSource = shaveConstant::kNoShadows; + } + + if (!mHaveInstances) mRenderInstances = false; + + // + // This will trigger getRenderMode() and getShadowSource() to use the + // cached values. + // + mRendering = true; +} + + + diff --git a/mayaPlug/shaveRenderer.h b/mayaPlug/shaveRenderer.h new file mode 100644 index 0000000..b209e65 --- /dev/null +++ b/mayaPlug/shaveRenderer.h @@ -0,0 +1,99 @@ +#ifndef shaveRenderer_h +#define shaveRenderer_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MObjectArray.h> + +#include "shaveConstant.h" +#include "shaveGlobals.h" + +class shaveHairShape; + + +class shaveRenderer +{ +public: + shaveRenderer(); + virtual ~shaveRenderer(); + + virtual void doShutter( + const MTime& curTime, shaveConstant::ShutterState shutter + ); + + virtual void frameEnd(const shaveGlobals::Globals& g); + virtual void frameStart(const shaveGlobals::Globals& g); + + virtual shaveConstant::RenderMode + getFilteredRenderMode( + const MObjectArray& shaveNodes, + bool* renderInstances = NULL + ) const; + + virtual shaveConstant::RenderMode + getBaseRenderMode(bool* renderInstances = NULL) const; + + virtual shaveConstant::ShadowSource + getBaseShadowSource() const; + + virtual void getGeomVisibility( + bool& hairPrimaryVisibility, + bool& hairSecondaryVisibility, + bool& instancePrimaryVisibility, + bool& instanceSecondaryVisibility + ); + + virtual float getPixelAspect() const; + + virtual void getRenderableShaveNodes(MObjectArray& nodes); + + virtual void getRenderableShaveNodesByRenderMode( + MObjectArray* geomNodes, MObjectArray* nonGeomNodes + ); + + virtual shaveConstant::RenderMode + getRenderMode(bool* renderInstances = NULL) const; + + virtual shaveConstant::ShadowSource + getShadowSource() const; + + // + // This method must return true if the shaveHairShape is to be rendered + // as normal Maya geometry and therefore does not need to be included + // in a Shave buffer render and should not be included in exported DRA + // archives. + // + virtual bool isGeomNode(const shaveHairShape* nodePtr) const = 0; + virtual bool needVertexColours() const; + + virtual void render( + float frame, + shaveConstant::ShutterState shutter, + const MDagPath& camera + ) = 0; + + virtual void renderEnd(); + virtual void renderInterrupted(); + virtual void renderStart(); + virtual void timeChange(const MTime& newTime) = 0; + +protected: + MObjectArray mGeometryNodes; + MObjectArray mNonGeometryNodes; + bool mRendering; + mutable bool mWarningGivenInvalidHairMode; + mutable bool mWarningGivenInvalidInstanceMode; + + // + // Note that these are only valid between startRender and endRender. + // + shaveConstant::RenderMode mHairRenderMode; + bool mHaveHair; + bool mHaveInstances; + bool mRenderInstances; + shaveConstant::ShadowSource mHairShadowSource; +}; + +#endif diff --git a/mayaPlug/shaveRenderman.cpp b/mayaPlug/shaveRenderman.cpp new file mode 100644 index 0000000..df65952 --- /dev/null +++ b/mayaPlug/shaveRenderman.cpp @@ -0,0 +1,98 @@ +// THIS FILE IS OBSOLETE. SEE shaveWriteRib.cpp INSTEAD. + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include <stdio.h> + +#include <maya/MFnDependencyNode.h> +#include <maya/MObjectArray.h> +#include <maya/MPlug.h> +#include <maya/MString.h> + +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveRenderer.h" +#include "shaveRenderman.h" +#include "shaveTextureStore.h" + + +MStatus shaveRenderman::dumpAll(MString filename) +{ + shaveRenderer* renderer = shaveRender::getRenderer(); + MObjectArray shaveHairShapes; + + renderer->getRenderableShaveNodes(shaveHairShapes); + + initTexInfoLookup(shaveHairShapes, ""); + + FILE* fp = stdout; + + if (filename != "") + { + fp = fopen(filename.asChar(), "w"); + + if (!fp) + { + MGlobal::displayError( + MString("shaveWriteRib: Could not open file '") + + filename + "' for RIB output." + ); + + return MS::kFailure; + } + } + + // + // Output the RIB file preamble. + // + if (globalRibStuff != "") + fprintf(fp, "%s\n", globalRibStuff.asChar()); + + int prmanCurvesOK = (shaveRibPrimType > 1) ? 0 : 1; + int prmanCurveType = (shaveRibPrimType > 1)?0:shaveRibPrimType; + unsigned int i; + unsigned int numShaveNodes = shaveHairShapes.length(); + + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode(); + + // + // Get the shaveHairShape's hair node and make sure it's loaded + // into the shave engine. + // + SHAVENODE* hairNode = theShaveNode->getHairNode(); + + theShaveNode->makeCurrent(); + + // + // Output the RIB preamble for this node. + // + MPlug plug(shaveHairShapes[i], shaveHairShape::ribInsert); + MString nodePreamble; + + plug.getValue(nodePreamble); + + fprintf(fp, "%s\n", nodePreamble.asChar()); + + // + // Output the node's RIB info. + // + SHAVErib_dump_node( + hairNode, + fp, + prmanCurvesOK, + hairNode->shavep.segs[theShaveNode->getHairGroup()], + prmanCurveType + ); + } + + if (filename != "") fclose(fp); + + return MS::kSuccess; +} diff --git a/mayaPlug/shaveRenderman.h b/mayaPlug/shaveRenderman.h new file mode 100644 index 0000000..afc9981 --- /dev/null +++ b/mayaPlug/shaveRenderman.h @@ -0,0 +1,16 @@ +#ifndef shaveRenderman_h +#define shaveRenderman_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MString.h> + +class shaveRenderman +{ +public: + static MStatus dumpAll(MString filename); +}; + +#endif diff --git a/mayaPlug/shaveSDKCALLBACKS.cpp b/mayaPlug/shaveSDKCALLBACKS.cpp new file mode 100644 index 0000000..070ed80 --- /dev/null +++ b/mayaPlug/shaveSDKCALLBACKS.cpp @@ -0,0 +1,601 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagPath.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPoint.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFloatVector.h> +#include <maya/MFloatVectorArray.h> +#include <maya/MFnField.h> +#include <maya/MFnNonAmbientLight.h> +#include <maya/MGlobal.h> +#include <maya/MItSelectionList.h> +#include <maya/MMatrix.h> +#include <maya/MPoint.h> +#include <maya/MPointArray.h> +#include <maya/MRenderUtil.h> +#include <maya/MVector.h> +#include <maya/MVectorArray.h> + +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveRender.h" +#include "shaveRenderCallback.h" +#include "shaveSDKTYPES.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" + + +extern "C" +{ +#include "shaveSDKCALLBACKS.h" + +VERT zeroVert = { 0.0f, 0.0f, 0.0f }; + +// Callback functions (shave calls you) + +#ifndef _DEBUG +VERT SHAVEapply_GI(VERT v,CURVEINFO *cc) +{ + return (zeroVert); +} +#endif + +void SHAVEapply_inst_color( + WFTYPE *instance_geom, int hairID, int slgID, unsigned long shaveINSTID +) +{} + + + +VERT SHAVEdisplace_root(VERT *root,CURVEINFO *ci,int nodeID) +{ + NODETEXINFO* nodeTexInfo = getTexInfoLookup((unsigned)nodeID); + VERT ret = zeroVert; + + if ((nodeTexInfo != NULL) + && (nodeTexInfo->maxPasses > 0) + && (ci->hairID < nodeTexInfo->count) + && (nodeTexInfo->displacement)) + { + // + // See SHAVEapply_texture() for an explanation of why we're doing + // this modulus. + // + int pass = ci->depthpass % nodeTexInfo->maxPasses; + + ret = nodeTexInfo->displacement[pass][ci->hairID]; + } + + return ret; +} + + +static int shadowTicksLeft = 0; +static bool progressBarActive = false; + +// +// This function gets called every 100 hairs when rendering so that you can +// make a progress bar. Estimated total is the number of times this +// function should get called during a render. If you want to cancel the +// render you should return 1 for the rest of the calls to this function +// until the render releases control. +// +int SHAVEprogress(int actual, int estimated_total) +{ + // + // If actual == -1, or if the render has been cancelled, then kill off + // the progress bar. + // + if (progressBarActive && ((actual == -1) || shaveRenderCancelled)) + { + MGlobal::executeCommand("shave_closeProgressBar"); + progressBarActive = false; + } + + if (shadowRender) + { + if ((actual > 0) && (--shadowTicksLeft > 0)) return 0; + + shadowTicksLeft = 400; + } + + if (shaveRenderCancelled) return 1; + + // + // We don't need a progress bar in batch mode. + // + if (MGlobal::mayaState() != MGlobal::kInteractive) return 0; + + int killit = 0; + + if (shaveEnableProgressBar) + { + if (actual > estimated_total) + actual = estimated_total; + + if (actual == 0) + { + if (!progressBarActive) + { + MString cmd = MString("shave_progressBarInit ") + + (double)estimated_total + + " \"" + (shadowRender?"Shadow":"Hair") + "\""; + + MGlobal::executeCommand(cmd); + progressBarActive = true; + } + } + else + { + MGlobal::executeCommand("shave_progressBarQuery()", killit); + + if (!killit) + { + MGlobal::executeCommand( + MString("shave_progressBarStep ") + (double)actual + ); + } + else + { + shaveRenderCancelled = true; + MGlobal::executeCommand("shave_closeProgressBar"); + progressBarActive = false; + } + } + + if (shaveRender::getFrameGlobals().verbose) + { + float pct = ((float)actual/(float)estimated_total)*100; + fprintf(stderr, "Done %f percent.\n",pct); + } + } + + return killit; +} + + +void SHAVEcoord_convertTOSHAVE(VERT *in) +{} + + +void SHAVEcoord_convertFROMSHAVE(VERT *in) +{} +#ifdef _DEBUG +VERT +SHAVEapply_GI( VERT vv, CURVEINFO * ci ) +{ + VERT t; + + t.x = 0.0f; + t.y = 0.0f; + t.z = 0.0f; + return ( t ); +} +extern float +SHAVEapply_falloff( int lightNUM, VERT pos, float cone ) +{ + return ( cone ); +} +#endif + +float SHAVEapply_texture( + CURVEINFO* ci, + VERT rest_root_worldpos, + unsigned long shaveINSTID, + int parm, + float inbound_value +) +{ + NODETEXINFO* nodeTexInfo = getTexInfoLookup(shaveINSTID); + if ((nodeTexInfo != NULL) + && (nodeTexInfo->maxPasses > 0) + && (ci->hairID < nodeTexInfo->count)) + { + // + // If there are multiple shaveHairShapes in the scene, then Shave + // will use the same number of passes for all of them. For + // example, if one shaveHairShape specifies 2 passes and another + // specifies 5, then both shaveHairShapes will be called for 5 + // passes. + // + // So it is possible that we are being called with a pass number + // which is larger than the number of passes specified for this + // particular shaveHairShape. In that case, we use a modulus to + // wrap around to the values used in earlier passes. + // + // No, this isn't a horrible kludge, this is really what Shave is + // expecting. :-) + // + int passToUse = ci->depthpass % nodeTexInfo->maxPasses; +passToUse=0; + // + // Copy the UV values for this hair into the CURVEINFO so that + // later callbacks have access to them. + // + ci->u = nodeTexInfo->u[passToUse][ci->hairID]; + ci->v = nodeTexInfo->v[passToUse][ci->hairID]; + + if (((int)ci->hairID < nodeTexInfo->maxIndex[parm]) + && nodeTexInfo->textured[parm]) + { + float texVal = (float)(nodeTexInfo->textureLookup[passToUse][parm][ci->hairID]); + + //printf("tex %i hair %i value %f\n",parm,ci->hairID,texVal );fflush(stdout); + + return (texVal); // * inbound_value); + } + } + + return inbound_value; +} + + +void MAYAexternal_forces(VERT *lastpos, VERT *velocity, int y) +{ + const MDagPathArray& fieldList = shaveUtil::getFields(); + + unsigned int fieldCount = fieldList.length(); + + if (fieldCount) + { + MStatus stat; + MDagPath dagPath; + MFnField field; + MPoint p(lastpos->x, lastpos->y, lastpos->z); + MVector v(velocity->x, velocity->y, velocity->z); + MPointArray pointArray; + pointArray.append(p); + MVectorArray forces; + MVectorArray velocityArray; + velocityArray.append(v); + MDoubleArray massArray; + unsigned int i; + + for(i = 0; i < fieldCount; i++) + { + // shaveUtil::getFields() only updates the field list when a + // new one is added, not when one is deleted, so we have to + // check the field to make sure that it still exists. + if (fieldList[i].isValid()) + { + field.setObject(fieldList[i]); + stat = field.getForceAtPoint( + pointArray, velocityArray, massArray, forces + ); + } + } + + velocity->x+=((float)forces[0].x)*((float)0.01); + velocity->y+=((float)forces[0].y)*((float)0.01); + velocity->z+=((float)forces[0].z)*((float)0.01); + } +} + + +// These methods must be used when running multiple threads. +// +// Call MAYAcache_forces(0) from Maya's main thread (or its proxy) to create +// the cache for *all* of the current node's guides. +// +// From within individual threads you can then call MAYAapply_cached_forces(..) +// for each guide vertex you want forces applied to. +// +// When done, call MAYAcache_forces(1) from Maya's main thread (or its proxy) +// to free up the cache. +// +MVectorArray forcesCache; + +void MAYAcache_forces(int clearCache) +{ + forcesCache.clear(); + + if (clearCache == 0) + { + const MDagPathArray& fieldList = shaveUtil::getFields(); + + unsigned int fieldCount = fieldList.length(); + + if (fieldCount > 0) + { + shaveHairShape* hairShape = shaveUtil::getLoadedHairShape(); + + // TODO: + // + // We want the new guide positions here, but they're not yet + // available. The call to this function is part of the process + // of calculating those new positions. + // + // For now we'll work around the problem by calculating the + // forces using the old positions. If we call getGuides() at + // this point it will still return the old positions because + // they haven't been updated yet, but it will also mark the + // guide cache as clean, which messes things up later down the + // line. So instead we call getDirtyGuides() which will return + // the current guide cache without attempting to update it. + // + const shaveHairShape::GuidesSnapshot& curGuides = hairShape->getDirtyGuides(); + const shaveHairShape::GuidesSnapshot& prevGuides = hairShape->getPrevGuides(); + MVectorArray points; + MVectorArray velocities; + MDoubleArray masses; + + points.setLength( + static_cast<unsigned int>(curGuides.guides.size() * SHAVE_VERTS_PER_GUIDE) + ); + + velocities.setLength( + static_cast<unsigned int>(curGuides.guides.size() * SHAVE_VERTS_PER_GUIDE) + ); + + // If the previous guide snapshot has the same time as the + // current one, or a different number of guides, then we won't + // be able to use it to calculate velocities and will have to + // assume zero velocity. + // + float deltaT = curGuides.frame - prevGuides.frame; + bool haveVelocity = ( + (fabs(deltaT) > 0.000001f) + && (prevGuides.guides.size() == curGuides.guides.size()) + ); + + unsigned int vertIdx = 0; + + for (size_t g = 0; g < curGuides.guides.size(); ++g) + { + const shaveHairShape::Guide& guide = curGuides.guides[g]; + + for (unsigned int v = 0; v < SHAVE_VERTS_PER_GUIDE; ++v) + { + points[vertIdx] = static_cast<MVector>(guide.verts[v]); + + if (haveVelocity) + { + velocities[vertIdx] = static_cast<MVector>( + (guide.verts[v] - prevGuides.guides[g].verts[v]) / deltaT + ); + } + else + { + velocities[vertIdx] = MVector::zero; + } + + ++vertIdx; + } + } + + for (unsigned int f = 0; f < fieldCount; ++f) + { + // shaveUtil::getFields() only updates the field list when a + // new one is added, not when one is deleted, so we have to + // check the field to make sure that it still exists. + // + if (fieldList[f].isValid()) + { + MFnField fieldFn(fieldList[f]); + + fieldFn.getForceAtPoint( + points, velocities, masses, forcesCache + ); + } + } + } + } +} + +// Forces are added to whatever value is already in velocity. +// +void MAYAapply_cached_forces(int guideNum, int vertNum, VERT* velocity) +{ + if ((guideNum >= 0) && (guideNum < static_cast<int>(forcesCache.length())) + && (vertNum >= 0) && (vertNum < SHAVE_VERTS_PER_GUIDE)) + { + MVector& delta = forcesCache[guideNum * SHAVE_VERTS_PER_GUIDE + vertNum]; + velocity->x += static_cast<float>(delta.x * 0.01); + velocity->y += static_cast<float>(delta.y * 0.01); + velocity->z += static_cast<float>(delta.z * 0.01); + } +} + +VERT SHAVEapply_illumination(int LIGHTID, VERT wpos,VERT vector, VERT color) +{ + return color; +} + +#ifndef _DEBUG +float SHAVEapply_falloff(int lightID, VERT p, float intensity) +{ + MStatus st; + + // + // When doing native illumination, the light samples taken in + // SHAVEapply_illuminationWF() will already have taken decay into + // account, so we don't want to further apply it here. + // + if (!shaveRender::getFrameGlobals().useNativeIllumination + && (intensity > 0.0f)) + { + int lightIndex = shaveUtil::getLightIndexFromID(lightID); + + if (lightIndex >= 0) + { + MDagPath lightDag = shaveUtil::globalLightList[lightIndex].path; + + // + // Only non-ambient lights have decay. + // + MFnNonAmbientLight lightFn(lightDag, &st); + + if (st) + { + short decay = lightFn.decayRate(); + + if (decay != 0) + { + // + // How far is the sample point from the light? + // + MTransformationMatrix lightWorldMatrix; + lightWorldMatrix = lightDag.inclusiveMatrix(); + + MVector lightPos = lightWorldMatrix.translation(MSpace::kWorld); + MVector samplePos((double)p.x, (double)p.y, (double)p.z); + float dist = (float)(samplePos - lightPos).length(); + + // + // Maya's lights don't decay within the first unit of + // distance. + // + if (dist > 1.0) + { + switch (decay) + { + case 1: // Linear + intensity = intensity / dist; + break; + + case 2: // Quadratic + intensity = intensity / (dist * dist); + break; + + case 3: // Cubic + intensity = intensity / (dist * dist * dist); + break; + + default: + break; + } + } + } + } + } + } + + return intensity; +} +#endif + +void SHAVEapply_illuminationWF(int LIGHTID, WFTYPE* samples) +{ + // ranges are 0.0 - 1.0 (return isn't clipped until after shading) + // modify or replace 'samples->color' - it contains light info for + // current test before shave shadows are applied + // samples->v is the position + // samples->totalverts is the total number of points + + if (shaveRender::getFrameGlobals().useNativeIllumination + && (samples != NULL) + && (samples->totalverts > 0)) + { + int lightIndex = shaveUtil::getLightIndexFromID(LIGHTID); + + if (lightIndex >= 0) + { + MStatus status; + MTransformationMatrix lightWorldMatrix; + MDagPath lightDag = shaveUtil::globalLightList[lightIndex].path; + + lightWorldMatrix = lightDag.inclusiveMatrix(); + MVector lightTranslation = lightWorldMatrix.translation(MSpace::kWorld ); + + MFloatVectorArray normals; + MFloatPointArray pointArray; + unsigned int i; + + for (i = 0; i < (unsigned int)samples->totalverts; i++) + { + pointArray.append( + samples->v[i].x, samples->v[i].y, samples->v[i].z + ); + + MFloatVector sampleAsVec( + samples->v[i].x, + samples->v[i].y, + samples->v[i].z + ); + + MFloatVector vec(lightTranslation - sampleAsVec); + vec.normalize(); + normals.append(vec); + } + + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + MFloatMatrix camMatrix; + MString attrName = lightDag.fullPathName() + ".lightIntensity"; + status = MRenderUtil::sampleShadingNetwork( + attrName, //attribute to sample + (int)pointArray.length(), //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + NULL, //u coords + NULL, //v coords + &normals, //normals + NULL, //ref points + NULL, //u tan + NULL, //v tan + NULL, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); + + if (status) + { + for (i = 0; i < vertColorArray.length(); i++) + { + samples->color[i].x = vertColorArray[i].x; + samples->color[i].y = vertColorArray[i].y; + samples->color[i].z = vertColorArray[i].z; + } + } + } + } +} + + +// this is a callback for applying atmospherics/depth cueing +VERT SHAVEapply_atmosphere(VERT wpos,VERT inbound_color ) +{ + // range = 0.0 - 1.0 (shave will clip out of bound returns) + // do your tint here based on wpos + // here's how: + // fogval=your_compute_func_for_fog(wpos); + // if (fog_val>1.0) fog_val=1.0; + // if (fog_val<0.0) fog_val=0.0; + // inbound_color.x=inbound_color.x*(1.0-fog_val)+fog_color.x*fog_val; + // inbound_color.y=inbound_color.y*(1.0-fog_val)+fog_color.y*fog_val; + // inbound_color.z=inbound_color.z*(1.0-fog_val)+fog_color.z*fog_val; + return inbound_color; +} + + +// +// This function callback gives you the opportunity to apply external +// vertpaint to any/every channel in shave. Normally the inbound_value +// will be 1.0 (unless you've painted a map inside shave) the return, +// should contain a value who's range is (float) 0-1. +// +float SHAVEapply_VMAP(long SHAVEINSTID,int VERTID,int chan, float inbound_value) +{ + return applyVertTexValue(SHAVEINSTID, VERTID, chan, inbound_value); +} + + +// This is called whenever Shave finishes rendering a tile into the pixel +// buffer. +void SHAVEdraw_tile_callback(VERT* min, VERT* max) +{ + shaveRenderCallback::tileRendered( + (unsigned int)min->x, + (unsigned int)max->x, + (unsigned int)min->y, + (unsigned int)max->y + ); +} + +} + diff --git a/mayaPlug/shaveShadowFilter.cpp b/mayaPlug/shaveShadowFilter.cpp new file mode 100644 index 0000000..80688fc --- /dev/null +++ b/mayaPlug/shaveShadowFilter.cpp @@ -0,0 +1,563 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArrayDataBuilder.h> +#include <maya/MArrayDataHandle.h> +#include <maya/MDataBlock.h> +#include <maya/MDataHandle.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatVector.h> +#include <maya/MFnCompoundAttribute.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnMatrixAttribute.h> +#include <maya/MFnMessageAttribute.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnNumericData.h> +#include <maya/MGlobal.h> +#include <maya/MPlug.h> +#include <maya/MRenderUtil.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> + +#include "shaveGlobals.h" +#include "shaveIO.h" +#include "shaveSDK.h" +#include "shaveShadowFilter.h" +#include "shaveUtil.h" + +MTypeId shaveShadowFilter::id(0x001029BC); + +const MString shaveShadowFilter::nodeTypeName("shaveShadowFilter"); + +MObject shaveShadowFilter::aLightDataArray; +MObject shaveShadowFilter::aLightDirection; +MObject shaveShadowFilter::aLightIntensity; +MObject shaveShadowFilter::aLightAmbient; +MObject shaveShadowFilter::aLightDiffuse; +MObject shaveShadowFilter::aLightSpecular; +MObject shaveShadowFilter::aLightShadowFraction; +MObject shaveShadowFilter::aPreShadowIntensity; +MObject shaveShadowFilter::aLightBlindData; + +MObject shaveShadowFilter::aLightDataArrayOut; +MObject shaveShadowFilter::aLightDirectionOut; +MObject shaveShadowFilter::aLightIntensityOut; +MObject shaveShadowFilter::aLightAmbientOut; +MObject shaveShadowFilter::aLightDiffuseOut; +MObject shaveShadowFilter::aLightSpecularOut; +MObject shaveShadowFilter::aLightShadowFractionOut; +MObject shaveShadowFilter::aPreShadowIntensityOut; +MObject shaveShadowFilter::aLightBlindDataOut; + +MObject shaveShadowFilter::aMatrixEyeToWorld; +MObject shaveShadowFilter::aNormalCamera; +MObject shaveShadowFilter::aObjectId; + +MObject shaveShadowFilter::aPointWorld; +MObject shaveShadowFilter::aRayDepth; +MObject shaveShadowFilter::aRayDirection; +MObject shaveShadowFilter::aRayOrigin; + + +shaveShadowFilter::shaveShadowFilter() +{ +} + + +shaveShadowFilter::~shaveShadowFilter() +{ +} + + +void* shaveShadowFilter::creator() +{ + return new shaveShadowFilter(); +} + + + +MStatus shaveShadowFilter::initialize() +{ + MStatus st; + MFnCompoundAttribute cAttr; + MFnMatrixAttribute matAttr; + MFnMessageAttribute msgAttr; + MFnNumericAttribute nAttr; + + // + // Renderer-Supplied Input Attributes + // + aLightDirection = nAttr.createPoint("lightDirection", "ld"); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + + + aLightIntensity = nAttr.createColor("lightIntensity", "li"); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + + + aLightAmbient = nAttr.create( + "lightAmbient", + "la", + MFnNumericData::kBoolean, + true + ); + + aLightDiffuse = nAttr.create( + "lightDiffuse", + "ldf", + MFnNumericData::kBoolean, + true + ); + + aLightSpecular = nAttr.create( + "lightSpecular", + "ls", + MFnNumericData::kBoolean, + false + ); + + aLightShadowFraction = nAttr.create( + "lightShadowFraction", + "lsf", + MFnNumericData::kFloat, + 0.5 + ); + + aPreShadowIntensity = nAttr.create( + "preShadowIntensity", + "psi", + MFnNumericData::kFloat, + 1.0 + ); + + aLightBlindData = nAttr.create( + "lightBlindData", "lbd", MFnNumericData::kLong, 0 + ); + + aLightDataArray = cAttr.create("lightDataArray", "ltd"); + cAttr.addChild(aLightDirection); + cAttr.addChild(aLightIntensity); + cAttr.addChild(aLightAmbient); + cAttr.addChild(aLightDiffuse); + cAttr.addChild(aLightSpecular); + cAttr.addChild(aLightShadowFraction); + cAttr.addChild(aPreShadowIntensity); + cAttr.addChild(aLightBlindData); + cAttr.setArray(true); + cAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aLightDataArray); + + + aMatrixEyeToWorld = matAttr.create( + "matrixEyeToWorld", + "etw", + MFnMatrixAttribute::kFloat + ); + matAttr.setHidden(true); + matAttr.setStorable(false); + addAttribute(aMatrixEyeToWorld); + + + aNormalCamera = nAttr.createPoint("normalCamera", "n"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aNormalCamera); + + + aObjectId = nAttr.createAddr("objectId", "oi"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aObjectId); + + + aPointWorld = nAttr.createPoint("pointWorld", "pw"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aPointWorld); + + + aRayDepth = nAttr.create("rayDepth", "rd", MFnNumericData::kInt); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRayDepth); + + + aRayDirection = nAttr.createPoint("rayDirection", "rad"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRayDirection); + + + aRayOrigin = nAttr.createPoint("rayOrigin", "ro"); + nAttr.setHidden(true); + nAttr.setStorable(false); + addAttribute(aRayOrigin); + + + // + // Custom Input Attributes + // + + + // + // Standard Renderer Output Attributes + // + + // + // Custom Output Attributes + // + aLightDirectionOut = nAttr.createPoint("lightDirectionOut", "ldo", &st); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightDirectionOut attr: " + + st.errorString() + ); + return st; + } + + + aLightIntensityOut = nAttr.createColor("lightIntensityOut", "lio", &st); + nAttr.setDefault(1.0f, 1.0f, 1.0f); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightIntensityOut attr: " + + st.errorString() + ); + return st; + } + + + aLightAmbientOut = nAttr.create( + "lightAmbientOut", + "lao", + MFnNumericData::kBoolean, + true, + &st + ); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightAmbientOut attr: " + + st.errorString() + ); + return st; + } + + aLightDiffuseOut = nAttr.create( + "lightDiffuseOut", + "ldfo", + MFnNumericData::kBoolean, + true, + &st + ); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightDiffuseOut attr: " + + st.errorString() + ); + return st; + } + + aLightSpecularOut = nAttr.create( + "lightSpecularOut", + "lso", + MFnNumericData::kBoolean, + false, + &st + ); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightSpecularOut attr: " + + st.errorString() + ); + return st; + } + + aLightShadowFractionOut = nAttr.create( + "lightShadowFractionOut", + "lsfo", + MFnNumericData::kFloat, + 0.5, + &st + ); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightShadowFractionOut attr: " + + st.errorString() + ); + return st; + } + + aPreShadowIntensityOut = nAttr.create( + "preShadowIntensityOut", + "psio", + MFnNumericData::kFloat, + 1.0, + &st + ); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create preShadowIntensityOut attr: " + + st.errorString() + ); + return st; + } + + aLightBlindDataOut = nAttr.create( + "lightBlindDataOut", + "lbdo", + MFnNumericData::kLong, + 0, + &st + ); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightBlindDataOut attr: " + + st.errorString() + ); + return st; + } + + aLightDataArrayOut = cAttr.create("lightDataArrayOut", "ltdo", &st); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot create lightDataArrayOut attr: " + + st.errorString() + ); + return st; + } + cAttr.addChild(aLightDirectionOut); + cAttr.addChild(aLightIntensityOut); + cAttr.addChild(aLightAmbientOut); + cAttr.addChild(aLightDiffuseOut); + cAttr.addChild(aLightSpecularOut); + cAttr.addChild(aLightShadowFractionOut); + cAttr.addChild(aPreShadowIntensityOut); + cAttr.addChild(aLightBlindDataOut); + cAttr.setArray(true); + cAttr.setUsesArrayDataBuilder(true); + cAttr.setHidden(true); + cAttr.setStorable(false); + st = addAttribute(aLightDataArrayOut); + if (!st) + { + MGlobal::displayError( + nodeTypeName + ": cannot add lightDataArrayOut attr: " + + st.errorString() + ); + return st; + } + + + // + // Set up attribute dependencies. + // + attributeAffects(aLightDataArray, aLightDataArrayOut); + attributeAffects(aMatrixEyeToWorld, aLightDataArrayOut); + attributeAffects(aNormalCamera, aLightDataArrayOut); + attributeAffects(aObjectId, aLightDataArrayOut); + attributeAffects(aPointWorld, aLightDataArrayOut); + attributeAffects(aRayDepth, aLightDataArrayOut); + attributeAffects(aRayDirection, aLightDataArrayOut); + attributeAffects(aRayOrigin, aLightDataArrayOut); + + return MS::kSuccess; +} + + +void shaveShadowFilter::postConstructor( ) +{ +// %%% Can we safely do this, given the use of globals by the Shave +// engine? +// +// setMPSafe(true); +} + + +MStatus shaveShadowFilter::compute(const MPlug& plug, MDataBlock& datablock) +{ + if ((plug == aLightDataArrayOut) + || (plug.parent() == aLightDataArrayOut)) + { + const MFloatVector& point = datablock + .inputValue(aPointWorld) + .asFloatVector(); + + const MFloatVector& normal = datablock + .inputValue(aNormalCamera) + .asFloatVector(); + + MArrayDataHandle lightArrayIn = + datablock.inputArrayValue(aLightDataArray); + + unsigned int numAffectingLights = 0; + unsigned int numLights = lightArrayIn.elementCount(); + + MArrayDataBuilder lightArrayOutBuilder( + &datablock, aLightDataArrayOut, numLights + ); + + int blindData; + unsigned int i; + MFloatVector intensity; + MFloatVector lightDir; + float lightShadow = 0.0f; + float netIllum = 0.0f; + float netShadow = 0.0f; + float preShadowIntensity; + float shadowFraction; + float totalPreShadowIntensity = 0.0f; + + VERT wpoint; + wpoint.x = point.x; + wpoint.y = point.y; + wpoint.z = point.z; + + for (i = 0; i < numLights; i++) + { + MDataHandle lightIn = lightArrayIn.inputValue(); + MDataHandle lightOut = lightArrayOutBuilder.addElement( + lightArrayIn.elementIndex() + ); + + // + // Get the values for this light. + // + blindData = lightIn.child(aLightBlindData).asAddr(); + intensity = lightIn.child(aLightIntensity).asFloatVector(); + lightDir = lightIn.child(aLightDirection).asFloatVector(); + preShadowIntensity = lightIn.child(aPreShadowIntensity).asFloat(); + shadowFraction = lightIn.child(aLightShadowFraction).asFloat(); + + // + // Checking for hair shadows is expensive, so let's avoid it if + // at all possible. + // + if ((preShadowIntensity > 0.0f) + && (shadowFraction < 1.0f) + && (shaveShadowDensityGlob > 0.0f) + && (intensity.x + intensity.y + intensity.z > 0.0f)) + { + // + // If the surface normal is facing away from the light then + // there won't be any hair shadows on it. + // + float hairIllum = 1.0f; + + if (lightDir * normal > 0.04f) + { + // + // We need to get this light's index in the + // globalLightList. + // + // If the light has a non-zero 'lightBlindData' + // attribute value, then that will be its node pointer, + // which is unique, so we can use it to look up the + // light's index. + // + int lightIndex = -1; + + if (blindData != 0) + lightIndex = shaveUtil::getLightIndex(blindData); + + // + // If we didn't find the light through its blind data, + // then try finding it based on its direction. + // + if (lightIndex < 0) + { + const MFloatMatrix& camToWorld = + datablock.inputValue(aMatrixEyeToWorld).asFloatMatrix(); + + lightDir *= camToWorld; + lightIndex = shaveUtil::getLightIndex(point, lightDir); + } + + if (lightIndex >= 0) + { + // + // How much of the light's illumination gets + // through the hair? + // + // Note that internally Shave creates multiple + // sub-lights for some types of lights. They're + // not supposed to overlap but sometimes they do, + // so we only want the brightest one. + // + int id; + float temp; + + hairIllum = 10000.0f; + + for (id = shaveUtil::globalLightList[lightIndex].minId; + id <= shaveUtil::globalLightList[lightIndex].maxId; + id++) + { + temp = SHAVEilluminate_point(wpoint, id); + if (temp < hairIllum) hairIllum = temp; + } + + hairIllum = hairIllum * shaveShadowDensityGlob + + (1.0f - shaveShadowDensityGlob); + + // + // Apply the hair's dimming effect to the light's + // intensity. + // + intensity *= hairIllum; + + // + // Add the dimming effect to the light's shadow + // fraction. + // + shadowFraction += 1.0f - hairIllum; + + if (shadowFraction > 1.0f) shadowFraction = 1.0f; + } + } + } + + // Fill in the output values. + lightOut.child(aLightDirectionOut).set(lightDir); + lightOut.child(aLightIntensityOut).set(intensity); + lightOut.child(aLightShadowFractionOut).set(shadowFraction); + lightOut.child(aLightBlindDataOut).set(blindData); + lightOut.child(aPreShadowIntensityOut).set(preShadowIntensity); + + lightOut.child(aLightAmbientOut).set( + lightIn.child(aLightAmbient).asBool() + ); + + lightOut.child(aLightDiffuseOut).set( + lightIn.child(aLightDiffuse).asBool() + ); + + lightOut.child(aLightSpecularOut).set( + lightIn.child(aLightSpecular).asBool() + ); + + if (!lightArrayIn.next()) break; + } + + datablock.outputArrayValue(aLightDataArrayOut).set( + lightArrayOutBuilder + ); + } + else + return MS::kUnknownParameter; + + return MS::kSuccess; +} + + diff --git a/mayaPlug/shaveShadowFilter.h b/mayaPlug/shaveShadowFilter.h new file mode 100644 index 0000000..0b5f53e --- /dev/null +++ b/mayaPlug/shaveShadowFilter.h @@ -0,0 +1,82 @@ +#ifndef shaveShadowFilter_h +#define shaveShadowFilter_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxNode.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> + + +class shaveShadowFilter : public MPxNode +{ + //****************************************************** + // + // Creation & Initialization Methods + // + //****************************************************** +public: + shaveShadowFilter(); + virtual ~shaveShadowFilter(); + static void * creator(); + static MStatus initialize(); + + + //****************************************************** + // + // Overloaded MPxNode Methods + // + //****************************************************** +public: + virtual MStatus compute(const MPlug&, MDataBlock&); + virtual void postConstructor(); + + + //****************************************************** + // + // Member Variables + // + //****************************************************** + public: + static MTypeId id; + static const MString nodeTypeName; + + + //****************************************************** + // + // Attributes + // + //****************************************************** +public: + static MObject aLightDataArray; + static MObject aLightDirection; + static MObject aLightIntensity; + static MObject aLightAmbient; + static MObject aLightDiffuse; + static MObject aLightSpecular; + static MObject aLightShadowFraction; + static MObject aPreShadowIntensity; + static MObject aLightBlindData; + + static MObject aLightDataArrayOut; + static MObject aLightDirectionOut; + static MObject aLightIntensityOut; + static MObject aLightAmbientOut; + static MObject aLightDiffuseOut; + static MObject aLightSpecularOut; + static MObject aLightShadowFractionOut; + static MObject aPreShadowIntensityOut; + static MObject aLightBlindDataOut; + + static MObject aMatrixEyeToWorld; + static MObject aNormalCamera; + static MObject aObjectId; + + static MObject aPointWorld; + static MObject aRayDepth; + static MObject aRayDirection; + static MObject aRayOrigin; +}; + +#endif diff --git a/mayaPlug/shaveShadowNode.cpp b/mayaPlug/shaveShadowNode.cpp new file mode 100644 index 0000000..2ee0334 --- /dev/null +++ b/mayaPlug/shaveShadowNode.cpp @@ -0,0 +1,309 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveGlobals.h" +#include "shaveShadowNode.h" +#include "shaveUtil.h" + +MTypeId shaveShadowNode::id(0x00106502); + + +void shaveShadowNode::postConstructor( ) +{ + setMPSafe(true); +} + + +const MString shaveShadowNode::nodeTypeName("shaveShadowNode"); + +MObject shaveShadowNode::IntensityValue; + +MObject shaveShadowNode::aPointWorld; +MObject shaveShadowNode::aPointWorldX; +MObject shaveShadowNode::aPointWorldY; +MObject shaveShadowNode::aPointWorldZ; +MObject shaveShadowNode::colorR; +MObject shaveShadowNode::colorG; +MObject shaveShadowNode::colorB; +MObject shaveShadowNode::shadColorR; +MObject shaveShadowNode::shadColorG; +MObject shaveShadowNode::shadColorB; +MObject shaveShadowNode::lightID; +MObject shaveShadowNode::aOutIntensity; +MObject shaveShadowNode::aOutColorR; +MObject shaveShadowNode::aOutColorG; +MObject shaveShadowNode::aOutColorB; +MObject shaveShadowNode::aOutColor; +MObject shaveShadowNode::color; +MObject shaveShadowNode::shadColor; + +// +// DESCRIPTION: +/////////////////////////////////////////////////////// +shaveShadowNode::shaveShadowNode() +{ +} + +// +// DESCRIPTION: +/////////////////////////////////////////////////////// +shaveShadowNode::~shaveShadowNode() +{ +} + +// +// DESCRIPTION: +/////////////////////////////////////////////////////// +void* shaveShadowNode::creator() +{ + return new shaveShadowNode(); +} + + + +MStatus shaveShadowNode::initialize() +{ + MFnNumericAttribute nAttr; + + + +// User defined input value + + IntensityValue = nAttr.create( "Intensity", "int", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(30.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + colorB = nAttr.create( "colorB", "cB", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + colorG = nAttr.create( "colorG", "cG", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + colorR = nAttr.create( "colorR", "cR", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + + shadColorB = nAttr.create( "shadowColorB", "scB", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + shadColorG = nAttr.create( "shadowColorG", "scG", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + shadColorR = nAttr.create( "shadowColorR", "scR", MFnNumericData::kFloat); + nAttr.setDefault(1.0f); + nAttr.setMin(0.0f); + nAttr.setMax(1.0f); + nAttr.setKeyable(true); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + + aOutColorB = nAttr.create( "outColorB", "ocB", MFnNumericData::kFloat); + nAttr.setKeyable(false); + nAttr.setStorable(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + aOutColorG = nAttr.create( "outColorG", "ocG", MFnNumericData::kFloat); + nAttr.setKeyable(false); + nAttr.setStorable(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + aOutColorR = nAttr.create( "outColorR", "ocR", MFnNumericData::kFloat); + nAttr.setKeyable(false); + nAttr.setStorable(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + aOutColor = nAttr.create( "outColor", "oc", aOutColorR, aOutColorG, aOutColorB); + nAttr.setKeyable(false); + nAttr.setStorable(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + shadColor = nAttr.create( "shadColor", "sc", shadColorR, shadColorG, shadColorB); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + + color = nAttr.create( "color", "col", colorR, colorG, colorB); + nAttr.setStorable(true); + nAttr.setUsedAsColor(true); + nAttr.setKeyable(true); + + + lightID = nAttr.create( "LightID", "lid", MFnNumericData::kShort); + nAttr.setDefault((short)0); + nAttr.setStorable(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + +// Point on surface in camera space, will be used to compute view vector + + aPointWorldX = nAttr.create( "pointWorldX", "px", MFnNumericData::kFloat); + nAttr.setStorable(false); + nAttr.setHidden(true); + + aPointWorldY = nAttr.create( "pointWorldY", "py", MFnNumericData::kFloat); + nAttr.setStorable(false); + nAttr.setHidden(true); + + aPointWorldZ = nAttr.create( "pointWorldZ", "pz", MFnNumericData::kFloat); + nAttr.setStorable(false); + nAttr.setHidden(true); + + aPointWorld = nAttr.create( "pointWorld","p", aPointWorldX, + aPointWorldY, aPointWorldZ); + nAttr.setStorable(false); + nAttr.setHidden(true); + nAttr.setReadable(true); + nAttr.setWritable(true); + +// Outputs +// +// Always set your output attributes to be read-only +// You should also mark any internal attributes that your node +// computes to be read-only also, this will prevent any connections. + + aOutIntensity = nAttr.create( "outIntensity", "oi", MFnNumericData::kFloat); + nAttr.setStorable(false); + nAttr.setHidden(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + addAttribute(aOutColor); + addAttribute(shadColor); + addAttribute(color); + + addAttribute(IntensityValue); + addAttribute(aPointWorld); + addAttribute(aOutIntensity); + addAttribute(lightID); + + attributeAffects (IntensityValue, aOutColor); + + attributeAffects (aPointWorldX, aOutColorB); + attributeAffects (aPointWorldY, aOutColorB); + attributeAffects (aPointWorldZ, aOutColorB); + attributeAffects (aPointWorld, aOutColorB); + attributeAffects (aPointWorldX, aOutColorR); + attributeAffects (aPointWorldY, aOutColorR); + attributeAffects (aPointWorldZ, aOutColorR); + attributeAffects (aPointWorld, aOutColorR); + attributeAffects (aPointWorldX, aOutColorG); + attributeAffects (aPointWorldY, aOutColorG); + attributeAffects (aPointWorldZ, aOutColorG); + attributeAffects (aPointWorld, aOutColorG); + attributeAffects (shadColorR, aOutColor); + attributeAffects (colorR, aOutColor); + + + return MS::kSuccess; +} + +// +// DESCRIPTION: +/////////////////////////////////////////////////////// +MStatus shaveShadowNode::compute( +const MPlug& plug, + MDataBlock& block ) +{ + bool k = false; + k |= (plug == aOutColor); + k |= (plug == aOutColorR); + k |= (plug == aOutColorG); + k |= (plug == aOutColorB); + if( !k ) return MS::kUnknownParameter; + + short lightIndex = block.inputValue ( lightID ).asShort(); + VERT wpoint; + wpoint.x = block.inputValue( aPointWorldX ).asFloat(); + wpoint.y = block.inputValue( aPointWorldY ).asFloat(); + wpoint.z = block.inputValue( aPointWorldZ ).asFloat(); + float cR = block.inputValue( colorR ).asFloat(); + float cG = block.inputValue( colorG ).asFloat(); + float cB = block.inputValue( colorB ).asFloat(); + float sCR = block.inputValue( shadColorR ).asFloat(); + float sCG = block.inputValue( shadColorG ).asFloat(); + float sCB = block.inputValue( shadColorB ).asFloat(); + float shadow; + + // + // We need to get the shadow density from the shaveGlobals node. + // However, it would be too expensive to call + // shaveGlobals::getGlobals() on every iteration of compute, so + // shaveRender::frameStart() does it for us, so we can just use the + // result global vars. + // + if (shaveShadowDensityGlob > 0.0) + { + float illum = 1255.0f; + int id; + int minId = shaveUtil::globalLightList[lightIndex].minId; + int maxId = shaveUtil::globalLightList[lightIndex].maxId; + float temp=10000.0; + + for (id = minId; id <= maxId; id++) + { + temp = SHAVEilluminate_point(wpoint, id); + if (temp < illum) illum = temp; // changed max to min + } + + shadow = (1.0f - illum) * shaveShadowDensityGlob; + } + else + shadow = shaveShadowDensityGlob; + + MDataHandle outColorRHandle = block.outputValue( aOutColorR ); + float& oCR = outColorRHandle.asFloat(); + MDataHandle outColorGHandle = block.outputValue( aOutColorG ); + float& oCG = outColorGHandle.asFloat(); + MDataHandle outColorBHandle = block.outputValue( aOutColorB ); + float& oCB = outColorBHandle.asFloat(); + oCR = cR + (sCR - cR) * shadow; + oCG = cG + (sCG - cG) * shadow; + oCB = cB + (sCB - cB) * shadow; + outColorRHandle.setClean(); + outColorGHandle.setClean(); + outColorBHandle.setClean(); + return MS::kSuccess; +} + + diff --git a/mayaPlug/shaveShadowNode.h b/mayaPlug/shaveShadowNode.h new file mode 100644 index 0000000..6b649ed --- /dev/null +++ b/mayaPlug/shaveShadowNode.h @@ -0,0 +1,63 @@ +#ifndef _SHADENODE +#define _SHADENODE + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <math.h> +#include <maya/MPxNode.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> +#include <maya/MDataBlock.h> +#include <maya/MDataHandle.h> +#include <maya/MArrayDataHandle.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnLightDataAttribute.h> +#include <maya/MFloatVector.h> +#include <maya/MGlobal.h> +//#include "glut.h" + +#include "shaveSDK.h" + +class shaveShadowNode : public MPxNode +{ +public: + shaveShadowNode(); + virtual ~shaveShadowNode(); + + virtual MStatus compute( const MPlug&, MDataBlock& ); + virtual void postConstructor(); + +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + static void * creator(); + static MStatus initialize(); + static MTypeId id; + static const MString nodeTypeName; + +protected: + static MObject IntensityValue; + static MObject aPointWorldX; + static MObject aPointWorldY; + static MObject aPointWorldZ; + static MObject aPointWorld; + static MObject colorR; + static MObject colorG; + static MObject colorB; + static MObject shadColorR; + static MObject shadColorG; + static MObject shadColorB; + static MObject lightID; + static MObject aOutIntensity; + static MObject aOutColorR; + static MObject aOutColorG; + static MObject aOutColorB; + static MObject aOutColor; + static MObject color; + static MObject shadColor; +}; + +#endif diff --git a/mayaPlug/shaveStyleCmd.cpp b/mayaPlug/shaveStyleCmd.cpp new file mode 100644 index 0000000..19f212a --- /dev/null +++ b/mayaPlug/shaveStyleCmd.cpp @@ -0,0 +1,441 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#include <QtCore/QEvent> +#include <QtCore/QElapsedTimer> +#include <QtGui/QMouseEvent> +#include <QtGui/QTabletEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDagPath.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnDoubleIndexedComponent.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MGlobal.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveSDK.h" +#include "shaveStyleCmd.h" +#include "shaveUtil.h" +#include "shaveCursorCtx.h" + +#include <vector> +#include <sstream> +#include <assert.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> + +static const char* flAttenuate = "-attenuate"; +static const char* fsAttenuate = "-at"; +static const char* flGrowSelection = "-growSelection"; +static const char* fsGrowSelection = "-gs"; +static const char* flHideSelection = "-hideSelection"; +static const char* fsHideSelection = "-hs"; +static const char* flInvertSelection = "-invertSelection"; +static const char* fsInvertSelection = "-is"; +static const char* flLock = "-lock"; +static const char* fsLock = "-l"; +static const char* flMergeSelection = "-mergeSelection"; +static const char* fsMergeSelection = "-ms"; +static const char* flPopSelected = "-popSelected"; +static const char* fsPopSelected = "-ps"; +static const char* flPopZeroSized = "-popZeroSized"; +static const char* fsPopZeroSized = "-pzs"; +static const char* flRecomb = "-recomb"; +static const char* fsRecomb = "-r"; +static const char* flReplaceRest = "-replaceRest"; +static const char* fsReplaceRest = "-rr"; +static const char* flRotateSelectionUp = "-rotateSelectionUp"; +static const char* fsRotateSelectionUp = "-rsu"; +static const char* flSplitSelection = "-splitSelection"; +static const char* fsSplitSelection = "-ss"; +static const char* flToggleCollision = "-tglCollision"; +static const char* fsToggleCollision = "-tc"; +static const char* flUndo = "-undo"; +static const char* fsUndo = "-u"; +#ifdef GLOBAL_FALLBACK +static const char* flRedrawOnIdle = "-redrawOnIdle"; +static const char* fsRedrawOnIdle = "-rdi"; +static const char* flFullRedrawOnIdle = "-fullRedrawOnIdle"; +static const char* fsFullRedrawOnIdle = "-fri"; +#endif + +const MString shaveStyleCmd::commandName("shaveStyle"); + + +shaveStyleCmd::shaveStyleCmd() {} +shaveStyleCmd::~shaveStyleCmd() {} + + +void* shaveStyleCmd::createCmd() +{ + return new shaveStyleCmd(); +} + + +MSyntax shaveStyleCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(false); + + syntax.addFlag(fsAttenuate, flAttenuate); + syntax.addFlag(fsGrowSelection, flGrowSelection); + syntax.addFlag(fsHideSelection, flHideSelection, MSyntax::kBoolean); + syntax.addFlag(fsInvertSelection, flInvertSelection); + syntax.addFlag(fsLock, flLock, MSyntax::kBoolean); + syntax.addFlag(fsMergeSelection, flMergeSelection); + syntax.addFlag(fsPopSelected, flPopSelected); + syntax.addFlag(fsPopZeroSized, flPopZeroSized); + syntax.addFlag(fsRecomb, flRecomb); + syntax.addFlag(fsReplaceRest, flReplaceRest); + syntax.addFlag(fsRotateSelectionUp, flRotateSelectionUp); + syntax.addFlag(fsSplitSelection, flSplitSelection); + syntax.addFlag(fsToggleCollision, flToggleCollision); + syntax.addFlag(fsUndo, flUndo); +#ifdef GLOBAL_FALLBACK + syntax.addFlag(fsRedrawOnIdle, flRedrawOnIdle); + syntax.addFlag(fsFullRedrawOnIdle, flFullRedrawOnIdle); +#endif + return syntax; +} + + +MStatus shaveStyleCmd::doIt(const MArgList& argList) +{ + MStatus st; + MArgDatabase args(syntax(), argList, &st); + + if (!st) return st; + + // + // Is there a hair shape selected? + // + unsigned i; + MFnDagNode nodeFn; + unsigned numHairShapes = 0; + MDagPath targetPath; + MSelectionList selection; + MDagPath tempPath; +#ifdef GLOBAL_FALLBACK + if (args.isFlagSet(fsRedrawOnIdle)) + { + redrawOnIdle(false); + return MS::kSuccess; + } + else if (args.isFlagSet(fsFullRedrawOnIdle)) + { + redrawOnIdle(true); + return MS::kSuccess; + } +#endif + MGlobal::getActiveSelectionList(selection); + + for (i = 0; i < selection.length(); i++) + { + if (selection.getDagPath(i, tempPath) && tempPath.isValid()) + { + tempPath.extendToShape(); + nodeFn.setObject(tempPath); + + if (nodeFn.typeId() == shaveHairShape::id) + { + if (numHairShapes++ == 0) + targetPath = tempPath; + else + break; + } + } + } + + if (numHairShapes == 0 +#ifdef GLOBAL_FALLBACK + && !args.isFlagSet(fsFullRedrawOnIdle) && !args.isFlagSet(fsRedrawOnIdle) +#endif + ) + { + //////// debug /////////////// + if(args.isFlagSet(fsAttenuate)) displayError("Shave Attenuate."); + if(args.isFlagSet(fsLock)) displayError("Shave Lock."); + if(args.isFlagSet(fsMergeSelection)) displayError("Shave Merge Selection."); + if(args.isFlagSet(fsPopSelected)) displayError("Shave Pop Selected."); + if(args.isFlagSet(fsPopZeroSized)) displayError("Shave Pop Zerosized."); + if(args.isFlagSet(fsRecomb)) displayError("Shave Recomb."); + if(args.isFlagSet(fsReplaceRest)) displayError("Shave Replace Rest."); + if(args.isFlagSet(fsSplitSelection))displayError("Shave Split Selection."); + if(args.isFlagSet(fsToggleCollision))displayError("Shave Toggle Collision."); + if(args.isFlagSet(fsUndo)) displayError("Shave Undo."); +#ifdef GLOBAL_FALLBACK + if(args.isFlagSet(fsRedrawOnIdle)) displayError("Redraw higr res on idle."); + if(args.isFlagSet(fsFullRedrawOnIdle)) displayError("Force update and redraw higr res on idle."); +#endif + ////////////////////////////// + + displayError("No Shave node selected."); + + return MS::kFailure; + } + + if (numHairShapes > 1) + { + displayWarning( + "Multiple Shave nodes are selected:" + " only the first will be affected." + ); + } + + // + // Make sure that the shaveNode is loaded into the engine. + // + nodeFn.setObject(targetPath); + + shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode(); + + hairShape->makeCurrent(); + hairShape->applySelections(); + + // + // Execute the command. + // + bool enable; + bool updateSelections = false; + + if (args.isFlagSet(fsAttenuate) + || args.isFlagSet(fsLock) + || args.isFlagSet(fsMergeSelection) + || args.isFlagSet(fsPopSelected) + || args.isFlagSet(fsPopZeroSized) + || args.isFlagSet(fsRecomb) + || args.isFlagSet(fsReplaceRest) + || args.isFlagSet(fsSplitSelection) + || args.isFlagSet(fsToggleCollision) + || args.isFlagSet(fsUndo)) + { + MGlobal::executeCommand("shave_prepareForEditing"); + + if (args.isFlagSet(fsAttenuate)) + SHAVEattenuate(); + else if (args.isFlagSet(fsLock)) + { + args.getFlagArgument(fsLock, 0, enable); + + if (enable) + SHAVElock(); + else + SHAVEunlock(); + } + else if (args.isFlagSet(fsPopSelected)) + SHAVEpop_selected(); + else if (args.isFlagSet(fsPopZeroSized)) + SHAVEpop_zero_sized(); + else if (args.isFlagSet(fsRecomb)) + SHAVErecomb(); + else if (args.isFlagSet(fsReplaceRest)) + SHAVEreplace_rest_interactive(); + else if (args.isFlagSet(fsToggleCollision)) + SHAVEtoggle_collision(); + else if (args.isFlagSet(fsUndo)) + SHAVEundo(); + else if (args.isFlagSet(fsSplitSelection)) + SHAVEsplit_selection(); + else if (args.isFlagSet(fsMergeSelection)) + SHAVEmerge_selection(); + + hairShape->applyEdits(true); + } + else if (args.isFlagSet(fsGrowSelection)) + { + SHAVEselect_grow(); + updateSelections = true; + } + else if (args.isFlagSet(fsHideSelection)) + { + args.getFlagArgument(fsHideSelection, 0, enable); + + if (enable) + hairShape->hideSelectedGuides(); + else + hairShape->unhideGuides(); + } + else if (args.isFlagSet(fsInvertSelection)) + { + invertSelection(); + updateSelections = true; + } + else if (args.isFlagSet(fsRotateSelectionUp)) + { + SHAVEselect_rotate_up(); + updateSelections = true; + } + //else if (args.isFlagSet(fsRedrawOnIdle)) + //{ + // redrawOnIdle(false); + //} + //else if (args.isFlagSet(fsFullRedrawOnIdle)) + //{ + // redrawOnIdle(true); + //} + + // + // Have the hair shape put it's component selections onto Maya's + // selection list. + // + if (updateSelections) hairShape->updateMayaComponentSelections(); + + return MS::kSuccess; +} + + +void shaveStyleCmd::invertSelection() +{ + // The behaviour of SHAVEselect_inverse isn't appropriate for tip + // selection mode: it turns off the tip and turns on all the other + // verts whereas if the tip is on we want it to turn off the entire + // guide. So we have to handle that mode ourselves. + MString selectType; + MGlobal::executeCommand("optionVar -q shaveBrushSelectMode", selectType); + + if (selectType == "tip") + { + SOFTGUIDE g; + int i; + + for (i = 0; SHAVEfetch_guide(i, &g) != -1; ++i) + { + g.select[0] = 1 - g.select[0]; + g.select[SHAVE_VERTS_PER_GUIDE-1] = g.select[0]; + SOFTput_guideSELECTONLY(i, &g); + } + } + else + { + SHAVEselect_inverse(); + } +} + +#ifdef GLOBAL_FALLBACK +//static LARGE_INTEGER now={ static_cast<LONGLONG>(0) }; +static qint64 now; + +bool shaveStyleCmd::redrawOnIdlDone = false; +void shaveStyleCmd::redrawOnIdle(bool fullUpdate) +{ + if(!redrawOnIdlDone) + { + if(IsMouseDown()) + { + redrawOnIdlDone = true; + return; + } + ////need some time intervall cut off to avoid flickering + //LARGE_INTEGER last = GetLastMoveTime(); + //QueryPerformanceCounter(&now); + + //LARGE_INTEGER freq; + //QueryPerformanceFrequency(&freq); + + //long long from_last = now.QuadPart - last.QuadPart; + //float t = (float)(from_last*1000.0/freq.QuadPart); + ////printf("t %f\n",t);fflush(stdout); + //if(t < 150.f /*&& !fullUpdate*/) + + now = GetQTimer().elapsed(); + qint64 t = now-GetLastMoveTime(); +// printf("t %i\n",(int)t);fflush(stdout); + + if(t < /*150*/300 /*|| QCoreApplication::hasPendingEvents()*/) + { + //redrawOnIdlDone = true; +// printf("refire \n");fflush(stdout); + + if(fullUpdate) + MGlobal::executeCommandOnIdle("shaveStyle -fullRedrawOnIdle"); + else + MGlobal::executeCommandOnIdle("shaveStyle -redrawOnIdle"); + return; + } + ////last = now; + SetLastMoveTime(now); + + MFnDagNode nodeFn; + MDagPathArray paths; + shaveUtil::getShaveNodes(paths); //it should not trigger ::compute + for (unsigned int i = 0; i < paths.length(); i++) + { + nodeFn.setObject(paths[i].node()); + + if (nodeFn.typeId() == shaveHairShape::id) + { + shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + shape->dirtyDisplay(); + shape->dirties.BRUSH_JUST_MOVED = 0; + shape->dirties.GLOBAL_MOUSE_DOWN = 0; + + /////// need to make sure that textures are connected /////// + shape->dirties.DIRTY_TEXTURE = 1; + shape->dirties.DIRTY_TEXTURE_JOE = 1; + ///////////////////////////////////////////////////////////// + + //////////////// + //shape->updateTexLookups(); + + if(fullUpdate) + { + //shape->makeCurrent(); + float trigger; + MPlug triggerPlug = nodeFn.findPlug("trigger"); + triggerPlug.getValue(trigger); + triggerPlug.setValue(trigger+1.0f); + + + } + + MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); + } + } + + M3dView::active3dView().refresh(true,true); //is it needed? + + redrawOnIdlDone = true; + + //for (unsigned int i = 0; i < paths.length(); i++) + //{ + // nodeFn.setObject(paths[i].node()); + + // if (nodeFn.typeId() == shaveHairShape::id) + // { + // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); + // shape->dirtyDisplay(); + // + // } + //} + } +} + +unsigned int shaveStyleCmd::getTotalHaircount() +{ + return 0; +} +unsigned int shaveStyleCmd::getTotalDisplayMem() +{ + return 0; +} +#endif diff --git a/mayaPlug/shaveStyleCmd.h b/mayaPlug/shaveStyleCmd.h new file mode 100644 index 0000000..b7b8790 --- /dev/null +++ b/mayaPlug/shaveStyleCmd.h @@ -0,0 +1,39 @@ +#ifndef shaveStyleCmd_h +#define shaveStyleCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + +class shaveStyleCmd : public MPxCommand +{ +public: + shaveStyleCmd(); + virtual ~shaveStyleCmd(); + + static void* createCmd(); + static MSyntax createSyntax(); + MStatus doIt( const MArgList& args ); + bool isUndoable() const { return false; } + + static const MString commandName; +#ifdef GLOBAL_FALLBACK + static bool redrawOnIdlDone; +#endif +private: + void invertSelection(); +#ifdef GLOBAL_FALLBACK + void redrawOnIdle(bool fullUpdate); + unsigned int getTotalHaircount(); + unsigned int getTotalDisplayMem(); +#endif + +}; + +#endif + diff --git a/mayaPlug/shaveTextureStore.cpp b/mayaPlug/shaveTextureStore.cpp new file mode 100644 index 0000000..148903d --- /dev/null +++ b/mayaPlug/shaveTextureStore.cpp @@ -0,0 +1,2812 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#include <QtCore/QEvent> +#include <QtCore/QElapsedTimer> +#include <QtGui/QMouseEvent> +#include <QtGui/QTabletEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include "shaveIO.h" + +#include <map> +#include <vector> + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDataBlock.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPoint.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFnSet.h> +#include <maya/MFnSubd.h> +#include <maya/MFnSubdNames.h> +#include <maya/MGlobal.h> +#include <maya/MIntArray.h> +#include <maya/MPlugArray.h> +#include <maya/MRenderUtil.h> +#include <maya/MSyntax.h> +#include <maya/MUint64Array.h> +#include <maya/MItMeshVertex.h> + +#include "shaveHairShape.h" +#include "ShavePerVertTexInfo.h" +#include "shaveRender.h" +#include "shaveSDK.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" +#include "shaveHairUI.h" + + + + +namespace +{ + typedef struct + { + int pntid; + float u; + float v; + } VertUVInfo; + + struct compMString + { + bool operator()(const MString s1, const MString s2) const + { + return (strcmp(s1.asChar(), s2.asChar()) < 0); + } + }; + + typedef std::vector<VertUVInfo> FaceUVInfo; + typedef std::vector<FaceUVInfo> SurfaceUVInfo; + typedef std::vector<SurfaceUVInfo*> UVSetUVInfo; + typedef std::map<MString, UVSetUVInfo*, compMString> UVInfoCache; +}; + + +static SurfaceUVInfo* cacheUVs( + shaveHairShape* sNode, + unsigned int surfIdx, + const MString& uvSet, + UVInfoCache& uvInfoCache +); + +static unsigned int* createFaceIDMap(const MIntArray& startIndices); + +static void makeTempColourConnections( + const MObject& shavenode, MDGModifier& dgMod +); + + +#define McheckErr(stat,m) \ + if ( MS::kSuccess != stat ) { \ + cerr << "ERROR: " << m << endl; \ + } + +static bool buildingLookups = false; +static int nodeCount = 0; +static int* texIDMap = NULL; +static unsigned texIDMapSize = 0; +static NODETEXINFO* texInfoLookup = NULL; +static unsigned texInfoLookupSize = 0; +static std::map<int, ShavePerVertTexInfo*> vertTexInfoMap; + +bool IsBuildingLookups() +{ + return buildingLookups; +} + + +// Some of the Shave parameters apply to growth vertices (i.e. for use by +// guides) while all the others apply to hairs. +static unsigned int vertParams[] = { 8, 21, 40 }; +static const unsigned int numVertParams = sizeof(vertParams) / sizeof(unsigned int); + +MObjectArray standinMeshes; +MDagPathArray standinMeshOrigSurfaces; + + +static inline void getHairUV( + const SurfaceUVInfo& surfUVInfo, + const unsigned int polyIdx, + const int baryPointIDs[3], + const float baryWeights[3], + float& u, + float& v +) +{ + u = v = 0.0f; + + if (polyIdx < surfUVInfo.size()) + { + const FaceUVInfo& faceUVInfo = surfUVInfo[polyIdx]; + size_t numVerts = faceUVInfo.size(); + + for (unsigned int i = 0; i < 3; ++i) + { + // faceUVInfo is a sparse array of VertUVInfo, with one for + // each vertex of this poly. So we have to run through them to + // find the one which has the same point ID as the bary coord. + for (size_t j = 0; j < numVerts; ++j) + { + if (faceUVInfo[j].pntid == baryPointIDs[i]) + { + u += faceUVInfo[j].u * baryWeights[i]; + v += faceUVInfo[j].v * baryWeights[i]; + break; + } + } + } + } +} + + +// This evaluates the hair-root textures. These are needed during +// rendering and export, but not during Live Mode. +void initTexInfoLookup2( + const MObjectArray& shaveHairShapes, + MString meshUVSet, + bool verbose, + bool displayHairsOnly, + MObject onlyThis +) +{ + //if (verbose) cerr << endl << "Beginning texture pre-process." << endl; +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("initTexInfoLookup2", NULL); +#endif + // + // There is a bit of recursion here which we need to be careful with. + // To build the texture lookups, we'll need to get the positions of + // each hair from Shave using either SHAVEmake_a_curve() or + // SHAVEmake_a_curveROOT(). + // + // Both of those functions will call the SHAVEapply_texture() callback + // to try and fill in any textured attributes for the hair. + // + // SHAVEapply_texture() will in turn call getTexInfoLookup() to + // retrieve the shaveHairShape's texture information -- which is exactly + // what we're in the process of building. + // + // To break the cycle, we set a flag which tells getTexInfoLookup() and + // getVertInfoLookup() to return null pointers until we're done. + // + buildingLookups = true; + + MStatus status; + shaveHairShape* onlyThisShape = NULL; + if (onlyThis != MObject::kNullObj) + { + MFnDependencyNode shaveDependNode(onlyThis); + onlyThisShape = (shaveHairShape*) shaveDependNode.userNode (&status); + } + + nodeCount = (int)shaveHairShapes.length(); + + unsigned int numShaveIDs = (unsigned)(shaveHairShape::getMaxShaveID() + 1); + + // We can only reuse the existing texInfoLookup and texIDMap arrays if + // we're initializing a single hair node (i.e. 'onlyThisShape' is not + // NULL) and the sizes of the arrays haven't changed. Otherwise we have to + // blow the old arrays away and make new ones. + // + bool needNewArrays = ( (onlyThisShape == NULL) + || (texIDMapSize != numShaveIDs) + || (nodeCount != texInfoLookupSize) + || (texInfoLookup == NULL) ); + + if (needNewArrays) { + freeTextureLookup(); + } + + // if we have nodes (we always should if we get here) then initialize + // the lookup structures. + if (nodeCount > 0) + { + // + // Allocate the top (node) level of the texture tables. + // + if (needNewArrays) + { + texInfoLookupSize = nodeCount; + texInfoLookup = (NODETEXINFO*)malloc(texInfoLookupSize*sizeof(NODETEXINFO)); + memset(texInfoLookup, 0, texInfoLookupSize*sizeof(NODETEXINFO)); + + // We need a lookup table to translate Shave node IDs into indices + // into our texture tables. + // + texIDMapSize = numShaveIDs; + texIDMap = new int[texIDMapSize]; + + for (int i = 0; i < (int)texIDMapSize; i++) + texIDMap[i] = -1; + } + + if (texInfoLookup == NULL) + { + cerr << "ERROR- COULD NOT ALLOCATE MEMORY FOR TEXTURE LOOKUP" + << " STRUCTURES, PROCEEDING W/O TEXTURES." << endl; + } + else + { + for (int node = 0; node < nodeCount; node++) + { + // get the shaveHairShape; + MFnDependencyNode shaveDependNode(shaveHairShapes[node]); + shaveHairShape* sNode = (shaveHairShape*) shaveDependNode.userNode (&status); + + //printf("02 - hasPendingEvents: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + if (needNewArrays || (onlyThisShape == sNode)) + { + NODETEXINFO* nodeInfo = &texInfoLookup[node]; + + if (onlyThisShape != NULL) + freeTextureLookup(nodeInfo); + else + { + nodeInfo->displacement = NULL; + nodeInfo->textureLookup = NULL; + nodeInfo->u = NULL; + nodeInfo->v = NULL; + } + + nodeInfo->node = sNode; + + // + // Get Shave's internal SHAVENODE structure. Note that + // getting it this way will force the guides to update if + // the geometry has changed, e.g. because we're on a new + // frame. + // + SHAVENODE* hairNode = sNode->getHairNode(); + int hairGroup = sNode->getHairGroup(); + + // + // Make sure that our parameters are all up to date with + // any changes the user may have made. + // + // %%% Shouldn't the 'getHairNode' call above already have + // taken care of that? + // + //sNode->updateParams(); + + // + // Add the node's Shave ID to our map. + // + texIDMap[sNode->getShaveID()] = node; + + // + // Let Shave know which node our calls will be referring + // to. + // + sNode->makeCurrent(); + + int numPasses = hairNode->shavep.passes[hairGroup]; + nodeInfo->maxPasses = numPasses; + + // our lookup will return a float, indexed by + // [pass][parm][hairid], here we allocate the [pass] + // level, now that we know what that number is. + nodeInfo->textureLookup = (float***) + // malloc(1*sizeof(float**)); + malloc(numPasses*sizeof(float**)); + // nodeInfo->u = new float*[1]; + // nodeInfo->v = new float*[1]; + nodeInfo->u = new float*[numPasses]; + nodeInfo->v = new float*[numPasses]; + + // get the haircount for this node + if (displayHairsOnly) + nodeInfo->count = sNode->getNumDisplayHairs(false); + else + nodeInfo->count = hairNode->shavep.haircount[hairGroup]*numPasses; + + // TODO: Why do we set these to 1 here? + numPasses=1; + nodeInfo->maxPasses=1; + + + // init the textured boolean to false; + for(int l = 0; l < SHAVE_NUM_PARAMS; l++) + { + nodeInfo->textured[l] = false; + nodeInfo->maxIndex[l] = -1; + } + + // we've got a bit of work to do to find the uv we want. + // We'll need to get the selection list for this node, then + // we'll need to be able to determing which mesh the + // faceIndex belongs to, and then we'll need to correlate + // the pointids to a local face vertid, in order to get the + // UVs. Yuck. + + + // get the texture node plug. We'll use this array plug in + // the next loop to figure out where we need to do our + // lookups from, and which parms are carrying textures. + MDGModifier dgMod; + MIntArray connectedIndexes; + + MPlug texPlug(shaveHairShapes[node], shaveHairShape::shaveTextureAttr); + MPlugArray texPlugArray; + MString currentNodeName = sNode->name(); + unsigned int numGrowthSurfaces = 0; + + //Only need to do this if the growth type is not Spline. + bool splineNode = (hairGroup == 4); + + if (!splineNode) + { + short uTess; + short vTess; + MPlug plug(shaveHairShapes[node], shaveHairShape::surfTessU); + plug.getValue(uTess); + plug.setAttribute(shaveHairShape::surfTessV); + plug.getValue(vTess); + + short sDept; + short sSamp; + MPlug plug2(shaveHairShapes[node], shaveHairShape::subdTessDept); + plug2.getValue(sDept); + plug2.setAttribute(shaveHairShape::subdTessSamp); + plug2.getValue(sSamp); + + + tesselateGrowthSurfaces( + sNode->exportData.meshes, uTess, vTess, sDept, sSamp + ); + + numGrowthSurfaces = sNode->exportData.meshes.length(); + } + //printf("03 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // + // Most texture connections are made directly to elements + // of the shaveHairShape's 'shaveTex' array attribute. + // + // However, a colour attribute is a compound, which can + // be connected either as a single compound plug, or as + // individual component plugs. Since the 'shaveTex' array + // doesn't allow for compounds, the shaveHairShape's colour + // attributes have their own separate connections. + // + // To make life easier on ourselves, we'd like everything + // to be in the 'shaveTex' array, so let's break the + // compounds up into their constituent parts and make + // temporary connections to the appropriate 'shaveTex' + // array elements. + // + makeTempColourConnections(shaveHairShapes[node], dgMod); + status = dgMod.doIt(); + + //printf("04 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // now we need to make sure that the plug is actually + // connected to something. + // Thanks, Maya. + MPlug checkPlug; + + unsigned int pi = 0; + for (pi = 0; pi < 60; pi++) // was 50 + { + // Filter out those parameters which apply to growth + // vertices, not hairs. + unsigned int j; + for (j = 0; j < numVertParams; ++j) + if (pi == vertParams[j]) break; + + if (j == numVertParams) + { + checkPlug = texPlug.elementByLogicalIndex(pi); + + if(checkPlug.isConnected()) + connectedIndexes.append(pi); + } + } + + bool haveTextures = (connectedIndexes.length() > 0); + + //printf("05 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // + // This structure defines those few items of + // information from each hair's CURVEINFO which we need + // to keep track of. + // + struct HairBaryInfo + { + int UTpid; + int pntid[3]; + float wgt[3]; + + } **hairInfo = NULL; + + // + // If we have textures, then we'll be needing the hairInfo + // array. + // + if (haveTextures) + hairInfo = new struct HairBaryInfo*[numPasses]; + + UVInfoCache uvInfoCache; + + nodeInfo->displacement = NULL; + + // Cache the vertex UVs so that we can later generate + // hair-root UVs for use in SHAVEapply_texture(). + unsigned int i; + SurfaceUVInfo** uvInfo = NULL; + + if (!splineNode) + { + uvInfo = new SurfaceUVInfo*[numGrowthSurfaces]; + + for (i = 0; i < numGrowthSurfaces; i++) + { + uvInfo[i] = NULL; + + MDagPath surf = sNode->exportData.meshes[i]; + surf.extendToShape(); + + #ifdef EVALUATE_POSITION_FIXED + if (!surf.hasFn(MFn::kMesh)) + #else + if (!surf.hasFn(MFn::kMesh) + && !surf.hasFn(MFn::kSubdiv)) + #endif + { + // For non-mesh surfaces Shave uses a mesh to + // approximate the surface. To get the hairs + // rooted at the correct point we calculate + // the 'displacement' (i.e. the difference + // between the approximate mesh and the real + // surface) for every hair root and cache it. + if (nodeInfo->displacement == NULL) + nodeInfo->displacement = new VERT*[numPasses]; + + // Non-meshes only have a single, default UV + // parameterization. + uvInfo[i] = cacheUVs(sNode, i, "", uvInfoCache); + } + else + { + uvInfo[i] = cacheUVs(sNode, i, meshUVSet, uvInfoCache); + } + } + } + + //printf("06 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // Cache the displacements and barycentric info for each hair. + CURVEINFO curveInfo; + unsigned int* faceToMesh = NULL; + int hairnum; + unsigned int meshIndex; + MDagPath meshNode; + int pass; + float u; + float v; + WFTYPE wt; + + init_geomWF(&wt); + + for (pass = 0; pass < numPasses; pass++) + { + if (nodeInfo->displacement) + nodeInfo->displacement[pass] = new VERT[nodeInfo->count]; + + nodeInfo->u[pass] = new float[nodeInfo->count]; + nodeInfo->v[pass] = new float[nodeInfo->count]; + + if (haveTextures) + { + hairInfo[pass] = + new struct HairBaryInfo[nodeInfo->count]; + } + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + SHAVEmake_a_curveROOT( + pass, + hairGroup, + hairnum, + &wt, + &curveInfo + ); + + if (haveTextures) + { + // Save the barycentric info from curveInfo, + // which we'll need later on. + hairInfo[pass][hairnum].UTpid = curveInfo.UTpid; + + for (int i = 0; i < 3; i++) + { + hairInfo[pass][hairnum].pntid[i] = + curveInfo.pntid[i]; + hairInfo[pass][hairnum].wgt[i] = + curveInfo.wgt[i]; + } + } + + // + // Shave's hair number indices include killed + // hairs, so we have to leave room for them in our + // arrays. But we don't need to calculate their + // uvs. + // + if (wt.totalfaces > 0) + { + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to actually be + // on the texture, so we have to nudge it + // in a bit. + // + nodeInfo->u[pass][hairnum] = curveInfo.wgt[0]; + nodeInfo->v[pass][hairnum] = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + // Get the index of the mesh that this + // hair is growing on. + meshIndex = faceToMesh[curveInfo.UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + + int normalizedPolyIdx = curveInfo.UTpid - + sNode->exportData.startFaces[meshIndex]; + + float u = 0.0f; + float v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + curveInfo.pntid, + curveInfo.wgt, + u, + v + ); + + nodeInfo->u[pass][hairnum] = u; + nodeInfo->v[pass][hairnum] = v; + + // Get this hair's displacement. + if (nodeInfo->displacement) + { + getDisplacement( + meshNode, + curveInfo, + wt.v[0], + u, + v, + sNode->exportData.startVerts[meshIndex], + nodeInfo->displacement[pass][hairnum] + ); + } + } + } + else + { + if (nodeInfo->displacement) + { + nodeInfo->displacement[pass][hairnum].x = 0.0f; + nodeInfo->displacement[pass][hairnum].y = 0.0f; + nodeInfo->displacement[pass][hairnum].z = 0.0f; + } + } + } + } + + //printf("07 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // + // SHAVEmake_a_curveROOT will free up the old WFTYPE's info + // on each call, but we have to free up the the final one + // that it left us with. + // + free_geomWF(&wt); + + // + // If we have any textured parameters then we need to + // iterate through all the passes and determine the texture + // information. + // + if (haveTextures) + { + if (verbose) cerr << "doing uv map preprocess... "; + + MObjectArray textures; + unsigned int ti; // which texture? + unsigned int numTextures = 0; + + // + // Get an array of all the textures being used by this + // shaveHairShape. + // + unsigned int i; + + for(i = 0; i < connectedIndexes.length(); i++) + { + int parmIndex; + parmIndex = (int)connectedIndexes[i]; + + // + // Get the plug which is driving this parameter. + // + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + // + // Grab its node. + // + MObject texture = texPlugArray[0].node(); + + //{ + // MFnDependencyNode dFn(texture); + // printf("texture node name %s\n",dFn.name().asChar() );fflush(stdout); + //} + + // + // If we haven't yet seen this one, add it to the + // array. + // + for (ti = 0; ti < numTextures; ti++) + if (texture == textures[ti]) break; + + if (ti == numTextures) + { + textures.append(texture); + numTextures++; + } + } + + // + // Build a UV map for each texture. Each map will have + // a single entry per hair, per pass. + // + HAIRUVS** textureUVMaps = new HAIRUVS*[numTextures]; + struct HairBaryInfo* hair; + + for (ti = 0; ti < numTextures; ti++) + { + textureUVMaps[ti] = new HAIRUVS[nodeInfo->maxPasses]; + + // + // Find the UV set used by each growth mesh for + // this texture. + // + MStringArray uvSets; + + if (splineNode) + { + // + // UV mapping for spline hair is handled + // specially. + // + uvSets.append(""); + } + else + { + MString uvSet; + + for (unsigned int m = 0; m < numGrowthSurfaces; m++) + { + MDagPath objectPath = + sNode->exportData.meshes[m]; + + if (!objectPath.hasFn(MFn::kMesh)) + { + // + // This growth object is not a mesh, so + // it doesn't support UV sets. + // + // We store a blank UV set name which + // will be an indicator later on to + // perform the default mapping for this + // growth object. + // + uvSet = ""; + } + else + { + // + // We have a mesh. + // + // Get the UV set used by this mesh for + // this texture. + // + uvSet = sNode->getUVSet( + objectPath.node(), + textures[ti] + ); + } + + uvSets.append(uvSet); + + // Cache the uvs for this UV set. + uvInfo[m] = cacheUVs(sNode, m, uvSet, uvInfoCache); + } + } + + for (pass = 0; + pass < nodeInfo->maxPasses; + pass++) + { + textureUVMaps[ti][pass].us = new float[nodeInfo->count]; + textureUVMaps[ti][pass].vs = new float[nodeInfo->count]; + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to + // actually be on the texture, so we + // have to nudge it in a bit. + // + u = hair->wgt[0]; + v = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + meshIndex = faceToMesh[hair->UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + meshNode.extendToShape(); + + // If this growth surface is a not + // really a mesh (i.e. it's a NURBS or + // subd) then it will be using the + // default UV parameterization, which + // we have already stored in nodeInfo + // so we can grab the value from there + // rather than recalculating it here. + // + // Similarly, if it *is* a mesh and we're + // using the same uv set as was cached + // in nodeInfo, then just grab the + // values from there. + if (!meshNode.hasFn(MFn::kMesh) + || (uvSets[meshIndex] == meshUVSet)) + { + u = nodeInfo->u[pass][hairnum]; + v = nodeInfo->v[pass][hairnum]; + } + else + { + int normalizedPolyIdx = hair->UTpid - + sNode->exportData.startFaces[meshIndex]; + + u = 0.0f; + v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + hair->pntid, + hair->wgt, + u, + v + ); + } + } + + textureUVMaps[ti][pass].us[hairnum] = u; + textureUVMaps[ti][pass].vs[hairnum] = v; + } + } + } + + //printf("08 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + if (verbose) + { + cerr << "done." << endl; + cerr << "getting texture values from maya... " << endl; + } + int maxtex=100000; + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + + + // malloc the top level of our lookup; + nodeInfo->textureLookup[pass] = (float**) + calloc(SHAVE_NUM_PARAMS, sizeof(float*)); + + MFloatArray uArray; + MFloatArray vArray; + MFloatPointArray pointArray; + MFloatPointArray pa; + VERT pos[3]; + struct HairBaryInfo* hair; + + // we need to to figure out the uv coords for each + // of the roots for this pass, and also the point + // locations for 3D texture evaluation. + MFloatMatrix camMatrix; + + // if we store the lookups we have done then we + // don't need to do them again. This will save both + // time and memory for high density scenes. + MStringArray doneLookups; + MIntArray doneLookupsIndex; + + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + int ind; + int total; + total=nodeInfo->count; + int parmIndex = (int)connectedIndexes[pi]; + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + MObject texture = texPlugArray[0].node(); + + for (ti = 0; ti < numTextures; ti++) + if (textures[ti] == texture) break; + + + nodeInfo->textureLookup[pass][parmIndex] + = (float*) calloc( + total, sizeof(float) + ); + + + for (ind=0;ind<nodeInfo->count;ind+=maxtex) + { + uArray.clear(); + vArray.clear(); + pointArray.clear(); + int szz=total; + + if (total>=maxtex) szz=total-ind; + if (szz>maxtex) szz=maxtex; + //fprintf (stdout,"ind = %d szz= %d\n",ind,szz);fflush(stdout); + + for (hairnum = ind; hairnum < ind+szz; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + for (int k = 0; k < 3; k++) + { + pos[k] = sNode->memShaveObj + .v[hair->pntid[k]]; + pos[k].x *= hair->wgt[k]; + pos[k].y *= hair->wgt[k]; + pos[k].z *= hair->wgt[k]; + } + + uArray.append( + textureUVMaps[ti][pass].us[hairnum] + ); + vArray.append( + textureUVMaps[ti][pass].vs[hairnum] + ); + + pointArray.append( + pos[0].x+pos[1].x+pos[2].x, + pos[0].y+pos[1].y+pos[2].y, + pos[0].z+pos[1].z+pos[2].z + ); + } + + // set aside a little memory for our lookup results. + + + { + { + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + + //////debug////// + //MFloatVectorArray uTang; + //MFloatVectorArray vTang; + //MFloatArray filter; + //uTang.setLength(szz); + //vTang.setLength(szz); + //filter.setLength(szz); + //for(unsigned int k = 0; k < szz; k++) + //{ + // uTang[k] = MFloatVector(1.0f,0.0f); + // vTang[k] = MFloatVector(0.0f,1.0f); + // filter[k] = 0.1f; + //} + ///////////////////////// + + //printf("texture name %s\n",texPlugArray[0].name().asChar() );fflush(stdout); + + // lets get the lookup values from the maya + // shading engine. + MStatus shRes = + MRenderUtil::sampleShadingNetwork( + texPlugArray[0].name(), //shade node name + szz, //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL /*&uTang*/, //u tang + NULL /*&vTang*/, //v tang + NULL /*&filter*/, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); + //fprintf (stdout,"done with lookup\n");fflush(stdout); + // we need to copy the returned values into the + // lookup array our pointer references. also, + // we'll store the name and parm index of this + // lookup so we can reuse it if the opportunity + // presents itself. + nodeInfo->maxIndex[parmIndex] + = (int)total; + + ///////// debug /////////////// + //if(shRes != MStatus::kSuccess) printf(" MRenderUtil::sampleShadingNetwork failed with result %i\n",shRes); + //for(int k=0; k < (int)szz; k++) + //{ + // printf("uv %f %f rgb %f %f %f t %f\n",uArray[k],vArray[k],vertColorArray[k].x,vertColorArray[k].y,vertColorArray[k].z,vertTranspArray[k]); + //} + //fflush(stdout); + /////////////////////////////// + + for(int k=0; k < (int)szz; k++) + { + nodeInfo->textureLookup[pass][parmIndex][k+ind] + = (float)vertColorArray[k].x; + // nodeInfo->textureLookup[pass][parmIndex][k] + // = (float)vertColorArray[k].x; + } + } + } + } + + } + } + //printf("09 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + if (verbose) + { + cerr << "done." << endl; + cerr << "freeing cached curveinfos and uvlists..." << endl; + } + + for (ti = 0; ti < numTextures; ti++) + { + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + delete [] textureUVMaps[ti][pass].us; + delete [] textureUVMaps[ti][pass].vs; + } + + delete [] textureUVMaps[ti]; + } + + delete [] textureUVMaps; + + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + delete [] hairInfo[pass]; + + delete [] hairInfo; + } + + delete [] faceToMesh; + + UVInfoCache::iterator setIter; + + for (setIter = uvInfoCache.begin(); + setIter != uvInfoCache.end(); + ++setIter) + { + UVSetUVInfo* uvSetUVInfo = (*setIter).second; + + for (unsigned int ui = 0; ui < uvSetUVInfo->size(); ++ui) + delete (*uvSetUVInfo)[ui]; + + delete uvSetUVInfo; + } + + //printf("10 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + uvInfoCache.clear(); + + // we can't set the textured member true until we've taken + // care of all the passes. Otherwise make a curve will fail + // trying to do a lookup. + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + unsigned int pIndex = connectedIndexes[pi]; + nodeInfo->textured[pIndex] = true; + } + + // remove temporary connections that were made in the + // hypergraph. not strictly necesary, but keeps thing + // looking cleaner for the user. + dgMod.undoIt(); + + // + // Let the node know that its texture cache has changed. + // + MPlug plug( + shaveHairShapes[node], + shaveHairShape::textureCacheUpdatedAttr + ); + plug.setValue(true); + + } //end if(onlyThisShape != NULL && onlyThisShape == sNode) + + } //end for node + } + } + + // + // It's now safe to serve up the texture lookup tables. + // + buildingLookups = false; +#ifdef DO_PROFILE + Profile::ProfileDump("initTexInfoLookup2 - done", NULL); +#endif +} + + +// This evaluates the hair-root textures. These are needed during +// rendering and export, but not during Live Mode. +void initTexInfoLookup( + const MObjectArray& shaveHairShapes, + MString meshUVSet, + bool verbose, + bool displayHairsOnly +) +{ + //if (verbose) cerr << endl << "Beginning texture pre-process." << endl; + + // + // There is a bit of recursion here which we need to be careful with. + // To build the texture lookups, we'll need to get the positions of + // each hair from Shave using either SHAVEmake_a_curve() or + // SHAVEmake_a_curveROOT(). + // + // Both of those functions will call the SHAVEapply_texture() callback + // to try and fill in any textured attributes for the hair. + // + // SHAVEapply_texture() will in turn call getTexInfoLookup() to + // retrieve the shaveHairShape's texture information -- which is exactly + // what we're in the process of building. + // + // To break the cycle, we set a flag which tells getTexInfoLookup() and + // getVertInfoLookup() to return null pointers until we're done. + // + buildingLookups = true; + + freeTextureLookup(); + + shaveHairShape* sNode; + bool splineNode; + MStringArray result; + MStatus status; + + // get our shaveHairShapes in a selection list + nodeCount = (int)shaveHairShapes.length(); + + + // if we have nodes (we always should if we get here) then allocate + // space for the lookup structures. + if (nodeCount > 0) + { + // + // We need a lookup table to translate Shave node ID's into indices + // into our texture tables. + // + int i; + + texIDMapSize = (unsigned)(shaveHairShape::getMaxShaveID() + 1); + texIDMap = new int[texIDMapSize]; + + for (i = 0; i < (int)texIDMapSize; i++) + texIDMap[i] = -1; + + // + // Allocate the top (node) level of the texture tables. + // + texInfoLookup = (NODETEXINFO*)malloc(nodeCount*sizeof(NODETEXINFO)); + + if (texInfoLookup == NULL) + { + cerr << "ERROR- COULD NOT ALLOCATE MEMORY FOR TEXTURE LOOKUP" + << " STRUCTURES, PROCEEDING W/O TEXTURES." << endl; + } + else + { + for (int node = 0; node < nodeCount; node++) + { + NODETEXINFO* nodeInfo = &texInfoLookup[node]; + nodeInfo->displacement = NULL; + nodeInfo->textureLookup = NULL; + nodeInfo->u = NULL; + nodeInfo->v = NULL; + + + // get the shaveHairShape; + MFnDependencyNode shaveDependNode(shaveHairShapes[node]); + sNode = (shaveHairShape*) shaveDependNode.userNode (&status); + + nodeInfo->node = sNode; + + // + // Get Shave's internal SHAVENODE structure. Note that + // getting it this way will force the guides to update if + // the geometry has changed, e.g. because we're on a new + // frame. + // + SHAVENODE* hairNode = sNode->getHairNode(); + int hairGroup = sNode->getHairGroup(); + + // + // Make sure that our parameters are all up to date with + // any changes the user may have made. + // + // %%% Shouldn't the 'getHairNode' call above already have + // taken care of that? + // + sNode->updateParams(); + + // + // Add the node's Shave ID to our map. + // + texIDMap[sNode->getShaveID()] = node; + + // + // Let Shave know which node our calls will be referring + // to. + // + sNode->makeCurrent(); + + int numPasses = hairNode->shavep.passes[hairGroup]; + nodeInfo->maxPasses = numPasses; + + // our lookup will return a float, indexed by + // [pass][parm][hairid], here we allocate the [pass] + // level, now that we know what that number is. + nodeInfo->textureLookup = (float***) +// malloc(1*sizeof(float**)); + malloc(numPasses*sizeof(float**)); +// nodeInfo->u = new float*[1]; +// nodeInfo->v = new float*[1]; + nodeInfo->u = new float*[numPasses]; + nodeInfo->v = new float*[numPasses]; + + // get the haircount for this node + if (displayHairsOnly) + nodeInfo->count = sNode->getNumDisplayHairs(false); + else + nodeInfo->count = hairNode->shavep.haircount[hairGroup]*numPasses; + +numPasses=1; +nodeInfo->maxPasses=1; + + + // init the textured boolean to false; + for(int l = 0; l < SHAVE_NUM_PARAMS; l++) + { + nodeInfo->textured[l] = false; + nodeInfo->maxIndex[l] = -1; + } + + // we've got a bit of work to do to find the uv we want. + // We'll need to get the selection list for this node, then + // we'll need to be able to determing which mesh the + // faceIndex belongs to, and then we'll need to correlate + // the pointids to a local face vertid, in order to get the + // UVs. Yuck. + + + // get the texture node plug. We'll use this array plug in + // the next loop to figure out where we need to do our + // lookups from, and which parms are carrying textures. + MDGModifier dgMod; + MIntArray connectedIndexes; + + MPlug texPlug(shaveHairShapes[node], shaveHairShape::shaveTextureAttr); + MPlugArray texPlugArray; + MString currentNodeName = sNode->name(); + unsigned int numGrowthSurfaces = 0; + + //Only need to do this if the growth type is not Spline. + splineNode = (hairGroup == 4); + + if (!splineNode) + { + short uTess; + short vTess; + MPlug plug(shaveHairShapes[node], shaveHairShape::surfTessU); + plug.getValue(uTess); + plug.setAttribute(shaveHairShape::surfTessV); + plug.getValue(vTess); + + short sDept; + short sSamp; + MPlug plug2(shaveHairShapes[node], shaveHairShape::subdTessDept); + plug2.getValue(sDept); + plug2.setAttribute(shaveHairShape::subdTessSamp); + plug2.getValue(sSamp); + + + tesselateGrowthSurfaces( + sNode->exportData.meshes, uTess, vTess, sDept, sSamp + ); + + numGrowthSurfaces = sNode->exportData.meshes.length(); + } + + // + // Most texture connections are made directly to elements + // of the shaveHairShape's 'shaveTex' array attribute. + // + // However, a colour attribute is a compound, which can + // be connected either as a single compound plug, or as + // individual component plugs. Since the 'shaveTex' array + // doesn't allow for compounds, the shaveHairShape's colour + // attributes have their own separate connections. + // + // To make life easier on ourselves, we'd like everything + // to be in the 'shaveTex' array, so let's break the + // compounds up into their constituent parts and make + // temporary connections to the appropriate 'shaveTex' + // array elements. + // + makeTempColourConnections(shaveHairShapes[node], dgMod); + status = dgMod.doIt(); + + // now we need to make sure that the plug is actually + // connected to something. + // Thanks, Maya. + MPlug checkPlug; + + unsigned int pi = 0; + for (pi = 0; pi < 60; pi++) // was 50 + { + // Filter out those parameters which apply to growth + // vertices, not hairs. + unsigned int j; + for (j = 0; j < numVertParams; ++j) + if (pi == vertParams[j]) break; + + if (j == numVertParams) + { + checkPlug = texPlug.elementByLogicalIndex(pi); + + if(checkPlug.isConnected()) + connectedIndexes.append(pi); + } + } + + bool haveTextures = (connectedIndexes.length() > 0); + + // + // This structure defines those few items of + // information from each hair's CURVEINFO which we need + // to keep track of. + // + struct HairBaryInfo + { + int UTpid; + int pntid[3]; + float wgt[3]; + + } **hairInfo = NULL; + + // + // If we have textures, then we'll be needing the hairInfo + // array. + // + if (haveTextures) + hairInfo = new struct HairBaryInfo*[numPasses]; + + UVInfoCache uvInfoCache; + + nodeInfo->displacement = NULL; + + // Cache the vertex UVs so that we can later generate + // hair-root UVs for use in SHAVEapply_texture(). + unsigned int i; + SurfaceUVInfo** uvInfo = NULL; + + if (!splineNode) + { + uvInfo = new SurfaceUVInfo*[numGrowthSurfaces]; + + for (i = 0; i < numGrowthSurfaces; i++) + { + uvInfo[i] = NULL; + + MDagPath surf = sNode->exportData.meshes[i]; + surf.extendToShape(); + +#ifdef EVALUATE_POSITION_FIXED + if (!surf.hasFn(MFn::kMesh)) +#else + if (!surf.hasFn(MFn::kMesh) + && !surf.hasFn(MFn::kSubdiv)) +#endif + { + // For non-mesh surfaces Shave uses a mesh to + // approximate the surface. To get the hairs + // rooted at the correct point we calculate + // the 'displacement' (i.e. the difference + // between the approximate mesh and the real + // surface) for every hair root and cache it. + if (nodeInfo->displacement == NULL) + nodeInfo->displacement = new VERT*[numPasses]; + + // Non-meshes only have a single, default UV + // parameterization. + uvInfo[i] = cacheUVs(sNode, i, "", uvInfoCache); + } + else + { + uvInfo[i] = cacheUVs(sNode, i, meshUVSet, uvInfoCache); + } + } + } + + // Cache the displacements and barycentric info for each hair. + CURVEINFO curveInfo; + unsigned int* faceToMesh = NULL; + int hairnum; + unsigned int meshIndex; + MDagPath meshNode; + int pass; + float u; + float v; + WFTYPE wt; + + init_geomWF(&wt); + + for (pass = 0; pass < numPasses; pass++) + { + if (nodeInfo->displacement) + nodeInfo->displacement[pass] = new VERT[nodeInfo->count]; + + nodeInfo->u[pass] = new float[nodeInfo->count]; + nodeInfo->v[pass] = new float[nodeInfo->count]; + + if (haveTextures) + { + hairInfo[pass] = + new struct HairBaryInfo[nodeInfo->count]; + } + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + SHAVEmake_a_curveROOT( + pass, + hairGroup, + hairnum, + &wt, + &curveInfo + ); + + if (haveTextures) + { + // Save the barycentric info from curveInfo, + // which we'll need later on. + hairInfo[pass][hairnum].UTpid = curveInfo.UTpid; + + for (int i = 0; i < 3; i++) + { + hairInfo[pass][hairnum].pntid[i] = + curveInfo.pntid[i]; + hairInfo[pass][hairnum].wgt[i] = + curveInfo.wgt[i]; + } + } + + // + // Shave's hair number indices include killed + // hairs, so we have to leave room for them in our + // arrays. But we don't need to calculate their + // uvs. + // + if (wt.totalfaces > 0) + { + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to actually be + // on the texture, so we have to nudge it + // in a bit. + // + nodeInfo->u[pass][hairnum] = curveInfo.wgt[0]; + nodeInfo->v[pass][hairnum] = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + // Get the index of the mesh that this + // hair is growing on. + meshIndex = faceToMesh[curveInfo.UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + + int normalizedPolyIdx = curveInfo.UTpid - + sNode->exportData.startFaces[meshIndex]; + + float u = 0.0f; + float v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + curveInfo.pntid, + curveInfo.wgt, + u, + v + ); + + nodeInfo->u[pass][hairnum] = u; + nodeInfo->v[pass][hairnum] = v; + + // Get this hair's displacement. + if (nodeInfo->displacement) + { + getDisplacement( + meshNode, + curveInfo, + wt.v[0], + u, + v, + sNode->exportData.startVerts[meshIndex], + nodeInfo->displacement[pass][hairnum] + ); + } + } + } + else + { + if (nodeInfo->displacement) + { + nodeInfo->displacement[pass][hairnum].x = 0.0f; + nodeInfo->displacement[pass][hairnum].y = 0.0f; + nodeInfo->displacement[pass][hairnum].z = 0.0f; + } + } + } + } + + // + // SHAVEmake_a_curveROOT will free up the old WFTYPE's info + // on each call, but we have to free up the the final one + // that it left us with. + // + free_geomWF(&wt); + + // + // If we have any textured parameters then we need to + // iterate through all the passes and determine the texture + // information. + // + if (haveTextures) + { + if (verbose) cerr << "doing uv map preprocess... "; + + MObjectArray textures; + unsigned int ti; // which texture? + unsigned int numTextures = 0; + + // + // Get an array of all the textures being used by this + // shaveHairShape. + // + unsigned int i; + + for(i = 0; i < connectedIndexes.length(); i++) + { + int parmIndex; + parmIndex = (int)connectedIndexes[i]; + + // + // Get the plug which is driving this parameter. + // + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + // + // Grab its node. + // + MObject texture = texPlugArray[0].node(); + + //{ + // MFnDependencyNode dFn(texture); + // printf("texture node name %s\n",dFn.name().asChar() );fflush(stdout); + //} + + // + // If we haven't yet seen this one, add it to the + // array. + // + for (ti = 0; ti < numTextures; ti++) + if (texture == textures[ti]) break; + + if (ti == numTextures) + { + textures.append(texture); + numTextures++; + } + } + + // + // Build a UV map for each texture. Each map will have + // a single entry per hair, per pass. + // + HAIRUVS** textureUVMaps = new HAIRUVS*[numTextures]; + struct HairBaryInfo* hair; + + for (ti = 0; ti < numTextures; ti++) + { + textureUVMaps[ti] = new HAIRUVS[nodeInfo->maxPasses]; + + // + // Find the UV set used by each growth mesh for + // this texture. + // + MStringArray uvSets; + + if (splineNode) + { + // + // UV mapping for spline hair is handled + // specially. + // + uvSets.append(""); + } + else + { + MString uvSet; + + for (unsigned int m = 0; m < numGrowthSurfaces; m++) + { + MDagPath objectPath = + sNode->exportData.meshes[m]; + + if (!objectPath.hasFn(MFn::kMesh)) + { + // + // This growth object is not a mesh, so + // it doesn't support UV sets. + // + // We store a blank UV set name which + // will be an indicator later on to + // perform the default mapping for this + // growth object. + // + uvSet = ""; + } + else + { + // + // We have a mesh. + // + // Get the UV set used by this mesh for + // this texture. + // + uvSet = sNode->getUVSet( + objectPath.node(), + textures[ti] + ); + } + + uvSets.append(uvSet); + + // Cache the uvs for this UV set. + uvInfo[m] = cacheUVs(sNode, m, uvSet, uvInfoCache); + } + } + + for (pass = 0; + pass < nodeInfo->maxPasses; + pass++) + { + textureUVMaps[ti][pass].us = new float[nodeInfo->count]; + textureUVMaps[ti][pass].vs = new float[nodeInfo->count]; + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to + // actually be on the texture, so we + // have to nudge it in a bit. + // + u = hair->wgt[0]; + v = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + meshIndex = faceToMesh[hair->UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + meshNode.extendToShape(); + + // If this growth surface is a not + // really a mesh (i.e. it's a NURBS or + // subd) then it will be using the + // default UV parameterization, which + // we have already stored in nodeInfo + // so we can grab the value from there + // rather than recalculating it here. + // + // Similarly, if it *is* a mesh and we're + // using the same uv set as was cached + // in nodeInfo, then just grab the + // values from there. + if (!meshNode.hasFn(MFn::kMesh) + || (uvSets[meshIndex] == meshUVSet)) + { + u = nodeInfo->u[pass][hairnum]; + v = nodeInfo->v[pass][hairnum]; + } + else + { + int normalizedPolyIdx = hair->UTpid - + sNode->exportData.startFaces[meshIndex]; + + u = 0.0f; + v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + hair->pntid, + hair->wgt, + u, + v + ); + } + } + + textureUVMaps[ti][pass].us[hairnum] = u; + textureUVMaps[ti][pass].vs[hairnum] = v; + } + } + } + + if (verbose) + { + cerr << "done." << endl; + cerr << "getting texture values from maya... " << endl; + } +int maxtex=100000; + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + + + // malloc the top level of our lookup; + nodeInfo->textureLookup[pass] = (float**) + calloc(SHAVE_NUM_PARAMS, sizeof(float*)); + + MFloatArray uArray; + MFloatArray vArray; + MFloatPointArray pointArray; + MFloatPointArray pa; + VERT pos[3]; + struct HairBaryInfo* hair; + + // we need to to figure out the uv coords for each + // of the roots for this pass, and also the point + // locations for 3D texture evaluation. + MFloatMatrix camMatrix; + + // if we store the lookups we have done then we + // don't need to do them again. This will save both + // time and memory for high density scenes. + MStringArray doneLookups; + MIntArray doneLookupsIndex; + + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + int ind; + int total; + total=nodeInfo->count; + int parmIndex = (int)connectedIndexes[pi]; + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + MObject texture = texPlugArray[0].node(); + + for (ti = 0; ti < numTextures; ti++) + if (textures[ti] == texture) break; + + + nodeInfo->textureLookup[pass][parmIndex] + = (float*) calloc( + total, sizeof(float) + ); + + + for (ind=0;ind<nodeInfo->count;ind+=maxtex) + { + uArray.clear(); + vArray.clear(); + pointArray.clear(); + int szz=total; + + if (total>=maxtex) szz=total-ind; + if (szz>maxtex) szz=maxtex; +//fprintf (stdout,"ind = %d szz= %d\n",ind,szz);fflush(stdout); + + for (hairnum = ind; hairnum < ind+szz; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + for (int k = 0; k < 3; k++) + { + pos[k] = sNode->memShaveObj + .v[hair->pntid[k]]; + pos[k].x *= hair->wgt[k]; + pos[k].y *= hair->wgt[k]; + pos[k].z *= hair->wgt[k]; + } + + uArray.append( + textureUVMaps[ti][pass].us[hairnum] + ); + vArray.append( + textureUVMaps[ti][pass].vs[hairnum] + ); + + pointArray.append( + pos[0].x+pos[1].x+pos[2].x, + pos[0].y+pos[1].y+pos[2].y, + pos[0].z+pos[1].z+pos[2].z + ); + } + + // set aside a little memory for our lookup results. + + +{ + { + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + + //////debug////// + //MFloatVectorArray uTang; + //MFloatVectorArray vTang; + //MFloatArray filter; + //uTang.setLength(szz); + //vTang.setLength(szz); + //filter.setLength(szz); + //for(unsigned int k = 0; k < szz; k++) + //{ + // uTang[k] = MFloatVector(1.0f,0.0f); + // vTang[k] = MFloatVector(0.0f,1.0f); + // filter[k] = 0.1f; + //} + ///////////////////////// + + //printf("texture name %s\n",texPlugArray[0].name().asChar() );fflush(stdout); + + // lets get the lookup values from the maya + // shading engine. + MStatus shRes = + MRenderUtil::sampleShadingNetwork( + texPlugArray[0].name(), //shade node name + szz, //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL /*&uTang*/, //u tang + NULL /*&vTang*/, //v tang + NULL /*&filter*/, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); +//fprintf (stdout,"done with lookup\n");fflush(stdout); + // we need to copy the returned values into the + // lookup array our pointer references. also, + // we'll store the name and parm index of this + // lookup so we can reuse it if the opportunity + // presents itself. + nodeInfo->maxIndex[parmIndex] + = (int)total; + + ///////// debug /////////////// + //if(shRes != MStatus::kSuccess) printf(" MRenderUtil::sampleShadingNetwork failed with result %i\n",shRes); + //for(int k=0; k < (int)szz; k++) + //{ + // printf("uv %f %f rgb %f %f %f t %f\n",uArray[k],vArray[k],vertColorArray[k].x,vertColorArray[k].y,vertColorArray[k].z,vertTranspArray[k]); + //} + //fflush(stdout); + /////////////////////////////// + + for(int k=0; k < (int)szz; k++) + { + nodeInfo->textureLookup[pass][parmIndex][k+ind] + = (float)vertColorArray[k].x; +// nodeInfo->textureLookup[pass][parmIndex][k] +// = (float)vertColorArray[k].x; + } + } +} + } + + } + } + + if (verbose) + { + cerr << "done." << endl; + cerr << "freeing cached curveinfos and uvlists..." << endl; + } + + for (ti = 0; ti < numTextures; ti++) + { + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + delete [] textureUVMaps[ti][pass].us; + delete [] textureUVMaps[ti][pass].vs; + } + + delete [] textureUVMaps[ti]; + } + + delete [] textureUVMaps; + + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + delete [] hairInfo[pass]; + + delete [] hairInfo; + } + + delete [] faceToMesh; + + UVInfoCache::iterator setIter; + + for (setIter = uvInfoCache.begin(); + setIter != uvInfoCache.end(); + ++setIter) + { + UVSetUVInfo* uvSetUVInfo = (*setIter).second; + + for (unsigned int ui = 0; ui < uvSetUVInfo->size(); ++ui) + delete (*uvSetUVInfo)[ui]; + + delete uvSetUVInfo; + } + + uvInfoCache.clear(); + + // we can't set the textured member true until we've taken + // care of all the passes. Otherwise make a curve will fail + // trying to do a lookup. + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + unsigned int pIndex = connectedIndexes[pi]; + nodeInfo->textured[pIndex] = true; + } + + // remove temporary connections that were made in the + // hypergraph. not strictly necesary, but keeps thing + // looking cleaner for the user. + dgMod.undoIt(); + + // + // Let the node know that its texture cache has changed. + // + MPlug plug( + shaveHairShapes[node], + shaveHairShape::textureCacheUpdatedAttr + ); + plug.setValue(true); + } + } + } + + // + // It's now safe to serve up the texture lookup tables. + // + buildingLookups = false; + + //if (verbose) cerr << "done with lookups." << endl << endl; +} + + + +MStatus freeTextureLookup(void) +{ + if (texInfoLookup) + { + for (unsigned int node = 0; node < texInfoLookupSize; node++) { + freeTextureLookup(&texInfoLookup[node]); + } + + free(texInfoLookup); + texInfoLookup = NULL; + texInfoLookupSize = 0; + } + + if (texIDMap) + { + delete [] texIDMap; + texIDMap = NULL; + texIDMapSize = 0; + } + + return MS::kSuccess; +} + +MStatus freeTextureLookup(NODETEXINFO* lookup) +{ + if (lookup) + { + bool istextured = false; + int clearPasses = lookup->maxPasses; + + for(int pass = 0; pass < clearPasses; pass++) + { + if (lookup->displacement) + { + delete [] lookup->displacement[pass]; + lookup->displacement[pass] = NULL; + } + delete [] lookup->u[pass]; + lookup->u[pass] = NULL; + + delete [] lookup->v[pass]; + lookup->v[pass] = NULL; + + for(int parm = 0; parm < SHAVE_NUM_PARAMS; parm++) + { + if(lookup->textured[parm]) + { + free(lookup->textureLookup[pass][parm]); + lookup->textureLookup[pass][parm] = NULL; + istextured = true; + } + } + + if(istextured) + { + free(lookup->textureLookup[pass]); + lookup->textureLookup[pass] = NULL; + } + } + + if (lookup->displacement) + { + delete [] lookup->displacement; + lookup->displacement = NULL; + } + delete [] lookup->u; + lookup->u = NULL; + delete [] lookup->v; + lookup->v = NULL; + + if (lookup->textureLookup) + { + free(lookup->textureLookup); + lookup->textureLookup = NULL; + } + } + + return MS::kSuccess; +} + + +void getDisplacement( + MDagPath& surface, + CURVEINFO& ci, + VERT& rootPos, + float rootU, + float rootV, + int surfaceStartFaceIndex, + VERT& displacement +) +{ + displacement.x = 0; + displacement.y = 0; + displacement.z = 0; + + if (surface.hasFn(MFn::kNurbsSurface)) + { + MPoint actualPoint; + MFnNurbsSurface nurbsFn(surface); + MPoint shavePt(rootPos.x, rootPos.y, rootPos.z); + + actualPoint = nurbsFn.closestPoint( + shavePt, NULL, NULL, true, MPoint_kTol, MSpace::kWorld + ); + + displacement.x = (float)(actualPoint.x - shavePt.x); + displacement.y = (float)(actualPoint.y - shavePt.y); + displacement.z = (float)(actualPoint.z - shavePt.z); + } +#ifdef EVALUATE_POSITION_FIXED + else if (surface.hasFn(MFn::kSubdiv)) + { + int i; + int level1FaceIdx = (ci->UTpid - surfaceStartFaceIndex) / 2; + MFnSubd subdFn(surface); + + int numLevel0Faces = subdFn.polygonCount(0); + + for (i = 0; i < numLevel0Faces; i++) + { + MUint64 level0FaceID; + MUint64Array level1Faces; + + MFnSubdNames::toMUint64(level0FaceID, i, 0, 0, 0, 0); + subdFn.polygonChildren(level0FaceID, level1Faces); + + if ((unsigned int)level1FaceIdx >= level1Faces.length()) + { + level1FaceIdx -= level1Faces.length(); + } + else + { + MPoint actualPoint; + + subdFn.evaluatePosition( + level1Faces[level1FaceIdx], + rootU, + rootV, + false, + actualPoint + ); + + // + // 'actualPoint' is in object space. Convert it to world. + // + MMatrix mat = surface.inclusiveMatrix(); + + actualPoint = actualPoint * mat; + + displacement.x = actualPoint.x - rootPos.x; + displacement.y = actualPoint.y - rootPos.y; + displacement.z = actualPoint.z - rootPos.z; + break; + } + } + } + else if (!surface.hasFn(MFn::kMesh) && !surface.hasFn(MFn::kNurbsCurve)) +#else + else if (!surface.hasFn(MFn::kMesh) + && !surface.hasFn(MFn::kSubdiv) + && !surface.hasFn(MFn::kNurbsCurve)) +#endif + { + MGlobal::displayWarning( + MString("shave: getDisplacement: got request for invalid") + + " growth surface '" + surface.fullPathName() + "'." + ); + } +} + + +NODETEXINFO* getTexInfoLookup(unsigned shaveID) +{ + if (buildingLookups + || (texIDMap == NULL) + || (shaveID < 0) + || (shaveID >= texIDMapSize)) + { + return NULL; + } + + int lookupID = texIDMap[shaveID]; + + if (lookupID == -1) return NULL; + + return &texInfoLookup[lookupID]; +} + + +// +// Connect the source plug to the specified element of the given +// shaveHairShape's texture array plug, using the supplied DGModifier. +// +static MStatus makeTextureConnection( + MPlug sourcePlug, MObject shavenode, int texPlugIndex, MDGModifier& dgMod +) +{ + MPlug texPlugArray(shavenode, shaveHairShape::shaveTextureAttr); + + dgMod.connect(sourcePlug, texPlugArray.elementByLogicalIndex(texPlugIndex)); + + return MS::kSuccess; +} + + +// +// If a colour shaveHairShape parameter has an incoming connection, create +// separate temporary connections to the 'tex' array for each of its RGB +// components. +// +static void makeTempCompoundConnection( + const MObject& shavenode, + MObject& colourAttr, + int startIndex, + MDGModifier& dgMod +) +{ + MPlug colourPlug(shavenode, colourAttr); + + if (colourPlug.isConnected()) + { + // + // Find the node which is feeding this connection. + // + MPlugArray connections; + + colourPlug.connectedTo(connections, true, false); + if(connections.length() > 0) + { + MFnDependencyNode texNode(connections[0].node()); + + // + // Connect the component plugs to the shaveHairShape's 'shaveTex' + // array. + // + colourPlug = texNode.findPlug("outColorR"); + makeTextureConnection(colourPlug, shavenode, startIndex, dgMod); + + colourPlug = texNode.findPlug("outColorG"); + makeTextureConnection(colourPlug, shavenode, startIndex+1, dgMod); + + colourPlug = texNode.findPlug("outColorB"); + makeTextureConnection(colourPlug, shavenode, startIndex+2, dgMod); + } + } +} + + +static void makeTempColourConnections( + const MObject& shavenode, MDGModifier& dgMod +) +{ + // first we need to hook up the 'special' nodes- the color + // ones. These nodes are handled differently than the rest + // because they hook one tex up to a vector value, rather + // than one float. + makeTempCompoundConnection( + shavenode, shaveHairShape::hairColorTexture, 9, dgMod + ); + + makeTempCompoundConnection( + shavenode, shaveHairShape::mutantHairColorTexture, 13, dgMod + ); + + makeTempCompoundConnection( + shavenode, shaveHairShape::rootHairColorTexture, 17, dgMod + ); + + MObject colourAttrs[] = + { + shaveHairShape::hairColorTextureR, + shaveHairShape::hairColorTextureG, + shaveHairShape::hairColorTextureB, + shaveHairShape::mutantHairColorTextureR, + shaveHairShape::mutantHairColorTextureG, + shaveHairShape::mutantHairColorTextureB, + shaveHairShape::rootHairColorTextureR, + shaveHairShape::rootHairColorTextureG, + shaveHairShape::rootHairColorTextureB + }; + + int attrIndices[] = { 9, 10, 11, 13, 14, 15, 17, 18, 19 }; + int numAttrs = sizeof(attrIndices) / sizeof(int); + int i; + MPlugArray texturePlug; + + for (i = 0; i < numAttrs; i++) + { + MPlug colourPlug(shavenode, colourAttrs[i]); + + if (colourPlug.isConnected()) + { + colourPlug.connectedTo(texturePlug, true, false); + + makeTextureConnection( + texturePlug[0], shavenode, attrIndices[i], dgMod + ); + } + } +} + + +MStatus tesselateGrowthSurfaces( + const MDagPathArray& growthSurfaces, + short uTess, short vTess, + short sDept, short sSamp +) +{ + MDagPath surface; + unsigned int i; + + standinMeshes.clear(); + standinMeshOrigSurfaces.clear(); + + for (i = 0; i < growthSurfaces.length(); i++) + { + surface = growthSurfaces[i]; + + // + // Make sure that we've got the shape and not its transform. + // + surface.extendToShape(); + +#ifdef EVALUATE_POSITION_FIXED + // + // If the shape is a NURBS surface, then we have to generate a + // temporary mesh for it. + // + if (surface.hasFn(MFn::kNurbsSurface)) +#else + // + // If the shape is a NURBS or subdivision surface, then we have to + // generate a temporary mesh for it. + // + if (surface.hasFn(MFn::kNurbsSurface) || surface.hasFn(MFn::kSubdiv)) +#endif + { + MObject tempMesh = shaveUtil::getMesh(surface, (int)uTess, (int)vTess, (int)sDept, (int)sSamp); + standinMeshes.append(tempMesh); + standinMeshOrigSurfaces.append(surface); + } + } + + return MS::kSuccess; +} + + +unsigned int* createFaceIDMap(const MIntArray& startIndices) +{ + if (startIndices.length() == 0) return new unsigned int[1]; + + unsigned int numFaces = startIndices[startIndices.length()-1]; + unsigned int* map = new unsigned int[numFaces]; + unsigned int i; + unsigned int j = 0; + + for (i = 1; i < startIndices.length(); i++) + { + while (j < (unsigned int)startIndices[i]) + map[j++] = i-1; + } + + return map; +} + + +const MString ShavePreprocTex::commandName("shaveUpdateTextures"); + +static const char* flVertexParams = "-vertexParams"; +static const char* fsVertexParams = "-vp"; + +ShavePreprocTex::~ShavePreprocTex() {} + +void* ShavePreprocTex::createCmd() +{ + return new ShavePreprocTex(); +} + + +MSyntax ShavePreprocTex::createSyntax() +{ + MSyntax syntax; + + syntax.addFlag(fsVertexParams, flVertexParams); + + return syntax; +} + + +MStatus ShavePreprocTex::doIt( const MArgList& args ) +{ + MStatus res = MS::kSuccess; + MArgDatabase argdb(syntax(), args, &res); + if (!res) return res; + + MObjectArray shaveHairShapes; + shaveUtil::getShaveNodes(shaveHairShapes); + + if (shaveHairShapes.length() > 0) + { + MGlobal::displayInfo("command shaveUpdateTextures"); + + shaveGlobals::Globals globals; + shaveGlobals::getGlobals(globals); + + if (argdb.isFlagSet(fsVertexParams)) + initVertTexInfoLookup(shaveHairShapes); + else +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapes, "", globals.verbose, true); +#else + initTexInfoLookup(shaveHairShapes, "", globals.verbose, true); +#endif + + MGlobal::executeCommand("currentTime `currentTime -q`"); + } + + return res; +} + + +// Initialize the per-vertex texture lookup for a single shaveHairShape. +// +// If this method is called during shaveHairShape::compute() then 'block' must +// be provided and must point to the same datablock as was passed to +// shaveHairShape::compute(). +// +void initVertTexInfoLookup(shaveHairShape* sNode, MObject node, MDataBlock* block) +{ + MPlug checkPlug; + MObject nodeObj = sNode->thisMObject(); + MPlug texPlug(nodeObj, shaveHairShape::shaveTextureAttr); + MPlugArray texPlugArray; + unsigned int vertCount = sNode->memShaveObj.totalverts; + + // Get get the hair shape's per-vertex texture data. + ShavePerVertTexInfo* vertInfo = &sNode->vertTexInfo; + + // Prepare it to accept new data. + vertInfo->init(vertCount); + + // Store a pointer to the node's info in a map where it can be + // quickly indexed by Shave ID. + vertTexInfoMap[sNode->getShaveID()] = vertInfo; + + + // Step through each of the per-vertex parameters and gather texture + // data for any which are textured. + unsigned int pi = 0; + for (pi = 0; pi < ShavePerVertTexInfo::numParams(); pi++) + { + int paramID = ShavePerVertTexInfo::getParamNumber(pi); + + checkPlug = texPlug.elementByLogicalIndex(paramID); + + if (checkPlug.isConnected()) + { + // + // Get the texture plug so we can determine what + // node to sample. + // + texPlugArray.clear(); + texPlug + .elementByLogicalIndex(paramID) + .connectedTo(texPlugArray, true, false); + + MFloatArray uArray; + MFloatArray vArray; + MFloatPointArray pointArray; + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + MFloatMatrix camMatrix; + VERT wfpos; + + for (unsigned int vi = 0; vi < vertCount; ++vi) + { + //uArray.append(sNode->memShaveObj.uv[vi].x); + //vArray.append(sNode->memShaveObj.uv[vi].y); + + wfpos = sNode->memShaveObj.v[vi]; + + pointArray.append( + MFloatPoint(wfpos.x, wfpos.y, wfpos.z) + ); + //printf("x %f y %f z %f\n", (float)wfpos.x,(float)wfpos.y,(float)wfpos.z); + } + + /// by some reason memShaveObj contains some weird UVs + /// so vertex maps are not mappad same as others + /// so we are trying to grab UVs from export mesh via Maya api + /// if something is wrong with it, the uvs from memShaveObj are used + unsigned int n = 0; + + //printf("num exp meshes %i\n",sNode->exportData.meshes.length());fflush(stdout); + + if(sNode->exportData.meshes.length() > 0) + { + short uTess; + short vTess; + short sDept; + short sSamp; + + if (block != NULL) + { + uTess = block->inputValue(shaveHairShape::surfTessU).asShort(); + vTess = block->inputValue(shaveHairShape::surfTessV).asShort(); + sDept = block->inputValue(shaveHairShape::subdTessDept).asShort(); + sSamp = block->inputValue(shaveHairShape::subdTessSamp).asShort(); + } + else + { + MPlug plug(node, shaveHairShape::surfTessU); + plug.getValue(uTess); + plug.setAttribute(shaveHairShape::surfTessV); + plug.getValue(vTess); + + plug.setAttribute(shaveHairShape::subdTessDept); + plug.getValue(sDept); + plug.setAttribute(shaveHairShape::subdTessSamp); + plug.getValue(sSamp); + } + + //MItMeshVertex vIter(sNode->exportData.meshes[0]); + + + for(unsigned int oo = 0; oo < sNode->exportData.meshes.length(); oo++) + { + MDagPath surface = sNode->exportData.meshes[oo]; + surface.extendToShape(); + MObject mesh = shaveUtil::getMesh(surface, (int)uTess, (int)vTess, (int)sDept, (int)sSamp); + MItMeshVertex vIter(mesh); + + for (vIter.reset(); !vIter.isDone(); vIter.next()) + { + int numUVs; + vIter.numUVs(numUVs); + if (numUVs > 0) + { + float2 vertUV; + vIter.getUV(vertUV); + uArray.append(vertUV[0]); + vArray.append(vertUV[1]); + //printf("u %f v %f\n", vertUV[0], vertUV[1]); + } + else + { + uArray.append(0.0f); + vArray.append(0.0f); + } + n++; + } + } + } + if(n != vertCount) + { + uArray.clear(); + vArray.clear(); + for (unsigned int vi = 0; vi < vertCount; ++vi) + { + uArray.append(sNode->memShaveObj.uv[vi].x); + vArray.append(sNode->memShaveObj.uv[vi].y); + } + } + //printf("texture %s\n",texPlugArray[0].name().asChar()); + //printf("uvs stored %i vertices %i\n", n, vertCount);fflush(stdout); + //////////////////////////// + + // Sample the texture at all vertices. + MRenderUtil::sampleShadingNetwork( + texPlugArray[0].name(), //shade node name + uArray.length(), //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL, //u tang + NULL, //v tang + NULL, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); + + // Store the sampled texture values. + for (unsigned int k = 0; k < vertColorArray.length(); ++k) + { + vertInfo->setValue(pi, k, (float)vertColorArray[k].x); + + //vertInfo->setValue(pi, k, k < vertColorArray.length()/2 ? 0.0f : 1.0f); //test - OK + //printf("r %f g %f b %f\n", (float)vertColorArray[k].x,(float)vertColorArray[k].y,(float)vertColorArray[k].z);fflush(stdout); + } + } + } +} + + +// Vertex-level textures are used for dynamics runup and Live Mode. +// They're not used in rendering or export. +void initVertTexInfoLookup(MObjectArray& shaveHairShapes) +{ + buildingLookups = true; + + unsigned int numNodes = shaveHairShapes.length(); + + for (unsigned int i = 0; i < numNodes; ++i) + { + // Get the hair shape. + MFnDependencyNode shaveDependNode(shaveHairShapes[i]); + shaveHairShape* node = (shaveHairShape*) shaveDependNode.userNode(); + + // Force the guides to update if the geometry has changed, + // e.g. because we're on a new frame. + node->getHairNode(); + + // Make sure that our parameters are all up to date with + // any changes the user may have made. + // + // %%% Shouldn't the 'getHairNode' call above already have + // taken care of that? + node->updateParams(); + + // Let Shave know which node our calls will be referring to. + node->makeCurrent(); + + // Update the node's per-vertex texture info. + initVertTexInfoLookup(node,shaveHairShapes[i]); + + // + // Let the node know that its texture cache has changed. + // + MPlug plug(shaveHairShapes[i], shaveHairShape::textureCacheUpdatedAttr); + plug.setValue(true); + } + + // It's now safe to serve up the texture lookup data. + buildingLookups = false; +} + + +float applyVertTexValue(long shaveID, int vertID, int paramID, float baseValue) +{ + if (!buildingLookups) + { + ShavePerVertTexInfo* vertTexInfo = vertTexInfoMap[shaveID]; + + if (vertTexInfo) + { + // Find the index of this parameter. + unsigned int paramIdx; + + if (ShavePerVertTexInfo::getParamIdx(paramID, paramIdx)) + return (baseValue * vertTexInfo->getValue(paramIdx, vertID)); + } + } + + return baseValue; +} + + +void cacheSurfaceUVs( + MItMeshPolygon& polyIter, + unsigned int pntidOffset, + const MString& uvSet, + SurfaceUVInfo* surfUVInfo +) +{ + FaceUVInfo faceUVInfo; + VertUVInfo vertUVInfo; + + surfUVInfo->reserve(polyIter.count ()); + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next()) + { + unsigned int numFaceVerts = polyIter.polygonVertexCount(); + unsigned int i; + + faceUVInfo.clear(); + faceUVInfo.reserve(numFaceVerts); + + for (i = 0; i < numFaceVerts; ++i) + { + float2 uv = { 0.0f, 0.0f }; + + // If no UV set is specified, use the mesh's default set. + if (uvSet.length() == 0) + { + polyIter.getUV(i, uv); + } + else + { + MStatus st = polyIter.getUV(i, uv, &uvSet); + + // If we couldn't find a UV it's probably because we have + // an uninitialized UV set. So fall back to the default + // mapping. + if (!st) + { + polyIter.getUV(i, uv); + } + } + + vertUVInfo.u = uv[0]; + vertUVInfo.v = uv[1]; + vertUVInfo.pntid = pntidOffset + polyIter.vertexIndex(i); + + faceUVInfo.push_back(vertUVInfo); + } + + surfUVInfo->push_back(faceUVInfo); + } +} + + +SurfaceUVInfo* cacheUVs( + shaveHairShape* sNode, + unsigned int surfIdx, + const MString& uvSet, + UVInfoCache& uvInfoCache +) +{ + UVSetUVInfo* uvSetUVInfo = NULL; + + // Do we have any data cached for this uv set? + UVInfoCache::iterator it = uvInfoCache.find(uvSet); + + if (it == uvInfoCache.end()) + { + // No data for this uv set yet, so create an entry with empty + // slots for each growth surface. + uvSetUVInfo = new UVSetUVInfo; + + unsigned int numSurfaces = sNode->exportData.meshes.length(); + + uvSetUVInfo->reserve(numSurfaces); + uvInfoCache.insert(UVInfoCache::value_type(uvSet, uvSetUVInfo)); + + for (unsigned int i = 0; i < numSurfaces; ++i) + uvSetUVInfo->push_back(NULL); + } + else + { + uvSetUVInfo = (*it).second; + + // If we've already cached the UVs for this growth mesh, return + // them. + if ((*uvSetUVInfo)[surfIdx] != NULL) return (*uvSetUVInfo)[surfIdx]; + } + + SurfaceUVInfo* surfUVInfo = new SurfaceUVInfo; + MDagPath surfPath = sNode->exportData.meshes[surfIdx]; + + // If hair we are dealing with is growing from a nurbs surface, + // then we cannot initialize the poly iterator with its path because + // the path doesn't point to a mesh. Instead we must initialize it + // with the tesselated mesh object that we created earlier. +#ifdef EVALUATE_POSITION_FIXED + if (surfPath.hasFn(MFn::kNurbsSurface)) +#else + if (surfPath.hasFn(MFn::kNurbsSurface) || surfPath.hasFn(MFn::kSubdiv)) +#endif + { + unsigned int i; + + for (i = 0; i < standinMeshOrigSurfaces.length(); i++) + { + if (standinMeshOrigSurfaces[i] == surfPath) break; + } + + if (i < standinMeshes.length()) + { + MItMeshPolygon polyIter(standinMeshes[i]); + + cacheSurfaceUVs( + polyIter, + sNode->exportData.startVerts[surfIdx], + uvSet, + surfUVInfo + ); + } + } + else + { + MItMeshPolygon polyIter(surfPath); + + // Meshes with history can sometimes get themselves into a state + // where an MFnMesh created from the dag path (or node) reports + // vertices but no faces. If that happens then we need to pull + // the mesh from its output plug. + if (polyIter.count() == 0) + { + MFnDagNode nodeFn(surfPath); + MPlug plug = nodeFn.findPlug("outMesh"); + MObject meshObj; + + plug.getValue(meshObj); + + MItMeshPolygon polyIter(meshObj); + + cacheSurfaceUVs( + polyIter, + sNode->exportData.startVerts[surfIdx], + uvSet, + surfUVInfo + ); + } + else + { + cacheSurfaceUVs( + polyIter, + sNode->exportData.startVerts[surfIdx], + uvSet, + surfUVInfo + ); + } + } + + (*uvSetUVInfo)[surfIdx] = surfUVInfo; + + return surfUVInfo; +} diff --git a/mayaPlug/shaveTextureStore.h b/mayaPlug/shaveTextureStore.h new file mode 100644 index 0000000..3799a70 --- /dev/null +++ b/mayaPlug/shaveTextureStore.h @@ -0,0 +1,109 @@ +#ifndef _STS_ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MString.h> +#include <maya/MSelectionList.h> +#include <maya/MFnMesh.h> +#include <maya/MObjectArray.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include "shaveSDK.h" + +#if MAYA_API_VERSION < 20180000 +class MDataBlock; +#endif + +class shaveHairShape; + +#define PER_NODE_TEXLOOKUP + + +typedef struct { + shaveHairShape* node; + int count; + VERT** displacement; + int maxIndex[SHAVE_NUM_PARAMS]; + int maxPasses; + bool textured[SHAVE_NUM_PARAMS]; + float*** textureLookup; + float** u; + float** v; +} NODETEXINFO; + +typedef struct { + float* us; + float* vs; + int passes; + char* uvset; +} HAIRUVS; + +bool IsBuildingLookups(); + + +float applyVertTexValue( + long shaveID, int vertID, int paramID, float baseValue + ); + +unsigned int findObjectIndex(int, MIntArray); +MStatus freeTextureLookup(void); +MStatus freeTextureLookup(NODETEXINFO* lookup); + +void getDisplacement( + MDagPath& surface, + CURVEINFO& ci, + VERT& rootPos, + float rootU, + float rootV, + int surfaceStartFaceIndex, + VERT& displacement + ); + +NODETEXINFO* getTexInfoLookup(unsigned nodeID); + +void initTexInfoLookup( + const MObjectArray& shaveNodes, + MString defaultUVSet, + bool verbose, + bool displayHairsOnly = false + ); + +void initTexInfoLookup2( + const MObjectArray& shaveNodes, + MString defaultUVSet, + bool verbose, + bool displayHairsOnly = false, + MObject onlythis = MObject() + ); + +void initVertTexInfoLookup( + shaveHairShape* sNode, MObject node, MDataBlock* block=NULL + ); + +void initVertTexInfoLookup(MObjectArray& shaveNodes); +MStatus shaveConnectNode(MString, MString, int, MStringArray&); + +MStatus tesselateGrowthSurfaces( + const MDagPathArray& growthSurfaces, + short uTess, + short vTess, + short sDept, + short sSamp + ); + + +class ShavePreprocTex : public MPxCommand +{ +public: + ShavePreprocTex() {}; + virtual ~ShavePreprocTex(); + + MStatus doIt( const MArgList& args ); + static void* createCmd(); + static MSyntax createSyntax(); + static const MString commandName; +}; + +#define _STS_ +#endif diff --git a/mayaPlug/shaveUtil.cpp b/mayaPlug/shaveUtil.cpp new file mode 100644 index 0000000..e6f97db --- /dev/null +++ b/mayaPlug/shaveUtil.cpp @@ -0,0 +1,1948 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDagModifier.h> +#include <maya/MDagPath.h> +#include <maya/MDoubleArray.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPoint.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFloatVector.h> +#include <maya/MFloatVectorArray.h> +#include <maya/MFn.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnDoubleIndexedComponent.h> +#include <maya/MFnLight.h> +#include <maya/MFnMeshData.h> +#include <maya/MFnNurbsSurface.h> +#include <maya/MFnSet.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MFnSubd.h> +#include <maya/MFnSubdNames.h> +#include <maya/MGlobal.h> +#include <maya/MIntArray.h> +#include <maya/MItDag.h> +#include <maya/MItDependencyNodes.h> +#include <maya/MItMeshVertex.h> +#include <maya/MMatrix.h> +#include <maya/MObjectArray.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> +#include <maya/MPointArray.h> +#include <maya/MRenderUtil.h> +#include <maya/MSelectionList.h> +#include <maya/MTransformationMatrix.h> +#include <maya/MUint64Array.h> + +#ifdef _WIN32 +# include <process.h> +# include <stdlib.h> +# include <time.h> +# include <winsock2.h> +#else +# include <sys/times.h> +# include <unistd.h> +#endif + +#include "shaveCheckObjectVisibility.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveIO.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveSDKTYPES.h" +#include "shaveUtil.h" + +#if defined(OSMac_) && !defined(OSMac_MachO_) +#include "shaveMacCarbon.h" +#endif + +std::vector<shaveUtil::LightInfo> shaveUtil::globalLightList; +MDagPathArray shaveUtil::mFields; +bool shaveUtil::mFieldsDirty = true; +bool shaveUtil::mIgnoreNextSelectionChange = false; +int shaveUtil::mLoadDepth = 0; + + +void shaveUtil::buildLightList() +{ + MStatus st; + + globalLightList.clear(); + + // + // Step through all the lights in the scene. + // + MItDag iter(MItDag::kDepthFirst, MFn::kLight); + LightInfo info; + + for (; !iter.isDone(); iter.next()) + { + iter.getPath(info.path); + + if (areObjectAndParentsVisible(info.path, false)) + { + bool addIt = false; + + // + // If all lights are being used then add the light to the list. + // Otherwise we only add it if it has had Shave shadow + // attributes added to it. + // + if (useAllLightsGlob) + addIt = true; + else + { + MFnLight lightFn(info.path); + MPlug plug = lightFn.findPlug("shaveShadowResolution", &st); + + if (st) addIt = true; + } + + if (addIt) + { + // + // In shaders, light info is passed in through attributes, + // so we don't know which light it belongs to. That makes + // it difficult to determine the lightID to use in calls to + // SHAVEilluminate_light. + // + // Fortunately, shaders are passed the light's blindData + // pointer, which appears to be unique for each light. So + // by saving each light's blind data pointer, we can + // provide a reverse lookup to give us the index into the + // globalLightList. + // + MFnDagNode nodeFn(info.path); + MPlug plug = nodeFn.findPlug("lightBlindData"); + + info.blindData = getAddrPlugValue(plug); + + // We won't know the light IDs until we add register them + // with Shave. + // + info.minId = -1; + info.maxId = -1; + + globalLightList.push_back(info); + } + } + } +} + + +void shaveUtil::classifyShaveNodes( + const MObjectArray& shaveHairShapes, + bool& haveHair, + bool& haveInstances +) +{ + haveHair = haveInstances = false; + + unsigned i; + + for (i = 0; i < shaveHairShapes.length(); i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + if (nodePtr) + { + if (nodePtr->isInstanced()) + haveInstances = true; + else + haveHair = true; + } + } +} + + +int shaveUtil::compareShaveVersions(MString v1, MString v2) +{ + int v1Parts[3]; + int v2Parts[3]; + + splitShaveVersion(v1, v1Parts); + splitShaveVersion(v2, v2Parts); + + if (v1Parts[0] < v2Parts[0]) return -1; + if (v1Parts[0] > v2Parts[0]) return 1; + + if (v1Parts[1] < v2Parts[1]) return -1; + if (v1Parts[1] > v2Parts[1]) return 1; + + if (v1Parts[2] < v2Parts[2]) return -1; + if (v1Parts[2] > v2Parts[2]) return 1; + + return 0; +} + + +// Copy one full line of input to the output and return as much of it as +// will fit in the buffer provided by the caller. +// +// Returns false if there was no input left to read. +bool shaveUtil::fileCopyLine( + FILE* input, + FILE* output, + char* buffer, + unsigned int bufferSize +) +{ + if (!fgets(buffer, bufferSize, input)) return false; + + fputs(buffer, output); + + size_t lineLen = strlen(buffer); + + // Did we get a full line? + // + // Note that CFM OSX uses '\r' for EOL, so we have to check for that + // as well. + if ((lineLen > 0) + && (buffer[lineLen-1] != '\n') + && (buffer[lineLen-1] != '\r')) + { + int c; + + // Copy chars from the input to the output until we reach the end + // of the line or the end of the file. + while ((c = fgetc(input)) != EOF) + { + fputc(c, output); + if ((c == '\n') || (c == '\r')) break; + } + } + + return true; +} + + +// Whenever the user changes Shave component selection types we +// automatically convert the current selection to match the new type. So if +// there are currently several guides selected and the selection mode +// changes to "tip" then we replace the selected guides with selections of +// their tip vertices. +// +// Note that in "root" mode while we are using the root verts to guide the +// selection, it is actually guides which are being selected. +// +bool shaveUtil::convertComponentSelections(bool* pHiliteListChanged) +{ + if (pHiliteListChanged) *pHiliteListChanged = false; + + if (MGlobal::selectionMode() != MGlobal::kSelectComponentMode) return false; + + bool currentIsHilited = true; + MDagPath currentShape; + MSelectionList hiliteList; + unsigned i; + unsigned j; + unsigned k; + const int tipIdx = SHAVE_VERTS_PER_GUIDE - 1; + + // The first shaveHairShape on the hilite list is the current node. + // + MGlobal::getHiliteList(hiliteList); + currentShape = getFirstHairShape(hiliteList); + + // If no shaveHairShape was found on the hilite list, then grab the + // first one which is on the selection list, or which has a component + // on the selection list. + // + MSelectionList selectionList; + + MGlobal::getActiveSelectionList(selectionList); + + if (!currentShape.isValid()) + { + currentIsHilited = false; + currentShape = getFirstHairShape(selectionList); + + // + // If we still don't have a hair shape then there's nothing to be + // done. + // + if (!currentShape.isValid()) return false; + } + + // Get Shave's component selection mode. + // + MString selModeStr; + + MGlobal::executeCommand( + "optionVar -q shaveBrushSelectMode", selModeStr + ); + + unsigned char selMode = *selModeStr.asChar(); + + // Remove from the selection list any hair shapes other than the + // current one and convert component selections for the current hair + // shape to match the current component selection type. + // + MObject comp; + MFnSingleIndexedComponent guideCompFn; + bool haveSelectedComponents = false; + MDagPath path; + bool selectionChanged = false; + MFnDoubleIndexedComponent vertCompFn; + + for (i = selectionList.length(); i > 0; i--) + { + unsigned itemIdx = i - 1; + + if (selectionList.getDagPath(itemIdx, path, comp)) + { + path.extendToShape(); + + MFnDagNode nodeFn(path); + + if (nodeFn.typeId() == shaveHairShape::id) + { + // If this is some other hair shape, remove it. + // + if (!(path == currentShape)) + { + selectionList.remove(itemIdx); + selectionChanged = true; + } + else if (!comp.isNull()) + { + MFn::Type compType = comp.apiType(); + MObject newComp; + + if (compType == shaveHairShape::kShaveGuideComponent) + { + // Get the indices of the selected guides. + // + MIntArray guides; + + guideCompFn.setObject(comp); + guideCompFn.getElements(guides); + + // If we're in tip selection mode then replace the + // selected guides with just their tip vertices. + // + // If we're in vertex selection mode then replace + // the selected guides with all of their vertices. + // + // If we're in guide, guide-by-root or guide-by-tip + // mode then we're happy with the guide selections as + // they are. + // + switch (selMode) + { + case 't': + newComp = vertCompFn.create( + shaveHairShape::kShaveGuideVertComponent + ); + + for (j = 0; j < guides.length(); j++) + { + vertCompFn.addElement(guides[j], tipIdx); + } + break; + + case 'v': + newComp = vertCompFn.create( + shaveHairShape::kShaveGuideVertComponent + ); + + for (j = 0; j < guides.length(); j++) + { + for (k = 0; k < SHAVE_VERTS_PER_GUIDE; k++) + vertCompFn.addElement(guides[j], k); + } + break; + } + + haveSelectedComponents = true; + } + else if (compType == shaveHairShape::kShaveGuideVertComponent) + { + // Get the indices of the selected verts. + // + MIntArray guides; + MIntArray verts; + + vertCompFn.setObject(comp); + vertCompFn.getElements(guides, verts); + + // If we're in guide or guide-by-root selection + // mode, then just select the corresponding guides. + // + // If we're in tip selection mode, then select just + // the tip vert. + // + // If we're in vert mode, we're happy with the + // selection as-is. + // + switch (selMode) + { + case 'g': + case 'r': + newComp = guideCompFn.create( + shaveHairShape::kShaveGuideComponent + ); + guideCompFn.addElements(guides); + break; + + case 't': + // If the verts are already all tips then we + // can leave the selection as-is. + // + for (j = 0; j < verts.length(); j++) + { + if (verts[j] != tipIdx) break; + } + + if (j < verts.length()) + { + newComp = vertCompFn.create( + shaveHairShape::kShaveGuideVertComponent + ); + + for (j = 0; j < verts.length(); j++) + { + vertCompFn.addElement(guides[j], tipIdx); + } + } + } + + haveSelectedComponents = true; + } + else + { + // + // I don't know what type of item this is, but it + // shouldn't be here, so remove it. + // + selectionList.remove(itemIdx); + selectionChanged = true; + } + + // + // If we have a new component, replace the existing one + // with it. + // + if (!newComp.isNull()) + { + selectionList.replace(itemIdx, currentShape, newComp); + selectionChanged = true; + } + } + } + } + } + + // + // If we didn't end up with any components selected, then select them + // all. + // + if (!haveSelectedComponents) + { + MFnDependencyNode nodeFn(currentShape.node()); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + unsigned numGuides = nodePtr->getGuideCount(); + + if (numGuides > 0) + { + if ((selMode == 'g') || (selMode == 'r')) + { + comp = guideCompFn.create(shaveHairShape::kShaveGuideComponent); + } + else + { + comp = vertCompFn.create( + shaveHairShape::kShaveGuideVertComponent + ); + } + + for (i = 0; i < numGuides; i++) + { + switch (selMode) + { + case 'g': + case 'r': + guideCompFn.addElement(i); + break; + + case 't': + vertCompFn.addElement(i, tipIdx); + break; + + case 'v': + for (j = 0; j < SHAVE_VERTS_PER_GUIDE; j++) + vertCompFn.addElement(i, j); + break; + } + } + + selectionList.add(currentShape, comp); + selectionChanged = true; + } + } + + // + // Remove all other hair shapes from the hilite list. + // + bool hiliteChanged = false; + + for (i = hiliteList.length(); i > 0; i--) + { + hiliteList.getDagPath(i-1, path); + path.extendToShape(); + + if (!(path == currentShape)) + { + hiliteList.remove(i-1); + hiliteChanged = true; + } + } + + // + // If the current node is not on the hilite list, add it. + // + if (!currentIsHilited) + { + hiliteList.add(currentShape); + hiliteChanged = true; + } + + // + // Update the hilite lists, if it's changed. + // + if (hiliteChanged) MGlobal::setHiliteList(hiliteList); + + if (pHiliteListChanged) *pHiliteListChanged = hiliteChanged; + + // + // Update the selection list, if it's changed. + // + // Note that this will cause the 'shave_selectionChanged' MEL proc + // to run which will then call this method again. Unfortunately, + // the selectionChanged event does not fire until *after* we have + // returned from this method, so methods to avoid recursion, such as + // setting a static flag, won't work because we're not actually + // recursing. For now we just take the performance hit, since it + // should be pretty small. + // + if (selectionChanged) MGlobal::setActiveSelectionList(selectionList); + + return selectionChanged; +} + + +MString shaveUtil::expandTempFileName(MString fileName) +{ + // + // MEL is much better at handling cross-platform stuff, so let's + // delegate this. + // + MGlobal::executeCommand( + MString("shaveExpandTempFilePath(\"") + fileName + "\")", + fileName + ); + + return fileName; +} + + +MString shaveUtil::expandStatFileName(MString fileName) +{ + // + // MEL is much better at handling cross-platform stuff, so let's + // delegate this. + // + MGlobal::executeCommand( + MString("shaveExpandStatFilePath(\"") + fileName + "\")", + fileName + ); + + return fileName; +} + + +void shaveUtil::fileDelete(MString fileName) +{ + //////// debug ////////// + //MGlobal::displayInfo( fileName); + + if ((fileName.length() > 0) && fileExists(fileName)) + { + MGlobal::executeCommand( + MString("sysFile -del \"") + fileName + "\"" + ); + } + + return; +} + + +bool shaveUtil::fileExists(MString fileName) +{ + int exists; + + //////// debug ////////// + //MGlobal::displayInfo( fileName); + + MGlobal::executeCommand( + MString("filetest -r \"") + fileName + "\"", exists + ); + return (exists != 0); +} + + +bool shaveUtil::fileRename(MString oldName, MString newName) +{ + MStatus st; + + if ((oldName.length() == 0) + || (newName.length() == 0) + || !fileExists(oldName)) + { + return false; + } + + st = MGlobal::executeCommand( + MString("sysFile -ren \"") + newName + "\" \"" + oldName + "\"" + ); + + return (bool)st; +} + + +MStatus shaveUtil::forceCompute(MObject shaveHairShape) +{ + MStatus status; + MFnDependencyNode nodeFn(shaveHairShape, &status); + + if (status) + { + if (nodeFn.typeId() == shaveHairShape::id) + { + // + // Since all of a shaveHairShape's inputs affect its output mesh, + // all we need do is read the output mesh plug and if any of + // the inputs have changed, the compute will be run. + // + MPlug plug = nodeFn.findPlug(shaveHairShape::outputMesh); + MObject data; + + status = plug.getValue(data); + + if (status) + { + MFnMeshData dataFn(data, &status); + } + } + else + status = MS::kInvalidParameter; + } + + return status; +} + + +void shaveUtil::forceComputeAll() +{ + MObjectArray shaveHairShapes; + getShaveNodes(shaveHairShapes); + + unsigned int numShaveNodes = shaveHairShapes.length(); + unsigned int i; + + for (i = 0; i < numShaveNodes; i++) + forceCompute(shaveHairShapes[i]); +} + + +MString shaveUtil::formatFrame(float frame) +{ + char frameStr[5]; + sprintf(frameStr, "%04d", (int)frame); + + return MString(frameStr); +} + + +// There is no MPlug::getValue() for attributes of type kAddr. However +// there is MDataHandle::asAddr(). So what we do is connect 'addrPlug' +// to 'shaveGlobals.address', which was created specifically for this +// purpose, and then ask shaveGlobals to read the value from its +// datablock. +void* shaveUtil::getAddrPlugValue(const MPlug& addrPlug, MStatus* st) +{ + MObject globalsNode = shaveGlobals::getDefaultNode(); + + if (!globalsNode.isNull()) + { + MFnDependencyNode nodeFn(globalsNode); + shaveGlobals* globalsPtr = (shaveGlobals*)nodeFn.userNode(); + MPlug globalsPlug = nodeFn.findPlug(shaveGlobals::aAddress); + MDGModifier dgmod; + + if (dgmod.connect(addrPlug, globalsPlug)) + { + if (dgmod.doIt()) + { + void* addr = globalsPtr->getAddress(); + dgmod.undoIt(); + + if (st) *st = MS::kSuccess; + + return addr; + } + } + } + + if (st) *st = MS::kFailure; + + return NULL; +} + + +void shaveUtil::getDisplayNodes(MObjectArray& displayNodes) +{ + MObjectArray shaveHairShapes; + getShaveNodes(shaveHairShapes); + + unsigned int numNodes = shaveHairShapes.length(); + unsigned int i; + + displayNodes.clear(); + + for (i = 0; i < numNodes; i++) + { + MFnDependencyNode nodeFn(shaveHairShapes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + MObject displayNode = nodePtr->getDisplayShape(); + + if (!displayNode.isNull()) displayNodes.append(displayNode); + } +} + + +// Returns the currently selected hair shape. +// +MDagPath shaveUtil::getCurrentHairShape(bool* pIsHilited) +{ + MDagPath currentHairShape; + bool isHilited = false; + MSelectionList list; + + MGlobal::getHiliteList(list); + currentHairShape = getFirstHairShape(list); + + if (!currentHairShape.isValid()) + { + MGlobal::getActiveSelectionList(list); + currentHairShape = getFirstHairShape(list); + } + else + isHilited = true; + + if (pIsHilited) *pIsHilited = isHilited; + + return currentHairShape; +} + + +// This is currently not used. The problem with sampling is that we only +// sample at the pre-render vertices. However, displacements can produce +// effects which are equivalent to adding new vertices. For example, we +// have a plane with just 4 verts at the corners. Apply a displacement +// shader with a hard ramp which in the middle of the plane jumps from a +// displacement of 0 to 1. On render that will produce a step-like piece +// of geometry. But since we just sample the corners, we instead get a +// smooth ramp. +// +// For that reason, we use the 'displacementToPoly' command to generate +// a suitably tesselated mesh, instead. I'm just keeping this code around +// to remind me how to sample displacements. +void shaveUtil::getDisplacedMeshVertices( + MObject mesh, + MObject shadingGroup, + MFloatPointArray& displacedVerts +) +{ + MStatus st; + + enum + { + kNoDisplacement, + kConstantDisplacement, + kVariableDisplacement + } displacementType = kNoDisplacement; + + MPlug displacementPlug; + float constDisplacement = 0.0f; + + if (!shadingGroup.isNull()) + { + // Does the shading group have a displacement shader? + MPlugArray conns; + MFnDependencyNode sgFn(shadingGroup); + displacementPlug = sgFn.findPlug("displacementShader"); + displacementPlug.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + // If the displacement shader is an actual Maya + // displacement shader *and* it's output is not being fed + // from another plug, then the displacement value is + // constant and we can save ourselves some effort by + // just grabbing that instead of sampling for all the + // points. + // + // In fact, this isn't an option since sampling a displacement + // shader's 'displacement' plug directly seems to fail. + if (conns[0].node().hasFn(MFn::kDisplacementShader) + && (conns[0].partialName(false, false, false, false, false, true) == "displacement")) + { + displacementPlug = conns[0]; + displacementPlug.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + displacementPlug = conns[0]; + displacementType = kVariableDisplacement; + } + else + { + displacementPlug.getValue(constDisplacement); + displacementType = kConstantDisplacement; + } + } + else + { + displacementPlug = conns[0]; + displacementType = kVariableDisplacement; + } + } + } + + // Get the vertices and apply displacements. + MItMeshVertex vtxIter(mesh); + unsigned int numVerts = vtxIter.count(); + unsigned int i; + + displacedVerts.setLength(numVerts); + + switch (displacementType) + { + case kNoDisplacement: + { + for (i = 0, vtxIter.reset(); + (i < numVerts) && !vtxIter.isDone(); + ++i, vtxIter.next()) + { + MPoint p = vtxIter.position(MSpace::kWorld); + displacedVerts[i] = MFloatPoint((float)p.x, (float)p.y, (float)p.z); + } + } + break; + + case kConstantDisplacement: + { + MVector normal; + + for (i = 0, vtxIter.reset(); + (i < numVerts) && !vtxIter.isDone(); + ++i, vtxIter.next()) + { + MPoint p = vtxIter.position(MSpace::kWorld); + + vtxIter.getNormal(normal, MSpace::kWorld); + + p += normal * constDisplacement; + displacedVerts[i] = MFloatPoint((float)p.x, (float)p.y, (float)p.z); + } + } + break; + + case kVariableDisplacement: + { + // Get the vertex positions, their normals, and anything + // else we need to sample a shading network. + MVector normal; + MFloatVectorArray normals(numVerts); + MFloatArray uCoords(numVerts); + MFloatArray vCoords(numVerts); + float2 uv; + + for (i = 0, vtxIter.reset(); + (i < numVerts) && !vtxIter.isDone(); + ++i, vtxIter.next()) + { + MPoint p = vtxIter.position(MSpace::kWorld); + displacedVerts[i] = MFloatPoint((float)p.x, (float)p.y, (float)p.z); + + vtxIter.getNormal(normal, MSpace::kWorld); + normals[i] = MFloatVector((float)normal.x, (float)normal.y, (float)normal.z); + + vtxIter.getUV(uv); + uCoords[i] = uv[0]; + vCoords[i] = uv[1]; + } + + + // Sample the displacements. + MFloatVectorArray displacements; + MFloatVectorArray dummyTransparencies; + MFloatMatrix dummyCamMatrix; + + st = MRenderUtil::sampleShadingNetwork( + displacementPlug.name(), + displacedVerts.length(), + false, // useShadowMaps + false, // reuseMaps + dummyCamMatrix, + &displacedVerts, + &uCoords, + &vCoords, + &normals, + &displacedVerts, + 0, // tangentUs + 0, // tangentVs + 0, // filterSizes + displacements, + dummyTransparencies + ); + + // Apply the displacements. + for (i = 0; i < displacedVerts.length(); ++i) + { + displacedVerts[i] += normals[i] * displacements[i].x; + } + } + break; + } +} + + +const MDagPathArray& shaveUtil::getFields() +{ + if (mFieldsDirty) + { + mFields.clear(); + + MItDag iter(MItDag::kDepthFirst, MFn::kField); + MDagPath path; + + for (; !iter.isDone(); iter.next()) + { + iter.getPath(path); + mFields.append(path); + } + + mFieldsDirty = false; + } + + return mFields; +} + + +MDagPath shaveUtil::getFirstHairShape(MSelectionList& list) +{ + unsigned i; + MDagPath path; + + for (i = 0; i < list.length(); i++) + { + list.getDagPath(i, path); + path.extendToShape(); + + MFnDagNode nodeFn(path); + + if (nodeFn.typeId() == shaveHairShape::id) return path; + } + + MDagPath noPath; + + return noPath; +} + + +MString shaveUtil::getHostName() +{ + MString hostName; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + MGlobal::executeCommand("system(\"uname -n\")", hostName); + + // + // There will be a newline at the end, so strip it off. + // + if (hostName.length() > 1) + hostName = hostName.substring(0, hostName.length()-2); +#else + char temp[100]; + gethostname(temp, sizeof(temp)); + hostName.set(temp); +#endif + + return hostName; +} + + +// +// Get a light's index in the globalLightList, using its blind data value. +// +int shaveUtil::getLightIndex(const void* lightBlindData) +{ + if (lightBlindData == 0) return -1; + + size_t numLights = globalLightList.size(); + size_t i; + + for (i = 0; i < numLights; i++) + { + if (globalLightList[i].blindData == lightBlindData) + return (int)i; + } + + return -1; +} + + +// +// Get a light's index in the globalLightList, using its direction from a +// specified point. +// +int shaveUtil::getLightIndex( + const MFloatVector& point, const MFloatVector& dirToLight +) +{ + unsigned int numLights = (unsigned int)globalLightList.size(); + int index = -1; + unsigned int i; + float minAngle = 10.0f; // Must be > 2pi + + for (i = 0; i < numLights; i++) + { + MTransformationMatrix mat(globalLightList[i].path.inclusiveMatrix()); + MFloatVector pos(mat.translation(MSpace::kWorld)); + MFloatVector dirToThisLight = pos - point; + float angle = (float)fabs(dirToThisLight.angle(dirToLight)); + + if (angle < minAngle) + { + minAngle = angle; + index = i; + } + } + + // + // If the angular difference is too great, then this simply cannot be + // the right light. (0.01 radians is about 5.7 degrees) + // + if (minAngle > 0.01f) index = -1; + + return index; +} + + +int shaveUtil::getLightIndexFromID(int id) +{ + unsigned i; + + for (i = 0; i < globalLightList.size(); i++) + { + if ((id >= globalLightList[i].minId) + && (id <= globalLightList[i].maxId)) + { + return i; + } + } + + return -1; +} + + +// Returns the hair shape which is currently loaded into the Shave +// engine. Normally this will be the same as that returned by +// getCurrentShape() but they can be different, e.g. if the user has +// selected a new shape but we've not yet loaded it into the engine. +// +shaveHairShape* shaveUtil::getLoadedHairShape() +{ + MDagPathArray nodes; + getShaveNodes(nodes); + + for (unsigned int i = 0; i < nodes.length(); ++i) + { + MFnDagNode nodeFn(nodes[i]); + shaveHairShape* nodePtr = dynamic_cast<shaveHairShape*>(nodeFn.userNode()); + + if ((nodePtr != NULL) && nodePtr->isCurrent()) + { + return nodePtr; + } + } + + return NULL; +} + + +// +// Tesselate a NURBS or subdivision surface into a poly mesh. +// +// To use the surface's default values for uTess, set it to -1. +// Ditto vTess. +// +MObject shaveUtil::getMesh( + MDagPath& surface, + int uTess, + int vTess, + int sDept, + int sSamp, + bool includeDisplacements +) +{ + // Make sure that we're looking at the shape and not its transform. + MDagPath surfaceShape(surface); + surfaceShape.extendToShape(); + + // Surfaces with displacement shaders require special handling. + if (includeDisplacements && isSurfaceDisplaced(surface)) + { + // Since we have displacements, we must use the + // 'displacementToPoly' command to generate a mesh representing + // the displaced surface. + MSelectionList savedSelection; + MGlobal::getActiveSelectionList(savedSelection); + + MGlobal::select( + surfaceShape, MObject::kNullObj, MGlobal::kReplaceList + ); + + MStringArray results; + MGlobal::executeCommand("displacementToPoly", results); + + // Get the local mesh from the resulting mesh node. + MSelectionList list; + list.add(results[0]); + + MDagPath displacedMesh; + list.getDagPath(0, displacedMesh); + + MFnMesh meshFn(displacedMesh); + MPlug plug = meshFn.findPlug("worldMesh").elementByLogicalIndex(0); + MObject meshObj; + plug.getValue(meshObj); + + // We don't need the displaced mesh any more, so delete it. + MDagModifier dagMod; + dagMod.deleteNode(displacedMesh.node()); + dagMod.doIt(); + + // Restore the selection list. + MGlobal::setActiveSelectionList(savedSelection); + + // Return the displaced mesh to the caller. + return meshObj; + } + + // + // If this is already a mesh, then just return it. + // + if (surfaceShape.hasFn(MFn::kMesh)) return surfaceShape.node(); + + // + // If this is a NURBS or subdivision surface, we'll need to tesselate + // it. + // + MFnMeshData tessData; + MObject tempMesh = tessData.create(); + + if (surfaceShape.hasFn(MFn::kNurbsSurface)) + { + MTesselationParams tessParams; + tessParams.setFormatType(MTesselationParams::kGeneralFormat); + tessParams.setOutputType(MTesselationParams::kTriangles); + tessParams.setUIsoparmType(MTesselationParams::kSpanEquiSpaced); + tessParams.setVIsoparmType(MTesselationParams::kSpanEquiSpaced); + tessParams.setSubdivisionFlag(MTesselationParams::kUseFractionalTolerance, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseChordHeightRatio, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseMinEdgeLength, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseMaxEdgeLength, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseMaxNumberPolys, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseMaxSubdivisionLevel, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseMinScreenSize, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseMaxUVRectangleSize, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseTriangleEdgeSwapping, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseRelativeTolerance, false); + tessParams.setSubdivisionFlag(MTesselationParams::kUseEdgeSmooth, false); + + // + // If either of the tesselation parameters passed in to us is + // invalid, then go get the explicit parameter from the surface + // itself. + // + MFnNurbsSurface nurbsFn(surfaceShape); + + if ((uTess < 1) || (vTess < 1)) + { + MPlug plug; + + if (uTess < 1) + { + plug = nurbsFn.findPlug("numberU"); + plug.getValue(uTess); + } + + if (vTess < 1) + { + plug = nurbsFn.findPlug("numberV"); + plug.getValue(vTess); + } + } + + tessParams.setUNumber(uTess); + tessParams.setVNumber(vTess); + + // + // There's a bug in Maya such that immediately after file load, a + // nurbsSurface node is in an inconsistent state. This will be + // rectified the first time that the node's geometry is retrieved + // from one of its plugs. However, if we try to tesselate the + // surface, it will do so on the *inconsistent* geometry. + // + // So let's first retrieve the surface's geom to ensure that it is + // in a consistent state before doing tesselation. + // + MPlug geomPlug = nurbsFn.findPlug("local"); + MObject surfaceGeom; + geomPlug.getValue(surfaceGeom); + + // + // Okay, now it's safe to tesselate. + // + tempMesh = nurbsFn.tesselate(tessParams, tempMesh); + } + else if (surfaceShape.hasFn(MFn::kSubdiv)) + { + // + // Get the object's tesselation parameters. + // + MStatus status; + MFnSubd subdFn(surfaceShape); + bool uniform = true; + int depth = sDept; //0; + int samples = sSamp; //1; + + +#if 0 //this logic seems correct, get matching + //shapes on rendering but not in viewport + int format; + MPlug plug = subdFn.findPlug("format"); + + plug.getValue(format); + uniform = (format == 0); + + if (uniform) + { + plug = subdFn.findPlug("depth"); + plug.getValue(depth); + } + else + { + depth = 0; + } + + plug = subdFn.findPlug("sampleCount"); + plug.getValue(samples); +#endif + +#if 0 //levels does not match on rendering + MPlug plug = subdFn.findPlug("dispResolution",&status); + if(status == MS::kSuccess) + plug.getValue(depth); +#endif + // + // Do the tesselation. + // + ////// debug //////// + //MGlobal::displayInfo(MString("tesselate ") + depth + " " + samples); + ///////////////////// + + tempMesh = subdFn.tesselate(uniform, depth, samples, tempMesh, &status); + + if (!status) + { + MGlobal::displayError( + MString("tesselating subd: ") + status.errorString() + ); + } + } + + tessData.setMatrix(surface.inclusiveMatrix()); + tessData.setObject(tempMesh); + + return tempMesh; +} + +MObject shaveUtil::getNode(MString nodeName) +{ + MSelectionList list; + MObject node; + + list.add(nodeName); + list.getDependNode(0, node); + + return node; +} + + +void shaveUtil::getNodesByTypeId(MTypeId nodeType, MObjectArray& nodes) +{ + MItDependencyNodes iter; + + nodes.clear(); + + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + MFnDependencyNode nodeFn(node); + + if (nodeFn.typeId() == nodeType) nodes.append(node); + } +} + + +void shaveUtil::getPathsByTypeId(MTypeId nodeType, MDagPathArray& paths) +{ + MItDag iter; + MDagPath path; + + paths.clear(); + + for (; !iter.isDone(); iter.next()) + { + iter.getPath(path); + + MFnDagNode nodeFn(path); + + if (nodeFn.typeId() == nodeType) paths.append(path); + } +} + + +long shaveUtil::getpid() +{ +#if defined(OSMac_) && !defined(OSMac_MachO_) + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + + return psn.lowLongOfPSN; +#else +# ifdef _WIN32 + return _getpid(); +# else + return ::getpid(); +# endif +#endif +} + + +void shaveUtil::getShadingGroups( + const MDagPath& path, MObjectArray& shadingGroups +) +{ + MFnDagNode dagFn(path); + + shadingGroups.clear(); + + MPlug plug = dagFn.findPlug("instObjGroups"); + + plug = plug.elementByLogicalIndex(path.instanceNumber()); + appendConnectedShadingGroups(plug, shadingGroups); + + MObject objectGroupsAttr = dagFn.attribute("objectGroups"); + + plug = plug.child(objectGroupsAttr); + + unsigned int i; + unsigned int numElements = plug.evaluateNumElements(); + + for (i = 0; i < numElements; i++) + { + appendConnectedShadingGroups( + plug.elementByPhysicalIndex(i), shadingGroups + ); + } +} + + +void shaveUtil::getShaveGlobalsNodes(MObjectArray& nodes) +{ + getNodesByTypeId(shaveGlobals::id, nodes); +} + + +void shaveUtil::getShaveNodes(MDagPathArray& paths) +{ + getPathsByTypeId(shaveHairShape::id, paths); +} + + +void shaveUtil::getShaveNodes(MObjectArray& nodes) +{ + getNodesByTypeId(shaveHairShape::id, nodes); +} + + +void shaveUtil::getSubdQuads( + MDagPath& subdPath, + MUint64Array& quadIDs, + MPointArray& verts, + MIntArray& vertIndices, + MDoubleArray* vertUs, + MDoubleArray* vertVs +) +{ + MFnSubd subdFn(subdPath); + MPlug worldSubdivArray = subdFn.findPlug("worldSubdiv"); + MPlug subdivPlug = worldSubdivArray.elementByLogicalIndex(subdPath.instanceNumber()); + MObject subdData; + + subdivPlug.getValue(subdData); + + getSubdQuads(subdData, quadIDs, verts, vertIndices, vertUs, vertVs); +} + + +void shaveUtil::getSubdQuads( + MObject subdData, + MUint64Array& quadIDs, + MPointArray& verts, + MIntArray& vertIndices, + MDoubleArray* vertUs, + MDoubleArray* vertVs +) +{ + MFnSubd subdFn(subdData); + int i, j, k; + MIntArray level0FirstSubfaceIndices; + int numLevel0Polys = subdFn.polygonCount(0); + + level0FirstSubfaceIndices.clear(); + quadIDs.clear(); + verts.clear(); + vertIndices.clear(); + + for (i = 0; i < numLevel0Polys; i++) + { + MUint64 level0FaceID; + MUint64Array level1Polys; + + MFnSubdNames::toMUint64(level0FaceID, i, 0, 0, 0, 0); + + level0FirstSubfaceIndices.append(quadIDs.length()); + subdFn.polygonChildren(level0FaceID, level1Polys); + + for (j = 0; j < (int)level1Polys.length(); j++) + { + // + // Add this quad's face ID to the list. + // + quadIDs.append(level1Polys[j]); + + // + // Get the IDs for its four vertices. + // + MUint64Array vertIDs; + MDoubleArray uValues; + MDoubleArray vValues; + + subdFn.polygonVertices(level1Polys[j], vertIDs); + + if (vertUs || vertVs) + subdFn.polygonGetVertexUVs(level1Polys[j], uValues, vValues); + + MPoint p; + + for (k = 0; k < 4; k++) + { + // + // Get the components which make up the vertex ID. + // + int level0Idx; + int level1Idx; + int level; // Should come back as 1. + int path; // Unused. + int corner; + + MFnSubdNames::fromMUint64( + vertIDs[k], level0Idx, level1Idx, level, path, corner + ); + + // + // The vertex ID will be based on the lowest-numbered quad + // which uses it. If that's us, then we must create an + // entry for the vert. Otherwise we use the entry already + // created by that lower-number quad. + // + if ((level0Idx == i) && (level1Idx == j)) + { + vertIndices.append(verts.length()); + + subdFn.vertexPositionGet(vertIDs[k], p, MSpace::kWorld); + verts.append(p); + + if (vertUs) vertUs->append(uValues[k]); + if (vertVs) vertVs->append(vValues[k]); + } + else + { + int overallQuadIdx = level0FirstSubfaceIndices[level0Idx] + level1Idx; + int overallVertIdx = overallQuadIdx *4 + corner; + + vertIndices.append(vertIndices[overallVertIdx]); + } + } + } + } +} + + +unsigned shaveUtil::getThreadSettings( + unsigned numItems, unsigned* pItemsPerThread +) +{ + int itemsPerThread; + unsigned numThreads; + + if (threadPerProcessorGlob) + numThreads = (unsigned)SHAVEnum_processors(); + else + numThreads = maxThreadsGlob; + + if (numThreads >= numItems) + { + itemsPerThread = 1; + numThreads = numItems; + } + else if (numThreads > 1) + { + itemsPerThread = numItems / numThreads + 1; + } + else + { + itemsPerThread = numItems; + numThreads = 0; + } + + if (pItemsPerThread) *pItemsPerThread = itemsPerThread; + + return numThreads; +} + + +bool shaveUtil::isSurfaceDisplaced(const MDagPath& surface) +{ + // Make sure that we're looking at the shape and not its transform. + MDagPath surfaceShape(surface); + surfaceShape.extendToShape(); + + // Search all the surface's shading groups for displacement shaders. + MObjectArray shadingGroups; + getShadingGroups(surface, shadingGroups); + + unsigned int i; + + for (i = 0; i < shadingGroups.length(); ++i) + { + // Does the shading group have a displacement shader? + MPlugArray conns; + MFnDependencyNode sgFn(shadingGroups[i]); + MPlug displacementPlug; + + displacementPlug = sgFn.findPlug("displacementShader"); + displacementPlug.connectedTo(conns, true, false); + + if (conns.length() > 0) return true; + } + + return false; +} + + +MString shaveUtil::makeStatFileName(MString nodeName, float frame) +{ + // The node name may contain ':' which are bad in filenames so replace + // them all with '_'. + nodeName = substituteAll(nodeName, ':', '_'); + + return expandStatFileName( + MString("shaveStatFile_") + nodeName + "." + + formatFrame(frame) + ".stat" + ); +} + + +MString shaveUtil::makeTempStatFileName( + MString nodeName, MString suffix, float frame +) +{ + // The node name may contain ':' which are bad in filenames so replace + // them all with '_'. + nodeName = substituteAll(nodeName, ':', '_'); + + return expandTempFileName( + MString("shaveStatFile_") + suffix + "_" + nodeName + "." + + formatFrame(frame) + ".stat" + ); +} + + +// This function is not thread-safe! +MString shaveUtil::makeUniqueTempFileName( + MString directory, MString prefix, MString suffix +) +{ + FILE* fp; + int i; + MString fileName; + MString templ; + + // Include the host name and process ID in the file name to avoid + // conflicts with other systems and processes generating files into + // the same directory. + templ = prefix + "_" + getHostName() + "_" + shaveUtil::getpid() + "_"; + + unsigned int l = directory.length(); + + if (l == 0) + templ = expandTempFileName(templ); + else if (directory.substring(l-1, l-1) == "/") + templ = directory + templ; + else + templ = directory + "/" + templ; + + // Pick a random starting point from 0 to 999 to reduce the likelihood + // of a collision. +#ifdef _WIN32 + int randStart = (int)(1000.0 * rand() / (RAND_MAX + 1.0)); +#else + int randStart = (int)(1000.0 * random() / (RAND_MAX + 1.0)); +#endif + + for (i = 0; i < 1000; i++) + { + fileName = templ + ((randStart + i) % 1000) + suffix; + + // There is still a race condition here between fileExists() + // and fopen(). Because the fileName is unique to a given system + // and process, this is only a problem when threading. + // + // Note that on Unix systems each thread is assigned its own + // process ID so in that case this function is thread-safe. + if (!fileExists(fileName)) + { +#if defined(OSMac_) && !defined(OSMac_MachO_) + MString hfsFileName = shaveMacCarbon::makeMacFilename(fileName); + + fp = fopen(hfsFileName.asChar(), "w"); +#else + fp = fopen(fileName.asChar(), "w"); +#endif + if (fp != NULL) + { + fclose(fp); + break; + } + } + } + /////// debug ///// + //MGlobal::displayInfo("------------temp file---------"); + //MGlobal::displayInfo(fileName); + + return fileName; +} + + +// +// Reverse the byte order of a 4-byte int value for those platforms which +// use a different endian. +// +int shaveUtil::reorderInt(int inVal) +{ + int outVal; + unsigned char* pIn = (unsigned char*)&inVal; + unsigned char* pOut = (unsigned char*)&outVal; + + if (sizeof(int) == 2) + { + pOut[0] = pIn[1]; + pOut[1] = pIn[0]; + } + else if (sizeof(int) == 4) + { + pOut[0] = pIn[3]; + pOut[1] = pIn[2]; + pOut[2] = pIn[1]; + pOut[3] = pIn[0]; + } + + return outVal; +} + + +MStatus shaveUtil::sampleCurves( + const MObjectArray& curves, unsigned int samplesPerCurve, WFTYPE& sampleData +) +{ + MStatus st; + unsigned int numCurves = curves.length(); + + init_geomWF(&sampleData); + + sampleData.totalfaces = (int)numCurves; + sampleData.totalverts = (int)(numCurves * samplesPerCurve); + sampleData.totalfverts = sampleData.totalverts; + + alloc_geomWF(&sampleData); + + unsigned int i, j; + int numVerts = 0; + + for (i = 0; i < numCurves; i++) + { + // Sample the curve at samplesPerCurve equally spaced + // points along its length and store the resulting points + // into a WFTYPE. + MFnNurbsCurve curveFn(curves[i]); + double curveLen = curveFn.length(); + double maxParam; + double minParam; + MPoint point; + double sampleLen = 0.0; + double sampleParam = 0.0; + double segmentLen; + + curveFn.getKnotDomain(minParam, maxParam); + + segmentLen = curveLen / (double)(samplesPerCurve-1); + + sampleData.face_start[i] = numVerts; + + for (j = 0; j < samplesPerCurve; j++) + { + if (j == 0) + sampleParam = minParam; + else if (j == samplesPerCurve-1) + sampleParam = maxParam; + else + sampleParam = curveFn.findParamFromLength(sampleLen); + + curveFn.getPointAtParam(sampleParam, point, MSpace::kWorld); + + sampleData.v[numVerts].x = (float)point.x; + sampleData.v[numVerts].y = (float)point.y; + sampleData.v[numVerts].z = (float)point.z; + + sampleData.facelist[numVerts] = numVerts; + + numVerts++; + sampleLen += segmentLen; + } + + sampleData.face_end[i] = numVerts; + } + + return st; +} + + +void shaveUtil::setFrame(float newFrame) +{ + MGlobal::executeCommand(MString("currentTime ") + newFrame); +} + + +void shaveUtil::setLoadingFile(bool nowLoading) +{ + if (nowLoading) + mLoadDepth++; + else + mLoadDepth--; +} + +void shaveUtil::setWFTYPEVelocities(WFTYPE* wf1, WFTYPE* wf2) +{ + int i; + int numVerts = wf1->totalverts; + + if (wf2->totalverts < numVerts) numVerts = wf2->totalverts; + + for (i = 0; i < numVerts; i++) + { + wf1->velocity[i].x = wf2->v[i].x - wf1->v[i].x; + wf1->velocity[i].y = wf2->v[i].y - wf1->v[i].y; + wf1->velocity[i].z = wf2->v[i].z - wf1->v[i].z; + } + + for (; i < wf1->totalverts; i++) + wf1->velocity[i].x = wf1->velocity[i].y = wf1->velocity[i].z = 0.0f; +} + + +void shaveUtil::splitFileName( + MString fileName, MString& baseName, MString& extension +) +{ + baseName = fileName; + extension = ""; + + int dotPos = fileName.rindex('.'); + + if ((dotPos >= 0) + && (dotPos > fileName.rindex('/')) + && (dotPos > fileName.rindex('\\'))) + { + if (dotPos + 1 < (int)fileName.length()) + extension = fileName.substring(dotPos+1, fileName.length()-1); + + if (dotPos > 0) + baseName = fileName.substring(0, dotPos-1); + else + { + // + // Whoops! + // + baseName = ""; + } + } +} + + +bool shaveUtil::splitShaveVersion(const MString versionStr, int versionParts[3]) +{ + MStringArray strParts; + + versionStr.split('.', strParts); + + if (strParts.length() == 2) + { + MStringArray tempParts; + + strParts[1].split('v', tempParts); + + if (tempParts.length() == 2) + { + versionParts[0] = strParts[0].asInt(); + versionParts[1] = tempParts[0].asInt(); + versionParts[2] = tempParts[1].asInt(); + return true; + } + } + + // If we reach here then the version string is malformed, so assume + // that it is 4.5v9 (the last version without scene versioning) and + // return false. + versionParts[0] = 4; + versionParts[1] = 5; + versionParts[2] = 9; + + return false; +} + + +MString shaveUtil::substituteAll(const MString& str, char oldChar, char newChar) +{ + if (newChar == oldChar) return str; + + char* strBuff = new char[str.length()+1]; + + strcpy(strBuff, str.asChar()); + + for (char* c = strBuff; *c; ++c) + if (*c == oldChar) *c = newChar; + + MString newStr(strBuff); + delete [] strBuff; + + return newStr; +} + + +MString shaveUtil::substituteAll( + const MString& str, const MString& oldSubstr, const MString& newSubstr +) +{ + if ((oldSubstr.length() == 0) || (newSubstr == oldSubstr)) return str; + + unsigned int i; + MString newStr = str; + + for (i = 0; i < newStr.length() - oldSubstr.length() + 1; i++) + { + if (newStr.substring(i, i + oldSubstr.length() - 1) == oldSubstr) + { + if (i == 0) + { + newStr = newSubstr + + newStr.substring(oldSubstr.length(), newStr.length()); + } + else + { + newStr = newStr.substring(0, i-1) + newSubstr + + newStr.substring(i + oldSubstr.length(), newStr.length()); + } + + i += newSubstr.length() - 1; + } + } + + return newStr; +} + + +void shaveUtil::timestamp(const MString& msg) +{ +#ifdef _WIN32 + clock_t time = clock(); +#else + clock_t time = times(0); +#endif + +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cout << time << ": " << msg.asChar() << endl; +#else + cout << time << ": " << msg << endl; +#endif +} + + +//***************************************************** +// +// Internal Utility Methods +// +//***************************************************** + +void shaveUtil::appendConnectedShadingGroups( + const MPlug& plug, MObjectArray& shadingGroups +) +{ + MPlugArray conns; + + plug.connectedTo(conns, false, true); + + unsigned int numConns = conns.length(); + unsigned int i; + + for (i = 0; i < numConns; i++) + { + if (conns[i].node().hasFn(MFn::kSet)) + { + MFnSet setFn(conns[i].node()); + + if (setFn.restriction() == MFnSet::kRenderableOnly) + shadingGroups.append(setFn.object()); + } + } +} diff --git a/mayaPlug/shaveUtil.h b/mayaPlug/shaveUtil.h new file mode 100644 index 0000000..0942e85 --- /dev/null +++ b/mayaPlug/shaveUtil.h @@ -0,0 +1,236 @@ +#ifndef _shaveUtil_h +#define _shaveUtil_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <math.h> +#include <stdio.h> +#include <vector> + +#include <maya/MDagPath.h> +#include <maya/MDagPathArray.h> +#include <maya/MFnMesh.h> +#include <maya/MIntArray.h> +#include <maya/MObjectArray.h> +#include <maya/MPointArray.h> +#include <maya/MString.h> +#include <maya/MUint64Array.h> + +#include "shaveSDKTYPES.h" + +#if MAYA_API_VERSION < 20180000 +class MFloatPointArray; +class MSelectionList; +#endif + +class shaveHairShape; +class shaveRenderer; + + +class shaveUtil +{ +public: + static void buildLightList(); + + static void classifyShaveNodes( + const MObjectArray& shaveNodes, + bool& haveHair, + bool& haveInstances + ); + + // Compare two Shave version strings of the form 1.2.3 or 1.2v3 + // Returns -1 if v1 < v2, 0 if v1 == v2, +1 if v1 > v2. + static int compareShaveVersions(MString v1, MString v2); + + // Returns true if it made changes to the selection list. + static bool convertComponentSelections( + bool* pHiliteListChanged = NULL + ); + + static MString expandStatFileName(MString fileName); + static MString expandTempFileName(MString fileName); + + // Copy one full line of input to the output, returning to the caller + // as much as will fit in the supplied buffer. + // + // Returns false if there was no input left to read. + static bool fileCopyLine( + FILE* input, + FILE* output, + char* buffer, + unsigned int bufferSize + ); + + static void fileDelete(MString fileName); + static bool fileExists(MString fileName); + static bool fileRename(MString oldName, MString newName); + static MStatus forceCompute(MObject shaveNode); + static void forceComputeAll(); + static MString formatFrame(float frame); + + // Returns the value of a plug of type kAddr. + static void* getAddrPlugValue(const MPlug& addrPlug, MStatus* st = NULL); + + static MDagPath getCurrentHairShape(bool* pIsHilited = NULL); + + // Displacements are applied in worldSpace so the returned vert + // positions will be in worldSpace. + static void getDisplacedMeshVertices( + MObject mesh, + MObject shadingGroup, + MFloatPointArray& displacedVerts + ); + + static void getDisplayNodes(MObjectArray& nodes); + + static const MDagPathArray& + getFields(); + + static int getLightIndex(const void* lightBlindData); + + static int getLightIndex( + const MFloatVector& point, + const MFloatVector& dirToLight + ); + + static int getLightIndexFromID(int id); + static int getLoadDepth(); + static shaveHairShape* getLoadedHairShape(); + + static MObject getMesh( + MDagPath& path, + int uTess = -1, + int vTess = -1, + int sDept = 1, + int sSamp = 1, + bool includeDisplacements = false + ); + + static MObject getNode(MString nodeName); + static void getNodesByTypeId(MTypeId nodeType, MObjectArray& nodes); + static void getPathsByTypeId(MTypeId nodeType, MDagPathArray& nodes); + static long getpid(); + + static void getShadingGroups( + const MDagPath& path, MObjectArray& shadingGroups + ); + + static void getShaveGlobalsNodes(MObjectArray& nodes); + static void getShaveNodes(MDagPathArray& nodes); + static void getShaveNodes(MObjectArray& nodes); + + static void getSubdQuads( + MDagPath& subdPath, + MUint64Array& quadIDs, + MPointArray& verts, + MIntArray& vertIndices, + MDoubleArray* vertUs = NULL, + MDoubleArray* vertVs = NULL + ); + + static void getSubdQuads( + MObject subdData, + MUint64Array& quadIDs, + MPointArray& verts, + MIntArray& vertIndices, + MDoubleArray* vertUs = NULL, + MDoubleArray* vertVs = NULL + ); + + static unsigned getThreadSettings( + unsigned numItems, unsigned* pItemsPerThread = NULL + ); + + static MString getHostName(); + static bool isIgnoringNextSelectionChange(); + static bool isLoadingFile(); + static bool isSurfaceDisplaced(const MDagPath& surface); + static MString makeStatFileName(MString nodeName, float frame); + + static MString makeTempStatFileName( + MString nodeName, MString suffix, float frame + ); + + // If 'path' is empty then the default temp directory will be used. + // Not thread-safe! + static MString makeUniqueTempFileName( + MString directory, MString prefix, MString suffix + ); + + static int reorderInt(int val); + + static MStatus sampleCurves( + const MObjectArray& curves, + unsigned int samplesPerCurve, + WFTYPE& sampleData + ); + + static void setFieldsDirty() { mFieldsDirty = true; } + static void setFrame(float newFrame); + static void setIgnoringNextSelectionChange(bool ignoreIt); + static void setLoadingFile(bool newState); + static void setWFTYPEVelocities(WFTYPE* wf1, WFTYPE* wf2); + + static void splitFileName( + MString fileName, + MString& baseName, + MString& extension + ); + + static bool splitShaveVersion( + const MString version, + int versionParts[3] + ); + + static MString substituteAll( + const MString& str, char oldChar, char newChar + ); + + static MString substituteAll( + const MString& str, + const MString& oldSubstr, + const MString& newSubstr + ); + + static void timestamp(const MString& msg); + +protected: + static void appendConnectedShadingGroups( + const MPlug& plug, MObjectArray& shadingGroups + ); + + static MDagPath getFirstHairShape(MSelectionList& list); + +public: + typedef struct + { + MDagPath path; + int minId; + int maxId; + void* blindData; + } LightInfo; + + static std::vector<LightInfo> globalLightList; + +private: + static MDagPathArray mFields; + static bool mFieldsDirty; + static bool mIgnoreNextSelectionChange; + static int mLoadDepth; +}; + + +inline int shaveUtil::getLoadDepth() +{ return mLoadDepth; } + +inline bool shaveUtil::isIgnoringNextSelectionChange() +{ return mIgnoreNextSelectionChange; } + +inline bool shaveUtil::isLoadingFile() +{ return (mLoadDepth > 0); } + +inline void shaveUtil::setIgnoringNextSelectionChange(bool ignoreIt) +{ mIgnoreNextSelectionChange = ignoreIt; } + +#endif diff --git a/mayaPlug/shaveUtilCmd.cpp b/mayaPlug/shaveUtilCmd.cpp new file mode 100644 index 0000000..703ba8c --- /dev/null +++ b/mayaPlug/shaveUtilCmd.cpp @@ -0,0 +1,987 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#ifdef _WIN32 +#include <time.h> +#else +#include <sys/times.h> +#endif + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDagPath.h> +#include <maya/MFnAttribute.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MGlobal.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> +#include <maya/M3dView.h> + +#include "shaveCallbacks.h" +#include "shaveGlobals.h" +#include "shaveUtil.h" +#include "shaveUtilCmd.h" +#include "shaveNode.h" +#include "shaveHairShape.h" +#include "shaveSDK.h" + +static const char* flAttrType = "-attrType"; +static const char* fsAttrType = "-at"; +static const char* flConvertComponentSelections = "-convertComponentSelections"; +static const char* fsConvertComponentSelections = "-ccs"; +static const char* flCopyAttr = "-copyAttr"; +static const char* fsCopyAttr = "-ca"; +static const char* flDuplicateInConnections = "-duplicateInConnections"; +static const char* fsDuplicateInConnections = "-dic"; +static const char* flForce = "-force"; +static const char* fsForce = "-f"; +static const char* flInitPlugin = "-initPlugin"; +static const char* fsInitPlugin = "-ip"; +static const char* flMakeTempFileName = "-makeTempFileName"; +static const char* fsMakeTempFileName = "-mtf"; +static const char* flMoveOutConnections = "-moveOutConnections"; +static const char* fsMoveOutConnections = "-moc"; +#ifdef PROFILE +static const char* flProfile = "-profile"; +static const char* fsProfile = "-p"; +#endif +static const char* flSilent = "-silent"; +static const char* fsSilent = "-s"; +static const char* flTest = "-test"; +static const char* fsTest = "-t"; +static const char* flTimestamp = "-timestamp"; +static const char* fsTimestamp = "-ts"; +static const char* flScaleAll = "-scaleAll"; +static const char* fsScaleAll = "-sa"; +static const char* flScaleCurrent = "-scaleCur"; +static const char* fsScaleCurrent = "-sc"; + + + +const MString shaveUtilCmd::commandName("shaveUtil"); + +#ifdef PROFILE +bool shaveUtilCmd::gProfiling = false; + +extern "C" void pct_on(char** argv); +extern "C" void pct_off(); +#endif + + +shaveUtilCmd::shaveUtilCmd() +: mForce(false) +, mSilent(false) +{} + +shaveUtilCmd::~shaveUtilCmd() {} + + +void* shaveUtilCmd::createCmd() +{ + return new shaveUtilCmd(); +} + + +MSyntax shaveUtilCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(false); + + syntax.addFlag(fsAttrType, flAttrType, MSyntax::kString); + syntax.addFlag(fsConvertComponentSelections, flConvertComponentSelections); + syntax.addFlag(fsCopyAttr, flCopyAttr, MSyntax::kString, MSyntax::kString); + syntax.addFlag(fsDuplicateInConnections, flDuplicateInConnections); + syntax.addFlag(fsForce, flForce); + syntax.addFlag(fsInitPlugin, flInitPlugin); + + syntax.addFlag( + fsMakeTempFileName, + flMakeTempFileName, + MSyntax::kString, + MSyntax::kString, + MSyntax::kString + ); + + syntax.addFlag(fsMoveOutConnections, flMoveOutConnections); +#ifdef PROFILE + syntax.addFlag(fsProfile, flProfile, MSyntax::kBoolean); +#endif + syntax.addFlag(fsSilent, flSilent); + syntax.addFlag(fsTest, flTest, MSyntax::kString, MSyntax::kString, MSyntax::kString); + syntax.addFlag(fsTimestamp, flTimestamp, MSyntax::kString); + + syntax.addFlag(fsScaleAll, flScaleAll, MSyntax::kDouble); + syntax.addFlag(fsScaleCurrent, flScaleCurrent, MSyntax::kDouble); + + return syntax; +} + + +static void threadCalc(unsigned threadID, void* data) +{ + int i; + unsigned seed = *(unsigned*)&data; + double val = (double)seed; + + for (i = 0; i < 100000000; i++) + val = 1.0 / val + seed; + + printf("thread %d, seed %d, result %lf\n", threadID, seed, val); +} + + +MStatus shaveUtilCmd::doIt(const MArgList& argList) +{ + MStatus st; + MArgDatabase args(syntax(), argList, &st); + + if (!st) return st; + + mForce = args.isFlagSet(fsForce); + mSilent = args.isFlagSet(fsSilent); + + if (args.isFlagSet(fsAttrType)) + { + MString plugName; + + args.getFlagArgument(fsAttrType, 0, plugName); + setResult(getAttrType(plugName)); + } + else if (args.isFlagSet(fsConvertComponentSelections)) + { + shaveUtil::convertComponentSelections(); + } + else if (args.isFlagSet(fsCopyAttr)) + { + MString srcPlugName; + MString destPlugName; + bool dupInConns = args.isFlagSet(fsDuplicateInConnections); + bool moveOutConns = args.isFlagSet(fsMoveOutConnections); + + args.getFlagArgument(fsCopyAttr, 0, srcPlugName); + args.getFlagArgument(fsCopyAttr, 1, destPlugName); + + st = copyAttr(srcPlugName, destPlugName, dupInConns, moveOutConns); + } + else if (args.isFlagSet(fsInitPlugin)) + { + finishPluginInitialization(); + } +#ifdef PROFILE + else if (args.isFlagSet(fsProfile)) + { + bool turnItOn; + + args.getFlagArgument(fsProfile, 0, turnItOn); + + if (turnItOn && !gProfiling) + { + static char* argv[] = { "maya.bin", NULL, NULL }; + + pct_on(argv); + } + else if (!turnItOn && gProfiling) + { + pct_off(); + } + + gProfiling = turnItOn; + } +#endif + else if (args.isFlagSet(fsTimestamp)) + { + MString msg; + args.getFlagArgument(fsTimestamp, 0, msg); + shaveUtil::timestamp(msg); + } + else if (args.isFlagSet(fsMakeTempFileName)) + { + MString dir; + MString prefix; + MString suffix; + + args.getFlagArgument(fsMakeTempFileName, 0, dir); + args.getFlagArgument(fsMakeTempFileName, 1, prefix); + args.getFlagArgument(fsMakeTempFileName, 2, suffix); + + MString fileName = shaveUtil::makeUniqueTempFileName(dir, prefix, suffix); + setResult(fileName); + } + else if (args.isFlagSet(fsTest)) + { + } + else if (args.isFlagSet(fsScaleAll)) + { + double s; + args.getFlagArgument(fsScaleAll, 0, s); + return scaleAll((float)s); + } + else if (args.isFlagSet(fsScaleCurrent)) + { + double s; + args.getFlagArgument(fsScaleCurrent, 0, s); + return scaleCurrent((float)s); + } + + return st; +} + + +MStatus shaveUtilCmd::copyAttr( + MString origPlugName, + MString dupPlugName, + bool dupInConns, + bool moveOutConns +) const +{ + MStatus st; + + MPlug dupPlug; + MSelectionList list; + MString msg; + MPlug origPlug; + + list.add(origPlugName); + list.getPlug(0, origPlug); + + list.clear(); + list.add(dupPlugName); + list.getPlug(0, dupPlug); + + if (origPlug.isNull()) + { + msg = MString("Cannot find source plug '") + origPlugName + "'."; + st = MS::kInvalidParameter; + } + else if (dupPlug.isNull()) + { + msg = MString("Cannot find destination plug '") + dupPlugName + "'."; + st = MS::kInvalidParameter; + } + else + { + st = copyPlug(origPlug, dupPlug, dupInConns, moveOutConns, true); + + if (st) st = copyPlug(origPlug, dupPlug, dupInConns, moveOutConns, false); + } + + if (!mSilent && (msg.length() > 0)) MGlobal::displayError(msg); + + return st; +} + + +MStatus shaveUtilCmd::copyInboundConnection(MPlug& origPlug, MPlug& dupPlug) + const +{ + MStatus st; + + // + // Does the original plug have an incoming connection which + // needs to be duplicated? + // + MPlugArray conns; + + origPlug.connectedTo(conns, true, false); + + if (conns.length() > 0) + { + MDGModifier mod; + + st = mod.connect(conns[0], dupPlug); + if (st) st = mod.doIt(); + + if (!st && !mSilent) + { + MGlobal::displayError( + MString("Cannot connect '") + conns[0].name() + + "' to '" + dupPlug.name() + "': " + + st.errorString() + ); + } + } + + return st; +} + + +MStatus shaveUtilCmd::copyPlug( + MPlug origPlug, + MPlug dupPlug, + bool dupInConns, + bool moveOutConns, + bool checkOnly +) const +{ + MStatus st = MS::kSuccess; + MString msg; + + MObject origAttr = origPlug.attribute(); + MObject dupAttr = dupPlug.attribute(); + + if (origAttr.apiType() != dupAttr.apiType()) + { + msg = "The source and destination attribute types do not match."; + st = MS::kInvalidParameter; + } + else if (origPlug.isArray() && !dupPlug.isArray()) + { + msg = "The source attribute is a multi, but the destination is not."; + st = MS::kInvalidParameter; + } + else if (!origPlug.isArray() && dupPlug.isArray()) + { + msg = "The destination attribute is a multi, but the source is not."; + st = MS::kInvalidParameter; + } + else if (origPlug.isCompound() && !dupPlug.isCompound()) + { + msg = "The source attribute is compound, but the destination is not."; + st = MS::kInvalidParameter; + } + else if (!origPlug.isCompound() && dupPlug.isCompound()) + { + msg = "The destination attribute is compound, but the source is not."; + st = MS::kInvalidParameter; + } + else if (origPlug.isArray()) + { + unsigned i; + MIntArray indices; + + origPlug.getExistingArrayAttributeIndices(indices); + + for (i = 0; i < indices.length(); i++) + { + st = copyPlug( + origPlug.elementByLogicalIndex(indices[i]), + dupPlug.elementByLogicalIndex(indices[i]), + dupInConns, + moveOutConns, + checkOnly + ); + + if (!st || checkOnly) break; + } + + // + // There may be connections to the entire array, so deal with those + // as well. + // + if (moveOutConns) moveOutboundConnections(origPlug, dupPlug); + if (dupInConns) copyInboundConnection(origPlug, dupPlug); + } + else if (origPlug.isCompound()) + { + unsigned i; + unsigned n = origPlug.numChildren(); + + if (dupPlug.numChildren() != n) + { + msg = "Source and destination attributes have different children."; + st = MS::kInvalidParameter; + } + else + { + for (i = 0; i < n; i++) + { + st = copyPlug( + origPlug.child(i), + dupPlug.child(i), + dupInConns, + moveOutConns, + checkOnly + ); + + if (!st) break; + } + + // + // There may be connections to the entire compound, so deal + // with those as well. + // + if (st && moveOutConns) + st = moveOutboundConnections(origPlug, dupPlug); + + if (st && dupInConns) + st = copyInboundConnection(origPlug, dupPlug); + } + } + else if (!checkOnly) + { + // + // Move output connections from the original plug over to the + // copy. + // + if (moveOutConns) + st = moveOutboundConnections(origPlug, dupPlug); + + if (st) + { + // + // If the copy plug already has an incoming connection then we + // won't be able to make another incoming connection and won't + // be able to set a value. + // + MPlugArray conns; + dupPlug.connectedTo(conns, true, false); + + if (mForce && (conns.length() > 0)) + { + // Break the existing incoming connection. + // + MDGModifier mod; + mod.disconnect(conns[0], dupPlug); + mod.doIt(); + + conns.clear(); + } + + if (conns.length() == 0) + { + // + // Does the original plug have an incoming connection which + // needs to be duplicated? + // + origPlug.connectedTo(conns, true, false); + + if (dupInConns && (conns.length() > 0)) + { + st = copyInboundConnection(origPlug, dupPlug); + } + else + { + // + // Copy the original plug's value to the copy. + // + switch (origAttr.apiType()) + { + case MFn::kDoubleAngleAttribute: + case MFn::kDoubleLinearAttribute: + { + double val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFn::kFloatAngleAttribute: + case MFn::kFloatLinearAttribute: + case MFn::kTimeAttribute: + { + float val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFn::kEnumAttribute: + { + short val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFn::kCompoundAttribute: + case MFn::kMessageAttribute: + // %%% give an error? + break; + + case MFn::kNumericAttribute: + { + MFnNumericAttribute nAttr(origAttr); + + switch (nAttr.unitType()) + { + case MFnNumericData::kBoolean: + { + bool val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFnNumericData::kByte: + case MFnNumericData::kChar: + { + char val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFnNumericData::kShort: + { + short val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFnNumericData::kLong: + { + int val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFnNumericData::kFloat: + { + float val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + case MFnNumericData::kDouble: + { + double val; + origPlug.getValue(val); + dupPlug.setValue(val); + } + break; + + default: + { + MObject data; + origPlug.getValue(data); + dupPlug.setValue(data); + } + break; + } + } + break; + + default: + { + MObject data; + origPlug.getValue(data); + dupPlug.setValue(data); + } + break; + } + } + } + } + } + + if (!mSilent && (msg.length() > 0)) MGlobal::displayError(msg); + + return st; +} + + +// Now that we know Maya has loaded shaveUI.mel and executed shaveUI(), we +// can enable all the bits and pieces which rely on procedures in those +// scripts. +void shaveUtilCmd::finishPluginInitialization() +{ + shaveCallbacks::registerCallbacks(); + shaveCallbacks::setCleanUpMELOnExit(true); +} + + +MString shaveUtilCmd::getAttrType(MString plugName) +{ + MString attrType; + MSelectionList list; + MPlug plug; + + list.add(plugName); + list.getPlug(0, plug); + + if (!plug.isNull()) + { + MObject attr = plug.attribute(); + + switch (attr.apiType()) + { + case MFn::kDoubleAngleAttribute: + attrType = "doubleAngle"; + break; + + case MFn::kFloatAngleAttribute: + attrType = "floatAngle"; + break; + + case MFn::kDoubleLinearAttribute: + attrType = "doubleLinear"; + break; + + case MFn::kFloatLinearAttribute: + attrType = "floatLinear"; + break; + + case MFn::kTimeAttribute: + attrType = "time"; + break; + + case MFn::kEnumAttribute: + attrType = "enum"; + break; + + case MFn::kCompoundAttribute: + attrType = "compound"; + break; + + case MFn::kGenericAttribute: + attrType = "generic"; + break; + + case MFn::kLightDataAttribute: + attrType = "lightData"; + break; + + case MFn::kMatrixAttribute: + attrType = "matrix"; + break; + + case MFn::kFloatMatrixAttribute: + attrType = "floatMatrix"; + break; + + case MFn::kMessageAttribute: + attrType = "message"; + break; + + case MFn::kAttribute2Double: + attrType = "double2"; + break; + + case MFn::kAttribute2Float: + attrType = "float2"; + break; + + case MFn::kAttribute2Short: + attrType = "short2"; + break; + + case MFn::kAttribute2Int: + attrType = "int2"; + break; + + case MFn::kAttribute3Double: + attrType = "double3"; + break; + + case MFn::kAttribute3Float: + attrType = "float3"; + break; + + case MFn::kAttribute3Short: + attrType = "short3"; + break; + + case MFn::kAttribute3Int: + attrType = "int3"; + break; + + case MFn::kAttribute4Double: + attrType = "double4"; + break; + + case MFn::kNumericAttribute: + { + MFnNumericAttribute nAttr(attr); + + switch (nAttr.unitType()) + { + case MFnNumericData::kBoolean: + attrType = "bool"; + break; + + case MFnNumericData::kByte: + attrType = "byte"; + break; + + case MFnNumericData::kChar: + attrType = "char"; + break; + + case MFnNumericData::kShort: + attrType = "short"; + break; + + case MFnNumericData::k2Short: + attrType = "short2"; + break; + + case MFnNumericData::k3Short: + attrType = "short3"; + break; + + // + // The 'int' types have the same enum values as the + // 'long' types, so we only specify one here. We've + // chosen to use the name 'long' because that's what + // the 'setAttr' command accepts. + // + case MFnNumericData::kLong: + attrType = "long"; + break; + + case MFnNumericData::k2Long: + attrType = "long2"; + break; + + case MFnNumericData::k3Long: + attrType = "long3"; + break; + + case MFnNumericData::kFloat: + attrType = "float"; + break; + + case MFnNumericData::k2Float: + attrType = "float2"; + break; + + case MFnNumericData::k3Float: + attrType = "float3"; + break; + + case MFnNumericData::kDouble: + attrType = "double"; + break; + + case MFnNumericData::k2Double: + attrType = "double2"; + break; + + case MFnNumericData::k3Double: + attrType = "double3"; + break; + + default: + break; + } + } + break; + + case MFn::kTypedAttribute: + { + MFnTypedAttribute tAttr(attr); + + switch (tAttr.attrType()) + { + case MFnData::kNumeric: + attrType = "numeric"; + break; + + case MFnData::kPlugin: + // + // This doesn't work. For plugin data attrType() + // returns MFnData::kInvalid. + // + attrType = "pluginData"; + break; + + case MFnData::kPluginGeometry: + attrType = "pluginGeometry"; + break; + + case MFnData::kString: + attrType = "string"; + break; + + case MFnData::kMatrix: + attrType = "matrix"; + break; + + case MFnData::kStringArray: + attrType = "stringArray"; + break; + + case MFnData::kDoubleArray: + attrType = "doubleArray"; + break; + + case MFnData::kIntArray: + attrType = "intArray"; + break; + + case MFnData::kPointArray: + attrType = "pointArray"; + break; + + case MFnData::kVectorArray: + attrType = "vectorArray"; + break; + + case MFnData::kComponentList: + attrType = "componentList"; + break; + + case MFnData::kMesh: + attrType = "mesh"; + break; + + case MFnData::kLattice: + attrType = "lattice"; + break; + + case MFnData::kNurbsCurve: + attrType = "nurbsCurve"; + break; + + case MFnData::kNurbsSurface: + attrType = "nurbsSurface"; + break; + + case MFnData::kDynArrayAttrs: + attrType = "arrayAttrs"; + break; + + case MFnData::kDynSweptGeometry: + attrType = "sweptGeometry"; + break; + + case MFnData::kSubdSurface: + attrType = "subd"; + break; + + default: + break; + } + } + break; + + default: + break; + } + } + + return attrType; +} + + +MStatus shaveUtilCmd::moveOutboundConnections(MPlug& origPlug, MPlug& dupPlug) + const +{ + MStatus st; + + MPlugArray conns; + unsigned i; + MDGModifier mod; + + origPlug.connectedTo(conns, false, true); + + for (i = 0; i < conns.length(); i++) + { + st = mod.disconnect(origPlug, conns[i]); + if (st) st = mod.connect(dupPlug, conns[i]); + if (!st) break; + } + + if (st) st = mod.doIt(); + + if (!st && !mSilent) + { + MGlobal::displayError( + MString("Cannot move output connections from '") + + origPlug.name() + "' to '" + dupPlug.name() + "': " + + st.errorString() + ); + } + + return st; +} + + +MStatus shaveUtilCmd::scaleAll(float s) +{ + MStatus stat = MS::kSuccess; + MGlobal::displayInfo(MString("Scale all ") + s); + + MObjectArray nodes; + shaveUtil::getShaveNodes(nodes); + + unsigned int numShaveNodes = nodes.length(); + unsigned int i; + + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode dFn(nodes[i]); + + shaveHairShape* shShape = (shaveHairShape*)dFn.userNode(); + + SHAVENODE* sh = shShape->getHairNode(); + shShape->doXform(); + SOFTscale_select(s); + SHAVEfetch_node(sh); + shShape->applyEdits(true); + + + float trigger; + MPlug triggerPlug = dFn.findPlug("trigger"); + triggerPlug.getValue(trigger); + triggerPlug.setValue(trigger+1.0f); + + + M3dView::active3dView().refresh(true,true); + + } + return stat; +} +MStatus shaveUtilCmd::scaleCurrent(float s) +{ + MStatus stat = MS::kSuccess; + //MGlobal::displayInfo(MString("Scale current ") + s); + + MSelectionList list; + MDagPath nodePath; + MObject component; + MFnDagNode sFn; + + MGlobal::getActiveSelectionList( list ); + for ( MItSelectionList listIter( list ); !listIter.isDone(); listIter.next() ) + { + + + listIter.getDagPath( nodePath, component ); + nodePath.extendToShape(); + sFn.setObject( nodePath ); + + MGlobal::displayInfo(sFn.name()); + + MObjectArray nodes; + shaveUtil::getShaveNodes(nodes); + + unsigned int numShaveNodes = nodes.length(); + unsigned int i; + for (i = 0; i < numShaveNodes; i++) + { + MFnDependencyNode dFn(nodes[i]); + if(dFn.name() == sFn.name()) + { + MGlobal::displayInfo(MString("Scale ") + dFn.name() + " " + s); + + shaveHairShape* shShape = (shaveHairShape*)dFn.userNode(); + + SHAVENODE* sh = shShape->getHairNode(); + shShape->doXform(); + SOFTscale_select(s); + SHAVEfetch_node(sh); + shShape->applyEdits(true); + + float trigger; + MPlug triggerPlug = dFn.findPlug("trigger"); + triggerPlug.getValue(trigger); + triggerPlug.setValue(trigger+1.0f); + } + + } + } + M3dView::active3dView().refresh(true,true); + return stat; +} diff --git a/mayaPlug/shaveUtilCmd.h b/mayaPlug/shaveUtilCmd.h new file mode 100644 index 0000000..2e8d6ba --- /dev/null +++ b/mayaPlug/shaveUtilCmd.h @@ -0,0 +1,71 @@ +#ifndef shaveUtilCmd_h +#define shaveUtilCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MArgList.h> +#include <maya/MPxCommand.h> +#include <maya/MSyntax.h> +#include <maya/MString.h> + + +class shaveUtilCmd : public MPxCommand +{ +public: + shaveUtilCmd(); + virtual ~shaveUtilCmd(); + + MStatus doIt( const MArgList& args ); + bool isUndoable() const; + + static void* createCmd(); + static MSyntax createSyntax(); + + static const MString commandName; + +private: + + MStatus scaleAll(float s); + MStatus scaleCurrent(float s); + + MStatus copyAttr( + MString origPlugName, + MString dupPlugName, + bool dupInConns, + bool moveOutConns + ) const; + + MStatus copyInboundConnection( + MPlug& origPlug, MPlug& dupPlug + ) const; + + MStatus copyPlug( + MPlug origPlug, + MPlug dupPlug, + bool dupInConns, + bool moveOutConns, + bool checkOnly + ) const; + + static void finishPluginInitialization(); + static MString getAttrType(MString plugName); + + MStatus moveOutboundConnections( + MPlug& origPlug, MPlug& dupPlug + ) const; + +#ifdef PROFILE + static bool gProfiling; +#endif + bool mForce; + bool mSilent; +}; + + +inline bool shaveUtilCmd::isUndoable() const +{ return false; } + +#endif + diff --git a/mayaPlug/shaveVertexShader.cpp b/mayaPlug/shaveVertexShader.cpp new file mode 100644 index 0000000..ec38a5e --- /dev/null +++ b/mayaPlug/shaveVertexShader.cpp @@ -0,0 +1,289 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MColor.h> +#include <maya/MDataBlock.h> +#include <maya/MDataHandle.h> +#include <maya/MFloatVector.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnMesh.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MGlobal.h> +#include <maya/MItDependencyGraph.h> +#include <maya/MItMeshPolygon.h> +#include <maya/MItMeshVertex.h> +#include <maya/MPlug.h> +#include <maya/MPointArray.h> +#include <maya/MSelectionList.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> + +#include <math.h> + +#include "shaveVertexShader.h" + +// Static data +MTypeId shaveVertexShader::id(0x00102998); +const MString shaveVertexShader::nodeTypeName("shaveVertexShader"); + + +// Attributes +MObject shaveVertexShader::pointAttr; +MObject shaveVertexShader::surfaceIndexAttr; +MObject shaveVertexShader::objectIdAttr; +MObject shaveVertexShader::primitiveIdAttr; +MObject shaveVertexShader::outColorAttr; +MObject shaveVertexShader::outAlphaAttr; + + +void shaveVertexShader::postConstructor() +{ + setMPSafe(true); +} + + +shaveVertexShader::shaveVertexShader() +{ +} + + +shaveVertexShader::~shaveVertexShader() +{ +} + + +void * shaveVertexShader::creator() +{ + return new shaveVertexShader(); +} + + +MStatus shaveVertexShader::initialize() +{ + MFnNumericAttribute nAttr; + + pointAttr = nAttr.create("pointObj", "po", MFnNumericData::k3Float); + nAttr.setStorable(false); + nAttr.setHidden(true); + + surfaceIndexAttr = nAttr.create("surfaceIndex", "si", MFnNumericData::kFloat); + nAttr.setHidden(true); + + objectIdAttr = nAttr.create("objectId", "oi", MFnNumericData::kLong); + nAttr.setHidden(true); + + primitiveIdAttr = nAttr.create("primitiveId", "pi", MFnNumericData::kLong); + nAttr.setHidden(true); + + outColorAttr = nAttr.create("outColor", "oc", MFnNumericData::k3Float); + nAttr.setStorable(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + outAlphaAttr = nAttr.create( "outAlpha", "oa", MFnNumericData::kFloat); + nAttr.setDisconnectBehavior(MFnAttribute::kReset); + nAttr.setStorable(false); + nAttr.setReadable(true); + nAttr.setWritable(false); + + addAttribute(pointAttr); + addAttribute(outColorAttr); + addAttribute(outAlphaAttr); + addAttribute(surfaceIndexAttr); + addAttribute(objectIdAttr); + addAttribute(primitiveIdAttr); + + attributeAffects(pointAttr, outColorAttr); + attributeAffects(surfaceIndexAttr, outColorAttr); + attributeAffects(objectIdAttr, outColorAttr); + attributeAffects(primitiveIdAttr, outColorAttr); + + attributeAffects(pointAttr, outAlphaAttr); + attributeAffects(surfaceIndexAttr, outAlphaAttr); + attributeAffects(objectIdAttr, outAlphaAttr); + attributeAffects(primitiveIdAttr, outAlphaAttr); + + return MS::kSuccess; +} + + +MStatus shaveVertexShader::compute( const MPlug& plug, MDataBlock& block ) +{ + if ((plug != outColorAttr) && (plug.parent() != outColorAttr) && + (plug != outAlphaAttr)) + { + return MS::kUnknownParameter; + } + + MStatus status; + MObject thisNode = thisMObject(); + MColor resultColour(0.5, 0.5, 0.5, 1.0); + + long surfaceIndex = (long)(block.inputValue(surfaceIndexAttr).asFloat() + 0.5); + long triangleID = block.inputValue(primitiveIdAttr).asLong(); + + // Location of the point we are shading + float3& samplePtIn = block.inputValue(pointAttr).asFloat3(); + MFloatVector samplePt(samplePtIn); + + // Find the Mesh object + MPlug outColorPlug = MFnDependencyNode(thisNode).findPlug("outColor", + &status); + + MItDependencyGraph depIt( outColorPlug, MFn::kShadingEngine, + MItDependencyGraph::kDownstream, + MItDependencyGraph::kBreadthFirst, + MItDependencyGraph::kNodeLevel, + &status); + + depIt.enablePruningOnFilter(); + + MObject shadingEngineNode = depIt.thisNode(); + + MPlug dagSetMembersPlug = MFnDependencyNode(shadingEngineNode).findPlug( + "dagSetMembers", &status + ); + + if (surfaceIndex < (long)dagSetMembersPlug.numElements()) + { + MPlug dagSetMembersElementPlug; + + dagSetMembersElementPlug = dagSetMembersPlug.elementByLogicalIndex( + surfaceIndex, &status + ); + + MPlugArray meshPlugArray; + + dagSetMembersElementPlug.connectedTo( + meshPlugArray, true, false, &status + ); + + if (meshPlugArray.length() > 0) + { + MObject meshNode = meshPlugArray[0].node(); + + int polygonID = 0; + int prevIndex = 0; + + int i; + MFnMesh meshFn(meshNode); + MItMeshPolygon iter(meshNode); + + int numPolygons = meshFn.numPolygons(); + + // + // If the mesh is empty, then there's nothing to do. + // + if (numPolygons > 0) + { + // We used to cache the face and triangle IDs for the + // current object, under the assumption that we would get + // many consecutive hits on the same object, thereby + // speeding things up. But the caching makes this node MP + // unsafe and disabling MP slows things down considerably. + // Also, it appears that the caching alone is slower than + // just handling each sample on the fly. So we no longer + // cache + for (i = 0; i < numPolygons; i++) + { + int nTri; + iter.setIndex(i, prevIndex); + iter.numTriangles(nTri); + + if (triangleID < nTri) + { + polygonID = i; + break; + } + + triangleID -= nTri; + } + + iter.setIndex(polygonID, prevIndex); + + MPointArray v; + MIntArray vertIndices; + + // + // Get the triangle's vertices. + // + status = iter.getTriangle( + triangleID, v, vertIndices, MSpace::kObject + ); + + MFloatVector p1((float)v[0].x, (float)v[0].y, (float)v[0].z); + MFloatVector p2((float)v[1].x, (float)v[1].y, (float)v[1].z); + MFloatVector p3((float)v[2].x, (float)v[2].y, (float)v[2].z); + + MColor colour1; + MColor colour2; + MColor colour3; + + MItMeshVertex vertIter(meshNode); + + prevIndex = 0; + + vertIter.setIndex(vertIndices[0], prevIndex); + vertIter.getColor(colour1, polygonID); + + vertIter.setIndex(vertIndices[1], prevIndex); + vertIter.getColor(colour2, polygonID); + + vertIter.setIndex(vertIndices[2], prevIndex); + vertIter.getColor(colour3, polygonID); + + // + // Calculate the point's barycentric coordinates. + // + samplePt = samplePt - p3; + p1 = p1 - p3; + p2 = p2 - p3; + + MFloatVector norm = p1 ^ p2; + float lenSquared = norm * norm; + + lenSquared = (norm * samplePt) / lenSquared; + + // + // The point may not be exactly on the triangle, so move it + // there. + // + samplePt = samplePt - (lenSquared * norm); + + float aa = p1 * p1; + float bb = p2 * p2; + float ab = p1 * p2; + float am = p1 * samplePt; + float bm = p2 * samplePt; + float det = aa*bb - ab*ab; + + MFloatVector abc; + abc.x = (am*bb - bm*ab) / det; + abc.y = (bm*aa - am*ab) / det; + abc.z = 1 - abc.x - abc.y; + + resultColour = (abc.x*colour1) + + (abc.y*colour2) + + (abc.z*colour3); + } + } + } + + MDataHandle outColorHandle = block.outputValue(outColorAttr); + MFloatVector& outColor = outColorHandle.asFloatVector(); + + outColor.x = resultColour.r; + outColor.y = resultColour.g; + outColor.z = resultColour.b; + outColorHandle.setClean(); + + MDataHandle outAlphaHandle = block.outputValue(outAlphaAttr); + float& outAlpha = outAlphaHandle.asFloat(); + + outAlpha = resultColour.a; + outAlphaHandle.setClean(); + + return MS::kSuccess; +} + diff --git a/mayaPlug/shaveVertexShader.h b/mayaPlug/shaveVertexShader.h new file mode 100644 index 0000000..a7c6057 --- /dev/null +++ b/mayaPlug/shaveVertexShader.h @@ -0,0 +1,61 @@ +#ifndef shaveVertexShader_h +#define shaveVertexShader_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDataBlock.h> +#include <maya/MDataHandle.h> +#include <maya/MFloatVector.h> +#include <maya/MPlug.h> +#include <maya/MPxNode.h> +#include <maya/MSceneMessage.h> +#include <maya/MTypeId.h> + + +class shaveVertexShader : public MPxNode +{ +public: + shaveVertexShader(); + virtual ~shaveVertexShader(); + + virtual MStatus compute(const MPlug&, MDataBlock&); + virtual void postConstructor(); + +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + static void* creator(); + static MStatus initialize(); + + static MTypeId id; + static const MString nodeTypeName; + +private: + static inline float dotProduct(const MFloatVector&, const MFloatVector&); + + // Input attributes + + static MObject pointAttr; + static MObject surfaceIndexAttr; + static MObject objectIdAttr; + static MObject primitiveIdAttr; + + // Output attributes + static MObject outColorAttr; + static MObject outAlphaAttr; + + // + // Local members. + // +}; + + +inline float shaveVertexShader::dotProduct( + const MFloatVector& v1, + const MFloatVector& v2 +) +{ return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); } + +#endif diff --git a/mayaPlug/shaveVolumeShader.cpp b/mayaPlug/shaveVolumeShader.cpp new file mode 100644 index 0000000..47cf70c --- /dev/null +++ b/mayaPlug/shaveVolumeShader.cpp @@ -0,0 +1,368 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MDataBlock.h> +#include <maya/MDataHandle.h> +#include <maya/MFloatVector.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnNumericData.h> +#include <maya/MPlug.h> +#include <maya/MString.h> +#include <maya/MTypeId.h> + +#include <maya/MAnimControl.h> +#include <maya/MTime.h> + +#include "shaveGlobals.h" +#include "shaveIO.h" +#include "shaveRender.h" +#include "shaveVolumeShader.h" + +MTypeId shaveVolumeShader::id(0x001029A2); +const MString shaveVolumeShader::nodeTypeName("shaveVolumeShader"); + +MObject shaveVolumeShader::aFarPointCamera; +MObject shaveVolumeShader::aFarPointWorld; +MObject shaveVolumeShader::aOutColor; +MObject shaveVolumeShader::aOutMatteOpacity; +MObject shaveVolumeShader::aOutTransparency; +MObject shaveVolumeShader::aPixelCenter; +MObject shaveVolumeShader::aPixelCenterX; +MObject shaveVolumeShader::aPixelCenterY; +MObject shaveVolumeShader::aPointCamera; +MObject shaveVolumeShader::aPointWorld; +MObject shaveVolumeShader::aRayDepth; +MObject shaveVolumeShader::aRayDirection; +MObject shaveVolumeShader::aRayOrigin; + + +shaveVolumeShader::shaveVolumeShader() +{ +} + + +shaveVolumeShader::~shaveVolumeShader() +{ +} + + +void* shaveVolumeShader::creator() +{ + return new shaveVolumeShader(); +} + + +MStatus shaveVolumeShader::initialize() +{ + MFnNumericAttribute nAttr; + + // + // Render Input Attributes + // + aFarPointCamera = nAttr.createPoint("farPointCamera", "fc"); + addAttribute(aFarPointCamera); + + + aFarPointWorld = nAttr.createPoint("farPointWorld", "fw"); + addAttribute(aFarPointWorld); + + + aPixelCenterX = nAttr.create( + "pixelCenterX", "pcx", MFnNumericData::kFloat + ); + + aPixelCenterY = nAttr.create( + "pixelCenterY", "pcy", MFnNumericData::kFloat + ); + + aPixelCenter = nAttr.create( + "pixelCenter", + "pc", + aPixelCenterX, + aPixelCenterY + ); + + addAttribute(aPixelCenter); + + + aPointCamera = nAttr.createPoint("pointCamera", "p"); + addAttribute(aPointCamera); + + + aPointWorld = nAttr.createPoint("pointWorld", "pw"); + addAttribute(aPointWorld); + + + aRayDepth = nAttr.create("rayDepth", "rd", MFnNumericData::kShort); + addAttribute(aRayDepth); + + + aRayDirection = nAttr.createPoint("rayDirection", "rad"); + addAttribute(aRayDirection); + + + aRayOrigin = nAttr.createPoint("rayOrigin", "ro"); + addAttribute(aRayOrigin); + + + // + // Render Output Attributes + // + aOutColor = nAttr.createColor("outColor", "oc"); + addAttribute(aOutColor); + + + aOutMatteOpacity = nAttr.createColor("outMatteOpacity", "omo"); + addAttribute(aOutMatteOpacity); + + + aOutTransparency = nAttr.createColor("outTransparency", "ot"); + addAttribute(aOutTransparency); + + + // + // Set up attribute dependencies. + // + attributeAffects(aFarPointCamera, aOutColor); + attributeAffects(aFarPointWorld, aOutColor); + attributeAffects(aPixelCenter, aOutColor); + attributeAffects(aPointCamera, aOutColor); + attributeAffects(aPointWorld, aOutColor); + attributeAffects(aRayDepth, aOutColor); + attributeAffects(aRayDirection, aOutColor); + attributeAffects(aRayOrigin, aOutColor); + + attributeAffects(aFarPointCamera, aOutMatteOpacity); + attributeAffects(aFarPointWorld, aOutMatteOpacity); + attributeAffects(aPixelCenter, aOutMatteOpacity); + attributeAffects(aPointCamera, aOutMatteOpacity); + attributeAffects(aPointWorld, aOutMatteOpacity); + attributeAffects(aRayDepth, aOutMatteOpacity); + attributeAffects(aRayDirection, aOutMatteOpacity); + attributeAffects(aRayOrigin, aOutMatteOpacity); + + attributeAffects(aFarPointCamera, aOutTransparency); + attributeAffects(aFarPointWorld, aOutTransparency); + attributeAffects(aPixelCenter, aOutTransparency); + attributeAffects(aPointCamera, aOutTransparency); + attributeAffects(aPointWorld, aOutTransparency); + attributeAffects(aRayDepth, aOutTransparency); + attributeAffects(aRayDirection, aOutTransparency); + attributeAffects(aRayOrigin, aOutTransparency); + + return MS::kSuccess; +} + + +void shaveVolumeShader::postConstructor() +{ +} + + +MStatus shaveVolumeShader::compute(const MPlug& plug, MDataBlock& block) +{ + bool hit = false; + MFloatVector* outValuePtr; + float defaultValue = 0.0f; + + int pixelX = (int)block.inputValue(aPixelCenterX).asFloat(); + int pixelY = (int)block.inputValue(aPixelCenterY).asFloat(); + + shaveRender::SceneInfo* sceneInfo = shaveRender::getSceneInfo(); + + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; + float a = 0.0f; + + MFloatVector& rayOrigin = block.inputValue(aRayOrigin) + .asFloatVector(); + + // + // Eye rays are handled by the buffer composite. + // + if (rayOrigin.isEquivalent(MFloatVector::zero)) + { + // + // If we're compositing and have an image from Shave, then check to + // see if this pixel contains any hair. + // + if (doShaveCompsGlob && !doShaveComp2dGlob && sceneInfo->bufferValid) + { + // + // Get shave's Z-value for this pixel. + // + float shaveZ = sceneInfo->shaveZBuffer[ + sceneInfo->width * pixelY + pixelX + ]; + + // + // If Shave's Z-value equals its far clip constant, then + // there's no hair in the pixel, so we can ignore it. + // + if (shaveZ < (SHAVE_FAR_CLIP - 1.0f)) + { + // + // Shave's Z-value is really the distance from the camera. + // + // The near and far points handed to us by the renderer are + // in the camera's *transformational* space, but not in + // perspective space. So their Z values do not equate to + // distance from the camera. + // + // So, we must compute the distances to the near and far + // points. + // + MFloatVector& farPoint = block.inputValue(aFarPointCamera) + .asFloatVector(); + MFloatVector& nearPoint = block.inputValue(aPointCamera) + .asFloatVector(); + + float nearDist = nearPoint.length(); + float farDist = farPoint.length(); + + // + // If it lies between the segment's endpoints, set the + // colour for the pixel. + // + if ((shaveZ >= nearDist) && (shaveZ <= farDist)) + { + hit = true; + + shaveRender::Pixel* shavePixel; + + shavePixel = &sceneInfo->shaveRenderPixels[ + sceneInfo->width*pixelY + pixelX + ]; + r = (float)shavePixel->r / 255.0f; + g = (float)shavePixel->g / 255.0f; + b = (float)shavePixel->b / 255.0f; + a = (float)shavePixel->a / 255.0f; + } + } + } + } + // + // Non-eye rays (e.g. reflections & refractions) are handled by a call + // to SHAVEtrace(). + // + else + { + if (visibleInReflectionsGlob) + { + MFloatVector& farPoint = block.inputValue(aFarPointWorld) + .asFloatVector(); + MFloatVector& nearPoint = block.inputValue(aPointWorld) + .asFloatVector(); + MFloatVector ray = farPoint - nearPoint; + + VOXSAMP sample; + VERT rbase; + VERT rdir; + float nearClip = 0.0f; + float farClip = ray.length(); + + rbase.x = nearPoint.x; + rbase.y = nearPoint.y; + rbase.z = -nearPoint.z; + + rdir.x = ray.x; + rdir.y = ray.y; + rdir.z = -ray.z; + + if (!sceneInfo->shaveTraceInitialized) + { + SHAVEtrace_init(); + sceneInfo->shaveTraceInitialized = true; + } + + if (SHAVEtrace(nearClip, farClip, rbase, rdir, 1, &sample)) + { + hit = true; + + r = sample.color.x; + g = sample.color.y; + b = sample.color.z; + a = sample.opacity; + } + } + } + + // + // In theory, the renderer could ask for either the parent plug's + // value (e.g. outColor) or each of its children individually + // (e.g. outColor.r). + // + // In practice, it only ever asks for the parent, and checking the + // children adds a tiny but measurable amount of overhead to the + // render. So we'll only check the parent plugs and if that causes a + // problem in some future version of the renderer, we'll make the + // change then. + // + if (plug == aOutColor) + { + outValuePtr = &block.outputValue(aOutColor).asFloatVector(); + + if (hit) + { + (*outValuePtr).x = r; + (*outValuePtr).y = g; + (*outValuePtr).z = b; + } + else + { + // + // If we didn't get a hit, then make the pixel black otherwise + // Maya's renderer will still use the colour, even if it's + // transparent. + // + defaultValue = 0.0f; + } + } + else if (plug == aOutMatteOpacity) + { + outValuePtr = &block.outputValue(aOutMatteOpacity).asFloatVector(); + + if (hit) + { + (*outValuePtr).x = a; + (*outValuePtr).y = a; + (*outValuePtr).z = a; + } + else + defaultValue = 0.0f; + } + else if (plug == aOutTransparency) + { + outValuePtr = &block.outputValue(aOutTransparency).asFloatVector(); + + if (hit) + { + float transparency = 1.0f - a; + + (*outValuePtr).x = transparency; + (*outValuePtr).y = transparency; + (*outValuePtr).z = transparency; + } + else + defaultValue = 1.0f; + } + else + return MS::kUnknownParameter; + + // + // If we didn't get a hit on a hair, then just output the default + // value, which is transparent black. + // + if (!hit) + { + (*outValuePtr).x = defaultValue; + (*outValuePtr).y = defaultValue; + (*outValuePtr).z = defaultValue; + } + + block.setClean(plug); + + return MS::kSuccess; +} diff --git a/mayaPlug/shaveVolumeShader.h b/mayaPlug/shaveVolumeShader.h new file mode 100644 index 0000000..6fb1bc5 --- /dev/null +++ b/mayaPlug/shaveVolumeShader.h @@ -0,0 +1,68 @@ +#ifndef shaveVolumeShader_h +#define shaveVolumeShader_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxNode.h> + + +class shaveVolumeShader : public MPxNode +{ + //****************************************************** + // + // Creation & Initialization Methods + // + //****************************************************** +public: + shaveVolumeShader(); + virtual ~shaveVolumeShader(); + static void * creator(); + static MStatus initialize(); + + + //****************************************************** + // + // Overloaded MPxNode Methods + // + //****************************************************** +public: + virtual MStatus compute( const MPlug&, MDataBlock& ); + virtual void postConstructor(); +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + + //****************************************************** + // + // Member Variables + // + //****************************************************** +public: + static MTypeId id; + static const MString nodeTypeName; + + + //****************************************************** + // + // Attributes + // + //****************************************************** +public: + static MObject aFarPointCamera; + static MObject aFarPointWorld; + static MObject aOutColor; + static MObject aOutMatteOpacity; + static MObject aOutTransparency; + static MObject aPixelCenter; + static MObject aPixelCenterX; + static MObject aPixelCenterY; + static MObject aPointCamera; + static MObject aPointWorld; + static MObject aRayDepth; + static MObject aRayDirection; + static MObject aRayOrigin; +}; + +#endif diff --git a/mayaPlug/shaveVrayCmd.cpp b/mayaPlug/shaveVrayCmd.cpp new file mode 100644 index 0000000..608e00d --- /dev/null +++ b/mayaPlug/shaveVrayCmd.cpp @@ -0,0 +1,1361 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVrayCmd.cpp + + DESCRIPTION: Maya node + + HISTORY: created 01-04-2010 + + *> + **********************************************************************/ + +#include "shaveRender.h" +#include "shaveVrayNode.h" +#include "shaveVrayCmd.h" +#include "shaveVrayRenderer.h" +#include "shaveVraySharedFunctions.h" + +#include "shaveHairShape.h" + +#include <maya/MDagPath.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MDGModifier.h> +#include <maya/MArgDatabase.h> +#include <maya/MPlugArray.h> +#include <maya/MDagModifier.h> +#include <maya/MSelectionList.h> +#include <maya/MFnRenderLayer.h> +#include <maya/MItDependencyNodes.h> + + +#include <assert.h> +#include <vector> +#include <string.h> + +#ifdef USE_VRAY + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Layer change callback | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#if 0 +static MCallbackId layerCbkId=0; +static void shaveVrayLayerCbk(MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug &otherPlug, void *clientData); +#endif + +static std::vector<MCallbackId> cbkIds; +static void shaveVrayShaderChangeCbk(MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug &otherPlug, void *clientData); + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Command | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MString shaveVrayCmd::cmd = "shaveVrayShader"; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Flags | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +char* shaveVrayCmd::sf_create = "c"; +char* shaveVrayCmd::lf_create = "create"; + +char* shaveVrayCmd::sf_delete = "d"; +char* shaveVrayCmd::lf_delete = "delete"; + +char* shaveVrayCmd::sf_all = "all"; +char* shaveVrayCmd::lf_all = "allShapes"; + +#if 0 +char* shaveVrayCmd::sf_addCbk = "ac"; +char* shaveVrayCmd::lf_addCbk = "addCallback"; + +char* shaveVrayCmd::sf_delCbk = "rc"; +char* shaveVrayCmd::lf_delCbk = "removeCallback"; +#endif + +//char* shaveVrayCmd::sf_shaveInit = "si"; +//char* shaveVrayCmd::lf_shaveInit = "shaveInit"; +// +//char* shaveVrayCmd::sf_shaveClear= "sc"; +//char* shaveVrayCmd::lf_shaveClear= "shaveClear"; + +//char* shaveVrayCmd::sf_frameStart= "fs"; +//char* shaveVrayCmd::lf_frameStart= "frameStart"; +// +//char* shaveVrayCmd::sf_frameEnd = "fe"; +//char* shaveVrayCmd::lf_frameEnd = "frameEnd"; + +//char* shaveVrayCmd::sf_shutterOpen = "so"; +//char* shaveVrayCmd::lf_shutterOpen = "shutterOpen"; +// +//char* shaveVrayCmd::sf_shutterClose= "sc"; +//char* shaveVrayCmd::lf_shutterClose= "shutterClose"; + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Methods | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MStatus shaveVrayCmd::doIt(const MArgList &maList) +{ + MStatus stat; + + setResult(0); // will be changed later. + + MArgDatabase parser(syntax(),maList,&stat); + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveVray: can not parse arguments."); + return stat; + } + { + if(parser.isFlagSet(sf_create)) + { + if(parser.isFlagSet(sf_all)) + { + shaveVrayRenderer* renderer = dynamic_cast<shaveVrayRenderer*>(shaveRender::getRenderer()); + if (renderer == NULL) { + MGlobal::displayError("'shaveVray -create -all' used when current renderer is not VRay"); + return MS::kFailure; + } + + MObjectArray shaveShapes; + renderer->getRenderableShaveNodesByRenderMode(NULL, &shaveShapes); + + if(shaveShapes.length() > 0) + { + + for(unsigned int i = 0; i < shaveShapes.length(); i++) + { + ////// + MFnDependencyNode nodeFn(shaveShapes[i]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + + MObject displayNode = nodePtr->getDisplayShape(); + + if (!displayNode.isNull()) + { + MFnDagNode dagNodeFn(displayNode); + //visibility + { + MPlug plug = dagNodeFn.findPlug("visibility"); + if(!plug.isNull()) + { + bool v; + plug.getValue(v); + if (!v) continue; + } + } + //overriden visibility + { + MPlug plug = dagNodeFn.findPlug("overrideEnabled"); + if(!plug.isNull()) + { + bool overridesEnabled; + plug.getValue(overridesEnabled); + if(overridesEnabled) + { + MPlug plug2 = dagNodeFn.findPlug("overrideVisibility"); + if(!plug2.isNull()) + { + bool ovi; + plug2.getValue(ovi); + if(!ovi) continue; + } + } + } + } + } + + if(!attachShaderTo(shaveShapes[i])) + { + MGlobal::displayError("shaveVray: can not attach the shader."); + return MStatus::kNotFound; + } + }//for shapes + + /////////////////////////////////////// +#if 0 + { + MObjectArray layers; + MFnRenderLayer::listAllRenderLayers(layers); + for(unsigned int oo = 0; oo < layers.length(); oo++) + { + + MObjectArray members; + MFnRenderLayer layerFn(layers[oo]); + MGlobal::displayInfo(MString(" Layer ") + layerFn.name()); + MGlobal::displayInfo("~~~~~~~~~~~~~~~~~~~~~~~~"); + + if(MStatus::kSuccess == layerFn.listMembers(members)) + { + for(unsigned int kk = 0; kk < members.length(); kk++) + { + MFnDependencyNode dFn(members[kk]); + MGlobal::displayInfo(MString("member ") + dFn.name()); + } + } + MGlobal::displayInfo("~~~~~~~~~~~~~~~~~~~~~~~~"); + } + } +#endif + ////////////////////////////////////// + } + } + else + { + MObject shaveShape; + if(FindCurrentShaveShape(shaveShape)) + { + if(attachShaderTo(shaveShape)) + { + setResult(1); + return MStatus::kSuccess; + } + else + { + MGlobal::displayError("shaveVray: can not attach the shader."); + return MStatus::kNotFound; + } + } + else + { + MGlobal::displayError("shaveVray: select the hairShape to attach the shader."); + return MStatus::kNotFound; + } + } + } + else if(parser.isFlagSet(sf_delete)) + { + deregisterShaderChangeCallacks(); + if(parser.isFlagSet(sf_all)) + { + MObjectArray shaveShapes; + if(FindAllShaveShapes(shaveShapes)) + { + for(unsigned int i = 0; i < shaveShapes.length(); i++) + { + if(!detachShaderFrom(shaveShapes[i])) + { + MGlobal::displayError("shaveVray: can not dettach the shader."); + return MStatus::kNotFound; + } + } + } + } + else + { + MObject shaveShape; + if(FindCurrentShaveShape(shaveShape)) + { + if(detachShaderFrom(shaveShape)) + { + setResult(1); + return MStatus::kSuccess; + } + else + { + MGlobal::displayError("shaveVray: can not dettach the shader."); + return MStatus::kNotFound; + } + } + else + { + MGlobal::displayError("shaveVray: select the hairShape to attach the shader."); + return MStatus::kNotFound; + } + } + } +#if 0 + else if(parser.isFlagSet(sf_addCbk)) + { + registerLayersCallback(); + } + else if(parser.isFlagSet(sf_delCbk)) + { + deregisterLayersCallack(); + } +#endif + } + setResult(1); + return MStatus::kSuccess; +} + + +MSyntax shaveVrayCmd::newSyntax() +{ + MSyntax syn; + + syn.enableQuery(true); + syn.addFlag(sf_create, lf_create); + syn.addFlag(sf_delete, lf_delete); + syn.addFlag(sf_all, lf_all); +#if 0 + syn.addFlag(sf_addCbk, lf_addCbk); + syn.addFlag(sf_delCbk, lf_delCbk); +#endif + return syn; +} + + +struct PlugPair { + MPlug from; + MPlug to; +}; +//static std::vector<PlugPair> restore; + +bool shaveVrayCmd::attachShaderTo(MObject shaveShape) +{ +#ifdef _DEBUG + { + MFnDependencyNode sFn(shaveShape); + MGlobal::displayInfo(MString("shaveVrayCmd::attachShaderTo( ") + sFn.name() + " )"); + } +#endif + MStatus stat; + MFnDependencyNode shapeFn(shaveShape,&stat); + if(stat == MStatus::kSuccess) + { + MPlug shaveStackIdxPlug = shapeFn.findPlug("stackIndex",&stat); + if(stat == MStatus::kSuccess && !shaveStackIdxPlug.isNull()) + { +#if 0 + /////////////////////////////////////////////// + if(shaveStackIdxPlug.isConnected()) + { + MPlugArray plugs; + if(shaveStackIdxPlug.connectedTo(plugs,false,true,&stat)) + { + for(unsigned int i = 0; i < plugs.length(); i++) + { + MObject shaderNode = plugs[i].node(); + if(!shaderNode.isNull()) + { + MObjectArray nodes; + EnumDownNodes(shaderNode,nodes); + MDagModifier dagMod; + for(unsigned int j = 0; j < nodes.length(); j++) + { + MFnDependencyNode dfn(nodes[j]); + + if(nodes[j].hasFn(MFn::kShape)) + { + MPlug visible = dfn.findPlug("visibility"); + visible.setValue(true); + + setupLayers(shaveShape,nodes[j]); + } + } + } + } + } + return true; + } +#endif + /////////////////////////////////////////////// + MDGModifier dgMod; + MObject vrayNode = dgMod.createNode(shaveVrayNode::typeId,&stat); + if(stat == MStatus::kSuccess && !vrayNode.isNull()) + { + MFnDependencyNode vrayFn(vrayNode,&stat); + if(stat == MStatus::kSuccess) + { + //connect stack index + MPlug vrayStackIdxPlug = vrayFn.findPlug(shaveVrayNode::attr_stackIndex,&stat); + if(stat == MStatus::kSuccess && !vrayStackIdxPlug.isNull()) + { + stat = dgMod.connect(shaveStackIdxPlug,vrayStackIdxPlug); + if(stat == MStatus::kSuccess) + { + dgMod.doIt(); + + /// connect instancing status + { + MPlug shaveIsInstancedPlug = shapeFn.findPlug("instancingStatus",&stat); + if(stat == MStatus::kSuccess && ! shaveIsInstancedPlug.isNull()) + { + MPlug vrayIsInstancedPlug = vrayFn.findPlug(shaveVrayNode::attr_isInstanced,&stat); + if(stat == MStatus::kSuccess && ! vrayIsInstancedPlug.isNull()) + { + stat = dgMod.connect(shaveIsInstancedPlug,vrayIsInstancedPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect instacing status plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.isInstanced plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.instancingStatus plug."); + } + + //// connect override surf shader flag + { + MPlug shaveOverrideShPlug = shapeFn.findPlug("overrideGeomShader",&stat); + if(stat == MStatus::kSuccess && ! shaveOverrideShPlug.isNull()) + { + MPlug vrayOverrideShPlug = vrayFn.findPlug(shaveVrayNode::attr_ownshader,&stat); + if(stat == MStatus::kSuccess && !vrayOverrideShPlug.isNull()) + { + stat = dgMod.connect(shaveOverrideShPlug,vrayOverrideShPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect overrideShader status plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.ownshader plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.overrideGeomShader plug."); + } + + + /////// connect tipfade plugs + { + MPlug shaveTipfadePlug = shapeFn.findPlug("tipFade",&stat); + if(stat == MStatus::kSuccess && ! shaveTipfadePlug.isNull()) + { + MPlug vrayTipfadePlug = vrayFn.findPlug(shaveVrayNode::attr_tipfade,&stat); + if(stat == MStatus::kSuccess && ! vrayTipfadePlug.isNull()) + { + stat = dgMod.connect(shaveTipfadePlug,vrayTipfadePlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect tipfade plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.tipfade plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.tipFade plug."); + } + /////// connect specular tint //////// + { + MPlug shavePlug = shapeFn.findPlug("specularTint",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_specTint,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect specular tint plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.specTint plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.specularTint plug."); + } + /////// connect specular tint 2 //////// + { + MPlug shavePlug = shapeFn.findPlug("specularTint2",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_specTint2,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect specular tint2 plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.specTint2 plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.specularTint2 plug."); + } + + /////// connect squirrel plugs + { + MPlug shaveSquirrelPlug = shapeFn.findPlug("squirrel",&stat); + if(stat == MStatus::kSuccess && ! shaveSquirrelPlug.isNull()) + { + MPlug vraySquirrelPlug = vrayFn.findPlug(shaveVrayNode::attr_squirrel,&stat); + if(stat == MStatus::kSuccess && ! vraySquirrelPlug.isNull()) + { + stat = dgMod.connect(shaveSquirrelPlug,vraySquirrelPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect squirrel plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.squirrel plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.squirrel plug."); + } + + /////// connect instance mesh + { + MPlug shaveInstancePlug = shapeFn.findPlug("instanceMesh",&stat); + if(stat == MStatus::kSuccess && ! shaveInstancePlug.isNull()) + { + MPlug vrayInstancePlug = vrayFn.findPlug(shaveVrayNode::attr_instanceMesh,&stat); + if(stat == MStatus::kSuccess && ! vrayInstancePlug.isNull()) + { + stat = dgMod.connect(shaveInstancePlug,vrayInstancePlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect instace mesh plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.instanceMesh plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.instanceMesh plug."); + } + + /////// connect self shadow + { + MPlug shavePlug = shapeFn.findPlug("selfShadow",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_selfshadow,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect selfshadow plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.selfshadow plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.selfShadow plug."); + } + + /////// connect cast shadow + { + MPlug shavePlug = shapeFn.findPlug("castsShadows",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_castshadow,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect castshadow plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.castshadow plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.castsShadows plug."); + } + /////// connect cast shadow + { + MPlug shavePlug = shapeFn.findPlug("receiveShadows",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_recvshadow,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect recvhadow plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.recvhadow plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.receiveShadows plug."); + } + + /////// connect primary visibility + { + MPlug shavePlug = shapeFn.findPlug("primaryVisibility",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_primaryvis,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect prmary visibility plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.primaryvis plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.primaryVisibility plug."); + } + + /// other attribs visibleInReflections visibleInRefractions castsShadows receiveShadows + + /////// connect visibility in reflectinss + { + MPlug shavePlug = shapeFn.findPlug("visibleInReflections",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_reflvis,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect refl visibility plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.reflvis plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.visibleInReflections plug."); + } + /////// connect visibility in refractinss + { + MPlug shavePlug = shapeFn.findPlug("visibleInRefractions",&stat); + if(stat == MStatus::kSuccess && ! shavePlug.isNull()) + { + MPlug vrayPlug = vrayFn.findPlug(shaveVrayNode::attr_refrvis,&stat); + if(stat == MStatus::kSuccess && ! vrayPlug.isNull()) + { + stat = dgMod.connect(shavePlug,vrayPlug); + if(stat == MStatus::kSuccess) + dgMod.doIt(); + else + MGlobal::displayError("shaveVrayCmd: can not connect refr visibility plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveVrayNode.refrvis plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.visibleInRefractions plug."); + } + + /// create dummy shape + + MCommandResult cmdResult; + MGlobal::executeCommand("polyPrimitive", cmdResult, false); + + MStringArray resArray; + cmdResult.getResult(resArray); + + MString node=resArray[0]; + MString gen=resArray[1]; + + MObject nodeObj=getNodeByName(node.asChar()); + MDagPath dagPath = MDagPath::getAPathTo(nodeObj); + dagPath.extendToShape(); + + setupLayers(shaveShape,nodeObj); + + // Find where to connect the node to. We need to leave the groupParts nodes to give us face-mtl id info. + MFnDependencyNode destNode(dagPath.node()); + MPlug destPlug = destNode.findPlug("inMesh"); + MPlug srcPlug; + bool hasSource = false; + while ((hasSource = getSourcePlugFromPlug(destPlug, srcPlug)) && srcPlug.node().apiType() == MFn::kGroupParts) + { + MFnDependencyNode srcNode(srcPlug.node()); + destPlug = srcNode.findPlug("inputGeometry"); + } + + // Make the necessary connections and set the attributes. + if (hasSource) + MGlobal::executeCommand("disconnectAttr " + srcPlug.name() + " " + destPlug.name() + ";"); + + MGlobal::executeCommand("connectAttr " + vrayFn.name() + ".output " + destPlug.name() + ";"); + + + shaveHairShape* shaveUserNode = (shaveHairShape*)shapeFn.userNode(); + if(shaveUserNode) + { + + /// connect material + MObject display = shaveUserNode->getDisplayShape(); + registerShaderChangeCallback(display); + MObject shader = shaveUserNode->getShader(); + if(!shader.isNull()) + { + MFnDependencyNode shaderFn(shader); + + MPlug instGroups = destNode.findPlug("instObjGroups"); + if(!instGroups.isNull()) + { + + MPlug instGroups0 = instGroups.elementByPhysicalIndex(0); + if(!instGroups0.isNull()) + { + if(instGroups0.isConnected()) + { + MPlugArray groupsTo; + instGroups0.connectedTo(groupsTo,false,true); + for(unsigned int k = 0; k < groupsTo.length(); k++) + { + MGlobal::executeCommand("disconnectAttr " +instGroups.name() + " " + groupsTo[k].name() + ";"); + //dgMod.disconnect(instGroups0,groupsTo[k]); + //dgMod.doIt(); + } + } + + MPlug dagSetPlug = shaderFn.findPlug("dagSetMembers"); + if(!dagSetPlug.isNull()) + { + //int dagSetN = dagSetPlug.numElements(); + //MPlug dagSetPlugK = dagSetPlug.elementByLogicalIndex(dagSetN+2); + MPlug dagSetPlugK; + for(int oo = 0; oo < 100000; oo++) + { + dagSetPlugK = dagSetPlug.elementByLogicalIndex(oo); + if(!dagSetPlugK.isConnected()) + break; + } +#ifdef _DEBUG + MGlobal::displayInfo("connectAttr " + instGroups0.name() + " " + dagSetPlugK.name() + ";"); +#endif + MGlobal::executeCommand("connectAttr " + instGroups0.name() + " " + dagSetPlugK.name() + ";"); + //MGlobal::executeCommand("connectAttr " + instGroups0.name() + " " + dagSetPlug.name() + ";"); + //stat = dgMod.connect(instGroups0,dagSetPlugK); + //if(stat == MStatus::kSuccess) + // dgMod.doIt(); + //else + // MGlobal::displayError("shaveVrayCmd: can not connect surface shader."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find 'dagSetMembers'."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find 'instObjGroups0' plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find 'instObjGroups' plug."); + + } + } + else + MGlobal::displayError("shaveVrayCmd: can not get 'userNode'."); + + + printf("shaveVrayCmd::attachShaderTo(...) -- OK\n");fflush(stdout); + + + + return true; + } + else + MGlobal::displayError("shaveVrayCmd: can not connect stack index plugs."); + } + else + MGlobal::displayError("shaveVrayCmd: find shaveVrayNode.stackIndex plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not attach MFnDependencyNode fucntion set to shaveVrayNode."); + } + else + MGlobal::displayError("shaveVrayCmd: can not create shaveVrayNode."); + } + else + MGlobal::displayError("shaveVrayCmd: can not find shaveHairShape.stackIndex plug."); + } + else + MGlobal::displayError("shaveVrayCmd: can not attach MFnDependencyNode fucntion set to shaveHairShape node."); + + return false; +} + +bool shaveVrayCmd::detachShaderFrom(MObject shShape) +{ +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayCmd::dettachShaderFrom(...)"); +#endif + + MStatus stat; + MFnDependencyNode shapeFn(shShape,&stat); + if(stat == MStatus::kSuccess) + { + MPlug shavePlug = shapeFn.findPlug("stackIndex",&stat); + if(stat == MStatus::kSuccess && !shavePlug.isNull()) + { + if(shavePlug.isConnected()) + { + MPlugArray plugs; + if(shavePlug.connectedTo(plugs,false,true,&stat)) + { + for(unsigned int i = 0; i < plugs.length(); i++) + { +#if 1 + MDGModifier dgMod; + dgMod.disconnect(shavePlug,plugs[i]); + dgMod.doIt(); +#endif + + + MObject shaderNode = plugs[i].node(); + if(!shaderNode.isNull()) + { + MObjectArray nodes; + EnumDownNodes(shaderNode,nodes); + MDagModifier dagMod; + for(unsigned int j = 0; j < nodes.length(); j++) + { +#if 0 + MFnDependencyNode dfn(nodes[j]); + + if(nodes[j].hasFn(MFn::kShape)) + { + MPlug visible = dfn.findPlug("visibility"); + visible.setValue(false); + + resetLayers(nodes[j]); + } +#endif + +#if 1 + dagMod.deleteNode(nodes[j]); +#endif + } +#if 1 + dagMod.doIt(); +#endif + } + } + } + } + } + } + else + return false; + + return true; +} + +bool shaveVrayCmd::setupLayers(MObject shaveShape, MObject vrayShape) +{ + MFnDependencyNode vrFn(vrayShape); + + MDagPath dagPath = MDagPath::getAPathTo(vrayShape); + dagPath.extendToShape(); + MFnDependencyNode destNode(dagPath.node()); + + MFnDependencyNode shapeFn(shaveShape); + + MFnDagNode shdagFn(shaveShape); + MObject hair = shdagFn.parent(0); + + /////// set up same render layers as shave display shape ////// + //// proably need to set up same as growth object ? + { + MObjectArray layers; + + shaveHairShape* nodePtr = (shaveHairShape*)shapeFn.userNode(); + MObject displayNode = nodePtr->getDisplayShape(); + + + MFnRenderLayer::listAllRenderLayers(layers); + for(unsigned int oo = 0; oo < layers.length(); oo++) + { + MObjectArray members; + MFnRenderLayer layerFn(layers[oo]); + + /////////////////// + //MGlobal::displayInfo(MString(" ----- Checking layer ---- ") + layerFn.name() + "---dest--" + destNode.name()+"---"+vrFn.name()); + ////////////////// + + if(MStatus::kSuccess == layerFn.listMembers(members)) + { + bool owner_found = false; + bool dummy_found = false; + int idx_to_remove = -1; + for(unsigned int xx = 0; xx < members.length(); xx++) + { + MFnDependencyNode dFn(members[xx]); + + //////////////////// + //MGlobal::displayInfo(dFn.name()); + //////////////////// + + + if(members[xx] == dagPath.node() || members[xx] == vrayShape) + { + dummy_found |= true; + /////////////////// + //MGlobal::displayInfo(MString("Dummy found ") + destNode.name() + " in " + layerFn.name() ); + ////////////////// + for(unsigned int kkk = 0; kkk < members.length(); kkk++) + { + if(members[kkk] == shaveShape || + members[kkk] == displayNode || + members[kkk] == hair) + { + owner_found |= true; + break; + } + } + idx_to_remove = xx; + break; + } + } + for(unsigned int kk = 0; kk < members.length(); kk++) + { + if(members[kk] == shaveShape || + members[kk] == displayNode || + members[kk] == hair) + { + /////////////////// + //MFnDependencyNode dFn(members[kk]); + //MGlobal::displayInfo(MString("Layer ") + layerFn.name() + " contains " + dFn.name()); + ////////////////// + bool found = false; + + for(unsigned int xx = 0; xx < members.length(); xx++) + { + if(members[xx] == dagPath.node()) + { + found = true; + break; + } + } + if(found) + continue; + + /////////////////// + //MGlobal::displayInfo(MString("Adding ") + destNode.name() + " to " + layerFn.name() ); + ////////////////// + + MString cmd = MString("editRenderLayerMembers -noRecurse ") + layerFn.name() + MString(" ") + destNode.name(); + MGlobal::executeCommand(cmd); + } + }//end for + //need to remove + if(!owner_found && dummy_found && idx_to_remove !=-1) + { + MFnDependencyNode dFn(members[idx_to_remove]); + + /////////////////// + //MGlobal::displayInfo(MString("Removing ") + dFn.name() + " from " + layerFn.name() ); + ////////////////// + + MString cmd = MString("editRenderLayerMembers -remove ") + layerFn.name() + MString(" ") + dFn.name(); + MGlobal::executeCommand(cmd); + } + } + } + } + + /////// set up same display layers as shave display shape ////// + { + + + MObjectArray layers; + MItDependencyNodes iter; + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + if(node.hasFn(MFn::kDisplayLayer)) + layers.append(node); + } + + shaveHairShape* nodePtr = (shaveHairShape*)shapeFn.userNode(); + MObject displayNode = nodePtr->getDisplayShape(); + for(unsigned int oo = 0; oo < layers.length(); oo++) + { + + MFnDependencyNode layerFn(layers[oo]); + MPlug drPlug = layerFn.findPlug("drawInfo"); + if(!drPlug.isNull()) + { + MStatus stat; + MPlugArray members; + if(drPlug.connectedTo(members,false,true,&stat) && stat == MStatus::kSuccess) + { + for(unsigned int kk = 0; kk < members.length(); kk++) + { + if(members[kk].node() == shaveShape || + members[kk].node() == displayNode || + members[kk].node() == hair) + { + bool found = false; + for(unsigned int xx = 0; xx < members.length(); xx++) + { + if(members[xx].node() == dagPath.node()) + { + found = true; + break; + } + } + if(found) + continue; + + MString cmd = MString("editDisplayLayerMembers -noRecurse ") + layerFn.name() + MString(" ") + destNode.name(); + MGlobal::executeCommand(cmd); + } + } + } + } + } + } + return true; +} + +bool shaveVrayCmd::resetLayers(MObject vrayShape) +{ + { //reset render layers + MObjectArray layers; + + MFnRenderLayer::listAllRenderLayers(layers); + for(unsigned int oo = 0; oo < layers.length(); oo++) + { + MObjectArray members; + MFnRenderLayer layerFn(layers[oo]); + + if(layerFn.name() == "defaultRenderLayer") + continue; + + if(MStatus::kSuccess == layerFn.listMembers(members)) + { + for(unsigned int kk = 0; kk < members.length(); kk++) + { + MFnDependencyNode mFn(members[kk]); + if(members[kk] == vrayShape) + { + + MString cmd = MString("editRenderLayerMembers -remove ") + layerFn.name() + MString(" ") + mFn.name(); + MGlobal::executeCommand(cmd); + } + } + } + } + } + { //reset display layers + + MObjectArray layers; + MItDependencyNodes iter; + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + if(node.hasFn(MFn::kDisplayLayer)) + layers.append(node); + } + for(unsigned int oo = 0; oo < layers.length(); oo++) + { + + MFnDependencyNode layerFn(layers[oo]); + MPlug drPlug = layerFn.findPlug("drawInfo"); + if(!drPlug.isNull()) + { + MStatus stat; + MPlugArray members; + if(drPlug.connectedTo(members,false,true,&stat) && stat == MStatus::kSuccess) + { + for(unsigned int kk = 0; kk < members.length(); kk++) + { + MFnDependencyNode mFn(members[kk].node()); + if(members[kk].node() == vrayShape) + { + + MString cmd = MString("editRenderLayerMembers -remove ") + layerFn.name() + MString(" ") + mFn.name(); + MGlobal::executeCommand(cmd); + + } + } + } + } + } + } + return true; +} +bool shaveVrayCmd::registerShaderChangeCallback(MObject shape) +{ + MStatus stat; + MCallbackId cbkId = MNodeMessage::addAttributeChangedCallback (shape, shaveVrayShaderChangeCbk, NULL, &stat); + if(stat!=MStatus::kSuccess) + { + MGlobal::displayError("shaveVrayCmd: can not register attribute change callback."); + return false; + } + cbkIds.push_back(cbkId); + return true; +} +bool shaveVrayCmd::deregisterShaderChangeCallacks() +{ + for(size_t i = 0; i < cbkIds.size(); i++) + { + MStatus stat = MNodeMessage::removeCallback(cbkIds[i]); + if(stat != MStatus::kSuccess) + MGlobal::displayError("shaveVrayCmd: can not deregister attribute change callback."); + } + cbkIds.clear(); + return true; +} + +void shaveVrayShaderChangeCbk(MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug &otherPlug, void *clientData) +{ +#ifdef _DEBUG + MGlobal::displayInfo(MString("shaveVrayShaderChangeCbk( ")+ plug.name() + " ---> " + otherPlug.name()+" )"); +#endif + + if( msg & MNodeMessage::kConnectionMade ) + { + std::string s(plug.name().asChar()); + if(s.find("instObjGroups") != std::string::npos) + { +#ifdef _DEBUG + MGlobal::displayInfo(plug.name()); +#endif + MObject display = plug.node(); + MFnDependencyNode dFn(display); + +#ifdef _DEBUG + MGlobal::displayInfo(MString("display node ") + dFn.name()); +#endif + + MPlug msg = dFn.findPlug("message"); + MPlugArray connections; + msg.connectedTo(connections, false, true); +#ifdef _DEBUG + MGlobal::displayInfo("message connections"); + for(int oo=0; oo<connections.length(); oo++) MGlobal::displayInfo(connections[oo].name()); + MGlobal::displayInfo("-------------------"); +#endif + //MPlug dsh = connections[0]; + int idx=-1; + for(int oo=0; oo<(int)connections.length(); oo++) + { + MFnDependencyNode cFn(connections[oo].node()); + if(cFn.typeId() == shaveHairShape::id) + { + MGlobal::displayInfo(MString("found ")+cFn.name()); + idx = oo; + break; + } + } + if(idx == -1) return; + MPlug dsh = connections[idx]; + MObject shape = dsh.node(); + + MFnDependencyNode dFn1(shape); +#ifdef _DEBUG + MGlobal::displayInfo(dFn1.name()); +#endif + //crap, userNode is NULL for referenced object :S in 2013 + //shaveHairShape* shaveNode = (shaveHairShape*)dFn1.userNode(); + //MObject shader = shaveNode->getShader(); + /////////////////////////////// + MObject shader; + { + //MPlug dplug= dFn1.findPlug("displayNode"); + //MPlugArray dconnections; + + //dplug.connectedTo(dconnections, true, false); + +#ifdef _DEBUG + //if (dconnections.length() == 0) MGlobal::displayInfo(plug.name() + " does not have connection!!!!"); +#endif + //if (dconnections.length() > 0) + { + //MObject displayNode = dconnections[0].node(); + //if (!displayNode.isNull()) + { + //MFnDependencyNode nodeFn(displayNode); + //MPlug groupPlug = nodeFn.findPlug("instObjGroups"); + + //MPlug groupPlug = plug; + MPlug groupPlug = dFn.findPlug("instObjGroups"); +#ifdef _DEBUG + MGlobal::displayInfo(groupPlug.name()); +#endif + if (groupPlug.numConnectedElements() > 0) + { + MPlugArray groupConnections; + groupPlug + .connectionByPhysicalIndex(0) + .connectedTo(groupConnections, false, true); + + // + // If we're connected to a shadingEngine node, then we're done. + // + if (groupConnections.length() > 0) + { + for(unsigned int i = 0; i < groupConnections.length(); i++) + { + MObject ashader = groupConnections[i].node(); + + if (ashader.hasFn(MFn::kShadingEngine)) + { + shader = ashader; +#ifdef _DEBUG + MGlobal::displayInfo("shader found..."); +#endif + break; + } + } + } + } + } + } + } + /////////////////////////////// + + MPlug stackIdxPlug = dFn1.findPlug("stackIndex"); + MPlugArray connections1; + stackIdxPlug.connectedTo(connections1, false, true); + if(connections1.length() > 0) + { +#ifdef _DEBUG + MGlobal::displayInfo(connections1[0].name()); +#endif + MObject vrayNode = connections1[0].node(); + MFnDependencyNode dFn2(vrayNode); + MPlug outputPlug = dFn2.findPlug("output"); + MPlugArray connections2; + outputPlug.connectedTo(connections2, false, true); + if(connections2.length() > 0) + { +#ifdef _DEBUG + MGlobal::displayInfo(connections2[0].name()); +#endif + MObject polyShape = connections2[0].node(); + MFnDependencyNode destNode(polyShape); + MFnDependencyNode shaderFn(shader); +#ifdef _DEBUG + MGlobal::displayInfo("shader name " + shaderFn.name()); +#endif + + MPlug instGroups = destNode.findPlug("instObjGroups"); + if(!instGroups.isNull()) + { + + MPlug instGroups0 = instGroups.elementByPhysicalIndex(0); + if(!instGroups0.isNull()) + { + if(instGroups0.isConnected()) + { + MPlugArray groupsTo; + instGroups0.connectedTo(groupsTo,false,true); + for(unsigned int k = 0; k < groupsTo.length(); k++) + { + MGlobal::executeCommand("disconnectAttr " +instGroups.name() + " " + groupsTo[k].name() + ";"); + } + } + + MPlug dagSetPlug = shaderFn.findPlug("dagSetMembers"); + if(!dagSetPlug.isNull()) + { + MPlug dagSetPlugK; + for(int oo = 0; oo < 100000; oo++) + { + dagSetPlugK = dagSetPlug.elementByLogicalIndex(oo); + if(!dagSetPlugK.isConnected()) + break; + } +#ifdef _DEBUG + MGlobal::displayInfo("connectAttr " + instGroups0.name() + " " + dagSetPlugK.name() + ";"); +#endif + MGlobal::executeCommand("connectAttr " + instGroups0.name() + " " + dagSetPlugK.name() + ";"); + } + else + MGlobal::displayError("shaveVrayShaderChangeCbk: can not find 'dagSetMembers'."); + } + else + MGlobal::displayError("shaveVrayShaderChangeCbk: can not find 'instObjGroups0' plug."); + } + else + MGlobal::displayError("shaveVrayShaderChangeCbk: can not find 'instObjGroups' plug."); + } + + } + +#if 0 + //works but create a lot of dumb looping + MGlobal::executeCommand("shaveVrayShader -delete -all"); + MGlobal::executeCommand("shaveVrayShader -create -all"); +#endif + } + + } +} + +#if 0 +bool shaveVrayCmd::registerLayersCallback() +{ + MObjectArray lm; + FindNodesByApiType(MFn::kRenderLayerManager,lm); + if(lm.length() > 0) + { + MStatus stat; + assert(layerCbkId==0); + layerCbkId = MNodeMessage::addAttributeChangedCallback (lm[0], shaveVrayLayerCbk, NULL, &stat); + if(stat!=MStatus::kSuccess) + { + MGlobal::displayError("shaveVrayCmd: can not register layer callback."); + return false; + } + return true; + } + return false; +} + +bool shaveVrayCmd::deregisterLayersCallack() +{ + if(layerCbkId!=0) + { + MStatus stat = MNodeMessage::removeCallback(layerCbkId); + layerCbkId = 0; + if(stat != MStatus::kSuccess) + { + MGlobal::displayError("shaveVrayCmd: can not deregister layer callback."); + + return false; + } + return true; + } + return false; +} + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~ callback ~~~~~~~~~~~~~~~~~~~~~~~~*/ + +void shaveVrayLayerCbk(MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug &otherPlug, void *clientData) +{ + //crap, correct shader is connected but it like + //we a step backward + if( msg & MNodeMessage::kAttributeSet ) + { + //this is called in time +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayLayerCbk"); +#endif + int v; + plug.getValue(v); +#ifdef _DEBUG + MGlobal::displayInfo(plug.name() + " value " + v); +#endif + MObjectArray layers; + MFnRenderLayer::listAllRenderLayers(layers); + MFnDependencyNode dFn(layers[v]); +#ifdef _DEBUG + MGlobal::displayInfo("Layer " + dFn.name()); +#endif + + //MGlobal::executeCommand("editRenderLayerGlobals -currentRenderLayer "+dFn.name()); //is it essencial? + //if se do it on the place incorrect mtls are fetched becuse connections were not updated yet + //if we do on idle correct materials are recived but we are too late + MGlobal::executeCommandOnIdle("shaveVrayShader -delete -all"); + MGlobal::executeCommandOnIdle("shaveVrayShader -create -all"); + } +} +#endif + +#endif diff --git a/mayaPlug/shaveVrayCmd.h b/mayaPlug/shaveVrayCmd.h new file mode 100644 index 0000000..2895781 --- /dev/null +++ b/mayaPlug/shaveVrayCmd.h @@ -0,0 +1,83 @@ +#ifndef _HAIR_VRAY_COMMAND_H_ +#define _HAIR_VRAY_COMMAND_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVrayCmd.h + + DESCRIPTION: Maya node + + HISTORY: created 01-04-2010 + + *> + **********************************************************************/ + +// Check for 64-bit OS and define the Bits64_ macro required by Maya headers +#ifdef X64 +#ifndef Bits64_ +#define Bits64_ +#endif +#endif + +#include <maya/MPxCommand.h> +#include <maya/MArgList.h> +#include <maya/MCommandResult.h> +#include <maya/MSyntax.h> +#include <maya/MNodeMessage.h> + +class shaveVrayCmd : public MPxCommand { + //friend void shaveVrayShaderChangeCbk(MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug &otherPlug, void *clientData); +public: + // command + // + static MString cmd; + + // flags + // + //attaches shader to selected or all existing shave shapes + static char* sf_create; + static char* lf_create; + + //deletes shader attached to selected or all existing shave shapes + static char* sf_delete; + static char* lf_delete; + + //can be used with -create/delete flag + static char* sf_all; + static char* lf_all; + +#if 0 + static char* sf_addCbk; + static char* lf_addCbk; + + static char* sf_delCbk; + static char* lf_delCbk; +#endif + + shaveVrayCmd(){} + virtual ~shaveVrayCmd(){} + + static void *creator() {return new shaveVrayCmd;} + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList&); + +protected: + + bool attachShaderTo (MObject shShape); + bool detachShaderFrom(MObject shShape); + bool setupLayers(MObject shaveShape, MObject vrayShape); + bool resetLayers(MObject vrayShape); +#if 0 + bool registerLayersCallback(); + bool deregisterLayersCallack(); +#endif + bool registerShaderChangeCallback(MObject shape); + bool deregisterShaderChangeCallacks(); +}; + +#endif diff --git a/mayaPlug/shaveVrayExporter.h b/mayaPlug/shaveVrayExporter.h new file mode 100644 index 0000000..a5d12bf --- /dev/null +++ b/mayaPlug/shaveVrayExporter.h @@ -0,0 +1,57 @@ +#ifndef _HAIR_VRAY_EXPORTER_H_ +#define _HAIR_VRAY_EXPORTER_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#ifdef SHAVEVRAYEXPORTER_EXPORTS +# ifdef __GNUC__ +# define SHAVEVRAYEXPORTER_EXPORT __attribute__((__visibility__("default"))) +# else +# define SHAVEVRAYEXPORTER_EXPORT __declspec(dllexport) +# endif +#else +# ifdef __GNUC__ +# define SHAVEVRAYEXPORTER_EXPORT +# else +# define SHAVEVRAYEXPORTER_EXPORT __declspec(dllimport) +# endif +#endif + +#include <maya/MColor.h> +#include <maya/MObject.h> + +// Implemented in supplemental library +// See usage in shaveVrayNode.cpp +class shaveVrayExporter { +public: + /// Create V-Ray plugin from passed parameters + virtual void createVRayPlugin( + void *voidGeomInfo, + const MString &libPath, + int stackIdx, + bool ownshader, + bool isInstanced, + const MObject &instance, + bool tipfade, + bool squirrel, + const MColor &spec_tint, + const MColor &spec_tint2, + bool camVis, + bool lightVis, + bool giVis, + bool reflVis, + bool refrVis, + float selfShadow, + bool recvShadow + )=0; + + /// Read DRA to the V-Ray plugin's dra param + virtual bool readDraToParam(const char *draFilename)=0; + + virtual ~shaveVrayExporter() {} +}; + +#endif // _HAIR_VRAY_EXPORTER_H_ + diff --git a/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.sln b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.sln new file mode 100644 index 0000000..c309797 --- /dev/null +++ b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaveVrayExporter-vs11", "shaveVrayExporter-vs11.vcxproj", "{AF1E6779-8553-420A-98D7-5300BE3406E8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + RelM20170V36|x64 = RelM20170V36|x64 + RelM20170V40|x64 = RelM20170V40|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Debug|x64.ActiveCfg = Debug|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Debug|x64.Build.0 = Debug|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Release|x64.ActiveCfg = Release|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Release|x64.Build.0 = Release|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20170V36|x64.ActiveCfg = RelM20170V36|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20170V36|x64.Build.0 = RelM20170V36|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20170V40|x64.ActiveCfg = RelM20170V40|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20170V40|x64.Build.0 = RelM20170V40|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.vcxproj b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.vcxproj new file mode 100644 index 0000000..f4bc96a --- /dev/null +++ b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.vcxproj @@ -0,0 +1,304 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20170V40|Win32"> + <Configuration>RelM20170V40</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20170V40|x64"> + <Configuration>RelM20170V40</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20170V36|Win32"> + <Configuration>RelM20170V36</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20170V36|x64"> + <Configuration>RelM20170V36</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{AF1E6779-8553-420A-98D7-5300BE3406E8}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>shaveVrayExporter</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v110</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|x64'"> + <LinkIncremental>false</LinkIncremental> + <TargetName>ShaveVray36Exporter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|x64'"> + <LinkIncremental>false</LinkIncremental> + <TargetName>ShaveVray40Exporter</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V36|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <AdditionalIncludeDirectories>$(AUTODESK_LOCATION)\Maya2017\include;$(SHAVE_VRAY_SDKS)\3.60.04\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(AUTODESK_LOCATION)\Maya2017\lib;$(SHAVE_VRAY_SDKS)\3.60.04\lib\win\vc11;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>plugman_s.lib;vray.lib;vutils_s.lib;Foundation.lib;OpenMaya.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)</AdditionalDependencies> + <OutputFile>..\..\raw64\release\20170\vrayExporter\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20170V40|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <AdditionalIncludeDirectories>$(AUTODESK_LOCATION)\Maya2017\include;$(SHAVE_VRAY_SDKS)\4.04.01\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(AUTODESK_LOCATION)\Maya2017\lib;$(SHAVE_VRAY_SDKS)\4.04.01\lib\win\vc11;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>plugman_s.lib;vray.lib;vutils_s.lib;Foundation.lib;OpenMaya.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)</AdditionalDependencies> + <OutputFile>..\..\raw64\release\20170\vrayExporter\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="..\shaveVrayExporter.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\shaveVrayExporterImpl.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.vcxproj.filters b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.vcxproj.filters new file mode 100644 index 0000000..ffe320e --- /dev/null +++ b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs11.vcxproj.filters @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\shaveVrayExporter.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\shaveVrayExporterImpl.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.sln b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.sln new file mode 100644 index 0000000..73807f9 --- /dev/null +++ b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaveVrayExporter-vs15", "shaveVrayExporter-vs15.vcxproj", "{AF1E6779-8553-420A-98D7-5300BE3406E8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + RelM20180V36|x64 = RelM20180V36|x64 + RelM20180V40|x64 = RelM20180V40|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Debug|x64.ActiveCfg = Debug|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Debug|x64.Build.0 = Debug|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Release|x64.ActiveCfg = Release|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.Release|x64.Build.0 = Release|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20180V36|x64.ActiveCfg = RelM20180V36|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20180V36|x64.Build.0 = RelM20180V36|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20180V40|x64.ActiveCfg = RelM20180V40|x64 + {AF1E6779-8553-420A-98D7-5300BE3406E8}.RelM20180V40|x64.Build.0 = RelM20180V40|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.vcxproj b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.vcxproj new file mode 100644 index 0000000..c8fa506 --- /dev/null +++ b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.vcxproj @@ -0,0 +1,305 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20180V40|Win32"> + <Configuration>RelM20180V40</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20180V40|x64"> + <Configuration>RelM20180V40</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20180V36|Win32"> + <Configuration>RelM20180V36</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="RelM20180V36|x64"> + <Configuration>RelM20180V36</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{AF1E6779-8553-420A-98D7-5300BE3406E8}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>shaveVrayExporter</RootNamespace> + <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|x64'"> + <LinkIncremental>false</LinkIncremental> + <TargetName>ShaveVray36Exporter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|x64'"> + <LinkIncremental>false</LinkIncremental> + <TargetName>ShaveVray40Exporter</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTERVS11_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V36|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <AdditionalIncludeDirectories>$(AUTODESK_LOCATION)\Maya2018\include;$(SHAVE_VRAY_SDKS)\3.60.04\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(AUTODESK_LOCATION)\Maya2018\lib;$(SHAVE_VRAY_SDKS)\3.60.04\lib\win\vc14;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>plugman_s.lib;vray.lib;vutils_s.lib;Foundation.lib;OpenMaya.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)</AdditionalDependencies> + <OutputFile>..\..\raw64\release\20180\vrayExporter\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelM20180V40|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;SHAVEVRAYEXPORTER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <SDLCheck>true</SDLCheck> + <AdditionalIncludeDirectories>$(AUTODESK_LOCATION)\Maya2018\include;$(SHAVE_VRAY_SDKS)\4.04.01\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalLibraryDirectories>$(AUTODESK_LOCATION)\Maya2018\lib;$(SHAVE_VRAY_SDKS)\4.04.01\lib\win\vc14;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>plugman_s.lib;vray.lib;vutils_s.lib;Foundation.lib;OpenMaya.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)</AdditionalDependencies> + <OutputFile>..\..\raw64\release\20180\vrayExporter\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="..\shaveVrayExporter.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\shaveVrayExporterImpl.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.vcxproj.filters b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.vcxproj.filters new file mode 100644 index 0000000..ffe320e --- /dev/null +++ b/mayaPlug/shaveVrayExporter/shaveVrayExporter-vs15.vcxproj.filters @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\shaveVrayExporter.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\shaveVrayExporterImpl.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/mayaPlug/shaveVrayExporterImpl.cpp b/mayaPlug/shaveVrayExporterImpl.cpp new file mode 100644 index 0000000..28c7202 --- /dev/null +++ b/mayaPlug/shaveVrayExporterImpl.cpp @@ -0,0 +1,422 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <sys/stat.h> +#include <stdio.h> + +#ifdef _WIN32 +#define fileno _fileno +#define fstat _fstat +typedef struct _stat StatType; +#else +#include <unistd.h> +typedef struct stat StatType; +#endif + +#include <maya/MGlobal.h> +#include <maya/MStatus.h> +#include <maya/MItDependencyNodes.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnMesh.h> +#include <maya/MPlug.h> +#include <maya/MFloatArray.h> + +#include "utils.h" +#include "vrayexport.h" +#include "plugman.h" +#include "vrayplugins.h" +#include "defparams.h" +#include "vraymayageom.h" + +#include "shaveVrayExporter.h" + +#ifndef VRAY_OVERRIDE +#define VRAY_OVERRIDE +#endif + +#define VR VUtils + +class shaveVrayExporterImpl: public shaveVrayExporter { +public: + shaveVrayExporterImpl(); + void createVRayPlugin( + void *voidGeomInfo, + const MString &libPath, + int stackIdx, + bool ownshader, + bool isInstanced, + const MObject &instance, + bool tipfade, + bool squirrel, + const MColor &spec_tint, + const MColor &spec_tint2, + bool camVis, + bool lightVis, + bool giVis, + bool reflVis, + bool refrVis, + float selfShadow, + bool recvShadow + ) VRAY_OVERRIDE; + bool readDraToParam(const char *draFilename) VRAY_OVERRIDE; +private: + // V-Ray plugin parameters + VR::DefIntParam m_stackIndexParam; + VR::DefIntParam m_instancedParam; + VR::DefIntParam m_ownshaderParam; + VR::DefStringParam m_draFileParam; + VR::DefIntListParam m_dataParam; + VR::DefIntParam m_dataSizeParam; + VR::DefStringParam m_libPathParam; + VR::DefIntParam m_squirrelParam; + VR::DefIntParam m_tipfadeParam; + VR::DefColorParam m_spectintParam; + VR::DefColorParam m_spectint2Param; + //instance uvs-related params + VR::DefIntParam m_numFacesPerInstParam; + VR::DefIntParam m_numUVSetsParam; + VR::DefVectorListParam m_uvsParam; + + //ray visibility params + VR::DefIntParam m_cameraVisibilityParam; + VR::DefIntParam m_reflVisibilityParam; + VR::DefIntParam m_refrVisibilityParam; + VR::DefIntParam m_lightVisibilityParam; + VR::DefIntParam m_GiVisibilityParam; + + VR::DefFloatParam m_selfShadowParam; + VR::DefIntParam m_recvShadowParam; + + VR::DefIntParam m_useGlobalHairTreeParam; // store all voxels in a global hair tree + VR::DefIntParam m_dynamicHairTesselation; // compute sub-segments based on distance to camera + VR::DefFloatParam m_hairTesselMaxEdgeLen; // max desired sub-segment length (in pixels) +}; + +shaveVrayExporterImpl::shaveVrayExporterImpl() : + m_stackIndexParam("stackId",0), + m_instancedParam("instanced",0), + m_ownshaderParam("ownshader",1), + m_draFileParam("draFile",""), + m_dataParam("draData"), + m_dataSizeParam("draSize",0), + m_libPathParam("libPath", ""), + m_squirrelParam("squirrel",0), + m_tipfadeParam("tipfade",0), + m_spectintParam("spectint",VR::Color(1.0f,1.0f,1.0f)), + m_spectint2Param("spectint2",VR::Color(0.0f,0.0f,0.0f)), + m_numFacesPerInstParam("numFacesPerInst",0), + m_numUVSetsParam("numUVSets",0), + m_uvsParam("uvs"), + m_cameraVisibilityParam("cameraVisibility",1), + m_reflVisibilityParam("reflVisibility",1), + m_refrVisibilityParam("refrVisibility",1), + m_lightVisibilityParam("lightVisibility",1), + m_GiVisibilityParam("GiVisibility",1), + m_selfShadowParam("selfShadow",1.0f), + m_recvShadowParam("recvShadow",1), + m_useGlobalHairTreeParam("useGlobalHairTree",1), + m_dynamicHairTesselation("dynamicHairTesselation",0), + m_hairTesselMaxEdgeLen("hairTesselMaxEdgleLen",4.0f) +{} + +void shaveVrayExporterImpl::createVRayPlugin( + void *voidGeomInfo, + const MString &libPath, + int stackIdx, + bool ownshader, + bool isInstanced, + const MObject &instance, + bool tipfade, + bool squirrel, + const MColor &spec_tint, + const MColor &spec_tint2, + bool camVis, + bool lightVis, + bool giVis, + bool reflVis, + bool refrVis, + float selfShadow, + bool recvShadow) +{ + if (!voidGeomInfo) + return; + + MStatus stat; + VR::VRayGeomInfo *geomInfo=reinterpret_cast<VR::VRayGeomInfo*>(voidGeomInfo); + + // Create the VRay plugin + bool existing=false; + VR::VRayPluginInterface* vrplug = geomInfo->newPlugin("hairVRay30Shader", existing); + if(!vrplug) + { + // Check for the VRay 2.4 shader. + // + vrplug = geomInfo->newPlugin("hairVRayShader", existing); + } + + if(!vrplug) + { + MGlobal::displayError("shaveVrayExporterImpl: can not create 'hairVRayShader' or 'hairVRay30Shader' V-Ray plugin."); + return; + } + + m_stackIndexParam.setInt(stackIdx,0,0); + m_ownshaderParam.setInt(ownshader ? 1 : 0,0,0); + m_instancedParam.setInt(isInstanced ? 1 : 0,0,0); + m_tipfadeParam.setInt(tipfade ? 1 : 0,0,0); + m_squirrelParam.setInt(squirrel ? 1 : 0,0,0); + m_spectintParam.setColor(VUtils::Color(spec_tint.r, spec_tint.g, spec_tint.b), 0, 0); + m_spectint2Param.setColor(VUtils::Color(spec_tint2.r, spec_tint2.g, spec_tint2.b), 0, 0); + m_libPathParam.setString(libPath.asChar(),0,0); + + /////// ray visibiltity + m_cameraVisibilityParam.setInt(camVis ? 1 : 0,0,0); + m_reflVisibilityParam.setInt(reflVis ? 1 : 0,0,0); + m_refrVisibilityParam.setInt(refrVis ? 1 : 0,0,0); + m_lightVisibilityParam.setInt(lightVis ? 1 : 0,0,0); + m_GiVisibilityParam.setInt(giVis ? 1 : 0,0,0); + m_selfShadowParam.setFloat(selfShadow,0,0); + //printf("set selfShadow %f\n",selfShadow);fflush(stdout); + m_recvShadowParam.setInt(recvShadow ? 1 : 0,0,0); + + if(isInstanced && !instance.isNull()) + { + MGlobal::displayInfo("shaveVrayExporterImpl: instance not NULL."); + MFnMesh meshFn(instance); + + int nf = meshFn.numPolygons(); + + MStringArray uvSetNames; + meshFn.getUVSetNames(uvSetNames); + int ns = (int)uvSetNames.length(); + + m_numFacesPerInstParam.setInt(nf,0,0); + //MGlobal::displayInfo(MString("num faces ") + nf ); + m_numUVSetsParam.setInt(ns,0,0); + + { + int count = 0; + for (unsigned int i = 0; i < uvSetNames.length(); i++) + { + //MFloatArray u; + //MFloatArray v; + MIntArray numUVsPerFace; + MIntArray uvIds; + MString uvSet = uvSetNames[i]; + + stat = meshFn.getAssignedUVs(numUVsPerFace, uvIds, &uvSet); + count += uvIds.length(); + } + m_uvsParam.setCount(count); +#ifdef WIN32 + MGlobal::displayInfo(MString("memory ") + (_CrtCheckMemory() ? 1 : 0)); +#endif + + int k = 0; + for (unsigned int i = 0; i < uvSetNames.length(); i++) + { + MFloatArray u; + MFloatArray v; + MIntArray numUVsPerFace; + MIntArray uvIds; + MString uvSet = uvSetNames[i]; + + stat = meshFn.getUVs(u, v, &uvSet); + stat = meshFn.getAssignedUVs(numUVsPerFace, uvIds, &uvSet); + + for (unsigned int j = 0; j < uvIds.length(); j++) + { + VR::Vector V(u[uvIds[j]],v[uvIds[j]],0.0f); + m_uvsParam[k] = V; + k++; + } + } + } + } + + vrplug->setParameter(&m_stackIndexParam); + vrplug->setParameter(&m_instancedParam); + vrplug->setParameter(&m_ownshaderParam); + vrplug->setParameter(&m_squirrelParam); + vrplug->setParameter(&m_tipfadeParam); + vrplug->setParameter(&m_spectintParam); + vrplug->setParameter(&m_spectint2Param); + vrplug->setParameter(&m_draFileParam); + vrplug->setParameter(&m_dataParam); + vrplug->setParameter(&m_dataSizeParam); + vrplug->setParameter(&m_libPathParam); + + //instance uvs-related params + vrplug->setParameter(&m_numFacesPerInstParam); + vrplug->setParameter(&m_numUVSetsParam); + vrplug->setParameter(&m_uvsParam); + + //ray visibility params + vrplug->setParameter(&m_cameraVisibilityParam); + vrplug->setParameter(&m_reflVisibilityParam); + vrplug->setParameter(&m_refrVisibilityParam); + vrplug->setParameter(&m_lightVisibilityParam); + vrplug->setParameter(&m_GiVisibilityParam); + vrplug->setParameter(&m_selfShadowParam); + vrplug->setParameter(&m_recvShadowParam); + vrplug->setParameter(&m_useGlobalHairTreeParam); + vrplug->setParameter(&m_dynamicHairTesselation); + vrplug->setParameter(&m_hairTesselMaxEdgeLen); + + if (!existing) + { + // The plugin did not exist before - we can do additional set up here, if necessary + } +} + +bool shaveVrayExporterImpl::readDraToParam(const char *draFilename) +{ +#ifdef LINUX + printf("shaveVrayExporterImpl: readDraToParam\n");fflush(stdout); +#endif + VR::DefIntListParam *li = &m_dataParam; + VR::DefIntParam *sa = &m_dataSizeParam; + if(!li) + { +#if 0 //we do not want to fire this warning if dummy shape is located at hidden display layer + MGlobal::displayError(MString("shaveVrayExporterImpl: NULL draData parameter")); +#ifdef LINUX + printf("shaveVrayExporterImpl: NULL draData parameter\n");fflush(stdout); +#endif +#endif + return false; + } + if(!sa) + { + MGlobal::displayError(MString("shaveVrayExporterImpl: NULL draDataSize parameter")); +#ifdef LINUX + printf("shaveVrayExporterImpl: NULL draDataSize parameter\n");fflush(stdout); +#endif + return false; + } + FILE* draFile = fopen(draFilename, "r+b"); + if(!draFile) + { + MGlobal::displayError(MString("shaveVrayExporterImpl: can not open DRA file\n")+draFilename); +#ifdef LINUX + printf("shaveVrayExporterImpl: can not open DRA file %s\n",draFilename);fflush(stdout); +#endif + return false; + } + else + { + //Get the size of the DRA file. + StatType fileInfo; + fstat(fileno(draFile), &fileInfo); + if(fileInfo.st_size == 0) + { + MGlobal::displayError("shaveVrayExporterImpl: DRA file is empty."); +#ifdef LINUX + printf("shaveVrayExporterImpl: DRA file is empty.");fflush(stdout); +#endif + return false; + } + else + { + long size = fileInfo.st_size; + char* data = (char*)malloc(size); + int sum = 0; + int c; + int p = 0; + while ((c = fgetc(draFile)) != EOF) + { + data[p] = c; + p++; + sum+=c; + } +#ifdef LINUX + printf("shaveVrayExporterImpl: checksum %i \n",sum);fflush(stdout); +#endif + + fclose(draFile); + if(p != size) + { + MGlobal::displayError(MString("shaveVrayExporterImpl: dra read error, bytes expected: ")+size+" bytes read: "+p); +#ifdef LINUX + printf("shaveVrayExporterImpl: dra read error, bytes expected %i bytes read %i \n",size,p);fflush(stdout); +#endif + } + + //pass data to vray parameter + + long n = size/sizeof(int); + long N = n; + long extra = 0; + if(n*sizeof(int) < size) + { + N = n+1; + extra = size - n*sizeof(int); + } + li->setCount(N); +// MGlobal::displayInfo(MString("shaveVrayExporterImpl: (debug) extra bytes: ") + extra); +//#ifdef LINUX +// printf("shaveVrayExporterImpl: (debug) extra bytes: %i \n",extra);fflush(stdout); +//#endif + + int* idata = (int*)data; + long chpos = 0; + long intsum = 0; + for(long oo = 0; oo < N; oo++) + { + if(oo < n) + { + int v = idata[oo]; + li->setInt(v,oo,0.0); + chpos += sizeof(int); + + intsum += v; + } + else + { + int A = 0; + char* AA = (char*)(&A); + for(int j = 0; j < extra; j++) + { + AA[j] = data[chpos + j]; + //MGlobal::displayInfo(MString("shaveVrayExporterImpl: (debug) idx: ") + (chpos + j)); + //MGlobal::displayInfo(MString("shaveVrayExporterImpl: (debug) byte: ") + data[chpos + j]); + + } + li->setInt(A,oo,0.0); + chpos += extra; + + intsum += A; + } + } + /////// checksum // +#ifdef LINUX + sum = 0; + for(int oo = 0; oo < size; oo++) + { + sum += (unsigned char)data[oo]; + } + printf("shaveVrayExporterImpl: checksum %i\n",sum); fflush(stdout); + printf("shaveVrayExporterImpl: intsum %i\n",intsum); fflush(stdout); + ///////////// +#endif + + free(data); + sa->setInt(size,0,0); + +#ifdef LINUX + printf("shaveVrayExporterImpl: readDraToParam - done\n");fflush(stdout); +#endif + return true; + } + } +} + +extern "C" SHAVEVRAYEXPORTER_EXPORT shaveVrayExporter *newShaveVrayExporter() { + return new shaveVrayExporterImpl(); +} + +extern "C" SHAVEVRAYEXPORTER_EXPORT void deleteShaveVrayExporter(shaveVrayExporter *exporter) { + delete exporter; +} + diff --git a/mayaPlug/shaveVrayNode.cpp b/mayaPlug/shaveVrayNode.cpp new file mode 100644 index 0000000..cec09e2 --- /dev/null +++ b/mayaPlug/shaveVrayNode.cpp @@ -0,0 +1,632 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVrayNode.cpp -- implementation file + + DESCRIPTION: Maya node + + HISTORY: created 29-03-2010 + + *> + **********************************************************************/ + +#include "shaveVrayNode.h" +#include "shaveRender.h" +#include "shaveGlobals.h" +#include "shaveVrayRenderer.h" +#include "shaveVraySharedFunctions.h" +#include "shaveVrayExporter.h" +//#include "shaveVrayDesc.h" +//#include "shaveVrayPlugin.h" +//#include "shaveVrayCallbacks.h" + +#include <maya/MFloatArray.h> +#include <maya/MIntArray.h> +#include <maya/MPlugArray.h> +#include <maya/MString.h> +#include <maya/MItDependencyNodes.h> + +#ifdef USE_VRAY + +#include <stdio.h> +#include <sys/stat.h> + +#ifdef _WIN32 +#define fileno _fileno +#define fstat _fstat +#define snprintf _snprintf +typedef struct _stat StatType; +#else +#include <unistd.h> +#include <dlfcn.h> +typedef struct stat StatType; +#endif + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| V-Ray export impl | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +typedef shaveVrayExporter *(*TFnNewShaveVrayExporter) (); +typedef void (*TFnDeleteShaveVrayExporter) (shaveVrayExporter *); + +// Pointer to function implementing the actual plugin creation +static TFnNewShaveVrayExporter newShaveVrayExporter=0; +static TFnDeleteShaveVrayExporter deleteShaveVrayExporter=0; + +// If load failed once already +static bool loadShaveVRayExportImplFailed=false; + +static bool startsWith(const MString &str, const char *beg) { + unsigned begLength = unsigned(strlen(beg)); + if (begLength > str.length()) { + return false; + } + MString pref = str.substring(0, begLength-1); + return pref == beg; +} + +// Load required dll and get function to create V-Ray plugin +static int loadShaveVRayExportImpl() { + if (loadShaveVRayExportImplFailed) { + return false; + } + if (newShaveVrayExporter && deleteShaveVrayExporter) { + return true; + } + MString dllPath; + MStatus st; + st=MGlobal::executeCommand("dirname(`pluginInfo -q -p shaveNode`)", dllPath); + if (st.error()) { + st.perror("Get shaveNode location"); + loadShaveVRayExportImplFailed=true; + return false; + } + MString vrayVersion; + st=MGlobal::executeCommand("vray version", vrayVersion); + if (st.error()) { + st.perror("Get V-Ray version"); + loadShaveVRayExportImplFailed=true; + return false; + } +#if defined(_WIN32) + dllPath+="/vray/ShaveVray"; +#elif defined(__linux__) || defined(OSMac_) + dllPath+="/vray/libShaveVray"; +#endif + if (startsWith(vrayVersion, "4.")) { + dllPath+="40"; + } else if (startsWith(vrayVersion, "3.")) { + dllPath+="36"; + } + dllPath+="Exporter"; +#if defined(_WIN32) + dllPath+=".dll"; + HMODULE hDll; +# if defined(_UNICODE) + hDll=LoadLibraryW(dllPath.asWChar()); +# else + hDll=LoadLibraryA(dllPath.asChar()); +# endif // UNICODE + if (!hDll) { + int errCode=GetLastError(); + char *msgbuf = NULL; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + errCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPSTR) &msgbuf, 0, NULL + ); + const int errBufLen=512; + char *errBuf=(char*)alloca(errBufLen); + errBuf[errBufLen]='\0'; + snprintf(errBuf, errBufLen, "LoadLibrary(%s) failed (%i): %s", dllPath.asChar(), errCode, msgbuf); + LocalFree(msgbuf); + MGlobal::displayError(errBuf); + loadShaveVRayExportImplFailed=true; + return false; + } + newShaveVrayExporter=(TFnNewShaveVrayExporter)GetProcAddress(hDll, "newShaveVrayExporter"); + deleteShaveVrayExporter=(TFnDeleteShaveVrayExporter)GetProcAddress(hDll, "deleteShaveVrayExporter"); +#elif defined(__linux__) || defined(OSMac_) + dllPath += ".so"; + const long openmode=RTLD_LAZY +# ifdef OSMac_ + | RTLD_LOCAL +# endif // OSMac_ + ; + void *hDll=dlopen(dllPath.asChar(), openmode); + if (!hDll) { + const int errBufLen=512; + char *errBuf=(char*)alloca(errBufLen); + errBuf[errBufLen]='\0'; + snprintf(errBuf, errBufLen, "dlopen(%s) failed: %s", dllPath.asChar(), dlerror()); + MGlobal::displayError(errBuf); + loadShaveVRayExportImplFailed=true; + return false; + } + newShaveVrayExporter=(TFnNewShaveVrayExporter)dlsym(hDll, "newShaveVrayExporter"); + deleteShaveVrayExporter=(TFnDeleteShaveVrayExporter)dlsym(hDll, "deleteShaveVrayExporter"); +#endif + if (!newShaveVrayExporter || !deleteShaveVrayExporter) { + newShaveVrayExporter=0; + deleteShaveVrayExporter=0; + MGlobal::displayError("Failed to find shaveCreateVRayPlugin function"); + loadShaveVRayExportImplFailed=true; +#if defined(_WIN32) + FreeLibrary(hDll); +#elif defined(__linux__) || defined(OSMac_) + dlclose(hDll); +#endif + return false; + } + return true; +} + + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| API type and name | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MTypeId shaveVrayNode::typeId(0x001029B9); + +MString shaveVrayNode::typeName("shaveVrayShader"); + +MString shaveVrayNode::apiType("VRayGeometry"); //Vray related + +MString shaveVrayNode::classification("geometry"); //Vray related + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Attributes | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MObject shaveVrayNode::m_vrayGeomInfo; + +MObject shaveVrayNode::m_vrayGeomResult; + +MObject shaveVrayNode::m_outApiType; + +MObject shaveVrayNode::m_outApiClassification; + +MObject shaveVrayNode::m_iconSize; + +MObject shaveVrayNode::m_outMesh; + +MObject shaveVrayNode::m_stackIndex; + +MObject shaveVrayNode::m_isInstanced; + +MObject shaveVrayNode::m_instanceMesh; + +MObject shaveVrayNode::m_tipfade; + +MObject shaveVrayNode::m_spectint; + +MObject shaveVrayNode::m_spectint2; + +MObject shaveVrayNode::m_squirrel; + +MObject shaveVrayNode::m_ownshader; + +MObject shaveVrayNode::m_selfshadow; + +MObject shaveVrayNode::m_castshadow; + +MObject shaveVrayNode::m_recvshadow; + +MObject shaveVrayNode::m_primaryvis; + +MObject shaveVrayNode::m_reflvis; + +MObject shaveVrayNode::m_refrvis; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Attribute Names | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +MString shaveVrayNode::attr_vrayGeomInfo = "vrayGeomInfo"; +MString shaveVrayNode::attr_vgi = "vgi"; + +MString shaveVrayNode::attr_vrayGeomResult = "vrayGeomResult"; +MString shaveVrayNode::attr_vgr = "vgr"; + +MString shaveVrayNode::attr_outApiType = "outApiType"; +MString shaveVrayNode::attr_oat = "oat"; + +MString shaveVrayNode::attr_outApiClassification= "outApiClassification"; +MString shaveVrayNode::attr_oac = "oac"; + +MString shaveVrayNode::attr_output = "output"; +MString shaveVrayNode::attr_out = "out"; + +MString shaveVrayNode::attr_stackIndex = "stackIndex"; +MString shaveVrayNode::attr_sti = "sti"; + +MString shaveVrayNode::attr_isInstanced = "isInstanced"; +MString shaveVrayNode::attr_isi = "isi"; + +MString shaveVrayNode::attr_instanceMesh = "instanceMesh"; +MString shaveVrayNode::attr_ime = "ime"; + +MString shaveVrayNode::attr_tipfade = "tipfade"; +MString shaveVrayNode::attr_tf = "tf"; + +MString shaveVrayNode::attr_specTint = "specTint"; +MString shaveVrayNode::attr_st = "st"; + +MString shaveVrayNode::attr_specTint2 = "specTint2"; +MString shaveVrayNode::attr_st2 = "st2"; + +MString shaveVrayNode::attr_squirrel = "squirrel"; +MString shaveVrayNode::attr_sq = "sq"; + +MString shaveVrayNode::attr_ownshader = "ownshader"; +MString shaveVrayNode::attr_osh = "osh"; + +MString shaveVrayNode::attr_selfshadow = "selfshadow"; +MString shaveVrayNode::attr_ss = "ss"; + +MString shaveVrayNode::attr_castshadow = "castshadow"; +MString shaveVrayNode::attr_cs = "cs"; + +MString shaveVrayNode::attr_recvshadow = "recvshadow"; +MString shaveVrayNode::attr_rs = "rs"; + +MString shaveVrayNode::attr_primaryvis = "primaryvis"; +MString shaveVrayNode::attr_pv = "pv"; + +MString shaveVrayNode::attr_refrvis = "refrvis"; +MString shaveVrayNode::attr_rr = "rr"; + +MString shaveVrayNode::attr_reflvis = "reflvis"; +MString shaveVrayNode::attr_rl = "rl"; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ +| Methods | +/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +shaveVrayNode::shaveVrayNode(): + m_vrayExporter(NULL) +{ + if (loadShaveVRayExportImpl()) { + m_vrayExporter = newShaveVrayExporter(); + } else { + MGlobal::displayError("You will not be able to render with V-Ray"); + } +} + +shaveVrayNode::~shaveVrayNode() +{ + if (m_vrayExporter) { + deleteShaveVrayExporter(m_vrayExporter); + m_vrayExporter = NULL; + } +} + +MStatus shaveVrayNode::initialize() +{ + MFnNumericAttribute nAttr; + MFnTypedAttribute tAttr; + MFnUnitAttribute uAttr; + + + // Create input attributes + m_vrayGeomInfo=nAttr.createAddr(attr_vrayGeomInfo, attr_vgi); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setStorable(false)); + CHECK_MSTATUS(nAttr.setReadable(true)); + CHECK_MSTATUS(nAttr.setWritable(true)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_vrayGeomResult=nAttr.create(attr_vrayGeomResult, attr_vgr, MFnNumericData::kInt); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setStorable(false)); + CHECK_MSTATUS(nAttr.setReadable(true)); + CHECK_MSTATUS(nAttr.setWritable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + + MFnStringData strData; + strData.create(shaveVrayNode::apiType); + m_outApiType = tAttr.create(attr_outApiType, attr_oat, MFnData::kString, strData.object()); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setKeyable(false)); + CHECK_MSTATUS(tAttr.setHidden(true)); + + strData.create(shaveVrayNode::classification); + m_outApiClassification = tAttr.create(attr_outApiClassification, attr_oac, MFnData::kString, strData.object()); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setKeyable(false)); + CHECK_MSTATUS(tAttr.setHidden(true)); + + m_iconSize=nAttr.create("iconSize", "is", MFnNumericData::kFloat, 1.0); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setReadable(true)); + CHECK_MSTATUS(nAttr.setWritable(true)); + + m_outMesh = tAttr.create(attr_output, attr_out, MFnMeshData::kMesh); + CHECK_MSTATUS(tAttr.setKeyable(false)); + //CHECK_MSTATUS(tAttr.setStorable(false)); + //CHECK_MSTATUS(tAttr.setReadable(true)); + //CHECK_MSTATUS(tAttr.setWritable(false)); + + m_stackIndex = nAttr.create(attr_stackIndex,attr_sti,MFnNumericData::kInt,0); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_isInstanced = nAttr.create(attr_isInstanced,attr_isi,MFnNumericData::kBoolean,false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_ownshader = nAttr.create(attr_ownshader,attr_osh,MFnNumericData::kBoolean,true); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_tipfade = nAttr.create(attr_tipfade,attr_tf,MFnNumericData::kBoolean,false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_spectint = nAttr.createColor(attr_specTint,attr_st); + CHECK_MSTATUS(nAttr.setDefault(1.0f, 1.0f, 1.0f)); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_spectint2 = nAttr.createColor(attr_specTint2,attr_st2); + CHECK_MSTATUS(nAttr.setDefault(0.0f, 0.0f, 0.0f)); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_squirrel = nAttr.create(attr_squirrel,attr_sq,MFnNumericData::kBoolean,false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_instanceMesh = tAttr.create(attr_instanceMesh,attr_ime,MFnData::kMesh); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setHidden(true)); + + m_selfshadow = nAttr.create(attr_selfshadow,attr_ss,MFnNumericData::kFloat,1.0); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_castshadow = nAttr.create(attr_castshadow,attr_cs,MFnNumericData::kBoolean,1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_recvshadow = nAttr.create(attr_recvshadow,attr_rs,MFnNumericData::kBoolean,1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_primaryvis = nAttr.create(attr_primaryvis,attr_pv,MFnNumericData::kBoolean,1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_reflvis = nAttr.create(attr_reflvis,attr_rl,MFnNumericData::kBoolean,1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + m_refrvis = nAttr.create(attr_refrvis,attr_rr,MFnNumericData::kBoolean,1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + + // Add attributes + CHECK_MSTATUS(addAttribute(m_vrayGeomInfo)); + CHECK_MSTATUS(addAttribute(m_vrayGeomResult)); + CHECK_MSTATUS(addAttribute(m_outApiType)); + CHECK_MSTATUS(addAttribute(m_outApiClassification)); + CHECK_MSTATUS(addAttribute(m_iconSize)); + CHECK_MSTATUS(addAttribute(m_outMesh)); + CHECK_MSTATUS(addAttribute(m_stackIndex)); + CHECK_MSTATUS(addAttribute(m_isInstanced)); + CHECK_MSTATUS(addAttribute(m_ownshader)); + CHECK_MSTATUS(addAttribute(m_tipfade)); + CHECK_MSTATUS(addAttribute(m_spectint)); + CHECK_MSTATUS(addAttribute(m_spectint2)); + CHECK_MSTATUS(addAttribute(m_squirrel)); + CHECK_MSTATUS(addAttribute(m_instanceMesh)); + CHECK_MSTATUS(addAttribute(m_selfshadow)); + CHECK_MSTATUS(addAttribute(m_recvshadow)); + CHECK_MSTATUS(addAttribute(m_castshadow)); + CHECK_MSTATUS(addAttribute(m_primaryvis)); + CHECK_MSTATUS(addAttribute(m_refrvis)); + CHECK_MSTATUS(addAttribute(m_reflvis)); + + + // Attribute affects + CHECK_MSTATUS(attributeAffects(m_vrayGeomInfo, m_vrayGeomResult)); + CHECK_MSTATUS(attributeAffects(m_iconSize, m_outMesh)); + + return MS::kSuccess; +} + +// V-Ray will attempt to compute the "vrayGeomResult" output of the plugin; +// at that point the plugin must add itself to the V-Ray scene description. +MStatus shaveVrayNode::compute(const MPlug &plug, MDataBlock &block) +{ + //printf("shaveVrayNode::compute(...) plug %s\n",plug.name().asChar());fflush(stdout); + + if (plug==m_vrayGeomResult) + { + if (!m_vrayExporter) { + MGlobal::displayError("Couldn't load Shave V-Ray Exporter!"); + return MStatus::kFailure; + } + //printf("m_vrayGeomResult\n");fflush(stdout); +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayNode::compute( m_vrayGeomResult ...)"); +#endif + + MDataHandle geomInfoHandle=block.inputValue(m_vrayGeomInfo); + void *geomInfo=geomInfoHandle.asAddr(); + + ///////// debug info ///////// + //printf("VR::VRayGeomInfo %s\n", geomInfo != NULL ? "not NULL" : "NULL");fflush(stdout); + //printf("vrayGeomInfo %s\n", gi.isConnected() ? "not connected" : "connected");fflush(stdout); + ///////////////////////////// + + if (geomInfo) + { + MDataHandle idxHandle=block.inputValue(m_stackIndex); + int stackIdx = idxHandle.asInt(); + + MDataHandle isiHandle = block.inputValue(m_isInstanced); + bool isInstanced = isiHandle.asBool(); + + MDataHandle oshHandle = block.inputValue(m_ownshader); + bool ownshader = oshHandle.asBool(); + + MDataHandle tfHandle = block.inputValue(m_tipfade); + bool tipfade = tfHandle.asBool(); + + MDataHandle spHandle = block.inputValue(m_spectint); + MColor spectint = MColor(spHandle.asFloat3()); + + MDataHandle sp2Handle = block.inputValue(m_spectint2); + MColor spectint2 = MColor(sp2Handle.asFloat3()); + + MDataHandle sqHandle = block.inputValue(m_squirrel); + bool squirrel = sqHandle.asBool(); + + MDataHandle imeHandle = block.inputValue(m_instanceMesh); + MObject instance = imeHandle.asMesh(); + + MDataHandle ssHandle = block.inputValue(m_selfshadow); + float selfShadow = ssHandle.asFloat(); + + //cast/recieve Shadows + MDataHandle csHandle = block.inputValue(m_castshadow); + bool castShadow = csHandle.asBool(); + + MDataHandle rsHandle = block.inputValue(m_recvshadow); + bool recvShadow = rsHandle.asBool(); + + //primary visibility + MDataHandle pvHandle = block.inputValue(m_primaryvis); + bool primaryVis = pvHandle.asBool(); + + MDataHandle rlHandle = block.inputValue(m_reflvis); + bool reflVis = rlHandle.asBool(); + + MDataHandle rrHandle = block.inputValue(m_refrvis); + bool refrVis = rrHandle.asBool(); + + if (m_vrayVersion == "") + { + MGlobal::executeCommand("vray version", m_vrayVersion); + } + + MString libPath; + if(GetLibPath(m_vrayVersion, libPath)) + { +#if defined(OSMac_) && !defined(OSMac_MachO_) + libPath = shaveMacCarbon::makeMacFilename(libPath); +#endif + } + else + { + MGlobal::displayWarning("shaveVrayNode: can not retrieve V-Ray plug-ins directory from enviornment variable, default path is used."); + libPath = ""; + } + + bool camVis = true; + bool lightVis = true; + bool giVis = true; + for (MItDependencyNodes iterNodes; !iterNodes.isDone(); iterNodes.next()) { + MObject node = iterNodes.item(); + MFnDependencyNode gFn(node); + if (gFn.typeId() == shaveGlobals::id) { + MPlug cp = gFn.findPlug("primCameraVis"); + MPlug lp = gFn.findPlug("primLightVis"); + MPlug gp = gFn.findPlug("primGiVis"); + cp.getValue(camVis); + lp.getValue(lightVis); + gp.getValue(giVis); + } + } + + if (!castShadow) { + lightVis = false; + } + if (!primaryVis) { + camVis = false; + } + + m_vrayExporter->createVRayPlugin( + geomInfo, + libPath, + stackIdx, + ownshader, + isInstanced, + instance, + tipfade, + squirrel, + spectint, + spectint2, + camVis, + lightVis, + giVis, + reflVis, + refrVis, + selfShadow, + recvShadow); + } + MDataHandle geomResultHandle=block.outputValue(m_vrayGeomResult); + geomResultHandle.asInt()=1; + geomResultHandle.setClean(); + + return MS::kSuccess; + } + else if (plug == m_outMesh) + { + //printf("m_outMesh\n");fflush(stdout); + + MDataHandle sizeHandle=block.inputValue(m_iconSize); + float size=sizeHandle.asFloat(); + + MStatus stat; + MFnMeshData dataCreator; + MDataHandle outputHandle=block.outputValue(m_outMesh); + MObject newOutputData=dataCreator.create(); + MFnMesh fnMesh; + + MPointArray vertices; + vertices.setLength(4); + vertices[0]=MPoint(-size, 0.0f, -size); + vertices[1]=MPoint(-size, 0.0f, size); + vertices[2]=MPoint(size, 0.0f, size); + vertices[3]=MPoint(size, 0.0f, -size); + + fnMesh.addPolygon(vertices, true, 0.0f, newOutputData); + + outputHandle.set(newOutputData); + outputHandle.setClean(); + + return MS::kSuccess; + } + + return MS::kUnknownParameter; +} + +bool shaveVrayNode::readDraToParam(const char *draFilename) +{ + if (!m_vrayExporter) { + return false; + } + return m_vrayExporter->readDraToParam(draFilename); +} + +#endif //USE_VRAY diff --git a/mayaPlug/shaveVrayNode.h b/mayaPlug/shaveVrayNode.h new file mode 100644 index 0000000..7350749 --- /dev/null +++ b/mayaPlug/shaveVrayNode.h @@ -0,0 +1,174 @@ +#ifndef _HAIR_VRAY_NODE_H_ +#define _HAIR_VRAY_NODE_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVrayNode.h + + DESCRIPTION: Maya node + + HISTORY: created 29-03-2010 + + *> + **********************************************************************/ + +// Get MAYA_API_VERSION. +// +#include <maya/MTypes.h> + +#define USE_VRAY + +// Check for 64-bit OS and define the Bits64_ macro required by Maya headers +#ifdef X64 +#ifndef Bits64_ +#define Bits64_ +#endif +#endif + +// Maya headers +#include <maya/MGlobal.h> +#include <maya/MString.h> +#include <maya/MPxNode.h> +#include <maya/MFnNumericAttribute.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MFnUnitAttribute.h> +#include <maya/MFnStringData.h> +#include <maya/MFnMesh.h> +#include <maya/MFnMeshData.h> +#include <maya/MPointArray.h> +#include <maya/MPoint.h> +#include <maya/MTime.h> +#include <maya/MAnimControl.h> + + +#ifdef USE_VRAY + +struct shaveVrayIntListParam; +struct shaveVrayIntParam; +class shaveVrayExporter; + +class shaveVrayNode: public MPxNode { +public: + static MTypeId typeId; + static MString typeName; + + static MString apiType; + static MString classification; + + static MString attr_vrayGeomInfo; + static MString attr_vgi; + + static MString attr_vrayGeomResult; + static MString attr_vgr; + + static MString attr_outApiType; + static MString attr_oat; + + static MString attr_outApiClassification; + static MString attr_oac; + + static MString attr_output; + static MString attr_out; + + static MString attr_stackIndex; + static MString attr_sti; + + static MString attr_isInstanced; + static MString attr_isi; + + static MString attr_instanceMesh; + static MString attr_ime; + + static MString attr_tipfade; + static MString attr_tf; + + static MString attr_specTint; + static MString attr_st; + + static MString attr_specTint2; + static MString attr_st2; + + static MString attr_squirrel; + static MString attr_sq; + + static MString attr_ownshader; + static MString attr_osh; + + static MString attr_selfshadow; + static MString attr_ss; + + static MString attr_castshadow; + static MString attr_cs; + + static MString attr_recvshadow; + static MString attr_rs; + + static MString attr_primaryvis; + static MString attr_pv; + + static MString attr_refrvis; + static MString attr_rr; + + static MString attr_reflvis; + static MString attr_rl; + + + shaveVrayNode(); + virtual ~shaveVrayNode(); + + virtual MStatus compute(const MPlug&, MDataBlock&); + +#if MAYA_API_VERSION >= 201600 + virtual SchedulingType schedulingType() const { return kUntrusted; } +#endif + + bool readDraToParam(const char *draFilename); + + //virtual MStatus setDependentsDirty ( const MPlug &plugBeingDirtied, + // MPlugArray &affectedPlugs ); + + static void* creator() {return new shaveVrayNode();} + static MStatus initialize(); + +private: + + shaveVrayExporter *m_vrayExporter; + MString m_vrayVersion; + + static MObject m_vrayGeomInfo; // This attribute is necessary for V-Ray to recognize the object as V-Ray geometry + static MObject m_vrayGeomResult; // This attribute is necessary for V-Ray to recognize the object as V-Ray geometry + static MObject m_outApiType; + static MObject m_outApiClassification; + + static MObject m_iconSize; + static MObject m_outMesh; + + static MObject m_stackIndex; + static MObject m_isInstanced; + static MObject m_instanceMesh; + + static MObject m_tipfade; + static MObject m_squirrel; + + static MObject m_spectint; + static MObject m_spectint2; + + static MObject m_ownshader; + + static MObject m_selfshadow; + + static MObject m_castshadow; + static MObject m_recvshadow; + static MObject m_primaryvis; + static MObject m_reflvis; + static MObject m_refrvis; +}; + +#endif //USE_VRAY + + +#endif diff --git a/mayaPlug/shaveVrayParams.h b/mayaPlug/shaveVrayParams.h new file mode 100644 index 0000000..177cd47 --- /dev/null +++ b/mayaPlug/shaveVrayParams.h @@ -0,0 +1,248 @@ +#ifndef _HAIR_VRAY_PARAMS_H_ +#define _HAIR_VRAY_PARAMS_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVrayParams.h + + DESCRIPTION: Vray plugin parameters + + HISTORY: created 29-03-2010 + + *> + **********************************************************************/ + +#include "vrayplugins.h" +#include "defparams.h" + +#ifndef VRAY_OVERRIDE // not defined in earlier sdk versions +#define VRAY_OVERRIDE +#endif + +struct shaveNamedParamBase : VR::VRayPluginParameter { + shaveNamedParamBase(const tchar* paramName) : name(paramName) {} + + // from VRayPluginParameter + const tchar* getName() VRAY_OVERRIDE { + return name; + } + +private: + const tchar* name; +}; + +//we need own params to avoid linking maya shave plugin with vray libs. +//fix linux SDK/Runtime param descrutor collision +struct shaveVrayIntParam: + shaveNamedParamBase, + VR::VRaySettableParamInterface, + VR::TextureIntInterface +{ + shaveVrayIntParam(const tchar *paramName, int value): shaveNamedParamBase(paramName), i(value) {} + PluginBase* getPlugin(void) { return static_cast<PluginBase*>(this); } + + int getInt(int index, double time) { return i; } + int getBool(int index, double time) { return i!=0; } + float getFloat(int index, double time) { return (float) i; } + PluginBase* getObject(int index, double time, const tchar **subparam=NULL) { return static_cast<PluginBase*>(this); } + + PluginInterface* newInterface(InterfaceID id) { + if (id==EXT_TEXTURE_INT) return static_cast<TextureIntInterface*>(this); + if (id==EXT_SETTABLE_PARAM) return static_cast<VRaySettableParamInterface*>(this); + return VRayPluginParameter::newInterface(id); + } + void setInt(int value, int index, double time) { i=value; } + void setBool(int value, int index, double time) { i=value; } + VR::VRayParameterType getType(int index, double time) { return VR::paramtype_int; } + + int getTexInt(const VR::VRayContext &rc) { return i; } +protected: + int i; +}; + + +struct shaveVrayFloatParam: + shaveNamedParamBase, + VR::VRaySettableParamInterface, +#ifdef VRAY30 + VR::TextureFloatInterface +#else + VR::TextureIntInterface +#endif +{ + shaveVrayFloatParam(const tchar *paramName, float fval): shaveNamedParamBase(paramName), f(fval) {} + PluginBase* getPlugin(void) { return static_cast<PluginBase*>(this); } + + int getInt(int index, double time) { return (int)f; } + int getBool(int index, double time) { return fabsf(f)<1e-12f? false: true; } + float getFloat(int index, double time) { return f; } + double getDouble(int index, double time) { return (double)f; } + PluginBase* getObject(int index, double time, const tchar **subparam=NULL) { return static_cast<PluginBase*>(this); } + + PluginInterface* newInterface(InterfaceID id) { +#ifdef VRAY30 + if (id==EXT_TEXTURE_FLOAT) return static_cast<TextureFloatInterface*>(this); +#else + if (id==EXT_TEXTURE_INT) return static_cast<TextureIntInterface*>(this); +#endif + if (id==EXT_SETTABLE_PARAM) return static_cast<VRaySettableParamInterface*>(this); + return VRayPluginParameter::newInterface(id); + } + void setInt(int value, int index, double time) { f=(float)value; } + void setBool(int value, int index, double time) { f=(float)value; } + void setFloat(float value, int index, double time) { f=value; } + void setDouble(double value, int index, double time) { f=(float)value; } + + VR::VRayParameterType getType(int index, double time) { return VR::paramtype_float; } + +#ifdef VRAY30 + VR::real getTexFloat(const VR::VRayContext &rc) { return f; } + void getTexFloatBounds(VR::real &fmin, VR::real &fmax) {} +#else + float getTexFloat(const VR::VRayContext &rc) { return f; } + void getTexFloatBounds(float &fmin, float &fmax) { fmin=fmax=f; } +#endif + int getTexInt(const VR::VRayContext &rc) { return (int)f; } + +protected: + float f; +}; + +struct shaveVrayColorParam: + shaveNamedParamBase, + VR::VRaySettableParamInterface, + VR::TextureInterface +{ + shaveVrayColorParam(const tchar *paramName, float R, float G, float B): shaveNamedParamBase(paramName), r(R), g(G), b(B) {} + PluginBase* getPlugin(void) { return static_cast<PluginBase*>(this); } + + virtual VR::Color getColor(int index, double time) { return VR::Color(r,g,b); } + virtual VR::AColor getAColor(int index, double time) { return VR::AColor(r,g,b,1.0f); } + + PluginBase* getObject(int index, double time, const tchar **subparam=NULL) { return static_cast<PluginBase*>(this); } + + PluginInterface* newInterface(InterfaceID id) { + if (id==EXT_TEXTURE) return static_cast<TextureInterface*>(this); + if (id==EXT_SETTABLE_PARAM) return static_cast<VRaySettableParamInterface*>(this); + return VRayPluginParameter::newInterface(id); + } + + virtual void setColor(const VR::Color &value, int index, double time) { r = value.r; g = value.g; b = value.b; } + virtual void setColor(float R, float G, float B /*, int index, double time*/) { r = R; g = G; b = B; } + virtual void setAColor(const VR::AColor &value, int index, double time) { r = value.color.r; g = value.color.g; b = value.color.b; } + + virtual VR::VRayParameterType getType(int index, double time) { return VR::paramtype_color; } + + VR::AColor getTexColor(const VR::VRayContext &rc) { return VR::AColor(r,g,b,1.0f); } + void getTexColorBounds(VR::AColor &cmin, VR::AColor &cmax) { cmin=cmax=VR::AColor(r,g,b,1.0f); } + VR::Vector getColorBumpGradient(const VR::VRayContext &rc) { return VR::Vector(0.0f, 0.0f, 0.0f); } +protected: + float r; + float g; + float b; +}; + + +struct shaveVrayStringParam: + shaveNamedParamBase, + VR::VRaySettableParamInterface +{ + shaveVrayStringParam(const tchar *paramName, const tchar *value): shaveNamedParamBase(paramName) { copyToBuf(value); } + PluginBase* getPlugin(void) { return static_cast<PluginBase*>(this); } + + ~shaveVrayStringParam(void) { deleteBuf(); } + + tchar *getString(int index, double time) { return buf; } + + PluginInterface* newInterface(InterfaceID id) { return (id==EXT_SETTABLE_PARAM)? (VRaySettableParamInterface*) this : VRayPluginParameter::newInterface(id); } + void setString(const tchar *value, int index, double time) {deleteBuf(); copyToBuf(value);} + VR::VRayParameterType getType(int index, double time) { return VR::paramtype_string; } +protected: + tchar *buf; + void deleteBuf() { + if (buf) delete[] buf; + buf=NULL; + } + void copyToBuf(const tchar *str) { + size_t len=strlen(str); + buf=new tchar[len+1]; + if (buf) vutils_memcpy(buf, str, (len+1)*sizeof(buf[0])); + } +}; + +/// A template class from which simple non-animated list parameters are derived. +template<typename T> +struct shaveVrayListParamBaseTyped: VR::DefListParamBase +{ + shaveVrayListParamBaseTyped(const tchar *paramName): DefListParamBase(paramName) {} + + /// Add a new element to the end of the list. + void operator+=(const T &i) { values+=i; } + + /// Set the number of elements in the list. + void setCount(int n) { values.setCount(n, true); } + + /// Return the i-th element in the list. + T& operator[](int i) { return values[i]; } + + /// Reserve space for count elements, but set the actual count to zero. + void reserve(int count) { values.setCount(count, true); values.clear(); } + + int getCount(double time) { return listLevel==0? values.count() : -1; } +protected: + VR::Table<T> values; +}; + + +// non-animated integer list parameter. +struct shaveVrayIntListParam: shaveVrayListParamBaseTyped<int> { + /// Constructor. + /// @param paramName The name of the parameter. The object can copy the string or it can just store the pointer(in which case the pointer must be valid for the life time of the object). + shaveVrayIntListParam(const tchar *paramName, int ownName = 0): shaveVrayListParamBaseTyped<int>(paramName/*, ownName*/) {} +// shaveVrayIntListParam(const shaveVrayIntListParam & other): shaveVrayListParamBaseTyped<int>(other) { } + + PluginBase* getPlugin(void) { return static_cast<PluginBase*>(this); } + + // From VRayPluginParameter + virtual int getInt(int index, double time) { return values[index]; } + //virtual int getBool(int index, double time) { return values[index] != 0; } + //virtual float getFloat(int index, double time) { return (float)values[index]; } + //virtual double getDouble(int index, double time) { return (double) values[index]; } + virtual VR::IntList getIntList(double time) { return VR::IntList(&values[0], values.count()); } + virtual VR::VRayParameterType getType(int index, double time) { return VR::paramtype_int; } + + // From VRaySettableParamInterface + virtual void setInt(int value, int index, double time) + { + if(index >= 0 && index < values.count()) values[index] = value; + else values += value; + } + //virtual void setBool(int value, int index, double time) { setInt(value, index, time); } + //virtual void setFloat(float value, int index, double time) { setInt((int)value, index, time); } + //virtual void setDouble(double value, int index, double time) { setInt((int)value, index, time); } + + // From VRayCloneableParamInterface + //virtual VR::VRayPluginParameter * clone() { return new shaveVrayIntListParam(*this); } +protected: +}; + + +/// A simple non-animated Vector list parameter. +struct shaveVrayVectorListParam: shaveVrayListParamBaseTyped<VR::Vector> { + /// Constructor. + /// @param paramName The name of the parameter. Note that the object just stores the pointer and so must be valid for the life time of the object. + shaveVrayVectorListParam(const tchar *paramName): shaveVrayListParamBaseTyped<VR::Vector>(paramName) {} + PluginBase* getPlugin(void) { return static_cast<PluginBase*>(this); } + + VR::Vector getVector(int index, double time) { return values[index]; } + VR::VectorList getVectorList(double time) { return VR::VectorList(&values[0], values.count()); } + VR::VRayParameterType getType(int index, double time) { return VR::paramtype_vector; } +}; + + +#endif //end of_HAIR_VRAY_PARAMS_H_ + diff --git a/mayaPlug/shaveVrayRenderer.cpp b/mayaPlug/shaveVrayRenderer.cpp new file mode 100644 index 0000000..28aa3bb --- /dev/null +++ b/mayaPlug/shaveVrayRenderer.cpp @@ -0,0 +1,621 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <ctype.h> +#include <maya/MTypes.h> +#include <string.h> + +#include "shaveIO.h" + +#include <maya/MAnimControl.h> +#include <maya/MFileIO.h> +#include <maya/MFileObject.h> +#include <maya/MFnAttribute.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnMesh.h> +#include <maya/MFnNonAmbientLight.h> +#include <maya/MFnSet.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MFnVectorArrayData.h> +#include <maya/MGlobal.h> +#include <maya/MItDependencyNodes.h> +#include <maya/MSceneMessage.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> +#include <maya/MTime.h> +#include <maya/MTypeId.h> +#include <maya/MVectorArray.h> + +#include <time.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> + +#ifdef _WIN32 +#define fileno _fileno +#define fstat _fstat +typedef struct _stat StatType; +#include <process.h> +#else +#include <unistd.h> +typedef struct stat StatType; +#endif + +#include "shaveConstant.h" +#include "shaveDebug.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveMaya.h" +#include "shaveVrayRenderer.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveSDK.h" +#include "shaveUtil.h" +#include "shaveVraySharedFunctions.h" + +#if defined(OSMac_) +#include "shaveMacCarbon.h" +#endif + +#ifdef USE_VRAY + +static MString draFile; + + +shaveVrayRenderer::shaveVrayRenderer() +: mTimeState(shaveVrayRenderer::kAwaitingNothing) +{ + //MGlobal::displayInfo(" new shaveVrayRenderer"); + exportOK = false; +} + + +shaveVrayRenderer::~shaveVrayRenderer() +{} + +void shaveVrayRenderer::renderStart() +{ +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayRenderer::renderStart"); +#endif + + ENTER(); + + MGlobal::getActiveSelectionList( list ); + + + char buf[200]; + time_t t; + time(&t); + srand((unsigned int)t); + int r1 = rand(); + int r2 = rand(); + sprintf(buf,"vrayHair_%i%i.dra",r1,r2); + tempDra = buf; + + MString fileDir; + MGlobal::executeCommand("eval(\"internalVar -userTmpDir\")",fileDir); + //MString fileName = fileDir + "vrayHair.dra"; + MString tempFile; + MGlobal::executeCommand("eval(\"getAttr shaveGlobals.vrayDraFile\");",tempFile); + if(tempFile == "" ) + tempFile = tempDra; + + fileName = fileDir + tempFile; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + MString testFile = shaveMacCarbon::makeMacFilename(fileDir + MString("vray_Shave.tmp")); +#else + MString testFile = fileDir + MString("vray_Shave.tmp"); +#endif + ioError = true; + FILE* testFileH = fopen(testFile.asChar(),"w"); + if(testFileH) + { + int nb = fprintf(testFileH,"hello"); + if(nb == strlen("hello")) + ioError = false; + + fclose(testFileH); + } + if(ioError) + { + MGlobal::displayError(MString("shaveVrayRenderer: can not write to dir ")+fileDir+MString(". Giving up.")); + LEAVE(); + return; + } + else + MGlobal::displayInfo(MString("shaveVrayRenderer: ")+fileDir+MString(" is writable.")); + + // not neeeded any more + //mIsAnimation = false; + //mFirstFrame; + //mIsFirstFrame= true; + //MSelectionList renderGlobalsList; + //renderGlobalsList.clear(); + //renderGlobalsList.add("defaultRenderGlobals"); + //if (renderGlobalsList.length() > 0) + //{ + // MObject rgNode; + // renderGlobalsList.getDependNode(0, rgNode); + + // MFnDependencyNode depFn(rgNode); + + // int animOn = 0; + // MPlug animOnPlug = depFn.findPlug("animation"); + // if(!animOnPlug.isNull()) + // { + // animOnPlug.getValue(animOn); + // MGlobal::displayInfo(MString("anim ") + animOn); + + // mIsAnimation = (animOn == 1); + // if(mIsAnimation) + // { + // MTime startF; + // MPlug startFPlug = depFn.findPlug("startFrame"); + // if(!animOnPlug.isNull()) + // { + // startFPlug.getValue(startF); + // mFirstFrame = startF; + // MGlobal::displayInfo(MString("start frame ") + startF.value()); + + // } + // } + // } + // else + // MGlobal::displayError("shave: can find plug."); + //} + //else + // MGlobal::displayError("shave: can not get defaultRenderGlobals."); + + + //set up vraySettings.preKeyframeMel + MStatus stat; + MSelectionList vraySettingsList; + vraySettingsList.add("vraySettings"); + + bool doCreate = false; + if (vraySettingsList.length() > 0) + { + MObject vraySettingsNode; + vraySettingsList.getDependNode(0, vraySettingsNode); + + MFnDependencyNode depFn(vraySettingsNode); + MPlug preKeyframeMelPlug = depFn.findPlug("preKeyframeMel"); + if(!preKeyframeMelPlug.isNull()) + { + MString kf("shaveRender -vrayKeyframe"); + stat = preKeyframeMelPlug.setValue(kf); + if(stat != MStatus::kSuccess) + MGlobal::displayError("shave: can not set vraySettings.preKeyframeMel"); + + } + else + MGlobal::displayError("shave: can not find vraySettings.preKeyframeMel"); + + } + else + { + //MGlobal::executeCommand("createNode -name vraySettings VRaySettingsNode"); + // + //it is better to use the procedure - vrayCreateVRaySettingsNode() + //It will check whether the node has already been created and create it if not. + //It will also register vraySettings as a global node for the V-Ray renderer. + //This is important for render settings presets (although V-Ray will register the + //node even if you created it manually, but still). vrayCreateVRaySettingsNode() + //also creates the vraySettings node as shared which is important when referencing + //the particular scene in another one. + MGlobal::executeCommand("vrayCreateVRaySettingsNode"); + doCreate = true; + } + if(doCreate) + { + vraySettingsList.add("vraySettings"); + if (vraySettingsList.length() > 0) + { + MObject vraySettingsNode; + vraySettingsList.getDependNode(0, vraySettingsNode); + + MFnDependencyNode depFn(vraySettingsNode); + MPlug preKeyframeMelPlug = depFn.findPlug("preKeyframeMel"); + if(!preKeyframeMelPlug.isNull()) + { + MString kf("shaveRender -vrayKeyframe"); + stat = preKeyframeMelPlug.setValue(kf); + if(stat != MStatus::kSuccess) + MGlobal::displayError("shave: can not set vraySettings.preKeyframeMel"); + + } + else + MGlobal::displayError("shave: can not find vraySettings.preKeyframeMel"); + + } + else + MGlobal::displayError("shave: can not get vraySettings."); + + } + //execute shave stuff + shaveRenderer::renderStart(); + + ///create shaders + MGlobal::executeCommand("shaveVrayShader -create -all"); + //MGlobal::executeCommand("shaveVrayShader -addCallback"); + + ////forse to set stack ids + MObjectArray shaveShapes; + getRenderableShaveNodesByRenderMode(NULL, &shaveShapes); + for(unsigned int i = 0; i < shaveShapes.length(); i++) + { + MFnDependencyNode nodeFn(shaveShapes[i]); + shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode(); + hairShape->setStackIndex(i); + } + shaveShapes.clear(); + + + LEAVE(); +} + +void shaveVrayRenderer::vrayKeyframeCallback() +{ +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayRenderer::vrayKeyframeCallback"); +#endif + + if(ioError) + return; + + ENTER(); + + MStatus stat; + bool isFirstSample = false; + bool isLastSample = false; + int numS = 0; + int curS = 0; + stat = MGlobal::executeCommand("eval(\"vrayRenderInfo -numKeyframes\")",numS); + stat = MGlobal::executeCommand("eval(\"vrayRenderInfo -currentKeyframe\")",curS); + MTime curTime = MAnimControl::currentTime(); + + MString dbgStr = MString("preKeyframeMel numKeyframes: ") + numS + + MString(" currentKeyframe: ") + curS + + MString(" currentTime: ") + curTime.value(); + + if(numS == 1) + { + isFirstSample = true; + isLastSample = true; + } + else + { + shaveMaya::doMotionBlur = true; + + if(curS == 0) + isFirstSample = true; + + if(curS == numS-1) + isLastSample = true; + } + if(isFirstSample) + { + //not good place at all, prmitieves do not render + //MGlobal::executeCommand("shaveVrayShader -delete -all"); + //MGlobal::executeCommand("shaveVrayShader -create -all"); + + dbgStr += MString(" << firstSample"); + // + // Get the nodes to be rendered as geometry, and those to be rendered + // not as geometry, into two separate lists. + // + mGeometryNodes.clear(); + mNonGeometryNodes.clear(); + + getRenderableShaveNodesByRenderMode(&mGeometryNodes, &mNonGeometryNodes); + + // + // Changes in animated attributes may have changed the list of + // renderable shaveHairShapes. Let's update the flags telling us which + // types of shaveHairShapes we have. + // + bool tempHaveHair; + bool tempHaveInstances; + + shaveUtil::classifyShaveNodes(mGeometryNodes, tempHaveHair, tempHaveInstances); + shaveUtil::classifyShaveNodes(mNonGeometryNodes, mHaveHair, mHaveInstances); + + mHaveHair = (mHaveHair || tempHaveHair); + mHaveInstances = (mHaveInstances || tempHaveInstances); + + //do shutter + // + // + // If motion blur is on, do shutter open and wait for shutter close. + // If motion blur is off, do both shutter open and shutter close now. + // + if (shaveMaya::doMotionBlur) + { + doShutter(curTime, shaveConstant::kShutterOpen); + mTimeState = kAwaitingShutterClose; + + } + else + { + //doShutter(curTime, shaveConstant::kShutterBoth); + //mTimeState = kAwaitingNothing; + mTimeState = kAwaitingShutterBoth; + } + } + if(isLastSample) + { + dbgStr += MString(" << lastSample"); + + switch (mTimeState) + { + case kAwaitingShutterClose: + doShutter(curTime, shaveConstant::kShutterClose); + + mTimeState = kAwaitingNothing; + break; + + case kAwaitingShutterBoth: + doShutter(curTime, shaveConstant::kShutterBoth); + + mTimeState = kAwaitingNothing; + break; + + default: + break; + } + // joexxx + // SHAVEclear_stack(); + + //gr, too late + //MGlobal::executeCommand("shaveVrayShader -delete -all"); + //MGlobal::executeCommand("shaveVrayShader -create -all"); + } + + //joexxxxxx SHAVEclear_stack(); + //MGlobal::displayInfo(dbgStr); + + LEAVE(); +} + + +void shaveVrayRenderer::frameStart(const shaveGlobals::Globals& g) +{ +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayRenderer::frameStart"); +#endif + if(ioError) + return; + + //here it's confused at all only the last layer renders correctly + //MGlobal::executeCommand("shaveVrayShader -delete -all"); + //MGlobal::executeCommand("shaveVrayShader -create -all"); + +#if 0 //do nothing - vrayKeyframeCallback is used instead + + ENTER(); + + shaveRenderer::frameStart(g); + + // + // If motion blur is on, do shutter open and wait for shutter close. + // If motion blur is off, do both shutter open and shutter close now. + // + MTime curTime = MAnimControl::currentTime(); + + MGlobal::displayInfo(MString("blur ") + shaveMaya::doMotionBlur + " time " + curTime.value()); + + if (shaveMaya::doMotionBlur) + { + //if(mIsAnimation && mIsFirstFrame) + //{ + // curTime = mFirstFrame - 0.5;//+ 1.0; + // MAnimControl::setCurrentTime(curTime); + //} + doShutter(curTime, shaveConstant::kShutterOpen); + mTimeState = kAwaitingShutterClose; + + } + else + { + //doShutter(curTime, shaveConstant::kShutterBoth); + //mTimeState = kAwaitingNothing; + + mTimeState = kAwaitingShutterBoth; + } + mIsFirstFrame = false; + LEAVE(); + +#endif +} + +void shaveVrayRenderer::render( + float frame, shaveConstant::ShutterState shutter, + const MDagPath& camera +) +{ +#ifdef _DEBUG + MGlobal::displayInfo("shaveVrayRenderer::render"); +#endif + + if(ioError) + return; + + ENTER(); + + bool isShutterClose = (shutter == shaveConstant::kShutterClose) + ||(shutter == shaveConstant::kShutterBoth); + + if(isShutterClose) + { + //MString fileDir; + //MGlobal::executeCommand("eval(\"internalVar -userTmpDir\")",fileDir); + ////MString fileName = fileDir + "vrayHair.dra"; + //MString tempFile; + //MGlobal::executeCommand("eval(\"getAttr shaveGlobals.vrayDraFile\");",tempFile); + //MGlobal::displayInfo(MString("tempFile :") + tempFile); + //if(tempFile == "" ) + // tempFile = tempDra; + + //fileName = fileDir + tempFile; + + //MGlobal::displayInfo(MString("export :") + fileName); + //MGlobal::displayInfo(MString("tempDra :") + tempDra); + + //SHAVEmult_vox_size(g.voxelScaling); + +#if defined(OSMac_) && !defined(OSMac_MachO_) + hfsFileName = shaveMacCarbon::makeMacFilename(fileName); + int res = SHAVEexport_archive((char*)hfsFileName.asChar(), voxelResolutionGlob); +#else + int res = SHAVEexport_archive((char*)fileName.asChar(), voxelResolutionGlob); +#endif + // SHAVEclear_stack(); //nope does not help + + //I hate such approaches, but ... + MObjectArray vnodes; + FindNodesByTypeId(shaveVrayNode::typeId,vnodes); + + const char *draFilename; +#if defined(OSMac_) && !defined(OSMac_MachO_) + MString hfsFileName = shaveMacCarbon::makeMacFilename(fileName); + draFilename = hfsFileName.asChar(); +#else + draFilename = fileName.asChar(); +#endif + if(vnodes.length() > 0) + { + for(unsigned int i = 0; i < vnodes.length(); i++) + { + MFnDependencyNode dFn(vnodes[i]); + shaveVrayNode* vnode = (shaveVrayNode*)dFn.userNode(); + vnode->readDraToParam(draFilename); + } + } + + exportOK = (res != -1); + } + LEAVE(); +} + +void shaveVrayRenderer::frameEnd(const shaveGlobals::Globals& g) +{ + (void) g; +#if 0 //do nothing - vrayKeyframeCallback is used instead + ENTER(); + + shaveRenderer::frameEnd(g); + + LEAVE(); +#endif +} +void shaveVrayRenderer::renderEnd() +{ + //MGlobal::displayInfo("shaveVrayRenderer::renderEnd"); + + if(ioError) + return; + + ENTER(); + + //re-set up vraySettings.preKeyframeMel + MStatus stat; + MSelectionList vraySettingsList; + vraySettingsList.clear(); + vraySettingsList.add("vraySettings"); + + if (vraySettingsList.length() > 0) + { + MObject vraySettingsNode; + vraySettingsList.getDependNode(0, vraySettingsNode); + + MFnDependencyNode depFn(vraySettingsNode); + MPlug preKeyframeMelPlug = depFn.findPlug("preKeyframeMel"); + if(!preKeyframeMelPlug.isNull()) + { + MString kf(""); + stat = preKeyframeMelPlug.setValue(kf); + if(stat != MStatus::kSuccess) + MGlobal::displayError("shave: can not reset vraySettings.preKeyframeMel"); + + } + else + MGlobal::displayError("shave: can not find vraySettings.preKeyframeMel"); + + } + else + MGlobal::displayError("shave: can not get vraySettings."); + + + shaveRenderer::renderEnd(); + + MGlobal::executeCommand("shaveVrayShader -delete -all"); + //MGlobal::executeCommand("shaveVrayShader -removeCallback"); + + //MString fileDir; + //MGlobal::executeCommand("eval(\"internalVar -userTmpDir\")",fileDir); + MString tempFile; + MGlobal::executeCommand("eval(\"getAttr shaveGlobals.vrayDraFile\");",tempFile); + if(fileName.length() > 0 && tempFile == "") + { + if(remove(fileName.asChar()) == -1) + MGlobal::displayInfo(MString("shave: can not delete ")+fileName); + } + + MGlobal::setActiveSelectionList( list, MGlobal::kReplaceList); + + LEAVE(); +} + + +void shaveVrayRenderer::timeChange(const MTime& newTime) +{ + if(ioError) + return; + +#if 0 //do nothing - vrayKeyframeCallback is used instead + ENTER(); + + switch (mTimeState) + { + case kAwaitingShutterOpen: + doShutter(newTime, shaveConstant::kShutterOpen); + + if (shaveRenderCancelled) + mTimeState = kAwaitingNothing; + else + mTimeState = kAwaitingShutterClose; + break; + + + case kAwaitingShutterClose: + doShutter(newTime, shaveConstant::kShutterClose); + + mTimeState = kAwaitingNothing; + break; + + case kAwaitingShutterBoth: + doShutter(newTime, shaveConstant::kShutterBoth); + + mTimeState = kAwaitingNothing; + break; + + default: + break; + } + + LEAVE(); +#endif +} + +bool shaveVrayRenderer::isGeomNode(const shaveHairShape* nodePtr) const +{ + return false; + + //if (nodePtr->isInstanced()) return false; + //return (getRenderMode() == shaveConstant::kGeometryRender); +} + +#endif //USE_VRAY diff --git a/mayaPlug/shaveVrayRenderer.h b/mayaPlug/shaveVrayRenderer.h new file mode 100644 index 0000000..146603d --- /dev/null +++ b/mayaPlug/shaveVrayRenderer.h @@ -0,0 +1,85 @@ +#ifndef shaveVrayRenderer_h +#define shaveVrayRenderer_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MSelectionList.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> +#include <maya/MTypes.h> + +#include "shaveConstant.h" +#include "shaveVrayNode.h" +#include "shaveRenderer.h" + +class shaveHairShape; + +#ifdef USE_VRAY + +class shaveVrayRenderer : public shaveRenderer +{ +public: + shaveVrayRenderer(); + virtual ~shaveVrayRenderer(); + + virtual void renderStart(); + virtual void renderEnd(); + // + // these will not do any job - vrayKeyframeCallback will be used instead + // + virtual void frameStart(const shaveGlobals::Globals& g); + virtual void timeChange(const MTime& newTime); + virtual void frameEnd(const shaveGlobals::Globals& g); + + + virtual bool isGeomNode(const shaveHairShape* nodePtr) const; + + virtual void render( + float frame, + shaveConstant::ShutterState shutter, + const MDagPath& camera + ); + + // + //This should be used instead of frameStart - timeChange - frameEnd + //will be called before each keyframe is created + //but after the current time is moved for the current keyframe. + void vrayKeyframeCallback(); + +//************************************************ +// +// Helper Methods +// +//************************************************ + + MString fileName; + bool exportOK; + bool ioError; + + MSelectionList list; + +protected: + enum TimeState + { + kAwaitingNothing, + kAwaitingShutterOpen, + //kAwaitingShutterOpenSwap, + kAwaitingCenterFrame, + kAwaitingShutterClose, + kAwaitingShutterBoth + }; + + TimeState mTimeState; + //bool mIsAnimation; + //MTime mFirstFrame; + //bool mIsFirstFrame; + + MString tempDra; + +}; + +#endif //USE_VRAY + +#endif diff --git a/mayaPlug/shaveVraySharedFunctions.cpp b/mayaPlug/shaveVraySharedFunctions.cpp new file mode 100644 index 0000000..f6748f3 --- /dev/null +++ b/mayaPlug/shaveVraySharedFunctions.cpp @@ -0,0 +1,258 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVraySharedFunctions.h + + DESCRIPTION: Various shared functions + + HISTORY: created 21-08-2008 ( as part of 3ds Max + VRay hair shaders) + merged 31-03-2010 + + *> + **********************************************************************/ + +#include "shaveVraySharedFunctions.h" + +#include <maya/MItDependencyGraph.h> +#include <maya/MDagPath.h> +#include <maya/MArgList.h> +#include <maya/MSelectionList.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnDagNode.h> +#include <maya/MPlugArray.h> +#include <maya/MItSelectionList.h> +#include <maya/MItDependencyGraph.h> +#include <maya/MItDependencyNodes.h> + +#include <assert.h> + +#ifdef USE_VRAY + +MObject getNodeByName(const char *nodeName) +{ + MString name(nodeName); + MSelectionList sList; + MGlobal::getSelectionListByName(name, sList); + if (sList.length() < 1) return MObject(); + MObject obj; + sList.getDependNode(0, obj); + return obj; +} + +bool getSourcePlugFromPlug(const MPlug destPlug, MPlug &plug) +{ + MPlugArray plugSources; + destPlug.connectedTo(plugSources, true, false); + + bool foundSource = false; + int len=plugSources.length(); + if (len == 1) + { + foundSource = true; + plug = plugSources[0]; + } + return foundSource; +} + +bool FindCurrentShaveShape(MObject &shShape) +{ + MStatus stat = MStatus::kSuccess; + + MDagPath nodePath; + MObject component; + MSelectionList list; + + MGlobal::getActiveSelectionList( list ); + + + for ( MItSelectionList listIter( list ); !listIter.isDone(); listIter.next() ) + { + MObject depNode; + listIter.getDependNode( depNode ); + + if( FindConnectedShaveShape ( depNode, shShape)) + return true; + + if(depNode.hasFn( MFn::kDagNode )) + { + MFnDagNode dagFn(depNode); + for(unsigned int i = 0; i < dagFn.childCount(); i++) + if( FindConnectedShaveShape( dagFn.child(i), shShape)) + return true; + } + } + +#ifdef _DEBUG + MGlobal::displayInfo(MString("shaveVray: shaveShape not found.")); +#endif + return false; + +} + +bool FindConnectedShaveShape(MObject depNode, MObject &shShape) +{ + MStatus stat; + { + MItDependencyGraph graphIt(depNode, + MFn::kInvalid, + MItDependencyGraph::kUpstream, + MItDependencyGraph::kDepthFirst, + MItDependencyGraph::kNodeLevel, + &stat); + + if(stat == MStatus::kSuccess) + { + for ( ; ! graphIt.isDone(); graphIt.next() ) + { + MObject itNode = graphIt.thisNode(); + + MFnDependencyNode itFn(itNode); + + if(itFn.typeId() == MTypeId(0x001029B7)) //shaveHairShape type id + { + shShape = itNode; + #ifdef _DEBUG + MGlobal::displayInfo(MString("shaveVray: shaveShape found: ") + itFn.name()); + #endif + return true; + } + + } + } + } + { + MItDependencyGraph graphIt(depNode, + MFn::kInvalid, + MItDependencyGraph::kDownstream, + MItDependencyGraph::kDepthFirst, + MItDependencyGraph::kNodeLevel, + &stat); + + if(stat == MStatus::kSuccess) + { + for ( ; ! graphIt.isDone(); graphIt.next() ) + { + MObject itNode = graphIt.thisNode(); + + //if(itNode.apiType() == MFn::kShape) + { + MFnDependencyNode itFn(itNode); + + if(itFn.typeId() == MTypeId(0x001029B7)) //shaveHairShape type id + { + shShape = itNode; + #ifdef _DEBUG + MGlobal::displayInfo(MString("shaveVray: shaveShape found: ") + itFn.name()); + #endif + return true; + } + } + } + } + } + return false; +} + +void FindNodesByTypeId(MTypeId nodeType, MObjectArray& nodes) +{ + MItDependencyNodes iter; + + nodes.clear(); + + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + MFnDependencyNode nodeFn(node); + + if (nodeFn.typeId() == nodeType) nodes.append(node); + } +} + +void FindNodesByApiType(MFn::Type type, MObjectArray& nodes) +{ + MItDependencyNodes iter; + + nodes.clear(); + + for (; !iter.isDone(); iter.next()) + { + MObject node = iter.item(); + if (node.hasFn(type)) nodes.append(node); + } +} + +bool FindAllShaveShapes(MObjectArray& shShapes) +{ + FindNodesByTypeId(MTypeId(0x001029B7),shShapes); + return shShapes.length() != 0; +} + +bool EnumDownNodes(MObject thisNode, MObjectArray& nodes) +{ + MStatus stat; + MItDependencyGraph graphIt(thisNode, + MFn::kInvalid, + MItDependencyGraph::kDownstream, + MItDependencyGraph::kDepthFirst, + MItDependencyGraph::kNodeLevel, + &stat); + + if(stat == MStatus::kSuccess) + { + for ( ; ! graphIt.isDone(); graphIt.next() ) + { + MObject itNode = graphIt.thisNode(); + nodes.append(itNode); + } + } + return nodes.length() != 0; +} + +bool GetLibPathEnv(const MString vrayVersion, MString &var) +{ + MStatus stat; +#if MAYA_API_VERSION < 20180000 + int apiMajor = MAYA_API_VERSION/100; +#else + int apiMajor = MAYA_API_VERSION/10000; +#endif + char envVarBuf[256] = {'\0'}; + int vrayMajorVersion = vrayVersion.substring(0, vrayVersion.index('.')-1).asInt(); + + if (vrayMajorVersion < 4) + { + sprintf(envVarBuf,"VRAY_FOR_MAYA%i_PLUGINS_x64",apiMajor); + } + else + { + sprintf(envVarBuf,"VRAY_FOR_MAYA%i_PLUGINS",apiMajor); + } + + //printf("V-Ray plug-ins dir eviornment variable %s\n",envVarBuf); + + var = envVarBuf; + return true; +} + +bool GetLibPath(const MString vrayVersion, MString &path) +{ + MStatus stat; + MString pathVar; + + GetLibPathEnv(vrayVersion, pathVar); + + char* vrayPlugDir = getenv(pathVar.asChar()); + if(!vrayPlugDir) + return false; + + //printf("V-Ray plug-ins dir %s\n",vrayPlugDir); + + path = MString(vrayPlugDir); + + return true; +} + +#endif //USE_VRAY diff --git a/mayaPlug/shaveVraySharedFunctions.h b/mayaPlug/shaveVraySharedFunctions.h new file mode 100644 index 0000000..0f84d19 --- /dev/null +++ b/mayaPlug/shaveVraySharedFunctions.h @@ -0,0 +1,65 @@ +#ifndef _HAIR_VR_PRIM_SHARED_FUCTIONS_H_ +#define _HAIR_VR_PRIM_SHARED_FUCTIONS_H_ + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +/********************************************************************** + *< + FILE: shaveVraySharedFunctions.h + + DESCRIPTION: Various shared functions + + HISTORY: created 21-08-2008 ( as part of 3ds Max + VRay hair shaders) + merged 31-03-2010 + + *> + **********************************************************************/ + +#include "shaveVrayNode.h" + +#ifdef USE_VRAY + + +#include <maya/MGlobal.h> +#include <maya/MPlug.h> +#include <maya/MObjectArray.h> +#include <maya/MTypeId.h> + +/* +// This code seems unused... +#include <vraybase.h> +inline void GenOrthVectors(const VR::Vector& in, VR::Vector& u, VR::Vector& v) +{ + u = (in.x != 0.0f || in.y != 0.0f) ? + normalize(VR::Vector(-in.y, in.x, 0.0f)) : + VR::Vector(1.0f, 0.0f, 0.0f); + + v = normalize(in^u); +} +// */ + +MObject getNodeByName(const char *nodeName); + +void FindNodesByTypeId(MTypeId nodeType, MObjectArray& nodes); + +void FindNodesByApiType(MFn::Type type, MObjectArray& nodes); + +bool getSourcePlugFromPlug(const MPlug destPlug, MPlug &plug); + +bool FindCurrentShaveShape(MObject &shShape); + +bool FindConnectedShaveShape(MObject node, MObject &shShape); + +bool FindAllShaveShapes(MObjectArray& shShapes); + +bool EnumDownNodes(MObject thisNode, MObjectArray& nodes); + +bool GetLibPathEnv(const MString vrayVersion, MString &path); + +bool GetLibPath(const MString vrayVersion, MString &var); + +#endif //USE_VRAY + +#endif //end of_HAIR_VR_PRIM_SHARED_FUCTIONS_H_ diff --git a/mayaPlug/shaveWriteHairCmd.cpp b/mayaPlug/shaveWriteHairCmd.cpp new file mode 100644 index 0000000..512df5a --- /dev/null +++ b/mayaPlug/shaveWriteHairCmd.cpp @@ -0,0 +1,72 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <stdio.h> + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDagPath.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +#include "shaveHairShape.h" +#include "shaveSDK.h" +#include "shaveUtil.h" +#include "shaveWriteHairCmd.h" + +const MString shaveWriteHairCmd::commandName = "shaveWriteHair"; + + +MSyntax shaveWriteHairCmd::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(false); + syntax.addArg(MSyntax::kString); + + return syntax; +} + + +MStatus shaveWriteHairCmd::doIt(const MArgList& args) +{ + MStatus st; + MArgDatabase argdb(syntax(), args, &st); + + if (!st) return st; + + MDagPath hairShape = shaveUtil::getCurrentHairShape(); + + if (!hairShape.isValid()) + { + MGlobal::displayError( + commandName + ": no currently selected shave node." + ); + return MS::kFailure; + } + + MString fileName; + argdb.getCommandArgument(0, fileName); + + FILE* f = fopen(fileName.asChar(), "w"); + + if (!f) + { + MGlobal::displayError( + commandName + ": cannot write to file '" + fileName + "'." + ); + return MS::kInvalidParameter; + } + + fclose(f); + + MFnDependencyNode nodeFn(hairShape.node()); + + shaveHairShape* shs = (shaveHairShape*)nodeFn.userNode(); + SHAVEwrite_hairDISK((char*)fileName.asChar(), shs->getHairNode()); + + return st; +} diff --git a/mayaPlug/shaveWriteHairCmd.h b/mayaPlug/shaveWriteHairCmd.h new file mode 100644 index 0000000..5d33dbc --- /dev/null +++ b/mayaPlug/shaveWriteHairCmd.h @@ -0,0 +1,23 @@ +#ifndef shaveWriteHairCmd_h +#define shaveWriteHairCmd_h + +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MPxCommand.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +class shaveWriteHairCmd : public MPxCommand +{ +public: + static void* createCmd() { return new shaveWriteHairCmd; } + static MSyntax createSyntax(); + virtual MStatus doIt(const MArgList& args); + virtual bool isUndoable() const { return false; } + + static const MString commandName; +}; + +#endif diff --git a/mayaPlug/shaveWriteRib.cpp b/mayaPlug/shaveWriteRib.cpp new file mode 100644 index 0000000..4d3bcbb --- /dev/null +++ b/mayaPlug/shaveWriteRib.cpp @@ -0,0 +1,3307 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include <maya/MAngle.h> +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MAnimControl.h> +#include <maya/MColor.h> +#include <maya/MColorArray.h> +#include <maya/MDagPath.h> +#include <maya/MFnCamera.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFileObject.h> +#include <maya/MFloatVector.h> +#include <maya/MFnMesh.h> +#include <maya/MGlobal.h> +#include <maya/MIntArray.h> +#include <maya/MItMeshFaceVertex.h> +#include <maya/MObjectArray.h> +#include <maya/MPlug.h> +#include <maya/MPxCommand.h> +#include <maya/MSelectionList.h> +#include <maya/MString.h> +#include <maya/MSyntax.h> + +#include <assert.h> +#include <string.h> + +# ifdef _WIN32 + // ri.h contains a lot of float/double mismatches. Let's stop VC++ from warning us about them. +# pragma warning(push) +# pragma warning(disable : 4244 ) +# endif + +# include <ri.h> + +# ifdef _WIN32 +# pragma warning(pop) +# endif + +#include "shaveEngine.h" +#include "shaveGlobals.h" +#include "shaveHairShape.h" +#include "shaveMaya.h" +#include "shaveRender.h" +#include "shaveRenderer.h" +#include "shaveRenderman.h" +#include "shaveSDK.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" +#include "shaveWriteRib.h" + +// Helper class to load symbols from RenderMan_for_Maya mll. +// +class RfMFuncs +{ +public: + RfMFuncs(const shaveGlobals::Globals&); + ~RfMFuncs(); + + bool initialized; + +#if defined(__linux__) || defined(OSMac_) + void *rfmDLL; +#elif defined(_WIN32) + HMODULE rfmDLL; +#endif + + // Functions + void (*RiArchiveRecord)(RtToken type, char* format, ...); + void (*RiBasis)(RtBasis u, int ustep, RtBasis v, int vstep); + void (*RiBegin)(RtToken name); + void (*RiCurvesV)( + RtToken type, int ncurves, int* nvertices, RtToken wrap, + int n, RtToken nms[], RtPointer vals[]); + RtToken (*RiDeclare)(char*, char*); + void (*RiEnd)(); + void (*RiMotionBegin)(int n, ...); + void (*RiMotionEnd)(); + void (*RiOption)(RtToken, ...); + void (*RiPointsPolygonsV)( + int npolys, int* nverts, int* verts, int n, + RtToken nms[], RtPointer vals[]); + void (*RiProcDelayedReadArchive)(RtPointer data, float detail); + void (*RiProcedural)( + RtPointer data, RtBound bound, RtProcSubdivFunc sfunc, + RtProcFreeFunc ffunc); + void (*RiShadingInterpolation)(RtToken type); + void (*RiTransformBegin)(); + void (*RiTransformEnd)(); + + // Variables + RtBasis *RiCatmullRomBasis; + RtToken *RI_CS; + RtToken *RI_CUBIC; + RtToken *RI_LINEAR; + RtToken *RI_N; + RtToken *RI_NONPERIODIC; + RtToken *RI_OS; + RtToken *RI_P; + RtToken *RI_S; + RtToken *RI_SMOOTH; + RtToken *RI_T; + RtToken *RI_VERBATIM; + RtToken *RI_WIDTH; +}; + +#if defined(__linux__) || defined(OSMac_) +# include <dlfcn.h> +# define getDLLSymbol(hdl,name) dlsym(hdl,name) +#elif defined(_WIN32) +# define getDLLSymbol(hdl,name) GetProcAddress(hdl,name) +#else +# error Platform not supported. +#endif + + +RfMFuncs::RfMFuncs(const shaveGlobals::Globals &g) +: initialized(false) +{ + // If a RIB library override has been provided, use that. + // + MString dllPath = g.rib.libOverride; + + if (dllPath.length() == 0) + { + int rfmLoaded = 0; + MGlobal::executeCommand("pluginInfo -q -l RenderMan_for_Maya", rfmLoaded); + + if (rfmLoaded == 0) + { + MGlobal::displayError( + "No access to RenderMan functions. Please load the " + "RenderMan_for_Maya plugin, or provide" + ); + MGlobal::displayError( + "the path to an alternate RIB library in the shaveGlobals " + "'RIB Library Override' field." + ); + return; + } + +#if defined(__linux__) || defined(OSMac_) + MGlobal::executeCommand("pluginInfo -q -p RenderMan_for_Maya", dllPath); +#elif defined(_WIN32) + // If no path is provided Windows will look for a match based just + // on the name, which improves our chances of finding it. + // + dllPath = "RenderMan_for_Maya.mll"; +#endif + } + +#if defined(__linux__) || defined(OSMac_) + rfmDLL = dlopen(dllPath.asChar(), RTLD_LAZY); + if (rfmDLL == nullptr) +#elif defined(_WIN32) + // Make sure the path uses backslashes as directory separators. + // + dllPath.substitute("/", "\\"); + + rfmDLL = LoadLibrary(dllPath.asChar()); + + if (rfmDLL == 0) +#endif + { + MGlobal::displayError( + MString("Could not access shared library '") + dllPath + + "'. Please ensure that it exists and is readable and executable." + ); + return; + } + + // Functions + + this->RiArchiveRecord = (void (*)(RtToken, char*, ...))getDLLSymbol(rfmDLL, "RiArchiveRecord"); + if (this->RiArchiveRecord == nullptr) + { + MGlobal::displayError( + MString("Could not find the 'RiArchiveRecord' function in '") + + dllPath + "'." + ); + + if (g.rib.libOverride.length() > 0) + { + MGlobal::displayError( + "Please check that the 'RIB Library Override' field in " + "shaveGlobals is correct and gives the path to a RIB-compliant " + "shared library." + ); + } + else + { + MGlobal::displayError( + "Please check that RenderMan_for_Maya is correctly installed." + ); + } + + return; + } + + this->RiBasis = (void (*)(RtBasis, int, RtBasis, int))getDLLSymbol(rfmDLL, "RiBasis"); + assert(this->RiBasis != nullptr); + + this->RiBegin = (void (*)(RtToken name))getDLLSymbol(rfmDLL, "RiBegin"); + assert(this->RiBegin != nullptr); + + this->RiCurvesV = (void (*)(RtToken, int, int*, RtToken, int, RtToken[], RtPointer[]))getDLLSymbol(rfmDLL, "RiCurvesV"); + assert(this->RiCurvesV != nullptr); + + this->RiDeclare = (RtToken (*)(char*, char*))getDLLSymbol(rfmDLL, "RiDeclare"); + assert(this->RiDeclare != nullptr); + + this->RiEnd = (void (*)())getDLLSymbol(rfmDLL, "RiEnd"); + assert(this->RiEnd != nullptr); + + this->RiMotionBegin = (void (*)(int, ...))getDLLSymbol(rfmDLL, "RiMotionBegin"); + assert(this->RiMotionBegin != nullptr); + + this->RiMotionEnd = (void (*)())getDLLSymbol(rfmDLL, "RiMotionEnd"); + assert(this->RiMotionEnd != nullptr); + + this->RiOption = (void (*)(RtToken, ...))getDLLSymbol(rfmDLL, "RiOption"); + assert(this->RiOption != nullptr); + + this->RiPointsPolygonsV = (void (*)(int, int*, int*, int, RtToken[], RtPointer[]))getDLLSymbol(rfmDLL, "RiPointsPolygonsV"); + assert(this->RiPointsPolygonsV != nullptr); + + this->RiProcDelayedReadArchive = (void (*)(RtPointer, float))getDLLSymbol(rfmDLL, "RiProcDelayedReadArchive"); + assert(this->RiProcDelayedReadArchive != nullptr); + + this->RiProcedural = (void (*)(RtPointer, RtBound, RtProcSubdivFunc, RtProcFreeFunc))getDLLSymbol(rfmDLL, "RiProcedural"); + assert(this->RiProcedural != nullptr); + + this->RiShadingInterpolation = (void (*)(RtToken))getDLLSymbol(rfmDLL, "RiShadingInterpolation"); + assert(this->RiShadingInterpolation != nullptr); + + this->RiTransformBegin = (void (*)())getDLLSymbol(rfmDLL, "RiTransformBegin"); + assert(this->RiTransformBegin != nullptr); + + this->RiTransformEnd = (void (*)())getDLLSymbol(rfmDLL, "RiTransformEnd"); + assert(this->RiTransformEnd != nullptr); + + + // Variables + + this->RiCatmullRomBasis = (RtBasis*)getDLLSymbol(rfmDLL, "RiCatmullRomBasis"); + assert(this->RiCatmullRomBasis != nullptr); + + this->RI_CS = (RtToken*)getDLLSymbol(rfmDLL, "RI_CS"); + assert(this->RI_CS != nullptr); + + this->RI_CUBIC = (RtToken*)getDLLSymbol(rfmDLL, "RI_CUBIC"); + assert(this->RI_CUBIC != nullptr); + + this->RI_LINEAR = (RtToken*)getDLLSymbol(rfmDLL, "RI_LINEAR"); + assert(this->RI_LINEAR != nullptr); + + this->RI_NONPERIODIC = (RtToken*)getDLLSymbol(rfmDLL, "RI_NONPERIODIC"); + assert(this->RI_NONPERIODIC != nullptr); + + this->RI_OS = (RtToken*)getDLLSymbol(rfmDLL, "RI_OS"); + assert(this->RI_OS != nullptr); + + this->RI_P = (RtToken*)getDLLSymbol(rfmDLL, "RI_P"); + assert(this->RI_P != nullptr); + + this->RI_S = (RtToken*)getDLLSymbol(rfmDLL, "RI_S"); + assert(this->RI_S != nullptr); + + this->RI_SMOOTH = (RtToken*)getDLLSymbol(rfmDLL, "RI_SMOOTH"); + assert(this->RI_SMOOTH != nullptr); + + this->RI_T = (RtToken*)getDLLSymbol(rfmDLL, "RI_T"); + assert(this->RI_T != nullptr); + + this->RI_VERBATIM = (RtToken*)getDLLSymbol(rfmDLL, "RI_VERBATIM"); + assert(this->RI_VERBATIM != nullptr); + + this->RI_WIDTH = (RtToken*)getDLLSymbol(rfmDLL, "RI_WIDTH"); + assert(this->RI_WIDTH != nullptr); + + initialized = true; +} + + +RfMFuncs::~RfMFuncs() +{ +#if defined(__linux__) || defined(OSMac_) + if (rfmDLL != nullptr) dlclose(rfmDLL); +#elif defined(_WIN32) + if (rfmDLL != 0) FreeLibrary(rfmDLL); +#endif +} + +const MString shaveWriteRib::commandName("shaveWriteRib"); + +static const char* fsBinary = "-b"; +static const char* flBinary = "-binary"; +static const char* fsCleanup = "-cln"; +static const char* flCleanup = "-cleanup"; +static const char* fsFrame = "-f"; +static const char* flFrame = "-frame"; +static const char* fsFrameRelativeTime = "-frt"; +static const char* flFrameRelativeTime = "-frameRelativeTime"; +static const char* fsFullPaths = "-fp"; +static const char* flFullPaths = "-fullPaths"; +static const char* fsGzip = "-gz"; +static const char* flGzip = "-gzip"; +static const char* fsHairNode = "-hn"; +static const char* flHairNode = "-hairNode"; +static const char* fsHelp = "-h"; +static const char* flHelp = "-help"; +static const char* fsIgnoreShaveGlobals = "-isg"; +static const char* flIgnoreShaveGlobals = "-ignoreShaveGlobals"; +static const char* fsMotionBlur = "-mb"; +static const char* flMotionBlur = "-motionBlur"; +static const char* fsNormals = "-n"; +static const char* flNormals = "-normals"; +static const char* fsOpacities = "-opa"; +static const char* flOpacities = "-opacities"; +static const char* fsRestoreFrame = "-rf"; +static const char* flRestoreFrame = "-restoreFrame"; +static const char* fsRootTipColors = "-rtc"; +static const char* flRootTipColors = "-rootTipColors"; +static const char* fsRootPositions = "-rp"; +static const char* flRootPositions = "-rootPositions"; +static const char* fsShutterClose = "-sc"; +static const char* flShutterClose = "-shutterClose"; +static const char* fsShutterCloseOffset = "-sco"; +static const char* flShutterCloseOffset = "-shutterCloseOffset"; +static const char* fsShutterOpen = "-so"; +static const char* flShutterOpen = "-shutterOpen"; +static const char* fsShutterOpenOffset = "-soo"; +static const char* flShutterOpenOffset = "-shutterOpenOffset"; +static const char* fsSurfaceNormals = "-sn"; +static const char* flSurfaceNormals = "-surfaceNormals"; +static const char* fsTimeUnits = "-tu"; +static const char* flTimeUnits = "-timeUnits"; +static const char* fsUVSet = "-uv"; +static const char* flUVSet = "-uvSet"; +static const char* fsVertexColors = "-vc"; +static const char* flVertexColors = "-vertexColors"; +static const char* fsVoxels = "-vox"; +static const char* flVoxels = "-voxels"; +static const char* fsWCoords = "-wc"; +static const char* flWCoords = "-wCoords"; + + +static struct +{ + const char* shortName; + const char* longName; + bool multiUse; + MSyntax::MArgType argType; + const char* argName; + const char* description; +} kFlags[] = +{ + { + fsBinary, flBinary, false, MSyntax::kNoArg, 0, + "output file as binary rather than text." + }, + { + fsCleanup, flCleanup, false, MSyntax::kNoArg, 0, + "don't dump any RIBs but instead delete the files generated by a" + " previous dump. You must specify all the same parameters as the dump" + " you want to clean up." + }, + { + fsFrame, flFrame, false, MSyntax::kDouble, "frame", + "number of frame to output. If not specified then the current frame" + " is used." + }, + { + fsFrameRelativeTime, flFrameRelativeTime, false, MSyntax::kNoArg, 0, + "when motion blur is enabled this causes all times in motion blocks" + " to be written as being relative offsets to the frame time rather than" + " as absolute time values. This is for use with the 'offset' shutter" + " option in Renderman." + }, + { + fsFullPaths, flFullPaths, false, MSyntax::kNoArg, 0, + "use in conjunction with -voxels. If this flag is present then the" + " main RIB file will refer to the per-voxel RIB files using full" + " pathnames. If not present then just the filename will be used, with" + " no path." + }, + { + fsGzip, flGzip, false, MSyntax::kNoArg, 0, + "compress the output file using 'gzip' format. Only valid with '-b'." + }, + { + fsHairNode, flHairNode, true, MSyntax::kString, "nodeName", + "only write RIB for the specified shaveHair node." + }, + { + fsHelp, flHelp, false, MSyntax::kNoArg, 0, + "display this help message." + }, + { + fsIgnoreShaveGlobals, flIgnoreShaveGlobals, false, MSyntax::kNoArg, 0, + "ignore any defaults set in Shave Globals and only use those flags and" + " values which are explicitly specified on the command line." + }, + { + fsMotionBlur, flMotionBlur, false, MSyntax::kNoArg, 0, + "write motion blur info to the file." + }, + { + fsNormals, flNormals, false, MSyntax::kNoArg, 0, + "for instance geometry, output surface normals for each vertex of each" + " instance." + }, + { + fsOpacities, flOpacities, false, MSyntax::kNoArg, 0, + "write opacities ('Os' parameter)." + }, + { + fsRestoreFrame, flRestoreFrame, false, MSyntax::kBoolean, "yes|no", + "if this flag is set to 'no' then when motion blur is enabled the" + " current time will be left at the shutter close time rather than" + " being restored to the time at the start of the command. This is" + " useful for scripts which are processing a series of frames and wish" + " to avoid redundant frame changes. The default is 'yes'." + }, + { + fsRootPositions, flRootPositions, false, MSyntax::kNoArg, 0, + "write per-hair root positions as the 'P_srf' parameter." + }, + { + fsRootTipColors, flRootTipColors, false, MSyntax::kNoArg, 0, + "write root and tip colors." + }, + { + fsSurfaceNormals, flSurfaceNormals, false, MSyntax::kNoArg, 0, + "for each hair generate an 'N_srf' parameter which is the normal of" + " the growth surface at the root of the hair." + }, + { + fsShutterOpen, flShutterOpen, false, MSyntax::kDouble, "time", + "when motion blur is enabled, use this as the exact shutter open time." + }, + { + fsShutterOpenOffset, flShutterOpenOffset, false, MSyntax::kDouble, "offset", + "same as '-shutterOpen' except this is an offset which is added to the" + " frame time. If both this and -shutterOpen are given the latter will" + " be used." + }, + { + fsShutterClose, flShutterClose, false, MSyntax::kDouble, "time", + "when motion blur is enabled, use this as the exact shutter close time." + }, + { + fsShutterCloseOffset, flShutterCloseOffset, false, MSyntax::kDouble, "offset", + "same as '-shutterClose' except this is an offset which is added to" + " the frame time. If both this and -shutterClose are given the latter" + " will be used." + }, + { + fsTimeUnits, flTimeUnits, false, MSyntax::kString, "frames|seconds", + "the units to be used when writing times to the RIB file, for example" + " in MotionBegin statements. \"frames\" uses Maya frame numbers, which" + " is the default, \"seconds\" converts frame numbers to seconds using" + " the currently specified frame rate. Note that this does not change" + " the units used by other command flags, such as -shutterClose: those" + " continue to be in frames." + }, + { + fsUVSet, flUVSet, false, MSyntax::kString, "uvSetName", + "write texture coords for the given uv set." + }, + { + fsVertexColors, flVertexColors, false, MSyntax::kNoArg, 0, + "write vertex colors ('Cs' parameter)." + }, + { + fsVoxels, flVoxels, false, MSyntax::kNoArg, 0, + "if present, the hair for each shaveHairShape will be divided into" + " several voxels and each voxel will be written to a numbered file." + " The main output file will then load them as delayed read archives." + }, + { + fsWCoords, flWCoords, false, MSyntax::kNoArg, 0, + "write 'w' texture coords, which give the parametric length along each" + " hair." + } +}; + + +static char* ktAmbDiff = "SHAVEambdiff"; +static char* ktGloss = "SHAVEgloss"; +static char* ktOpacity = "SHAVEopacity"; +static char* ktRootColor = "rootcolor"; +static char* ktRootPosition = "P_Srf"; +static char* ktSelfShadow = "SHAVEselfshad"; +static char* ktSpecular = "SHAVEspec"; +static char* ktSquirrel = "SHAVEsquirrel"; +static char* ktSpecularColor = "SHAVEspec_color"; +static char* ktSpecularColor2 = "SHAVEspec_color2"; +static char* ktSurfaceNormal = "N_Srf"; +static char* ktTipColor = "tipcolor"; +static char* ktWCoord = "w"; +static char* ktIndex = "index"; + + +shaveWriteRib::shaveWriteRib() +: fDoBinary(false) +, fDoFullPaths(false) +, fDoGzip(false) +, fDoMotionBlur(false) +, fDoNormals(false) +, fDoOpacities(false) +, fRestoreFrame(true) +, fDoRootPositions(false) +, fDoRootTipColors(false) +, fDoSurfaceNormals(false) +, fDoUVs(false) +, fDoVertexColors(false) +, fDoVoxels(false) +, fDoWCoords(false) +#ifndef NO_PRMAN +, fFrameRelativeTime(false) +, fTimeFactor(1.0) +, fTimeUnits(shaveGlobals::kFrames) +#endif +{ +} + + +shaveWriteRib::~shaveWriteRib() {} + + +#ifndef NO_PRMAN +void shaveWriteRib::addCurve( + int firstVertIdxIdx, + int lastVertIdxIdx, + const int* vertIndices, + const VERT* verts, + const VERT* vertColours, + const VERT* rootColour, + const VERT* tipColour, + float hairU, + float hairV, + float rootRadius, + float tipRadius, + const VERT* hairNormal, + bool tipFade, + int indx +) +{ + int numHairVerts = lastVertIdxIdx - firstVertIdxIdx + 1; + + + fVertsPerHair[fCurveIdx] = numHairVerts; + + if (fUseCubicCurves) fVertsPerHair[fCurveIdx] += 2; + + // + // If per-vertex colours were not supplied then we'll have to + // interpolate them from the root and tip colours. + // + VERT colourPerVert = { 0.0f, 0.0f, 0.0f }; + float numSegs = (float)(numHairVerts - 1); + + if ((vertColours == NULL) && (rootColour != NULL) && (tipColour != NULL)) + { + colourPerVert.x = (tipColour->x - rootColour->x) / numSegs; + colourPerVert.y = (tipColour->y - rootColour->y) / numSegs; + colourPerVert.z = (tipColour->z - rootColour->z) / numSegs; + } + + // + // Set up all the per-vertex parameter values. + // + int i; + int j; + + for (i = firstVertIdxIdx; i <= lastVertIdxIdx; i++) + { + int vertIdx = vertIndices[i]; + + // + // We use a loop to double up on the first and last CVs when + // generating cubic curves. + // + for (j = 0; j < 2; j++) + { + fVert[fVertIdx][0] = verts[vertIdx].x; + fVert[fVertIdx][1] = verts[vertIdx].y; + fVert[fVertIdx][2] = verts[vertIdx].z; + + if (fDoVertexColors) + { + if (vertColours) + { + fVertexColor[fVertIdx][0] = vertColours[vertIdx].x; + fVertexColor[fVertIdx][1] = vertColours[vertIdx].y; + fVertexColor[fVertIdx][2] = vertColours[vertIdx].z; + } + else if (rootColour && tipColour) + { + float portion = (float)(i - firstVertIdxIdx); + + fVertexColor[fVertIdx][0] = + rootColour->x + colourPerVert.x * portion; + + fVertexColor[fVertIdx][1] = + rootColour->y + colourPerVert.y * portion; + + fVertexColor[fVertIdx][2] = + rootColour->z + colourPerVert.z * portion; + } + else + { + fVertexColor[fVertIdx][0] = 0.5; + fVertexColor[fVertIdx][1] = 0.5; + fVertexColor[fVertIdx][2] = 0.5; + } + } + + fVertIdx++; + + // + // If this isn't the first or last cv of a + // cubic curve then don't double up on it. + // + if (!fUseCubicCurves + || ((i != firstVertIdxIdx) && (i != lastVertIdxIdx))) + { + break; + } + } + } + + // + // We need one width, opacity and w-coord for each unique cv in the + // curve. So it doesn't matter whether we're doing linear or cubic + // curves as we output the same number of widths in both cases. + // + float radiusPerVert = (tipRadius - rootRadius) / numSegs; + float opacityPerVert = fBaseOpacity / numSegs; + + for (j = 0; j < numHairVerts; j++) + { + fWidth[fWidthIdx] = (rootRadius + radiusPerVert * (float)j) * 2.0f; + + if (fDoOpacities) + { + float opacity = fBaseOpacity; + + if (tipFade) + { + if (j == numHairVerts - 1) + opacity = 0.0f; + else + { + opacity -= opacityPerVert * (float)j; + } + } + + fOpacity[fWidthIdx][0] = opacity; + fOpacity[fWidthIdx][1] = opacity; + fOpacity[fWidthIdx][2] = opacity; + } + + if (fDoWCoords) + { + // + // It should be impossible to have just one vert as that would + // imply no segments in the curve. Still, better safe than + // sorry. + // + if (numHairVerts == 1) + fWCoord[fWidthIdx] = 0.5; + else + { + fWCoord[fWidthIdx] = + (float)j / (float)(numHairVerts - 1); + } + } + + fWidthIdx++; + } + + // + // Put the hair-specific info into their arrays. + // + + if (fDoRootPositions) + { + int firstVertIdx = vertIndices[firstVertIdxIdx]; + + fRootPosition[fCurveIdx][0] = verts[firstVertIdx].x; + fRootPosition[fCurveIdx][1] = verts[firstVertIdx].y; + fRootPosition[fCurveIdx][2] = verts[firstVertIdx].z; + } + + + if (fDoRootTipColors) + { + if (rootColour) + { + fRootColor[fCurveIdx][0] = rootColour->x; + fRootColor[fCurveIdx][1] = rootColour->y; + fRootColor[fCurveIdx][2] = rootColour->z; + } + else if (vertColours) + { + int firstVertIdx = vertIndices[firstVertIdxIdx]; + + fRootColor[fCurveIdx][0] = vertColours[firstVertIdx].x; + fRootColor[fCurveIdx][1] = vertColours[firstVertIdx].y; + fRootColor[fCurveIdx][2] = vertColours[firstVertIdx].z; + } + + if (tipColour) + { + fTipColor[fCurveIdx][0] = tipColour->x; + fTipColor[fCurveIdx][1] = tipColour->y; + fTipColor[fCurveIdx][2] = tipColour->z; + } + else + { + int lastVertIdx = vertIndices[lastVertIdxIdx]; + + fTipColor[fCurveIdx][0] = vertColours[lastVertIdx].x; + fTipColor[fCurveIdx][1] = vertColours[lastVertIdx].y; + fTipColor[fCurveIdx][2] = vertColours[lastVertIdx].z; + } + } + + if (fDoSurfaceNormals && hairNormal) + { + fSurfaceNormal[fCurveIdx][0] = hairNormal->x; + fSurfaceNormal[fCurveIdx][1] = hairNormal->y; + fSurfaceNormal[fCurveIdx][2] = hairNormal->z; + } + + if (fDoUVs) + { + // + // For curves, UVs are defined to be per-hair. + // + fSCoord[fCurveIdx] = hairU; + fTCoord[fCurveIdx] = hairV; + } + + + fIndex[fCurveIdx]=indx; + + fCurveIdx++; +} + + +void shaveWriteRib::beginFile(RfMFuncs &rfm, MString filename, MString verbatim) const +{ + // + // Set up the RIB file's formatting. + // + if (fDoBinary) + { + RtString format[1] = {"binary"}; + rfm.RiOption("rib", "format", (RtPointer)format, RI_NULL); + } + else + { + RtString format[1] = {"ascii"}; + rfm.RiOption("rib", "format", (RtPointer)format, RI_NULL); + } + + if (fDoGzip) + { + RtString str[1] = {"gzip"}; + rfm.RiOption("rib", "compression", (RtPointer)str, RI_NULL); + } + else + { + RtString str[1] = {"none"}; + rfm.RiOption("rib", "compression", (RtPointer)str, RI_NULL); + } + + // + // Begin the RIB file. + // + if (filename == "") + rfm.RiBegin(RI_NULL); + else + rfm.RiBegin ((char *)filename.asChar()); + + // + // If there is global RIB text, output it as a verbatim record. + // + if (verbatim != "") { + rfm.RiArchiveRecord(*rfm.RI_VERBATIM, "%s\n", verbatim.asChar()); + + // If the global RIB text contains the definition for our + // standard Reyes shader, then output the patterns and Bxdf + // declaration needed for our standard RIS shader as well. + // + // NOTE: Currently we only generate ShaveHairBxdf on Windows, + // but a RIB generated on Linux or OSX could be rendered + // on a Windows machine, so we want to include the Bxdf + // declarations on all platforms. + // + const MString reyesShaderDecl("Surface \"Shave\""); + + if (verbatim.indexW(reyesShaderDecl) != -1) { + rfm.RiArchiveRecord( + *rfm.RI_VERBATIM, + "Pattern \"PxrPrimvar\" \"shave_OsVar\" \"string varname\" [\"Os\"] \"string type\" [\"color\"]\n" + "Pattern \"PxrPrimvar\" \"shave_rootcolorVar\" \"string varname\" [\"rootcolor\"] \"string type\" [\"color\"]\n" + "Pattern \"PxrPrimvar\" \"shave_tipcolorVar\" \"string varname\" [\"tipcolor\"] \"string type\" [\"color\"]\n" + "Pattern \"PxrPrimvar\" \"shave_ambdiffVar\" \"string varname\" [\"SHAVEambdiff\"] \"string type\" [\"float\"]\n" + "Pattern \"PxrPrimvar\" \"shave_opacityVar\" \"string varname\" [\"SHAVEopacity\"] \"string type\" [\"float\"]\n" + "Pattern \"PxrPrimvar\" \"shave_glossVar\" \"string varname\" [\"SHAVEgloss\"] \"string type\" [\"float\"]\n" + "Pattern \"PxrPrimvar\" \"shave_selfshadVar\" \"string varname\" [\"SHAVEselfshad\"] \"string type\" [\"float\"]\n" + "Pattern \"PxrPrimvar\" \"shave_specVar\" \"string varname\" [\"SHAVEspec\"] \"string type\" [\"float\"]\n" + "Pattern \"PxrPrimvar\" \"shave_specColorVar\" \"string varname\" [\"SHAVEspec_color\"] \"string type\" [\"color\"]\n" + "Pattern \"PxrPrimvar\" \"shave_specColor2Var\" \"string varname\" [\"SHAVEspec_color2\"] \"string type\" [\"color\"]\n" + "Bxdf \"ShaveHairBxdf\" \"shaveMaterial\"\n" + " \"reference color Os\" [\"shave_OsVar:resultRGB\"]\n" + " \"reference color rootcolor\" [\"shave_rootcolorVar:resultRGB\"]\n" + " \"reference color tipcolor\" [\"shave_tipcolorVar:resultRGB\"]\n" + " \"reference float SHAVEambdiff\" [\"shave_ambdiffVar:resultF\"]\n" + " \"reference float SHAVEopacity\" [\"shave_opacityVar:resultF\"]\n" + " \"reference float SHAVEgloss\" [\"shave_glossVar:resultF\"]\n" + " \"reference float SHAVEselfshad\" [\"shave_selfshadVar:resultF\"]\n" + " \"reference float SHAVEspec\" [\"shave_specVar:resultF\"]\n" + " \"reference color SHAVEspec_color\" [\"shave_specColorVar:resultRGB\"]\n" + " \"reference color SHAVEspec_color2\" [\"shave_specColor2Var:resultRGB\"]\n" + ); + } + } + + // + // Begin the scene description. + // + rfm.RiTransformBegin(); +} + + +void shaveWriteRib::cleanup( + const MObjectArray& hairShapes, const shaveGlobals::Globals& g +) const +{ + if (fDoVoxels) + { + MString baseFilename = getBaseFilename(); + int i; + int v; + int numVoxels = g.rib.voxels.resolution + * g.rib.voxels.resolution + * g.rib.voxels.resolution; + MString voxelFile; + + for (i = 0; i < (int)hairShapes.length(); i++) + { + MFnDependencyNode nodeFn(hairShapes[i]); + + for (v = 0; v < numVoxels; v++) + { + voxelFile = getVoxelFilename(baseFilename, nodeFn.name(), v); + shaveUtil::fileDelete(voxelFile); + } + } + } + + shaveUtil::fileDelete(fFilename); +} + + +void shaveWriteRib::clearCurves() +{ + fCurveIdx = 0; + fVertIdx = 0; + fWidthIdx = 0; +} +#endif + + +void* shaveWriteRib::createCmd() +{ + return new shaveWriteRib(); +} + + +MSyntax shaveWriteRib::createSyntax() +{ + MSyntax syntax; + + syntax.enableEdit(false); + syntax.enableQuery(false); + + int numFlags = sizeof(kFlags) / sizeof(kFlags[0]); + int i; + + for (i = 0; i < numFlags; i++) + { + syntax.addFlag( + kFlags[i].shortName, kFlags[i].longName, kFlags[i].argType + ); + + if (kFlags[i].multiUse) syntax.makeFlagMultiUse(kFlags[i].shortName); + } + + syntax.setObjectType(MSyntax::kStringObjects, 0, 1); + + return syntax; +} + + +#ifdef NO_PRMAN +static RtToken (*zot)(char *name, char *decl) = nullptr; +static GLuint (*blat)(GLenum shaderType) = nullptr; + +MStatus shaveWriteRib::doIt(const MArgList& args) +{ + MGlobal::displayError( + "shaveWriteRib: command is not supported on this platform." + ); + return MS::kFailure; +} +#else +MStatus shaveWriteRib::doIt(const MArgList& args) +{ + MStatus st; + MArgDatabase argdb(syntax(), args, &st); + + if (!st) return st; + + if (argdb.isFlagSet(fsHelp)) + { + showHelp(); + return MS::kSuccess; + } + + // Are we doing cleanup of an earlier dump? + bool doCleanup = argdb.isFlagSet(fsCleanup); + + // + // Make sure that we have the default shaveGlobals node. + // + MObject shaveGlobalsNode = shaveGlobals::getDefaultNode(); + + if (shaveGlobalsNode.isNull()) + { + MGlobal::displayError( + commandName + ": Could not find default shaveGlobals node." + ); + return MS::kFailure; + } + + shaveGlobals::Globals g; + shaveGlobals::getGlobals(g); + + // This second call is still needed because some of the external + // methods we call still expect to find their shaveGlobals values in + // global variables. + shaveGlobals::getGlobals(); + +// fetch the offsets +openoffset=g.rib.blur.shutterOpenOffset; +closeoffset=g.rib.blur.shutterCloseOffset; + + + + // If we're not ignoring defaults from Shave Globals, then pull those + // in first. + bool ignoreDefaults = argdb.isFlagSet(fsIgnoreShaveGlobals); + + if (!ignoreDefaults) + { + fDoBinary = g.rib.binary; + fDoGzip = g.rib.compress; + fDoNormals = g.rib.normals; + fDoOpacities = g.rib.opacities; + fDoRootPositions = g.rib.rootPositions; + fDoRootTipColors = g.rib.rootTipColors; + fDoSurfaceNormals = g.rib.normals; + fDoUVs = g.rib.uvCoords; + fDoVertexColors = g.rib.vertexColors; + fDoWCoords = g.rib.wCoords; + fDoMotionBlur = g.rib.blur.enable; + fDoVoxels = g.rib.voxels.enable; + fDoFullPaths = g.rib.voxels.fullPaths; + fFrameRelativeTime = (g.rib.blur.timeBasis == shaveConstant::kTimeRelative); + fRestoreFrame = g.rib.blur.restoreFrame; + fTimeUnits = g.rib.timeUnits; + + // If we're inheriting settings from Renderman then see if it has + // blur enabled. If we can't find the renderManGlobals node then + // we will fall back to inheriting from Maya. + if (g.rib.blur.inheritSettings == + shaveConstant::kRibBlurInheritRenderman) + { + // Find the renderManGlobals node. + MObject globalsNode = shaveUtil::getNode("renderManGlobals"); + + if (globalsNode.isNull()) + g.rib.blur.inheritSettings = shaveConstant::kRibBlurInheritMaya; + else + { + MFnDependencyNode nodeFn(globalsNode); + + + MPlug plug = nodeFn.findPlug("rman__torattr___motionBlur"); + MPlug plug2 = nodeFn.findPlug("rman__torattr___cameraBlur"); + + + if (plug.isNull() || plug2.isNull()) + g.rib.blur.inheritSettings = shaveConstant::kRibBlurInheritMaya; + else + { + int temp, temp2; + plug.getValue(temp); + plug2.getValue(temp2); + fDoMotionBlur = (temp != 0) || (temp2 != 0); + } + } + } + + if (g.rib.blur.inheritSettings == shaveConstant::kRibBlurInheritMaya) + { + shaveMaya::getRenderGlobals(); + fDoMotionBlur = shaveMaya::doMotionBlur; + } + } + + fDoBinary |= argdb.isFlagSet(fsBinary); + fDoGzip |= argdb.isFlagSet(fsGzip); + + if (fDoGzip && !fDoBinary) + { + MGlobal::displayWarning( + commandName + ": the " + fsGzip + "/" + flGzip + + " flag is only valid with " + fsBinary + "/" + + flBinary + ); + + fDoGzip = false; + } + + fDoFullPaths |= argdb.isFlagSet(fsFullPaths); + fDoMotionBlur |= argdb.isFlagSet(fsMotionBlur); + fDoNormals |= argdb.isFlagSet(fsNormals); + fDoOpacities |= argdb.isFlagSet(fsOpacities); + fDoRootPositions |= argdb.isFlagSet(fsRootPositions); + fDoRootTipColors |= argdb.isFlagSet(fsRootTipColors); + fDoSurfaceNormals |= argdb.isFlagSet(fsSurfaceNormals); + fDoVertexColors |= argdb.isFlagSet(fsVertexColors); + fDoVoxels |= argdb.isFlagSet(fsVoxels); + fDoWCoords |= argdb.isFlagSet(fsWCoords); + fFrameRelativeTime |= argdb.isFlagSet(fsFrameRelativeTime); + + if (argdb.isFlagSet(fsRestoreFrame)) + argdb.getFlagArgument(fsRestoreFrame, 0, fRestoreFrame); + + if (argdb.isFlagSet(fsTimeUnits)) + { + MString timeUnitStr; + argdb.getFlagArgument(fsTimeUnits, 0, timeUnitStr); + + if (timeUnitStr == "frames") + fTimeUnits = shaveGlobals::kFrames; + else if (timeUnitStr == "seconds") + fTimeUnits = shaveGlobals::kSeconds; + else + { + MGlobal::displayError( + commandName + ": '" + timeUnitStr + + "' is not a valid time unit. Must be one of" + " \"frames\" or \"seconds\"." + ); + return MS::kInvalidParameter; + } + } + + if (fTimeUnits == shaveGlobals::kSeconds) + { + // Set up a time variable containing a single frame of time at + // the current frame rate. + MTime timeConv(1.0, MTime::uiUnit()); + + // Time in seconds. + fTimeFactor = timeConv.as(MTime::kSeconds); + } + + if (argdb.isFlagSet(fsUVSet)) + { + fDoUVs = true; + argdb.getFlagArgument(fsUVSet, 0, fUVSet); + } + + // + // Get the various frames times we need. + // + double curFrame = MAnimControl::currentTime().as(MTime::uiUnit()); + + getFrameTimes(argdb, g, curFrame); + + // Get the shaveHair nodes for which we're writing RIB. + MObjectArray hairNodes; + st = getHairShapes(argdb, hairNodes); + + if (!st || (hairNodes.length() == 0)) return st; + + // + // Get the name of the output file. + // + MStringArray optArgs; + argdb.getObjects(optArgs); + + if (optArgs.length() > 0) + { + fFilename = optArgs[0]; + + //check write permissions + FILE* fp = fopen(fFilename.asChar(),"wb"); + if (fp == NULL) + { + MGlobal::displayError(MString("Can't write rib '") + fFilename + "'. Directory is write protected."); + return MStatus::kFailure; + } + else + fclose(fp); +#if 0 + unsigned int flen = fFilename.length(); + if(flen > 0) + { + bool found = false; + unsigned int pos = 0; + std::string fn(fFilename.asChar()); + for(unsigned int i = flen-1; i > 0; i--) + { + if(fn[i] == '/' || fn[i] == '\\') + { + found = true; + pos = i; + break; + } + } + if(found) + { + int res = 0; + MString dir = fFilename.substring(0,pos); + + /// debug /// + //MGlobal::displayInfo(dir); + ///////////// + + + //test fails if backslash is used \\, need / + //I really do not like this approach + std::string dn(dir.asChar()); + for(unsigned int i = 0; i < dn.length(); i++) + { + if(dn[i] == '\\') + dn[i] = '/'; + } + MString dir2(dn.data()); + MGlobal::executeCommand(MString("filetest -w \"") + dir2 + MString("\""),res); + + if(res == 0) + { + MGlobal::displayError(MString("Can't write rib. Directory '") + dir + "' is write protected"); + return MStatus::kFailure; + } + } + } +#endif + if (g.verbose && !doCleanup) { +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + cerr << "Writing rib to file '" << fFilename.asChar() << "' ..." << endl; +#else + cerr << "Writing rib to file '" << fFilename << "' ..." << endl; +#endif + } + + } + else + fFilename = ""; + + // + // How should we represent the hairs: curves or polys? + // + fUseCubicCurves = (g.rib.primitiveType == 1); + + if (doCleanup) + { + cleanup(hairNodes, g); + } + else + { + RfMFuncs rfm(g); + + if (!rfm.initialized) return MS::kFailure; + + // + // If we're not already there, move to the frame we're writing. + // + if (fShutterOpen != curFrame) shaveUtil::setFrame((float)fShutterOpen); + + // + // Output RIB info for all of the renderable shaveHairShapes. + // + st = writeRib(rfm, hairNodes, g); + + // + // If motion blur is on we will be left at the shutter close time + // which will be between frames, so jump back to the actual frame + // time. + // + if (fDoMotionBlur && fRestoreFrame) + shaveUtil::setFrame((float)fFrameCenter); + + if (g.verbose && (fFilename != "")) + cerr << "Done writing rib file." << endl; + } +SHAVEclear_stack(); + return st; +} + + +void shaveWriteRib::endFile(RfMFuncs &rfm) const +{ + rfm.RiTransformEnd(); + rfm.RiEnd(); +} + + +void shaveWriteRib::freeBuffers(bool isInstanced) +{ + delete [] fVert; + delete [] fVertsPerHair; + + if (fDoRootTipColors) + { + delete [] fRootColor; + delete [] fTipColor; + } + if (!isInstanced) + if (fDoSurfaceNormals) delete [] fSurfaceNormal; + if (!isInstanced) + if (fDoVertexColors) + delete [] fVertexColor; + + if (fDoUVs) + { + delete [] fSCoord; + delete [] fTCoord; + } + + delete [] fIndex; + + if (isInstanced) + { + delete [] fVertIndices; + + delete [] fNormal; + delete [] fVertexColor; + } + else + { + delete [] fWidth; + + if (fDoOpacities) delete [] fOpacity; + if (!isInstanced) + if (fDoRootPositions) delete [] fRootPosition; + if (fDoWCoords) delete [] fWCoord; + } + + // + // Free up the parameter list buffers. + // + delete [] fParams; + delete [] fTokens; +} + + +MString shaveWriteRib::getBaseFilename() const +{ + MString baseFilename = fFilename; + + int lastDot = baseFilename.rindex('.'); + int lastSlash = baseFilename.rindex('/'); + int lastBSlash = baseFilename.rindex('\\'); + + if ((lastDot > lastSlash) && (lastDot > lastBSlash)) + baseFilename = baseFilename.substring(0, lastDot-1); + + return baseFilename; +} + + +void shaveWriteRib::getFrameTimes( + const MArgDatabase& argdb, const shaveGlobals::Globals& g, double curFrame +) +{ + bool ignoreGlobalDefaults = argdb.isFlagSet(fsIgnoreShaveGlobals); + + // If -frame was specified, use that as frame center, otherwise use + // the current frame. + fFrameCenter = curFrame; +{ +openoffset=g.rib.blur.shutterOpenOffset; +closeoffset=g.rib.blur.shutterCloseOffset; +} + + +if (argdb.isFlagSet(fsFrame)) + argdb.getFlagArgument(fsFrame, 0, fFrameCenter); + + // Determine the shutter open and close times, if explicitly + // specified. + bool useShaveGlobals = fDoMotionBlur + && !ignoreGlobalDefaults + && (g.rib.blur.inheritSettings == shaveConstant::kRibBlurInheritOff); + bool explicitShutterOpen = true; + + if (argdb.isFlagSet(fsShutterOpen)) + argdb.getFlagArgument(fsShutterOpen, 0, fShutterOpen); + else if (argdb.isFlagSet(fsShutterOpenOffset)) + { + double offset; + argdb.getFlagArgument(fsShutterOpenOffset, 0, offset); + fShutterOpen = fFrameCenter + offset; + } + else if (useShaveGlobals) + fShutterOpen = fFrameCenter + g.rib.blur.shutterOpenOffset; + else + explicitShutterOpen = false; + + bool explicitShutterClose = true; + + if (argdb.isFlagSet(fsShutterClose)) + argdb.getFlagArgument(fsShutterClose, 0, fShutterClose); + else if (argdb.isFlagSet(fsShutterCloseOffset)) + { + double offset; + argdb.getFlagArgument(fsShutterCloseOffset, 0, offset); + fShutterClose = fFrameCenter + offset; + } + else if (useShaveGlobals) + fShutterClose = fFrameCenter + g.rib.blur.shutterCloseOffset; + else + explicitShutterClose = false; + + if (fDoMotionBlur) + { + // Get the shutter open and close times. There are several + // possibilities here: + // + // 1) We have -shutterOpen and -shutterClose: we use those values and + // ignore -frame, if present. + // + // 2) We have -shutterOpen and -frame but no -shutterClose: we use + // -shutterOpen as the open time and treat the difference between + // it and -frame as half the blur length. + // + // 3) We have -shutterClose and -frame but no -shutterOpen: we use + // -shutterClose as the close time and treat the difference between + // it and -frame as half the blur length. + // + // 4) We have just one of -frame with no -shutterOpen or + // -shutterClose: calculate the blur delta from the camera's + // shutter angle and apply it to whichever value was provided. + if (explicitShutterOpen && explicitShutterClose) + { + // If we picked up absolute shutter open/close times from the + // command line, as opposed to relative ones, then the -frame + // value will be ignored, so warn the user. + if (argdb.isFlagSet(fsShutterOpen) + && argdb.isFlagSet(fsShutterClose) + && argdb.isFlagSet(fsFrame)) + { + displayWarning( + commandName + ": both " + flShutterOpen + " and " + + flShutterClose + " were specified so " + + flFrame + " will be ignored." + ); + } + } + else if (explicitShutterOpen) + { + fShutterClose = fFrameCenter + (fFrameCenter - fShutterOpen); + } + else if (explicitShutterClose) + { + fShutterOpen = fFrameCenter - (fShutterClose - fFrameCenter); + } + else + { + if (g.rib.blur.inheritSettings == + shaveConstant::kRibBlurInheritRenderman) + { + getRendermanShutterTimes(); + } + else + getMayaShutterTimes(); + } + } + else + { + // If we have -frame, use it. If not, then use -shutterOpen. If + // we don't have that either, then use the current frame. + if (argdb.isFlagSet(fsFrame)) + fShutterOpen = fFrameCenter; + else if (!argdb.isFlagSet(fsShutterOpen)) + fShutterOpen = curFrame; + + // -shutterClose is meaningless when motion blur is off, so warn + // the user if they specified it. + if (argdb.isFlagSet(fsShutterClose)) + { + displayWarning( + commandName + ": motion blur not enabled: ignoring " + + flShutterClose + ); + } + } +} + + +MStatus shaveWriteRib::getHairShapes( + const MArgDatabase& argdb, MObjectArray& hairShapes +) const +{ + // If shaveHair nodes were explicitly supplied in the command line, + // return those, otherwise return all renderable shaveHair nodes in + // the scene. + if (argdb.isFlagSet(fsHairNode)) + { + unsigned int numNodes = argdb.numberOfFlagUses(fsHairNode); + unsigned int i; + + for (i = 0; i < numNodes; ++i) + { + MArgList flagArgs; + argdb.getFlagArgumentList(fsHairNode, i, flagArgs); + + MString nodeName = flagArgs.asString(0); + MSelectionList list; + MDagPath path; + + list.add(nodeName); + list.getDagPath(0, path); + + if (path.hasFn(MFn::kTransform)) path.extendToShape(); + + MFnDagNode nodeFn(path); + + if (nodeFn.typeId() != shaveHairShape::id) + { + displayError( + MString("'") + nodeName + "' is not a shaveHair node." + ); + return MS::kInvalidParameter; + } + + hairShapes.append(path.node()); + } + } + else + { + shaveRenderer* renderer = shaveRender::getRenderer(); + renderer->getRenderableShaveNodes(hairShapes); + + unsigned int numShaveNodes = hairShapes.length(); + + if (numShaveNodes == 0) + { + MGlobal::displayWarning( + commandName + + ": There are no renderable shaveHairShapes in the scene." + ); + + return MS::kSuccess; + } + } + + return MS::kSuccess; +} + + +void shaveWriteRib::getMayaShutterTimes() +{ + shaveMaya::getRenderGlobals(); + + // Get the current render camera. + MDagPath cameraPath = shaveRender::getRenderCamPath(); + + if (cameraPath.isValid()) + { + // Get the camera's shutter angle. + MFnCamera cameraFn(cameraPath); + MAngle shutterAngle(cameraFn.shutterAngle(), MAngle::kRadians); + + // Calculate the amount of slider time needed for blurring. + double blurDelta = shutterAngle.asDegrees() / 360.0 + * shaveMaya::motionBlurByFrame / 2.0; + + // Calculate the shutter open/close times. + fShutterOpen = fFrameCenter - blurDelta; + fShutterClose = fFrameCenter + blurDelta; + } + else + { + // We couldn't find a valid render camera, so turn off motion + // blur. + displayWarning( + commandName + ": could not find a valid render camera." + + " Disabling motion blur." + ); + + fDoMotionBlur = false; + fShutterOpen = fFrameCenter; + } +} + + +int shaveWriteRib::getNumParams(bool isInstanced) const +{ + // Allocate the parameter list used to call Ri geometry functions. + // + int numParams = 0; + // + // Allocate buffers and fill in the parameter list. + // + int paramIdx = 0; + + // RI_P is always param 0. + + if (!isInstanced) + { + // RI_WIDTH is always param 1 for hair curves. + paramIdx = 2; + + // Opacity and the W-coord are also "varying" parameters, which + // means that there is one per vertex of the curve's linear form. + // (Or alternatively, one at the start of each segment, plus one + // at the very end of the curve.) + if (fDoOpacities) + { + // RI_OS + paramIdx++; + } + + if (fDoWCoords) + { + // ktWCoord + paramIdx++; + } + } + else + { + paramIdx = 1; + } + + if (fDoRootPositions && !isInstanced) + { + // ktRootPosition + paramIdx++; + } + + if (fDoRootTipColors) + { + // ktRootColor + paramIdx++; + + // ktTipColor + paramIdx++; + } + + // We only do vertex normals for instance geometry. + // + // TODO: Apparently we now ignore fDoNormals so we + // should get rid of it. + // + if (isInstanced) + { + // RI_N + paramIdx++; + } + + if (fDoSurfaceNormals && !isInstanced) + { + // ktSurfaceNormal + paramIdx++; + } + + // We always do vertex colors for instances, but for hairs + // only if we are asked to. + // + if (isInstanced || fDoVertexColors) + { + // RI_CS + paramIdx++; + } + + if (fDoUVs) + { + // RI_S + paramIdx++; + + // RI_T + paramIdx++; + } + + // ktIndex + paramIdx++; + + // ktAmbDiff + paramIdx++; + + // ktOpacity + paramIdx++; + + // ktGloss + paramIdx++; + + // ktSelfShadow + paramIdx++; + + // ktSpecular + paramIdx++; + + // ktSquirrel + paramIdx++; + + // ktSpecularColor + paramIdx++; + + // ktSpecularColor2 + paramIdx++; + + numParams=paramIdx; + return numParams; +} + + +void shaveWriteRib::getRendermanShutterTimes() +{ + // Find the renderManGlobals node. + MObject globalsNode = shaveUtil::getNode("renderManGlobals"); + + if (!globalsNode.isNull()) + { + MFnDependencyNode nodeFn(globalsNode); + MPlug plug; + + plug = nodeFn.findPlug("rman__toropt___motionBlurType"); + + if (!plug.isNull()) + { + float blurDelta = 1.0; + MString blurType; + plug.getValue(blurType); + + // If 'Motion Blur Type' is 'Frame' then the blur delta is + // always one full frame, regardless of camera angle. + // + // If 'Motion Blur Type' is 'Subframe' then the blur delta is + // the camera angle divided by 360. + if (blurType == "frame") + { + blurDelta = 1.0; + } + else if (blurType == "subframe") + { + float angle; + + plug = nodeFn.findPlug("rman__toropt___shutterAngle"); + plug.getValue(angle); + + blurDelta = angle / 360.0f; + } + else + { + displayWarning( + MString("shaveWriteRib: unrecognized renderManGlobals") + + " Motion Blur Type, '" + blurType + "'. Assuming 'frame'." + ); + blurDelta = 1.0f; + } + + MString shutterTiming; + plug = nodeFn.findPlug("rman__toropt___shutterTiming"); + plug.getValue(shutterTiming); + + //new params - 29Jun2012 + bool doBlur = false; + int prmanMoBlur; + plug = nodeFn.findPlug("rman__torattr___motionBlur"); + plug.getValue(prmanMoBlur); + + int prmanCamBlur; + plug = nodeFn.findPlug("rman__torattr___cameraBlur"); + plug.getValue(prmanCamBlur); + + doBlur = (prmanMoBlur != 0) || (prmanCamBlur != 0); + + float prmanSutterOpen; + plug = nodeFn.findPlug("rman__riopt__Camera_shutteropening0"); + plug.getValue(prmanSutterOpen); + + float prmanSutterClose; + plug = nodeFn.findPlug("rman__riopt__Camera_shutteropening1"); + plug.getValue(prmanSutterClose); + + /////// dump //////////// + printf(" --------------- prman ---------------\n"); + printf("prman: Motion Blur: %i\n",prmanMoBlur); + printf("prman: Camera Blur: %i\n",prmanCamBlur); + printf("prman: Sutter Angle/360: %f\n",blurDelta); + printf("prman: Sutter Open: %f\n",prmanSutterOpen); + printf("prman: Sutter Close: %f\n",prmanSutterClose); + printf("prman: Sutter Timing: %s\n",shutterTiming.asChar()); + printf("prman: Blur Type: %s\n",blurType.asChar()); + fflush(stdout); + ///////////////////////// + + // If 'Shutter Timing' is 'Frame Open' then the open offset is 0 + // and the close offset is the blur delta. + // + // If 'Shutter Timing' is 'Frame Center' then the open offset is + // negative half the blur delta and the close offset is + // positive half the blur delta. + // + // If 'Shutter Timing' is 'Frame Close' then the open offset is + // negative the blur delta and the close offset is 0. + // + // The actual shutter open/close times are simply the frame + // time with those offsets applied. + if (shutterTiming == "frameOpen") + { + fShutterOpen = fFrameCenter; + fShutterClose = fFrameCenter + blurDelta; + } + else if (shutterTiming == "frameCenter") + { + fShutterOpen = fFrameCenter - blurDelta / 2.0f; + fShutterClose = fFrameCenter + blurDelta / 2.0f; + } + else if (shutterTiming == "frameClose") + { + fShutterOpen = fFrameCenter - blurDelta; + fShutterClose = fFrameCenter; + } + else + { + displayWarning( + MString("shaveWriteRib: unrecognized renderManGlobals") + + " Shutter Timing, '" + shutterTiming + + "'. Assuming 'frameOpen'." + ); + + fShutterOpen = fFrameCenter; + fShutterClose = fFrameCenter + blurDelta; + } + + return; + } + } + + // We couldn't find the necessary renderManGlobals settings, so print + // a warning and use Maya's instead. + displayWarning( + MString("shaveWriteRib: renderMan blur defaults requested but") + + " renderManGlobals are unavailable. Using Maya defaults instead." + ); + + getMayaShutterTimes(); +} + + +MString shaveWriteRib::getVoxelFilename( + const MString& baseFilename, const MString& nodeName, int voxel +) const +{ + // The node name may contain a ':' which is bad in a filename, so + // let's convert those all to '_'. + MString modifiedNodeName = shaveUtil::substituteAll(nodeName, ':', '_'); + + MString voxelFile = baseFilename + "_" + modifiedNodeName + "_"; + voxelFile += voxel; + voxelFile += ".rib"; + + return voxelFile; +} + + +void shaveWriteRib::initBuffers( + RfMFuncs &rfm, + MFnDependencyNode &nodeFn, + int numHairs, + int numVerts, + int numFaceVerts, + float baseOpacity, + bool isInstanced +) +{ + // + // Allocate the parameter list used to call Ri geometry functions. + // + int numParams = getNumParams(isInstanced); + + fParams = new RtPointer[numParams]; + fTokens = new RtToken[numParams]; + + // + // Allocate buffers and fill in the parameter list. + // + int paramIdx = 0; + + if (!isInstanced) + { + int numWidths = numVerts; + + fWidth = new RtFloat[numWidths]; + + fTokens[1] = *rfm.RI_WIDTH; + fParams[1] = fWidth; + + paramIdx = 2; + + // Opacity and the W-coord are also "varying" parameters, which + // means that there is one per vertex of the curve's linear form. + // (Or alternatively, one at the start of each segment, plus one + // at the very end of the curve.) + if (fDoOpacities) + { + fOpacity = new RtColor[numWidths]; + fTokens[paramIdx] = *rfm.RI_OS; + fParams[paramIdx] = fOpacity; + paramIdx++; + } + + if (fDoWCoords) + { + fWCoord = new RtFloat[numWidths]; + + fTokens[paramIdx] = ktWCoord; + fParams[paramIdx] = fWCoord; + paramIdx++; + } + + // + // If we are creating cubic curves, then we need to double up on the + // start and end cvs to anchor the hair's endpoints. + // + // Note that we only supply widths for each unique cv, so we do not + // want to increment the width count to include these extra cvs. + // + if (fUseCubicCurves) numVerts += 2 * numHairs; + + // + // For curves, the number of face-verts and the number of verts + // should be the same. + // + numFaceVerts = numVerts; + } + else + { + fVertIndices = new RtInt[numFaceVerts]; + paramIdx = 1; + } + + fVert = new RtPoint[numVerts]; + + fTokens[0] = *rfm.RI_P; + fParams[0] = fVert; + + fVertsPerHair = new RtInt[numHairs]; + + if (fDoRootPositions && !isInstanced) + { + fRootPosition = new RtPoint[numHairs]; + fTokens[paramIdx] = ktRootPosition; + fParams[paramIdx] = fRootPosition; + paramIdx++; + } + + if (fDoRootTipColors) + { + fRootColor = new RtColor[numHairs]; + fTipColor = new RtColor[numHairs]; + + fTokens[paramIdx] = ktRootColor; + fParams[paramIdx] = fRootColor; + paramIdx++; + + fTokens[paramIdx] = ktTipColor; + fParams[paramIdx] = fTipColor; + paramIdx++; + } + + + // We only do vertex normals for instance geometry. + // + if (isInstanced) + { + fNormal = new RtNormal[numVerts]; + fTokens[paramIdx] = *rfm.RI_N; + fParams[paramIdx] = fNormal; + paramIdx++; + } + + if (fDoSurfaceNormals && !isInstanced) + { + fSurfaceNormal = new RtNormal[numHairs]; + fTokens[paramIdx] = ktSurfaceNormal; + fParams[paramIdx] = fSurfaceNormal; + paramIdx++; + } + + if (fDoVertexColors || isInstanced) + { + fVertexColor = new RtColor[numVerts]; + fTokens[paramIdx] = *rfm.RI_CS; + fParams[paramIdx] = fVertexColor; + paramIdx++; + } + + if (fDoUVs) + { + // For instance geometry we output the per face-vert UVs from the + // original geometry. + // + // For hair curves the entire curve acquires the UV of the + // point on the growth surface to which it is rooted, so the UVs + // are per-curve. + // + if (isInstanced) + { + fSCoord = new RtFloat[numFaceVerts]; + fTCoord = new RtFloat[numFaceVerts]; + } + else + { + fSCoord = new RtFloat[numHairs]; + fTCoord = new RtFloat[numHairs]; + } + + fTokens[paramIdx] = *rfm.RI_S; + fParams[paramIdx] = fSCoord; + paramIdx++; + + fTokens[paramIdx] = *rfm.RI_T; + fParams[paramIdx] = fTCoord; + paramIdx++; + } + + + fIndex = new RtInt[numHairs]; + fTokens[paramIdx] = ktIndex; + fParams[paramIdx] = fIndex; + paramIdx++; + + + // Set the Shave material constants + MPlug plug; + + fTokens[paramIdx] = ktAmbDiff; + plug = nodeFn.findPlug("amb/diff"); + plug.getValue(fAmbDiff); + fParams[paramIdx] = &fAmbDiff; + paramIdx++; + + fTokens[paramIdx] = ktOpacity; + fBaseOpacity = baseOpacity; + fParams[paramIdx] = &fBaseOpacity; + paramIdx++; + + fTokens[paramIdx] = ktGloss; + plug = nodeFn.findPlug("gloss"); + plug.getValue(fGloss); + fParams[paramIdx] = &fGloss; + paramIdx++; + + fTokens[paramIdx] = ktSelfShadow; + plug = nodeFn.findPlug("selfShadow"); + plug.getValue(fSelfShadow); + fParams[paramIdx] = &fSelfShadow; + paramIdx++; + + + + fTokens[paramIdx] = ktSpecular; + plug = nodeFn.findPlug("specular"); + plug.getValue(fSpecular); + fParams[paramIdx] = &fSpecular; + paramIdx++; + + fTokens[paramIdx] = ktSquirrel; + plug = nodeFn.findPlug("squirrel"); + plug.getValue(fSquirrel); + fParams[paramIdx] = &fSquirrel; + paramIdx++; + + + fTokens[paramIdx] = ktSpecularColor; + plug = nodeFn.findPlug("specularTint"); + plug.child(0).getValue(fSpecularColor[0]); + plug.child(1).getValue(fSpecularColor[1]); + plug.child(2).getValue(fSpecularColor[2]); + fParams[paramIdx] = &fSpecularColor; + paramIdx++; + + fTokens[paramIdx] = ktSpecularColor2; + plug = nodeFn.findPlug("specularTint2"); + plug.child(0).getValue(fSpecularColor2[0]); + plug.child(1).getValue(fSpecularColor2[1]); + plug.child(2).getValue(fSpecularColor2[2]); + fParams[paramIdx] = &fSpecularColor2; + paramIdx++; + + clearCurves(); +} + + +static void myFree(void* ptr) +{ +} + + +void shaveWriteRib::outputCurves(RfMFuncs &rfm, int numCurves, bool isShutterOpen) +{ + int numParams = getNumParams(false); + + if (fDoMotionBlur && isShutterOpen) + outputMotionBegin(rfm); + + rfm.RiCurvesV( + (fUseCubicCurves ? *rfm.RI_CUBIC : *rfm.RI_LINEAR), + numCurves, + fVertsPerHair, + *rfm.RI_NONPERIODIC, + numParams, + fTokens, + fParams + ); + + if (fDoMotionBlur && !isShutterOpen) + rfm.RiMotionEnd(); +} + + +void shaveWriteRib::outputInstanceGeom( + RfMFuncs& rfm, + MFnDependencyNode& nodeFn, + bool isShutterOpen, + MObject instanceMesh, + int numFaces, + int numVerts, + int numFaceVerts, + const int* faceList, + const int* faceStarts, + const int* faceEnds, + const VERT* verts, + const VERT* vertColors, + const VERT* vertNormals, + const VERT* growthSurfNormal, + int index +) +{ + MStatus st; + + MFnMesh meshFn(instanceMesh); + int facesPerInst = meshFn.numPolygons(); + int faceVertsPerInst = meshFn.numFaceVertices(); + int numInsts = numFaces / facesPerInst; + + // + // We can only do those parameters for which we have been passed + // appropriate data. + // + bool savedDoNormals = fDoNormals; + bool savedDoRootTipColors = fDoRootTipColors; + bool savedDoSurfaceNormals = fDoSurfaceNormals; + bool savedDoUVs = fDoUVs; + bool savedDoVertexColors = fDoVertexColors; + + fDoNormals = fDoNormals && (vertNormals != NULL); + + fDoRootTipColors = false; + + fDoSurfaceNormals = fDoSurfaceNormals && (growthSurfNormal != NULL); + + fDoUVs = fDoUVs && !instanceMesh.isNull(); + + fDoVertexColors = fDoVertexColors && + (!instanceMesh.isNull() || (vertColors != NULL)); + + // + // Now that we've adjusted some of the settings, get the number of + // parameters which we'll be dumping. + // + int numParams = getNumParams(true); + + initBuffers(rfm, nodeFn, numFaces, numVerts, numFaceVerts, 1.0f, true); + + int i; + + for (i = 0; i < numFaces; i++) + { + fVertsPerHair[i] = faceEnds[i] + - faceStarts[i]; + } + + for (i = 0; i < numFaceVerts; i++) + fVertIndices[i] = faceList[i]; + + for (i = 0; i < numVerts; i++) + { + fVert[i][0] = verts[i].x; + fVert[i][1] = verts[i].y; + fVert[i][2] = verts[i].z; + } + +// if (fDoNormals) + { + for (i = 0; i < numVerts; i++) + { + fNormal[i][0] = vertNormals[i].x; + fNormal[i][1] = vertNormals[i].y; + fNormal[i][2] = vertNormals[i].z; +MFloatVector v(vertNormals[i].x, vertNormals[i].y, vertNormals[i].z); +float mag = v.length(); +if ((mag < 0.995f) || (mag > 1.005f)) +{ +(std::cerr << "--- normal " << i << " has mag " << mag << std::endl).flush(); +} + } + } +if (0==1) + if (fDoSurfaceNormals) + { + // + // At the moment, we only ever get passed one surface normal which + // must then be applied to all the faces (which will generally be + // multiple strands of the same hair). + // + for (i = 0; i < numFaces; i++) + { + fSurfaceNormal[i][0] = growthSurfNormal[i].x; + fSurfaceNormal[i][1] = growthSurfNormal[i].y; + fSurfaceNormal[i][2] = growthSurfNormal[i].z; + } + } + + { +// if (vertColors) + { +// for (i = 0; i < numFaceVerts; i++) + for (i = 0; i < numVerts; i++) + { +// fVertexColor[i][0] = vertColors[faceList[i]].x; +// fVertexColor[i][1] = vertColors[faceList[i]].y; +// fVertexColor[i][2] = vertColors[faceList[i]].z; + fVertexColor[i][0] = vertColors[i].x; + fVertexColor[i][1] = vertColors[i].y; + fVertexColor[i][2] = vertColors[i].z; + } + } + } + + if (fDoUVs) + { + // + // Get the uvs from the original instance mesh. + // + MString* uvSet = NULL; + + if (fUVSet != "") uvSet = &fUVSet; + + int firstInstFVIndex = 0; + int instNum; + MItMeshFaceVertex iter(instanceMesh); + float2 uv; + bool uvSetWarningGiven = false; + + for (iter.reset(); !iter.isDone(); iter.next()) + { + int thisInstFVIndex = firstInstFVIndex; + + st = iter.getUV(uv, uvSet); + + if (!st && (uvSet != NULL)) + { + MGlobal::displayWarning( + commandName + ": '" + nodeFn.name() + + "'s instance mesh does not have a UV set named '" + + *uvSet + "'. Using the default UV set instead." + ); + + uvSet = NULL; + + st = iter.getUV(uv, uvSet); + } + + if (!st && !uvSetWarningGiven) + { + MGlobal::displayWarning( + commandName + ": '" + nodeFn.name() + + "'s instance mesh does not have a default UV set." + + " All UVs for this mesh will be zero." + ); + + uvSetWarningGiven = true; + } + + for (instNum = 0; instNum < numInsts; instNum++) + { + if (st) + { + fSCoord[thisInstFVIndex] = uv[0]; + fTCoord[thisInstFVIndex] = uv[1]; + } + else + { + fSCoord[thisInstFVIndex] = 0.0f; + fTCoord[thisInstFVIndex] = 0.0f; + } + + thisInstFVIndex += faceVertsPerInst; + } + + firstInstFVIndex++; + } + } + int xx; + for (xx = 0; xx < numFaces; xx++) + { +// fprintf (stdout,"xx = %d\n",xx);fflush(stdout); + fIndex[xx]=index; + } + + + if (fDoMotionBlur && isShutterOpen) + outputMotionBegin(rfm); + + rfm.RiPointsPolygonsV( + numFaces, + fVertsPerHair, + fVertIndices, + numParams, + fTokens, + fParams + ); + + if (fDoMotionBlur && !isShutterOpen) + rfm.RiMotionEnd(); + + freeBuffers(true); + + fDoNormals = savedDoNormals; + fDoRootTipColors = savedDoRootTipColors; + fDoSurfaceNormals = savedDoSurfaceNormals; + fDoUVs = savedDoUVs; + fDoVertexColors = savedDoVertexColors; +} + + +// +// Output those declarations which will be the same for all objects, +// regardless of whether they are hairs or instances. +// +void shaveWriteRib::outputGlobalDecls(RfMFuncs &rfm) const +{ + // + // Output declarations. + // + if (fUseCubicCurves) + rfm.RiBasis(*rfm.RiCatmullRomBasis, 1, *rfm.RiCatmullRomBasis, 1); + + rfm.RiDeclare(*rfm.RI_WIDTH, "varying float"); + + rfm.RiDeclare(*rfm.RI_OS, "varying color"); + + +// if (!isInstanced) + if (fDoRootPositions) + rfm.RiDeclare(ktRootPosition, "uniform point"); + + if (fDoRootTipColors) + { + rfm.RiDeclare(ktRootColor, "uniform color"); + rfm.RiDeclare(ktTipColor, "uniform color"); + } + + if (fDoSurfaceNormals) + rfm.RiDeclare(ktSurfaceNormal, "uniform normal"); + + // + // The w texture coord runs up the length of the hair, so it's + // different for each vertex along the hair. + // + if (fDoWCoords) + rfm.RiDeclare(ktWCoord, "varying float"); + + rfm.RiDeclare(ktIndex, "uniform integer"); + + // Basic Shave texture params + rfm.RiDeclare(ktAmbDiff, "constant float"); + rfm.RiDeclare(ktOpacity, "constant float"); + rfm.RiDeclare(ktGloss, "constant float"); + rfm.RiDeclare(ktSelfShadow, "constant float"); + rfm.RiDeclare(ktSpecular, "constant float"); + rfm.RiDeclare(ktSquirrel, "constant integer"); + rfm.RiDeclare(ktSpecularColor, "constant color"); + rfm.RiDeclare(ktSpecularColor2, "constant color"); +} + + +void shaveWriteRib::outputMotionBegin(RfMFuncs &rfm) const +{ + float openTime = 0.0f; + float closeTime = 0.0f; + if (fFrameRelativeTime) + { +// we put this in because someone was having problems with negative motion blocks +// openTime = 0.0; +// closeTime = (fShutterClose - fShutterOpen) * fTimeFactor; + + openTime = openoffset;//*fTimeFactor; + closeTime = closeoffset;//*fTimeFactor; +// openTime = (-(fShutterClose - fShutterOpen)/2.0f) * fTimeFactor; +// closeTime = ((fShutterClose - fShutterOpen)/2.0f) * fTimeFactor; + } + else + { + openTime = (float)(fShutterOpen * fTimeFactor); + closeTime = (float)(fShutterClose * fTimeFactor); + } + + rfm.RiMotionBegin(2, openTime, closeTime); +} + + +void shaveWriteRib::outputObjSpecificDecls(RfMFuncs &rfm, bool isInstanced) const +{ +#if 0 + // Opacity ("Os") is a standard geometric parameter so we don't need + // to declare it. + if (fDoOpacities && !isInstanced) + { + rfm.RiDeclare(*rfm.RI_OS, "vertex color"); + } +#endif + + rfm.RiShadingInterpolation(*rfm.RI_SMOOTH); + + if (fDoVertexColors) + { + rfm.RiDeclare(*rfm.RI_CS, "vertex color"); + } + + if (fDoUVs) + { + // + // For instance geometry we output the per face-vert UVs from the + // original geometry. + // + // For hair curves the entire curve acquires the UV of the point on + // the growth surface to which it is rooted, so the UVs are per + // curve. + // + if (isInstanced) + { + rfm.RiDeclare(*rfm.RI_S, "facevarying float"); + rfm.RiDeclare(*rfm.RI_T, "facevarying float"); + } + else + { + rfm.RiDeclare(*rfm.RI_S, "uniform float"); + rfm.RiDeclare(*rfm.RI_T, "uniform float"); + } + } +} + + +void shaveWriteRib::showHelp() const +{ + // Use 'indent1' if you're including the hypen after the indent (e.g. + // when the flag name overran where the description should start so + // the description starts on the second line). + // + // Use 'indent2' when just continuing a description. + MString indent1 = " "; + MString indent2 = indent1 + " "; + + MGlobal::displayInfo( + MString("Usage: ") + commandName + " [flags] [\"ribFileName\"]" + ); + + MGlobal::displayInfo(""); + MGlobal::displayInfo("where [flags] are:"); + + unsigned int numFlags = sizeof(kFlags) / sizeof(kFlags[0]); + unsigned int i; + const MString indent = " "; + unsigned int indentLen = indent.length(); + MString line; + const unsigned int kMaxLen = 78; + + for (i = 0; i < numFlags; i++) + { + line = MString(" ") + kFlags[i].shortName + "/" + kFlags[i].longName; + + if (kFlags[i].argName) line += MString(" ") + kFlags[i].argName; + + if (line.length() == indentLen - 2) + line += "- "; + else if (line.length() < indentLen - 2) + line += indent.substring(0, indentLen - line.length() - 3) + "- "; + else + { + MGlobal::displayInfo(line); + line = indent.substring(0, indentLen-3) + "- "; + } + + line += kFlags[i].description; + + if (kFlags[i].multiUse) line += " (multi-use)"; + + while (line.length() > kMaxLen) + { + MString temp = line.substring(0, kMaxLen-1); + int wordBreak = temp.rindex(' '); + + temp = temp.substring(0, wordBreak-1); + MGlobal::displayInfo(temp); + line = indent + line.substring(wordBreak+1, line.length()+1); + } + + if (line.length() > indent.length()) MGlobal::displayInfo(line); + } + + MGlobal::displayInfo(""); + + MGlobal::displayInfo( + " ribFileName - file to send output to. Must be in quotes." + ); + MGlobal::displayInfo(indent2 + "If not specified then output will be written"); + MGlobal::displayInfo(indent2 + "to the console."); + + MGlobal::displayInfo(""); + + MGlobal::displayInfo( + "Note that most settings will default to those specified in Shave Globals" + ); + MGlobal::displayInfo( + "unless the -isg/-ignoreShaveGlobals flag is used." + ); +} + + +// +// Write out a RIB file for each shaveHairShape's voxels, and have the main file +// include them all as delayed read archives. +// +MStatus shaveWriteRib::writeMultipleRibs( + RfMFuncs &rfm, MObjectArray shaveHairShapes, const shaveGlobals::Globals& g +) +{ + //vlad|16jan2013: you can get visibility flags from there + //g.cameraVis + //g.lightVis + //g.giVis + + MStatus st; + + // + // Create a name for the temporary Shave archive file we'll be using + // by stripping the extension from the name of the RIB file and adding + // "_shaveTemp.dat". + // + // %%% If the filename is empty, need to find the temp dir and create + // a default name there. + // + MString baseFilename = getBaseFilename(); + MString archiveFile = baseFilename + "_shaveTemp.dat"; + + // + // Create the Shave archive file. + // + fprintf (stdout, "Creating archive file..\n");fflush(stdout); + st = shaveRender::renderToDRAFile( + kShaveHostPRMAN, + archiveFile, + (int)g.rib.voxels.resolution, + shaveHairShapes, + fDoMotionBlur, + (float)fShutterOpen, + (float)fShutterClose, + true + ); + fprintf (stdout, "Done Creating archive file..\n");fflush(stdout); + + // + // Initialize Shave's PRIM library from the archive. + // + PRIMlogin(); + + int numVoxels = PRIMinit_hairstack((char*)archiveFile.asChar()); + + // + // Process each shaveHairShape separately. + // + std::vector<NodeInfo> nodes; + const unsigned int numBlurPasses = (fDoMotionBlur ? 2 : 1); + unsigned int sn; + fprintf (stdout, "Fetching Voxels..\n");fflush(stdout); + + for (sn = 0; sn < shaveHairShapes.length(); sn++) + { + MFnDependencyNode nodeFn(shaveHairShapes[sn]); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + MFnMesh instanceMeshFn; + MObject instanceMesh; + bool isInstanced = nodePtr->isInstanced(); + NodeInfo nodeInfo; + MString nodeName = nodeFn.name(); + MString nodePreamble; + MPlug plug = nodeFn.findPlug(shaveHairShape::ribInsert); + bool tipFade = false; + + plug.getValue(nodePreamble); + + plug = nodeFn.findPlug(shaveHairShape::aTipFade); + plug.getValue(tipFade); + + // + // If the node is instanced then we'll need its instance mesh. + // + if (isInstanced) + { + plug = nodeFn.findPlug(shaveHairShape::instanceMesh); + plug.getValue(instanceMesh); + instanceMeshFn.setObject(instanceMesh); + } + + // + // Step through each voxel for this shaveHairShape and output its hair + // into a separate RIB file. + // + int i; + NodeInfo::VoxelInfo voxel; + + for (i = 0; i < numVoxels; i++) + { + // + // Get the hair which this shaveHairShape has within this voxel. + // + HAIRTYPE hair; + UVSETS uvSets; + UVSETS* pUVSets = (fDoUVs ? &uvSets : 0); + + PRIMfetch_voxel_by_name2( + i, (char*)nodeName.asChar(), &hair, pUVSets, false + ); + + if (hair.totalfaces > 0) + { + bool haveUVs = (fDoUVs && (uvSets.totalUVSets > 0)); + + // + // Create the name for this voxel's RIB file and save it + // for later output to the main RIB file. + // + voxel.fFilename = getVoxelFilename(baseFilename, nodeName, i); + + // + // Get the voxel's bounding box and save it for later + // output to the main RIB file. + // + VERT bbMin; + VERT bbMax; + + PRIMfetch_bbox(i, &bbMin, &bbMax); + + voxel.fBBox[0] = bbMin.x; + voxel.fBBox[1] = bbMax.x; + voxel.fBBox[2] = bbMin.y; + voxel.fBBox[3] = bbMax.y; + voxel.fBBox[4] = bbMin.z; + voxel.fBBox[5] = bbMax.z; + + // + // Add this voxel's info to the shaveHairShape's array. + // + nodeInfo.fVoxels.push_back(voxel); + + // + // Start the RIB file for this voxel. + // + beginFile(rfm, (char *)voxel.fFilename.asChar(), nodePreamble); + outputGlobalDecls(rfm); + outputObjSpecificDecls(rfm, isInstanced); + + if (!isInstanced) + { + initBuffers( + rfm, + nodeFn, + hair.totalfaces, + hair.totalverts, + hair.totalfverts, + sqrt(hair.opacity[0]), // Opacity is const per-node + false + ); + } + + // + // If motion blur is on, we need to create separate motion + // blocks for shutter open and close. We do this by making + // two passes over the data. + // + unsigned int blurPass; + int j; + + for (blurPass = 0; blurPass < numBlurPasses; blurPass++) + { + // + // If this is the second pass for motion blur, add the + // velocities to the vertex positions to get their + // positions at shutter close. + // + if (blurPass == 1) + { + for (j = 0; j < hair.totalverts; j++) + { + VERT& vert = hair.v[j]; + VERT& vel = hair.velocity[j]; + + vert.x += vel.x; + vert.y += vel.y; + vert.z += vel.z; + } + } + + // + // At this point, curves and instances must part + // company as they are handled quite differently. + // + if (isInstanced) + { +#ifndef PRIM_BUG_FIXED + // + // %%% There is a bug in the PRIM functions such that + // they don't return the correct face-vertex counts + // or lists for instances, so we have to do those + // ourselves. + // + int facesPerInst = instanceMeshFn.numPolygons(); + int faceVertsPerInst = instanceMeshFn.numFaceVertices(); + int numInsts = hair.totalfaces / facesPerInst; + int numFaceVerts = numInsts * faceVertsPerInst; + int vertsPerInst = instanceMeshFn.numVertices(); + + if (hair.totalfverts < numFaceVerts) + { + if (hair.facelist) free(hair.facelist); + if (hair.face_start) free(hair.face_start); + if (hair.face_end) free(hair.face_end); + + hair.totalfverts = numFaceVerts; + hair.facelist = (int*)malloc(sizeof(int)*numFaceVerts); + hair.face_start = (int*)malloc(sizeof(int)*hair.totalfaces+1); + hair.face_end = (int*)malloc(sizeof(int)*hair.totalfaces+1); + + int faceStart = 0; + int faceEnd = 0; + int faceNum; + int instNum; + + for (faceNum = 0; faceNum < facesPerInst; faceNum++) + { + MIntArray vertIds; + + instanceMeshFn.getPolygonVertices( + faceNum, vertIds + ); + + faceEnd = faceStart + (int)vertIds.length(); + + for (instNum = 0; instNum < numInsts; instNum++) + { + int vertOffset = instNum * vertsPerInst; + int faceVertOffset = instNum * faceVertsPerInst; + int fv; + + for (fv = 0; fv < (int)vertIds.length(); fv++) + { + hair.facelist[ + faceVertOffset + faceStart + fv + ] = vertOffset + vertIds[fv]; + } + + hair.face_start[instNum*facesPerInst + faceNum] + = faceVertOffset + faceStart; + hair.face_end[instNum*facesPerInst + faceNum] + = faceVertOffset + faceEnd; + } + + faceStart = faceEnd; + } + + hair.face_start[hair.totalfaces] = hair.face_end[hair.totalfaces-1]; + hair.face_end[hair.totalfaces] = hair.face_end[hair.totalfaces-1]; + } +#endif + + outputInstanceGeom( + rfm, + nodeFn, + (blurPass == 0), + instanceMesh, + hair.totalfaces, + hair.totalverts, + hair.totalfverts, + hair.facelist, + hair.face_start, + hair.face_end, + hair.v, + NULL, + hair.surfNorm, + NULL, + hair.index[0] + ); + } + else + { + // + // Clear out any curve data from previous passes. + // + clearCurves(); + + // + // Fill in the arrays for the hairs. + // + int hairIdx; + int uvIdx = 0; + + for (hairIdx = 0; hairIdx < hair.totalfaces; hairIdx++) + { + int faceStart = hair.face_start[hairIdx]; + int faceEnd = hair.face_end[hairIdx] - 1; + + addCurve( + faceStart, + faceEnd, + hair.facelist, + hair.v, + NULL, + &hair.colorroot[hairIdx], + &hair.colortip[hairIdx], + (haveUVs ? uvSets.uvRoot[uvIdx].x : 0.0f), + (haveUVs ? uvSets.uvRoot[uvIdx].y : 0.0f), + hair.radiusroot[hairIdx], + hair.radiustip[hairIdx], + &hair.surfNorm[hairIdx], + tipFade, + hair.index[hairIdx] + + ); + + if (haveUVs) uvIdx += uvSets.totalUVSets; + } + + outputCurves(rfm, hair.totalfaces, (blurPass == 0)); + } + } + + endFile(rfm); + + if (!isInstanced) + freeBuffers(false); + } + + if (fDoUVs) SHAVEfree_UV(pUVSets); + + PRIMfree_hairtype(&hair); + } + + nodes.push_back(nodeInfo); + } + + // + // Create the main RIB file. + // + beginFile(rfm, fFilename, g.rib.filePreamble); + appendToResult(fFilename); + + for (sn = 0; sn < nodes.size(); sn++) + { + NodeInfo& node = nodes[sn]; + + // + // Output delayed read archives for each of the node's voxel + // files. + // this stuff will need to be inserted at the node level for prman export + // come back here for pixar .. + + RtString args[1]; + unsigned int v; + + for (v = 0; v < node.fVoxels.size(); v++) + { + NodeInfo::VoxelInfo& voxel = node.fVoxels[v]; + MString filename = voxel.fFilename; + + appendToResult(filename); + + // + // If the -fullPaths option was not specified, then strip the + // path from the filename. + // + if (!fDoFullPaths) + { + MFileObject fileObj; + + fileObj.setFullName(filename); + filename = fileObj.name(); + } + +#ifdef RiProcedural_BROKEN + rfm.RiArchiveRecord( + *rfm.RI_VERBATIM, + "Procedural \"DelayedReadArchive\" [\"%s\"] [%f %f %f %f %f %f]\n", + filename.asChar(), + voxel.fBBox[0], + voxel.fBBox[1], + voxel.fBBox[2], + voxel.fBBox[3], + voxel.fBBox[4], + voxel.fBBox[5] + ); +#else + args[0] = (char*)filename.asChar(); + + rfm.RiProcedural( + (RtPointer)args, voxel.fBBox, rfm.RiProcDelayedReadArchive, myFree + ); +#endif + } + } + + endFile(rfm); + + PRIMlogout(); + + shaveUtil::fileDelete(archiveFile); + + return MS::kSuccess; +} + + +MStatus shaveWriteRib::writeRib( + RfMFuncs &rfm, const MObjectArray& shaveHairShapes, const shaveGlobals::Globals& g +) +{ + MStatus st; + + unsigned int numShaveNodes = shaveHairShapes.length(); + + if (numShaveNodes == 0) + { + MGlobal::displayWarning( + commandName + + ": There are no renderable shaveHairShapes in the scene." + ); + + return MS::kSuccess; + } + + // + // If we're not doing motion blur, then we can get the texture info for + // all of the shaveHairShapes at once. + // + if (!fDoMotionBlur) +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapes, fUVSet, g.verbose); +#else + initTexInfoLookup(shaveHairShapes, fUVSet, g.verbose); +#endif + + // + // Generate the rib file(s). + // + if (fDoVoxels) + st = writeMultipleRibs(rfm, shaveHairShapes, g); + else + st = writeSingleRib(rfm, shaveHairShapes, g); + + return st; +} + +MStatus shaveWriteRib::writeSingleRib( + RfMFuncs &rfm, MObjectArray shaveHairShapes, const shaveGlobals::Globals& g +) +{ + //vlad|16jan2013: you can get visibility flags from there + //g.cameraVis + //g.lightVis + //g.giVis + + MStatus st; + + // + // Start the file. + // + beginFile(rfm, fFilename, g.rib.filePreamble); + outputGlobalDecls(rfm); + + // We always want the return type to be an array of strings, so even + // though we only have one filename in this case, let's put it into an + // array. + MStringArray fileNames; + fileNames.append(fFilename); + setResult(fileNames); + + // + // Output the hair for each shave node. + // + unsigned int sn; + unsigned int blurPass; + + for (sn = 0; sn < shaveHairShapes.length(); sn++) + { + MFnDependencyNode nodeFn(shaveHairShapes[sn]); + shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode(); + bool isInstanced = theShaveNode->isInstanced(); + bool savedDoMotionBlur = fDoMotionBlur; + + // Motion blur is not currently supported when doing geometry + // output in non-voxel mode, because the overhead is simply too + // great: we would either have to store all of the instance + // geometry between shutter open and close, thereby defeating the + // point of the hair-at-a-time approach, which is to keep memory + // overhead low; or we would have to switch frames for every + // instance which would be horribly slow. +// if (isInstanced) fDoMotionBlur = false; + + theShaveNode->makeCurrent(); + outputObjSpecificDecls(rfm, isInstanced); + + if (fDoMotionBlur) + { + // + // If motion blur is on, and we have multiple shaveHairShapes, + // then the previous shaveHairShape will have left us at the + // shutterClose time, in which case we need to move back to + // shutterOpen. + // + if (sn > 0) shaveUtil::setFrame((float)fShutterOpen); + + // + // Generate this shaveHairShape's texture info. + // + // (When motion blur is off, texture info for all of the nodes + // is generated at once, back near the start of this method.) + // + MObjectArray shaveHairShapeAsAnArray; + + shaveHairShapeAsAnArray.append(shaveHairShapes[sn]); +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapeAsAnArray, fUVSet, g.verbose); +#else + initTexInfoLookup(shaveHairShapeAsAnArray, fUVSet, g.verbose); +#endif + } + + // + // If this shaveHairShape has RIB text, output it as a verbatim + // record. + // + MPlug plug = nodeFn.findPlug(shaveHairShape::ribInsert); + MString nodePreamble; + + plug.getValue(nodePreamble); + + if (nodePreamble != "") + rfm.RiArchiveRecord(*rfm.RI_VERBATIM, "%s\n", nodePreamble.asChar()); + + bool tipFade = false; + plug = nodeFn.findPlug(shaveHairShape::aTipFade); + plug.getValue(tipFade); + + SHAVENODE* hairNode = theShaveNode->getHairNode(); + int hairGroup = theShaveNode->getHairGroup(); + int numHairs = hairNode->shavep.haircount[hairGroup]; + int numPasses = hairNode->shavep.passes[hairGroup]; + int numSegs = hairNode->shavep.segs[hairGroup]; + int pass; + WFTYPE wf; + + init_geomWF(&wf); + + // + // If we're generating the hair as curves, we need to figure out + // how big our arrays have to be. + // + CURVEINFO curveInfo; + MObject instanceMesh; + unsigned int totalCurves = 0; + unsigned int totalCVs = 0; + + if (isInstanced) + { + // + // Get the original instance mesh. + // + plug = nodeFn.findPlug(shaveHairShape::instanceMesh); + plug.getValue(instanceMesh); + } + else + { +// for (pass = 0; pass < numPasses; pass++) + pass=0; + { + int hair; + + for (hair = 0; hair < numHairs*(numPasses+1); hair++) + { + // For speed, we call SHAVEmake_a_curveROOT rather than + // SHAVEmake_a_curve. Note, however, that this means + // our counts will include some curves which may + // eventually be killed. + SHAVEmake_a_curveROOT( + pass, hairGroup, hair, &wf, &curveInfo + ); + + // + // Each face is effectively a separate hair. + // + totalCurves += wf.totalfaces; + } + } + + // + // If we ended up with no curves, then skip this shaveHairShape. + // + if (totalCurves == 0) continue; + + totalCVs = totalCurves * (numSegs + 1); + float np=(float)numPasses; +// np=(np)/2; + if (np<1.0f) np=1.0f; + float opacity = 1.0f / np; + opacity=sqrt(opacity); +//opacity*=opacity; + + initBuffers( + rfm, nodeFn, totalCurves, totalCVs, totalCVs, opacity, false + ); + } + + const unsigned int numBlurPasses = (fDoMotionBlur ? 2 : 1); + + for (blurPass = 0; blurPass < numBlurPasses; blurPass++) + { + // + // If this is the second pass for motion blur, move to the + // shutter close time. + // + if (blurPass == 1) + { + shaveUtil::setFrame((float)fShutterClose); + + // + // Changing time causes all shaveHairShapes to reload + // themselves, which means that whichever shaveHairShape was + // last is the one which will now be loaded into Shave. So + // let's make sure that *this* shaveHairShape is the one that's + // loaded. + // + theShaveNode->makeCurrent(); + } + + // + // At this point, curves and polys must part company as they are + // handled quite differently. + // + if (isInstanced) + { + int pass=0; + int np; + np=numHairs; + if (np<1) np=1; +// np*=numPasses; + // + // Generate each instance as a poly. + // + pass=0; +// for (pass = 0; pass < numPasses; pass++) + { + int hair; + int ff; + + VERT *snorm; + + SHAVEmake_a_hair(pass, hairGroup, 1, 14, &wf); +if (numPasses<=0) numPasses=1; +if (wf.totalverts>0) + snorm=(VERT *) malloc((np*numPasses+1)*wf.totalverts*sizeof(VERT)); +ff=wf.totalverts; +free_geomWF(&wf); + for (hair = 0; hair < np*numPasses; hair++) + { + +/// SHAVEmake_a_curve( +/// pass, hairGroup, hair, 1, &wf, &curveInfo +// ); + + SHAVEmake_a_curveROOT( + pass, hairGroup, hair, &wf, &curveInfo + ); + +// if (curveInfo.killme) continue; +//if (wf.totalfaces>0) +// SHAVEmake_a_hair(pass, hairGroup, hair, numSegs, &wf); +int qq; +for (qq=0;qq<wf.totalverts;qq++) + snorm[qq+ff*hair]=curveInfo.norm; +free_geomWF(&wf); + } +int list_total=0; +int c; +int *inlist; +inlist=(int *) malloc (np*numPasses*sizeof(int)); +// list_total=0; + for (c=0;c<np*numPasses;c++) + { + inlist[c]=c; + list_total++; + } + + MTbunch_of_hairs(list_total, inlist,hairGroup,0, &wf,&curveInfo); +free(inlist); + + // + // Note that currently motion blur is not supported + // when doing geometry output in non-voxel mode, + // because the overhead is simply too great. + // + // So the second arg in the call below is + // meaningless. Which is a good thing because + // otherwise we would end up generating a whole + // bunch of RiMotionBegin's followed by a bunch of + // RiMotionEnd's, rather than having them be + // properly paired up. + // + if (wf.totalfaces>0) + outputInstanceGeom( + rfm, + nodeFn, + (blurPass == 0), + instanceMesh, + wf.totalfaces, + wf.totalverts, + wf.totalfverts, + wf.facelist, + wf.face_start, + wf.face_end, + wf.v, + wf.color, + wf.vn, + snorm, + curveInfo.hairID + ); +if (wf.totalfaces>0) + free(snorm); +if (wf.totalfaces>0) +free_geomWF(&wf); + } + } + else + { + // + // Clear out any curve data from previous passes. + // + clearCurves(); + + // Now we get to find out how many curves we *really* + // have. + totalCurves = 0; + + // + // Fill in the arrays for the hairs. + // + pass=0; +// for (pass = 0; pass < numPasses; pass++) + { +// int hair; + int nh; + int b=0,c=0; + int chunk=0; + int np; + np=numPasses; + if (np<1) np=1; + nh=numHairs*np;//*(np); + +// for (hair = 0; hair < numHairs; hair++) + while (b<nh) + { + int inlist[1001]; + int list_total=0; + chunk=1; + if (nh>1000) + { + chunk=1000; + } + if (chunk>nh-b) + chunk=nh-b; + +// SHAVEmake_a_curve( +// pass, hairGroup, hair, numSegs, &wf, &curveInfo +// ); + list_total=0; + for (c=b;c<b+chunk;c++) + { + inlist[list_total]=c; + list_total++; + } + + MTbunch_of_hairs(list_total, inlist,hairGroup,pass, &wf,&curveInfo); + + int face; + + for (face = 0; face < wf.totalfaces; face++) + { + // + // If this is spline hair, then the first two + // weights in the barycentric coords in + // curveInfo are the UV coords we want. + // + float u; + float v; + +// if (theShaveNode->getHairGroup() == 4) +// { + u = wf.uv[wf.facelist[wf.face_start[face]]].x; + v = wf.uv[wf.facelist[wf.face_start[face]]].y; +// u = curveInfo.wgt[0]; +// v = curveInfo.wgt[1]; +// } +// else +// { +// u = curveInfo.u; +// v = curveInfo.v; +// } + + addCurve( + wf.face_start[face], + wf.face_end[face] - 1, + wf.facelist, + wf.v, + wf.color, + NULL, + NULL, + u, + v, +// curveInfo.baserad, +// curveInfo.tiprad, + wf.r1[face], + wf.r2[face], + &wf.vn[wf.face_start[face]], + tipFade, + wf.index[face] + ); + } + // + // Each face is effectively a separate hair and + // will thus require its own curve. + // + totalCurves += wf.totalfaces; + b+=chunk; + free_geomWF(&wf); + + } + } + + outputCurves(rfm, totalCurves, (blurPass == 0)); + } // end of curves vs. polys split + } // end of blur pass loop + + // + // If we were generating instances then restore the motion blur + // setting, otherwise clean up the buffers used to create curves. + // + if (isInstanced) + fDoMotionBlur = savedDoMotionBlur; + else + freeBuffers(false); + + } // end of shaveHairShape loop + + endFile(rfm); + + return st; +} +#endif diff --git a/mayaPlug/shaveWriteRib.h b/mayaPlug/shaveWriteRib.h new file mode 100644 index 0000000..83a54b7 --- /dev/null +++ b/mayaPlug/shaveWriteRib.h @@ -0,0 +1,249 @@ +#ifndef _WRITERIB +#define _WRITERIB +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#ifndef NO_PRMAN +# ifdef _WIN32 + // ri.h contains a lot of float/double mismatches. Let's stop VC++ from warning us about them. +# pragma warning(push) +# pragma warning(disable : 4244 ) +# endif + +# include <ri.h> + +# ifdef _WIN32 +# pragma warning(pop) +# endif +#endif + +#include <vector> + +#include <maya/MPxCommand.h> +#include <maya/MString.h> + +#include "shaveGlobals.h" +#include "shaveSDK.h" + +#if MAYA_API_VERSION < 20180000 +class MArgDatabase; +#endif + +class RfMFuncs; + + +class shaveWriteRib : public MPxCommand +{ +public: + shaveWriteRib(); + virtual ~shaveWriteRib(); + MStatus doIt(const MArgList& args); + static void* createCmd(); + static MSyntax createSyntax(); + + static const MString commandName; + +protected: +#ifndef NO_PRMAN + typedef struct NodeInfo + { + typedef struct + { + RtBound fBBox; + MString fFilename; + } VoxelInfo; + + std::vector<VoxelInfo> fVoxels; + } NodeInfo; + + void addCurve( + int firstVertIdxIdx, + int lastVertIdxIdx, + const int* vertIndices, + const VERT* verts, + const VERT* vertColours, + const VERT* rootColour, + const VERT* tipColour, + float hairU, + float hairV, + float rootRadius, + float tipRadius, + const VERT* hairNormal, + bool tipFade, + int index + ); + + void beginFile( + RfMFuncs &rfm, + MString filename, + MString verbatim + ) const; + + void cleanup( + const MObjectArray& hairNodes, + const shaveGlobals::Globals& g + ) const; + + void clearCurves(); + void endFile(RfMFuncs &rfm) const; + void freeBuffers(bool isInstanced); + MString getBaseFilename() const; + + void getFrameTimes( + const MArgDatabase& argdb, + const shaveGlobals::Globals& g, + double curFrame + ); + + MStatus getHairShapes( + const MArgDatabase& argdb, + MObjectArray& hairShapes + ) const; + + void getMayaShutterTimes(); + int getNumParams(bool isInstanced) const; + void getRendermanShutterTimes(); + + MString getVoxelFilename( + const MString& baseFilename, + const MString& nodeName, + int voxel + ) const; + + void initBuffers( + RfMFuncs &rfm, + MFnDependencyNode &nodeFn, + int numHairs, + int numVerts, + int numVertIndices, + float baseOpacity, + bool isInstanced + ); + + void outputCurves( + RfMFuncs &rfm, + int numCurves, + bool isShutterOpen + ); + + void outputInstanceGeom( + RfMFuncs &rfm, + MFnDependencyNode& nodeFn, + bool isShutterOpen, + MObject instanceMesh, + int numHairs, + int numVerts, + int numFaceVerts, + const int* faceList, + const int* faceStarts, + const int* faceEnds, + const VERT* verts, + const VERT* vertColors, + const VERT* vertNormals, + const VERT* growthSurfNormal, + int index + + ); + + void outputGlobalDecls(RfMFuncs &rfm) const; + void outputMotionBegin(RfMFuncs &rfm) const; + + void outputObjSpecificDecls( + RfMFuncs &rfm, bool isInstanced + ) const; + + void showHelp() const; + + MStatus writeMultipleRibs( + RfMFuncs& rfm, + MObjectArray shaveNodes, + const shaveGlobals::Globals& g + ); + + MStatus writeRib( + RfMFuncs& rfm, + const MObjectArray& shaveNodes, + const shaveGlobals::Globals& g + ); + + MStatus writeSingleRib( + RfMFuncs& rfm, + MObjectArray shaveNodes, + const shaveGlobals::Globals& g + ); +#endif + + // + // Command flags. + // + bool fDoBinary; + bool fDoFullPaths; + bool fDoGzip; + bool fDoMotionBlur; + bool fDoNormals; + bool fDoOpacities; + bool fRestoreFrame; + bool fDoRootPositions; + bool fDoRootTipColors; + bool fDoSurfaceNormals; + bool fDoUVs; + bool fDoVertexColors; + bool fDoVoxels; + bool fDoWCoords; + +#ifndef NO_PRMAN + // + // Buffers. + // + RtFloat fAmbDiff; + RtFloat fBaseOpacity; + RtFloat fGloss; + RtNormal* fNormal; + RtColor* fOpacity; + RtPointer* fParams; + RtColor* fRootColor; + RtPoint* fRootPosition; + RtFloat* fSCoord; + RtFloat fSelfShadow; + RtFloat fSpecular; + RtInt fSquirrel; + RtColor fSpecularColor; + RtColor fSpecularColor2; + RtNormal* fSurfaceNormal; + RtFloat* fTCoord; + RtColor* fTipColor; + RtToken* fTokens; + RtPoint* fVert; + RtColor* fVertexColor; + RtInt* fVertIndices; + RtInt* fVertsPerHair; + RtFloat* fWCoord; + RtFloat* fWidth; + RtInt* fIndex; + + // + // Other + // + int fCurveIdx; + MString fFilename; + double fFrameCenter; + bool fFrameRelativeTime; + double fShutterOpen; + double fShutterClose; + +float openoffset,closeoffset; + + double fTimeFactor; + shaveGlobals::RibTimeUnits fTimeUnits; + bool fUseCubicCurves; + MString fUVSet; + int fVertIdx; + int fWidthIdx; +#endif +}; + +#endif + diff --git a/mayaPlug/shaveXPM.cpp b/mayaPlug/shaveXPM.cpp new file mode 100644 index 0000000..b5ae555 --- /dev/null +++ b/mayaPlug/shaveXPM.cpp @@ -0,0 +1,430 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MColor.h> +#include <maya/MColorArray.h> +#include <maya/MIntArray.h> +#include <maya/MString.h> +#include <maya/MStringArray.h> + +#include <math.h> +#include <string.h> + +#include "shaveIO.h" +#include "shaveXPM.h" + +shaveXPM::Node* shaveXPM::Node::head = NULL; +unsigned long shaveXPM::Node::newMinError; +unsigned long shaveXPM::Node::nextIndex = 0; +unsigned long shaveXPM::Node::numLeafNodes = 0; + + +shaveXPM::Node::Node( + unsigned char irmin, unsigned char igmin, unsigned char ibmin, + unsigned char irmax, unsigned char igmax, unsigned char ibmax +) +: numPixels(0) +, numLeafPixels(0) +, sumLeafR(0) +, sumLeafG(0) +, sumLeafB(0) +, quantError(0) +, rmax(irmax) +, rmid((irmax + irmin)/2) +, rmin(irmin) +, gmax(igmax) +, gmid((igmax + igmin)/2) +, gmin(igmin) +, bmax(ibmax) +, bmid((ibmax + ibmin)/2) +, bmin(ibmin) +{ + int i; + + for (i = 0; i < 8; i++) + child[i] = NULL; +} + + +void shaveXPM::Node::init() +{ + numLeafNodes = 0; + head = new Node(0, 0, 0, 255, 255, 255); +} + + +void shaveXPM::Node::cleanup() +{ + if (head) + { + head->doCleanup(); + + delete head; + head = NULL; + } +} + + +void shaveXPM::Node::doCleanup() +{ + unsigned int i; + + for (i = 0; i < 8; i++) + { + if (child[i]) + { + child[i]->doCleanup(); + delete child[i]; + child[i] = NULL; + } + } +} + + +void shaveXPM::Node::addPixel(unsigned char r, unsigned char g, unsigned char b) +{ + head->doAddPixel(r, g, b, 0); +} + + +void shaveXPM::Node::doAddPixel( + unsigned char r, unsigned char g, unsigned char b, unsigned short level +) +{ + numPixels++; + + if (level == 7) + { + if (numLeafPixels == 0) numLeafNodes++; + + numLeafPixels++; + sumLeafR += r; + sumLeafG += g; + sumLeafB += b; + } + else + { + // + // This node's colour space can be divided into 8 sub-spaces by + // cutting along the midpoint of R, G and B. + // + // The pixel should go into one of those 8 sub-spaces, so let's + // determine which one it falls into. + // + int i = 0; + + if (r > rmid) i += 1; + if (g > gmid) i += 2; + if (b > bmid) i += 4; + + // + // If we haven't yet created a node for that sub-space, do so now. + // + if (child[i] == NULL) + { + unsigned char crmin = rmin; + unsigned char crmax = rmid; + unsigned char cgmin = gmin; + unsigned char cgmax = gmid; + unsigned char cbmin = bmin; + unsigned char cbmax = bmid; + + if (r > rmid) + { + crmin = rmid + 1; + crmax = rmax; + } + + if (g > rmid) + { + cgmin = gmid + 1; + cgmax = gmax; + } + + if (b > bmid) + { + cbmin = bmid + 1; + cbmax = bmax; + } + + child[i] = new Node(crmin, cgmin, cbmin, crmax, cgmax, cbmax); + } + + child[i]->doAddPixel(r, g, b, level+1); + } + + // + // Add this pixel's quantizing error to the overall error for this + // node. + // + int rd = (int)r - (int)rmid; + int gd = (int)g - (int)gmid; + int bd = (int)b - (int)bmid; + + quantError += (unsigned long)(rd*rd + gd*gd + bd*bd); +} + + +unsigned long shaveXPM::Node::prune(unsigned long minError) +{ + newMinError = 0; + head->doPrune(minError, false); + + return newMinError; +} + + +bool shaveXPM::Node::doPrune(unsigned long minError, bool force) +{ + unsigned int i; + + // + // If our quantizing error is within that being pruned, then force all + // of our children to be pruned as well, since it is possible that they + // have higher errors and would not otherwise be pruned. + // + if (quantError <= minError) force = true; + + for (i = 0; i < 8; i++) + { + if (child[i] && child[i]->doPrune(minError, force)) + { + // + // The child is being pruned, so add its pixels to our own and + // delete the child. + // + // Note that moving pixels into this node makes it a leaf node. + // If it was not already a leaf node, then we must increment + // the leaf node count. + // + if (numLeafPixels == 0) numLeafNodes++; + + numLeafPixels += child[i]->numLeafPixels; + sumLeafR += child[i]->sumLeafR; + sumLeafG += child[i]->sumLeafG; + sumLeafB += child[i]->sumLeafB; + + delete child[i]; + + child[i] = NULL; + + // + // Decrement the leaf node count to take account of the child + // we just deleted. + // + numLeafNodes--; + } + } + + if (force) return true; + + if ((newMinError == 0) || (quantError < newMinError)) + newMinError = quantError; + + return false; +} + + +void shaveXPM::Node::getColours(struct Colour* colours) +{ + nextIndex = 0; + head->doGetColours(colours); +} + + +void shaveXPM::Node::doGetColours(struct Colour* colours) +{ + if (numLeafPixels > 0) + { + index = nextIndex++; + + unsigned int round = numLeafPixels / 2; + + colours[index].r = (unsigned char)((sumLeafR + round) / numLeafPixels); + colours[index].g = (unsigned char)((sumLeafG + round) / numLeafPixels); + colours[index].b = (unsigned char)((sumLeafB + round) / numLeafPixels); + } + + unsigned int i; + + for (i= 0; i < 8; i++) + if (child[i]) child[i]->doGetColours(colours); +} + + +unsigned long shaveXPM::Node::getIndex( + unsigned char r, unsigned char g, unsigned char b +) +{ + return head->doGetIndex(r, g, b); +} + + +unsigned long shaveXPM::Node::doGetIndex( + unsigned char r, unsigned char g, unsigned char b +) +{ + int i = 0; + + if (r > rmid) i += 1; + if (g > gmid) i += 2; + if (b > bmid) i += 4; + + if (child[i] == NULL) return index; + + return child[i]->doGetIndex(r, g, b); +} + + +MString shaveXPM::toHex(unsigned char val) +{ + static char* hexChars = "0123456789abcdef"; + static char hexStr[3]; + + hexStr[0] = hexChars[val >> 4]; + hexStr[1] = hexChars[val & 0x0f]; + hexStr[2] = '\0'; + + return MString(hexStr); +} + + +bool shaveXPM::write( + int width, int height, const unsigned char* rgba, const MString& filename +) +{ + ofstream f(filename.asChar()); + + if (!f.good()) return false; + + // + // Which chars can we use for colour indices in the XPM file? + // + const char* validChars = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "-_=+[]{}';:|/?.,>< `~!@#$%^&*()"; + unsigned int numValidChars = (unsigned int)strlen(validChars); + + // + // Sort the pixels in the image into an 8-way colour tree. + // + int numPixels = width * height; + int numPixelElements = numPixels * 4; + int i; + + Node::init(); + + for (i = 0; i < numPixelElements; i += 4) + { + Node::addPixel(rgba[i], rgba[i+1], rgba[i+2]); + } + + // + // The XPM funcs that Maya uses on Windows don't allow more than 2 + // chars per colour index. So that marks the limit on the number of + // colours we can use. + // + unsigned long maxColours = numValidChars * numValidChars; + unsigned long minError = 0; + + // + // Iteratively prune the colour tree until we're under our limit. + // + while (Node::numColours() > maxColours) + { + minError = Node::prune(minError); + } + + unsigned int numColours = Node::numColours(); + + struct Colour* colours = new struct Colour[numColours]; + + Node::getColours(colours); + + // + // Get colour indices for all of the pixels in the image. + // + unsigned int* indices = new unsigned int[numPixels]; + + for (i = 0; i < numPixelElements; i += 4) + { + indices[i >> 2] = Node::getIndex(rgba[i], rgba[i+1], rgba[i+2]); + } + + Node::cleanup(); + + // + // Output XPM header info. + // + // Note that on Windows, Maya will reject an XPM file if the opening + // brace is not on the same line as the 'static char' declaration. I + // kid you not. + // + f << "/* XPM */" << endl; + f << "static char* swatch[] = {" << endl; + + f << "/* width height ncolours charsPerPixel */" << endl; + f << "\"" << width << " " << height << " " << numColours << " 2\"," << endl; + + // + // Generate and output the colour database. That is, the association + // between character sequences and the colours they will represent in + // the 'pixels' section. + // + MStringArray colourCodes; + char code[3]; + f << "/* colours */" << endl; + + for (i = 0; i < (int)numColours; i++) + { + unsigned int index = i; + + code[0] = validChars[index / numValidChars]; + code[1] = validChars[index % numValidChars]; + code[2] = '\0'; + colourCodes.append(code); + +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + f << "\"" << code << " c #" << toHex(colours[i].r).asChar() + << toHex(colours[i].g).asChar() << toHex(colours[i].b).asChar() + << "\"," << endl; +#else + f << "\"" << code << " c #" << toHex(colours[i].r) + << toHex(colours[i].g) << toHex(colours[i].b) << "\"," << endl; +#endif + } + + delete [] colours; + + // + // Output the pixel data. Note that image data starts at the *bottom* + // of the image, but XPM format starts at the top. So we have to flip + // things around. + // + int col; + + f << "/* pixels */" << endl; + + for (i = numPixels - width; i >= 0; i -= width) + { + f << "\""; + + for (col = 0; col < width; col++) { +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + f << colourCodes[indices[i+col]].asChar(); +#else + f << colourCodes[indices[i+col]]; +#endif + } + + f << "\"," << endl; + } + + f << "};" << endl; + + f.close(); + + return true; +} diff --git a/mayaPlug/shaveXPM.h b/mayaPlug/shaveXPM.h new file mode 100644 index 0000000..d983942 --- /dev/null +++ b/mayaPlug/shaveXPM.h @@ -0,0 +1,111 @@ +#ifndef shaveXPM_h +#define shaveXPM_h +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include <maya/MTypes.h> + +#if MAYA_API_VERSION < 20180000 +class MString; +#endif + +class shaveXPM +{ +public: + struct Colour + { + unsigned char r; + unsigned char g; + unsigned char b; + }; + + static bool write( + int width, + int height, + const unsigned char* rgba, + const MString& filename + ); + + static MString toHex(unsigned char val); + +protected: + class Node + { + public: + Node( + unsigned char irmin, unsigned char igmin, + unsigned char ibmin, unsigned char irmax, + unsigned char igmax, unsigned char ibmax + ); + + static void addPixel( + unsigned char r, + unsigned char g, + unsigned char b + ); + + static void cleanup(); + + static void getColours(struct Colour* colours); + + static unsigned long getIndex( + unsigned char r, + unsigned char g, + unsigned char b + ); + + static void init(); + + static unsigned long numColours(); + + static unsigned long prune(unsigned long minError); + + protected: + void doAddPixel( + unsigned char r, + unsigned char g, + unsigned char b, + unsigned short level + ); + + void doCleanup(); + void doGetColours(struct Colour* colours); + + unsigned long doGetIndex( + unsigned char r, + unsigned char g, + unsigned char b + ); + + bool doPrune(unsigned long minError, bool force); + + unsigned long index; + unsigned long numPixels; + unsigned long numLeafPixels; + unsigned long sumLeafR; + unsigned long sumLeafG; + unsigned long sumLeafB; + unsigned long quantError; + unsigned char rmax; + unsigned char rmid; + unsigned char rmin; + unsigned char gmax; + unsigned char gmid; + unsigned char gmin; + unsigned char bmax; + unsigned char bmid; + unsigned char bmin; + Node* child[8]; + + static Node* head; + static unsigned long newMinError; + static unsigned long nextIndex; + static unsigned long numLeafNodes; + }; +}; + +inline unsigned long shaveXPM::Node::numColours() +{ return numLeafNodes; } + +#endif |