Skip to content

Commit befa930

Browse files
authored
Add FOR macro to GLSL preprocessor and J3MD (#1758)
* Add FOR macro to GLSL preprocessor * Code cleanup * fix comment
1 parent 8f06fec commit befa930

File tree

5 files changed

+219
-5
lines changed

5 files changed

+219
-5
lines changed

jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
import com.jme3.util.blockparser.BlockLanguageParser;
5151
import com.jme3.util.blockparser.Statement;
5252
import com.jme3.util.clone.Cloner;
53+
import jme3tools.shader.Preprocessor;
54+
import java.io.ByteArrayInputStream;
55+
import java.io.ByteArrayOutputStream;
5356
import java.io.IOException;
5457
import java.io.InputStream;
5558
import java.util.*;
@@ -798,6 +801,7 @@ public Object load(AssetInfo info) throws IOException {
798801
} else if (key.getExtension().equals("j3md") && key instanceof MaterialKey) {
799802
throw new IOException("Material definitions must be loaded via AssetKey");
800803
}
804+
in = Preprocessor.apply(in);
801805
loadFromRoot(BlockLanguageParser.parse(in));
802806
} finally {
803807
if (in != null){

jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
import com.jme3.asset.*;
3535
import com.jme3.asset.cache.AssetCache;
36-
36+
import jme3tools.shader.Preprocessor;
3737
import java.io.*;
3838
import java.util.*;
3939

@@ -85,8 +85,9 @@ private ShaderDependencyNode loadNode(Reader reader, String nodeName) {
8585
}
8686

8787
while ((ln = bufferedReader.readLine()) != null) {
88-
if (ln.trim().startsWith("#import ")) {
89-
ln = ln.trim().substring(8).trim();
88+
String tln = ln.trim();
89+
if (tln.startsWith("#import ")) {
90+
ln = tln.substring(8).trim();
9091
if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3) {
9192
// import user code
9293
// remove quotes to get filename
@@ -105,7 +106,7 @@ private ShaderDependencyNode loadNode(Reader reader, String nodeName) {
105106

106107
node.addDependency(sb.length(), dependNode);
107108
}
108-
} else if (ln.trim().startsWith("#extension ")) {
109+
} else if (tln.startsWith("#extension ")) {
109110
sbExt.append(ln).append('\n');
110111
} else {
111112
sb.append(ln).append('\n');
@@ -165,7 +166,9 @@ public Object load(AssetInfo info) throws IOException {
165166
// The input stream provided is for the vertex shader,
166167
// to retrieve the fragment shader, use the content manager
167168
this.assetManager = info.getManager();
168-
Reader reader = new InputStreamReader(info.openStream());
169+
InputStream in = info.openStream();
170+
in = Preprocessor.apply(in);
171+
Reader reader = new InputStreamReader(in);
169172
boolean injectDependencies = true;
170173
if (info.getKey() instanceof ShaderAssetKey) {
171174
injectDependencies = ((ShaderAssetKey) info.getKey()).isInjectDependencies();
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2009-2022 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.shader;
33+
34+
import static org.junit.Assert.assertEquals;
35+
import static org.junit.Assert.assertNotNull;
36+
37+
import java.io.BufferedReader;
38+
import java.io.ByteArrayInputStream;
39+
import java.io.InputStream;
40+
import java.io.InputStreamReader;
41+
42+
import com.jme3.asset.AssetInfo;
43+
import com.jme3.asset.AssetKey;
44+
import com.jme3.system.TestUtil;
45+
46+
import org.junit.Test;
47+
48+
import jme3tools.shader.Preprocessor;
49+
50+
51+
public class GLSLPreprocessorTest {
52+
53+
String readAllAsString(InputStream is) throws Exception{
54+
String output = "";
55+
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
56+
while (true) {
57+
String l = reader.readLine();
58+
if (l == null) break;
59+
if (output != "") output += "\n";
60+
output += l;
61+
}
62+
reader.close();
63+
return output;
64+
}
65+
66+
@Test
67+
public void testFOR() throws Exception{
68+
String source = "#for i=0..2 (#ifdef IS_SET$i $0 #endif)\n" +
69+
" uniform float m_Something$i;\n" +
70+
"#endfor";
71+
String processedSource= readAllAsString(Preprocessor.apply(new ByteArrayInputStream(source.getBytes("UTF-8"))));
72+
73+
AssetInfo testData = TestUtil.createAssetManager().locateAsset(new AssetKey("GLSLPreprocessorTest.testFOR.validOutput"));
74+
assertNotNull(testData);
75+
String sourceCheck=readAllAsString(testData.openStream());
76+
assertEquals(sourceCheck, processedSource);
77+
}
78+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
#ifdef IS_SET0
3+
uniform float m_Something0;
4+
#endif
5+
6+
#ifdef IS_SET1
7+
uniform float m_Something1;
8+
#endif
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright (c) 2009-2022 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package jme3tools.shader;
33+
34+
import java.io.ByteArrayInputStream;
35+
import java.io.ByteArrayOutputStream;
36+
import java.io.IOException;
37+
import java.io.InputStream;
38+
import java.util.regex.Matcher;
39+
import java.util.regex.Pattern;
40+
41+
/**
42+
* GLSL Preprocessor
43+
*
44+
* @author Riccardo Balbo
45+
*/
46+
public class Preprocessor {
47+
48+
public static InputStream apply(InputStream in) throws IOException {
49+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
50+
byte chunk[] = new byte[1024];
51+
int read;
52+
while ((read = in.read(chunk)) != -1) {
53+
bos.write(chunk, 0, read);
54+
}
55+
bos.close();
56+
in.close();
57+
58+
String code = bos.toString("UTF-8");
59+
60+
code = Preprocessor.forMacro(code);
61+
62+
return new ByteArrayInputStream(code.getBytes("UTF-8"));
63+
}
64+
65+
/**
66+
* #for i=0..100 ( #ifdef ENABLE_INPUT_$i $0 #endif )
67+
* do something with $i
68+
* #endfor
69+
*/
70+
private static final Pattern FOR_REGEX = Pattern.compile("([^=]+)=\\s*([0-9]+)\\s*\\.\\.\\s*([0-9]+)\\s*\\((.+)\\)");
71+
72+
public static String forMacro(String code) {
73+
StringBuilder expandedCode = new StringBuilder();
74+
StringBuilder currentFor = null;
75+
String forDec = null;
76+
int skip = 0;
77+
String codel[] = code.split("\n");
78+
boolean captured = false;
79+
for (String l : codel) {
80+
if (!captured) {
81+
String ln = l.trim();
82+
if (ln.startsWith("#for")) {
83+
if (skip == 0) {
84+
forDec = ln;
85+
currentFor = new StringBuilder();
86+
skip++;
87+
continue;
88+
}
89+
skip++;
90+
} else if (ln.startsWith("#endfor")) {
91+
skip--;
92+
if (skip == 0) {
93+
forDec = forDec.substring("#for ".length()).trim();
94+
95+
Matcher matcher = FOR_REGEX.matcher(forDec);
96+
if (matcher.matches()) {
97+
String varN = "$" + matcher.group(1);
98+
int start = Integer.parseInt(matcher.group(2));
99+
int end = Integer.parseInt(matcher.group(3));
100+
String inj = matcher.group(4);
101+
if (inj.trim().isEmpty()) inj = "$0";
102+
String inCode = currentFor.toString();
103+
currentFor = null;
104+
for (int i = start; i < end; i++) {
105+
expandedCode.append("\n").append(inj.replace("$0", "\n" + inCode ).replace(varN, "" + i)).append("\n");
106+
}
107+
captured = true;
108+
continue;
109+
}
110+
}
111+
}
112+
}
113+
if (currentFor != null) currentFor.append(l).append("\n");
114+
else expandedCode.append(l).append("\n");
115+
}
116+
code = expandedCode.toString();
117+
if (captured) code = forMacro(code);
118+
return code;
119+
}
120+
121+
}

0 commit comments

Comments
 (0)