@@ -711,6 +711,30 @@ static bool shader_filter_from_file_changed(obs_properties_t *props,
711
711
return true;
712
712
}
713
713
714
+ static bool shader_filter_text_changed (obs_properties_t * props ,
715
+ obs_property_t * p , obs_data_t * settings )
716
+ {
717
+ UNUSED_PARAMETER (p );
718
+ struct shader_filter_data * filter = obs_properties_get_param (props );
719
+ if (!filter )
720
+ return false;
721
+
722
+ const char * shader_text = obs_data_get_string (settings , "shader_text" );
723
+ bool can_convert = strstr (shader_text , "void mainImage( out vec4" ) ||
724
+ strstr (shader_text , "void mainImage(out vec4" ) ||
725
+ strstr (shader_text , "void main()" );
726
+ obs_property_t * shader_convert =
727
+ obs_properties_get (props , "shader_convert" );
728
+ bool visible =
729
+ obs_property_visible (obs_properties_get (props , "shader_text" ));
730
+ if (obs_property_visible (shader_convert ) != (can_convert && visible )) {
731
+ obs_property_set_visible (shader_convert ,
732
+ can_convert && visible );
733
+ return true;
734
+ }
735
+ return false;
736
+ }
737
+
714
738
static bool shader_filter_file_name_changed (obs_properties_t * props ,
715
739
obs_property_t * p ,
716
740
obs_data_t * settings )
@@ -719,8 +743,9 @@ static bool shader_filter_file_name_changed(obs_properties_t *props,
719
743
const char * new_file_name =
720
744
obs_data_get_string (settings , obs_property_name (p ));
721
745
722
- if (dstr_is_empty (& filter -> last_path ) ||
723
- dstr_cmp (& filter -> last_path , new_file_name ) != 0 ) {
746
+ if ((dstr_is_empty (& filter -> last_path ) && strlen (new_file_name )) ||
747
+ (filter -> last_path .array &&
748
+ dstr_cmp (& filter -> last_path , new_file_name ) != 0 )) {
724
749
filter -> reload_effect = true;
725
750
dstr_copy (& filter -> last_path , new_file_name );
726
751
size_t l = strlen (new_file_name );
@@ -782,6 +807,305 @@ static bool add_source_to_list(void *data, obs_source_t *source)
782
807
return true;
783
808
}
784
809
810
+ static void convert_float_init (struct dstr * effect_text , char * name , int count )
811
+ {
812
+ const size_t len = strlen (name );
813
+
814
+ char * pos = strstr (effect_text -> array , name );
815
+ while (pos ) {
816
+ size_t diff = pos - effect_text -> array ;
817
+ char * begin = strstr (pos + len , "(" );
818
+ char * end = strstr (pos + len , ")" );
819
+ char * comma = strstr (pos + len , "," );
820
+ if (end && (!begin || end < begin ) && (!comma || end < comma )) {
821
+ bool only_numbers = true;
822
+ for (char * ch = pos + len ; ch < end ; ch ++ ) {
823
+ if ((* ch < '0' || * ch > '9' ) && * ch != '.' &&
824
+ * ch != ' ' ) {
825
+ only_numbers = false;
826
+ break ;
827
+ }
828
+ }
829
+ if (only_numbers ) {
830
+ //only 1 simple arg in the float4
831
+ struct dstr found = {0 };
832
+ dstr_init (& found );
833
+ dstr_ncat (& found , pos , end - pos + 1 );
834
+
835
+ struct dstr replacement = {0 };
836
+ dstr_init_copy (& replacement , name );
837
+ dstr_ncat (& replacement , pos + len ,
838
+ end - (pos + len ));
839
+ for (int i = 1 ; i < count ; i ++ ) {
840
+ dstr_cat (& replacement , "," );
841
+ dstr_ncat (& replacement , pos + len ,
842
+ end - (pos + len ));
843
+ }
844
+ dstr_cat (& replacement , ")" );
845
+
846
+ dstr_replace (effect_text , found .array ,
847
+ replacement .array );
848
+
849
+ dstr_free (& replacement );
850
+ dstr_free (& found );
851
+ }
852
+ }
853
+ pos = strstr (effect_text -> array + diff + len , name );
854
+ }
855
+ }
856
+
857
+ static bool shader_filter_convert (obs_properties_t * props ,
858
+ obs_property_t * property , void * data )
859
+ {
860
+ UNUSED_PARAMETER (props );
861
+ if (!data )
862
+ return false;
863
+ struct shader_filter_data * filter = data ;
864
+ obs_data_t * settings = obs_source_get_settings (filter -> context );
865
+ if (!settings )
866
+ return false;
867
+ struct dstr effect_text = {0 };
868
+ dstr_init_copy (& effect_text ,
869
+ obs_data_get_string (settings , "shader_text" ));
870
+
871
+ size_t start_diff = 24 ;
872
+ bool main_no_args = false;
873
+ char * main_pos = strstr (effect_text .array , "void mainImage(out vec4" );
874
+ if (!main_pos ) {
875
+ main_pos =
876
+ strstr (effect_text .array , "void mainImage( out vec4" );
877
+ start_diff ++ ;
878
+ }
879
+ if (!main_pos ) {
880
+ main_pos = strstr (effect_text .array , "void main()" );
881
+ if (main_pos )
882
+ main_no_args = true;
883
+ }
884
+ if (!main_pos ) {
885
+ dstr_free (& effect_text );
886
+ obs_data_release (settings );
887
+ return false;
888
+ }
889
+ bool uv = false;
890
+ struct dstr return_color_name = {0 };
891
+ struct dstr coord_name = {0 };
892
+ if (main_no_args ) {
893
+
894
+ dstr_replace (& effect_text , "void main()" ,
895
+ "float4 mainImage(VertData v_in) : TARGET" );
896
+
897
+ if (strstr (effect_text .array , "fNormal" )) {
898
+ uv = true;
899
+ dstr_init_copy (& coord_name , "fNormal" );
900
+ } else {
901
+ uv = false;
902
+ dstr_init_copy (& coord_name , "gl_FragCoord" );
903
+ }
904
+
905
+ char * out_start = strstr (effect_text .array , "out vec4" );
906
+ if (out_start ) {
907
+ char * start = out_start + 9 ;
908
+ while (* start == ' ' )
909
+ start ++ ;
910
+ char * end = start ;
911
+ while (* end != ' ' && * end != ',' && * end != '(' &&
912
+ * end != ')' && * end != ';' && * end != 0 )
913
+ end ++ ;
914
+ dstr_ncat (& return_color_name , start , end - start );
915
+ while (* end == ' ' )
916
+ end ++ ;
917
+ if (* end == ';' )
918
+ dstr_remove (& effect_text ,
919
+ out_start - effect_text .array ,
920
+ (end + 1 ) - out_start );
921
+ } else {
922
+ dstr_init_copy (& return_color_name , "gl_FragColor" );
923
+ }
924
+ } else {
925
+
926
+ char * start = main_pos + start_diff ;
927
+ while (* start == ' ' )
928
+ start ++ ;
929
+ char * end = start ;
930
+ while (* end != ' ' && * end != ',' && * end != ')' && * end != 0 )
931
+ end ++ ;
932
+
933
+ dstr_ncat (& return_color_name , start , end - start );
934
+
935
+ start = strstr (end , "," );
936
+ if (!start ) {
937
+ dstr_free (& effect_text );
938
+ dstr_free (& return_color_name );
939
+ obs_data_release (settings );
940
+ return false;
941
+ }
942
+ start ++ ;
943
+ while (* start == ' ' )
944
+ start ++ ;
945
+ if (* start == 'i' && * (start + 1 ) == 'n' && * (start + 2 ) == ' ' )
946
+ start += 3 ;
947
+ while (* start == ' ' )
948
+ start ++ ;
949
+ if (* start == 'v' && * (start + 1 ) == 'e' &&
950
+ * (start + 2 ) == 'c' && * (start + 3 ) == '2' &&
951
+ * (start + 4 ) == ' ' )
952
+ start += 5 ;
953
+ while (* start == ' ' )
954
+ start ++ ;
955
+
956
+ end = start ;
957
+ while (* end != ' ' && * end != ',' && * end != ')' && * end != 0 )
958
+ end ++ ;
959
+
960
+ dstr_ncat (& coord_name , start , end - start );
961
+
962
+ while (* end != ')' && * end != 0 )
963
+ end ++ ;
964
+ size_t idx = main_pos - effect_text .array ;
965
+ dstr_remove (& effect_text , idx , end - main_pos + 1 );
966
+ dstr_insert (& effect_text , idx ,
967
+ "float4 mainImage(VertData v_in) : TARGET" );
968
+ }
969
+
970
+ if (dstr_cmp (& coord_name , "fragCoord" ) != 0 ) {
971
+ dstr_replace (& effect_text , coord_name .array , "fragCoord" );
972
+ }
973
+ dstr_free (& coord_name );
974
+
975
+ dstr_replace (& effect_text , "varying vec3" , "//varying vec3" );
976
+ dstr_replace (& effect_text , "precision highp float;" ,
977
+ "//precision highp float;" );
978
+ if (uv ) {
979
+ dstr_replace (& effect_text , "fragCoord.xy" , "v_in.uv" );
980
+ dstr_replace (& effect_text , "fragCoord" , "float3(v_in.uv,0.0)" );
981
+ } else {
982
+
983
+ dstr_replace (& effect_text , "fragCoord.xy / iResolution.xy" ,
984
+ "v_in.uv" );
985
+ dstr_replace (& effect_text , "fragCoord / iResolution.xy" ,
986
+ "v_in.uv" );
987
+ dstr_replace (& effect_text , "fragCoord" , "(v_in.uv * uv_size)" );
988
+ }
989
+ dstr_replace (& effect_text , "u_resolution" , "uv_size" );
990
+ dstr_replace (& effect_text , "uResolution" , "uv_size" );
991
+ dstr_replace (& effect_text , "iResolution.xy" , "uv_size" );
992
+ dstr_replace (& effect_text , "iResolution.x" , "uv_size.x" );
993
+ dstr_replace (& effect_text , "iResolution.y" , "uv_size.y" );
994
+ dstr_replace (& effect_text , "iResolution" ,
995
+ "float4(uv_size,uv_pixel_interval)" );
996
+
997
+ dstr_replace (& effect_text , "uniform vec2 uv_size;" , "" );
998
+
999
+ if (strstr (effect_text .array , "iTime" ))
1000
+ dstr_replace (& effect_text , "iTime" , "elapsed_time" );
1001
+ else if (strstr (effect_text .array , "uTime" ))
1002
+ dstr_replace (& effect_text , "uTime" , "elapsed_time" );
1003
+ else if (strstr (effect_text .array , "u_time" ))
1004
+ dstr_replace (& effect_text , "u_time" , "elapsed_time" );
1005
+ else
1006
+ dstr_replace (& effect_text , "time" , "elapsed_time" );
1007
+
1008
+ dstr_replace (& effect_text , "uniform float elapsed_time;" , "" );
1009
+
1010
+ dstr_replace (& effect_text , "vec4" , "float4" );
1011
+ dstr_replace (& effect_text , "vec3" , "float3" );
1012
+
1013
+ dstr_replace (& effect_text , "vec2" , "float2" );
1014
+
1015
+ convert_float_init (& effect_text , "float2(" , 2 );
1016
+ convert_float_init (& effect_text , "float3(" , 3 );
1017
+ convert_float_init (& effect_text , "float4(" , 4 );
1018
+
1019
+ dstr_cat (& return_color_name , "=" );
1020
+ dstr_replace (& effect_text , return_color_name .array , "return " );
1021
+ dstr_replace (& return_color_name , "=" , " =" );
1022
+ dstr_replace (& effect_text , return_color_name .array , "return " );
1023
+
1024
+ dstr_free (& return_color_name );
1025
+
1026
+ dstr_replace (& effect_text , "#version " , "//#version " );
1027
+
1028
+ struct dstr insert_text = {0 };
1029
+ dstr_init_copy (& insert_text , "#ifndef OPENGL\n" );
1030
+
1031
+ if (dstr_find (& effect_text , "mat2" ))
1032
+ dstr_cat (& insert_text , "#define mat2 float2x2\n" );
1033
+ if (dstr_find (& effect_text , "mat3" ))
1034
+ dstr_cat (& insert_text , "#define mat3 float3x3\n" );
1035
+ if (dstr_find (& effect_text , "mat3" ))
1036
+ dstr_cat (& insert_text , "#define mat4 float4x4\n" );
1037
+ if (dstr_find (& effect_text , "fract(" ))
1038
+ dstr_cat (& insert_text , "#define fract frac\n" );
1039
+ if (dstr_find (& effect_text , "mix(" ))
1040
+ dstr_cat (& insert_text , "#define mix lerp\n" );
1041
+ if (dstr_find (& effect_text , "mod(" ))
1042
+ dstr_cat (& insert_text , "float mod(float x, float y)\n\
1043
+ {\n\
1044
+ return x - y * floor(x / y);\n\
1045
+ }\n" );
1046
+ dstr_cat (& insert_text , "#endif\n" );
1047
+
1048
+ int num_textures = 0 ;
1049
+ struct dstr texture_name = {0 };
1050
+ struct dstr replacing = {0 };
1051
+ struct dstr replacement = {0 };
1052
+
1053
+ char * texture = strstr (effect_text .array , "texture(" );
1054
+ while (texture ) {
1055
+ const size_t diff = texture - effect_text .array ;
1056
+ char * start = texture + 8 ;
1057
+ while (* start == ' ' )
1058
+ start ++ ;
1059
+ char * end = start ;
1060
+ while (* end != ' ' && * end != ',' && * end != ')' &&
1061
+ * end != '\n' && * end != 0 )
1062
+ end ++ ;
1063
+ texture_name .len = 0 ;
1064
+ dstr_ncat (& texture_name , start , end - start );
1065
+
1066
+ replacing .len = 0 ;
1067
+ dstr_ncat (& replacing , texture , end - texture );
1068
+
1069
+ replacement .len = 0 ;
1070
+
1071
+ if (num_textures ) {
1072
+ dstr_cat_dstr (& replacement , & texture_name );
1073
+ dstr_cat (& replacement , ".Sample(textureSampler" );
1074
+
1075
+ dstr_insert (& effect_text , diff , texture_name .array );
1076
+ dstr_cat (& insert_text , "uniform texture2d " );
1077
+ dstr_cat (& insert_text , texture_name .array );
1078
+ dstr_cat (& insert_text , ";\n" );
1079
+ } else {
1080
+ dstr_cat (& replacement , "image.Sample(textureSampler" );
1081
+ }
1082
+ dstr_replace (& effect_text , replacing .array , replacement .array );
1083
+ num_textures ++ ;
1084
+
1085
+ texture = strstr (effect_text .array + diff + 8 , "texture(" );
1086
+ }
1087
+ dstr_free (& replacing );
1088
+ dstr_free (& replacement );
1089
+ dstr_free (& texture_name );
1090
+
1091
+ if (insert_text .len > 24 ) {
1092
+ dstr_insert_dstr (& effect_text , 0 , & insert_text );
1093
+ }
1094
+ dstr_free (& insert_text );
1095
+
1096
+ obs_data_set_string (settings , "shader_text" , effect_text .array );
1097
+
1098
+ dstr_free (& effect_text );
1099
+
1100
+ obs_data_release (settings );
1101
+ obs_property_set_visible (property , false);
1102
+
1103
+ filter -> reload_effect = true;
1104
+
1105
+ obs_source_update (filter -> context , NULL );
1106
+ return true;
1107
+ }
1108
+
785
1109
static const char * shader_filter_texture_file_filter =
786
1110
"Textures (*.bmp *.tga *.png *.jpeg *.jpg *.gif);;" ;
787
1111
@@ -825,9 +1149,15 @@ static obs_properties_t *shader_filter_properties(void *data)
825
1149
obs_property_set_modified_callback (from_file ,
826
1150
shader_filter_from_file_changed );
827
1151
828
- obs_properties_add_text (props , "shader_text" ,
829
- obs_module_text ("ShaderFilter.ShaderText" ),
830
- OBS_TEXT_MULTILINE );
1152
+ obs_property_t * shader_text = obs_properties_add_text (
1153
+ props , "shader_text" ,
1154
+ obs_module_text ("ShaderFilter.ShaderText" ), OBS_TEXT_MULTILINE );
1155
+ obs_property_set_modified_callback (shader_text ,
1156
+ shader_filter_text_changed );
1157
+
1158
+ obs_properties_add_button2 (props , "shader_convert" ,
1159
+ obs_module_text ("ShaderFilter.Convert" ),
1160
+ shader_filter_convert , data );
831
1161
832
1162
obs_property_t * file_name = obs_properties_add_path (
833
1163
props , "shader_file_name" ,
0 commit comments