Skip to content

Commit d3d00f3

Browse files
committed
Add FOR macro to GLSL preprocessor
1 parent 78bfa81 commit d3d00f3

File tree

5 files changed

+228
-4
lines changed

5 files changed

+228
-4
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@
5050
import com.jme3.util.blockparser.BlockLanguageParser;
5151
import com.jme3.util.blockparser.Statement;
5252
import com.jme3.util.clone.Cloner;
53+
54+
import jme3tools.shader.Preprocessor;
55+
56+
import java.io.ByteArrayInputStream;
57+
import java.io.ByteArrayOutputStream;
5358
import java.io.IOException;
5459
import java.io.InputStream;
5560
import java.util.*;
@@ -798,6 +803,9 @@ public Object load(AssetInfo info) throws IOException {
798803
} else if (key.getExtension().equals("j3md") && key instanceof MaterialKey) {
799804
throw new IOException("Material definitions must be loaded via AssetKey");
800805
}
806+
807+
in = Preprocessor.apply(in);
808+
801809
loadFromRoot(BlockLanguageParser.parse(in));
802810
} finally {
803811
if (in != null){

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import com.jme3.asset.*;
3535
import com.jme3.asset.cache.AssetCache;
3636

37+
import jme3tools.shader.Preprocessor;
38+
3739
import java.io.*;
3840
import java.util.*;
3941

@@ -85,8 +87,9 @@ private ShaderDependencyNode loadNode(Reader reader, String nodeName) {
8587
}
8688

8789
while ((ln = bufferedReader.readLine()) != null) {
88-
if (ln.trim().startsWith("#import ")) {
89-
ln = ln.trim().substring(8).trim();
90+
String tln = ln.trim();
91+
if (tln.startsWith("#import ")) {
92+
ln = tln.substring(8).trim();
9093
if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3) {
9194
// import user code
9295
// remove quotes to get filename
@@ -105,7 +108,7 @@ private ShaderDependencyNode loadNode(Reader reader, String nodeName) {
105108

106109
node.addDependency(sb.length(), dependNode);
107110
}
108-
} else if (ln.trim().startsWith("#extension ")) {
111+
} else if (tln.startsWith("#extension ")) {
109112
sbExt.append(ln).append('\n');
110113
} else {
111114
sb.append(ln).append('\n');
@@ -165,7 +168,11 @@ public Object load(AssetInfo info) throws IOException {
165168
// The input stream provided is for the vertex shader,
166169
// to retrieve the fragment shader, use the content manager
167170
this.assetManager = info.getManager();
168-
Reader reader = new InputStreamReader(info.openStream());
171+
172+
InputStream in = info.openStream();
173+
in = Preprocessor.apply(in);
174+
175+
Reader reader = new InputStreamReader(in);
169176
boolean injectDependencies = true;
170177
if (info.getKey() instanceof ShaderAssetKey) {
171178
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: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 ) do something with $i
67+
* #endfor
68+
*/
69+
private static final Pattern FOR_REGEX = Pattern.compile("([^=]+)=\\s*([0-9]+)\\s*\\.\\.\\s*([0-9]+)\\s*\\((.+)\\)");
70+
71+
public static String forMacro(String code) {
72+
StringBuilder expandedCode = new StringBuilder();
73+
StringBuilder currentFor = null;
74+
String forDec = null;
75+
int skip = 0;
76+
String codel[] = code.split("\n");
77+
boolean captured = false;
78+
for (String l : codel) {
79+
if (!captured) {
80+
String ln = l.trim();
81+
if (ln.startsWith("#for")) {
82+
if (skip == 0) {
83+
forDec = ln;
84+
currentFor = new StringBuilder();
85+
skip++;
86+
continue;
87+
}
88+
skip++;
89+
} else if (ln.startsWith("#endfor")) {
90+
skip--;
91+
if (skip == 0) {
92+
forDec = forDec.substring("#for ".length()).trim();
93+
94+
Matcher matcher = FOR_REGEX.matcher(forDec);
95+
System.out.println(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+
103+
String inCode = currentFor.toString();
104+
currentFor = null;
105+
106+
for (int i = start; i < end; i++) {
107+
expandedCode.append("\n").append(inj.replace("$0", "\n" + inCode ).replace(varN, "" + i)).append("\n");
108+
}
109+
captured = true;
110+
continue;
111+
}
112+
}
113+
}
114+
}
115+
if (currentFor != null) currentFor.append(l).append("\n");
116+
else expandedCode.append(l).append("\n");
117+
}
118+
code = expandedCode.toString();
119+
if (captured) code = forMacro(code);
120+
return code;
121+
}
122+
123+
}

0 commit comments

Comments
 (0)